@flanksource/clicky-ui 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/dist/components/Combobox.cjs +291 -0
  2. package/dist/components/Combobox.cjs.map +1 -0
  3. package/dist/components/Combobox.d.ts +59 -0
  4. package/dist/components/Combobox.d.ts.map +1 -0
  5. package/dist/components/Combobox.js +291 -0
  6. package/dist/components/Combobox.js.map +1 -0
  7. package/dist/components/FilterBar.cjs +153 -124
  8. package/dist/components/FilterBar.cjs.map +1 -1
  9. package/dist/components/FilterBar.d.ts +25 -1
  10. package/dist/components/FilterBar.d.ts.map +1 -1
  11. package/dist/components/FilterBar.js +153 -124
  12. package/dist/components/FilterBar.js.map +1 -1
  13. package/dist/components/FormatOptionsDropdown.cjs +62 -0
  14. package/dist/components/FormatOptionsDropdown.cjs.map +1 -0
  15. package/dist/components/FormatOptionsDropdown.d.ts +37 -0
  16. package/dist/components/FormatOptionsDropdown.d.ts.map +1 -0
  17. package/dist/components/FormatOptionsDropdown.js +62 -0
  18. package/dist/components/FormatOptionsDropdown.js.map +1 -0
  19. package/dist/components/SplitButton.cjs +87 -0
  20. package/dist/components/SplitButton.cjs.map +1 -0
  21. package/dist/components/SplitButton.d.ts +32 -0
  22. package/dist/components/SplitButton.d.ts.map +1 -0
  23. package/dist/components/SplitButton.js +87 -0
  24. package/dist/components/SplitButton.js.map +1 -0
  25. package/dist/components/icon-menu-picker.cjs +14 -3
  26. package/dist/components/icon-menu-picker.cjs.map +1 -1
  27. package/dist/components/icon-menu-picker.d.ts.map +1 -1
  28. package/dist/components/icon-menu-picker.js +15 -4
  29. package/dist/components/icon-menu-picker.js.map +1 -1
  30. package/dist/components.cjs +7 -0
  31. package/dist/components.cjs.map +1 -1
  32. package/dist/components.d.ts +3 -0
  33. package/dist/components.d.ts.map +1 -1
  34. package/dist/components.js +7 -0
  35. package/dist/components.js.map +1 -1
  36. package/dist/data/Badge.d.ts +1 -1
  37. package/dist/data/ProgressBar.cjs +4 -2
  38. package/dist/data/ProgressBar.cjs.map +1 -1
  39. package/dist/data/ProgressBar.d.ts +1 -1
  40. package/dist/data/ProgressBar.d.ts.map +1 -1
  41. package/dist/data/ProgressBar.js +4 -2
  42. package/dist/data/ProgressBar.js.map +1 -1
  43. package/dist/data/TaskManager.cjs +162 -0
  44. package/dist/data/TaskManager.cjs.map +1 -0
  45. package/dist/data/TaskManager.d.ts +21 -0
  46. package/dist/data/TaskManager.d.ts.map +1 -0
  47. package/dist/data/TaskManager.js +162 -0
  48. package/dist/data/TaskManager.js.map +1 -0
  49. package/dist/data/TaskProgress.cjs +125 -0
  50. package/dist/data/TaskProgress.cjs.map +1 -0
  51. package/dist/data/TaskProgress.d.ts +20 -0
  52. package/dist/data/TaskProgress.d.ts.map +1 -0
  53. package/dist/data/TaskProgress.js +125 -0
  54. package/dist/data/TaskProgress.js.map +1 -0
  55. package/dist/data/TaskSnapshot.cjs +23 -0
  56. package/dist/data/TaskSnapshot.cjs.map +1 -0
  57. package/dist/data/TaskSnapshot.d.ts +49 -0
  58. package/dist/data/TaskSnapshot.d.ts.map +1 -0
  59. package/dist/data/TaskSnapshot.js +23 -0
  60. package/dist/data/TaskSnapshot.js.map +1 -0
  61. package/dist/data/TimeseriesPanel.axes.cjs +39 -0
  62. package/dist/data/TimeseriesPanel.axes.cjs.map +1 -0
  63. package/dist/data/TimeseriesPanel.axes.d.ts +28 -0
  64. package/dist/data/TimeseriesPanel.axes.d.ts.map +1 -0
  65. package/dist/data/TimeseriesPanel.axes.js +39 -0
  66. package/dist/data/TimeseriesPanel.axes.js.map +1 -0
  67. package/dist/data/TimeseriesPanel.cjs +324 -0
  68. package/dist/data/TimeseriesPanel.cjs.map +1 -0
  69. package/dist/data/TimeseriesPanel.d.ts +141 -0
  70. package/dist/data/TimeseriesPanel.d.ts.map +1 -0
  71. package/dist/data/TimeseriesPanel.js +324 -0
  72. package/dist/data/TimeseriesPanel.js.map +1 -0
  73. package/dist/data/Tree.cjs.map +1 -1
  74. package/dist/data/Tree.d.ts.map +1 -1
  75. package/dist/data/Tree.js.map +1 -1
  76. package/dist/data/TreeNode.cjs +44 -7
  77. package/dist/data/TreeNode.cjs.map +1 -1
  78. package/dist/data/TreeNode.d.ts +19 -1
  79. package/dist/data/TreeNode.d.ts.map +1 -1
  80. package/dist/data/TreeNode.js +44 -7
  81. package/dist/data/TreeNode.js.map +1 -1
  82. package/dist/data/diagnostics/DiagnosticsDetailPanel.cjs +66 -44
  83. package/dist/data/diagnostics/DiagnosticsDetailPanel.cjs.map +1 -1
  84. package/dist/data/diagnostics/DiagnosticsDetailPanel.d.ts +21 -1
  85. package/dist/data/diagnostics/DiagnosticsDetailPanel.d.ts.map +1 -1
  86. package/dist/data/diagnostics/DiagnosticsDetailPanel.js +65 -43
  87. package/dist/data/diagnostics/DiagnosticsDetailPanel.js.map +1 -1
  88. package/dist/data/diagnostics/DiagnosticsTree.cjs +2 -1
  89. package/dist/data/diagnostics/DiagnosticsTree.cjs.map +1 -1
  90. package/dist/data/diagnostics/DiagnosticsTree.js +2 -1
  91. package/dist/data/diagnostics/DiagnosticsTree.js.map +1 -1
  92. package/dist/data/diagnostics/FrameSourceWindow.cjs +37 -0
  93. package/dist/data/diagnostics/FrameSourceWindow.cjs.map +1 -0
  94. package/dist/data/diagnostics/FrameSourceWindow.d.ts +15 -0
  95. package/dist/data/diagnostics/FrameSourceWindow.d.ts.map +1 -0
  96. package/dist/data/diagnostics/FrameSourceWindow.js +37 -0
  97. package/dist/data/diagnostics/FrameSourceWindow.js.map +1 -0
  98. package/dist/data/diagnostics/JvmStackTrace.cjs +25 -12
  99. package/dist/data/diagnostics/JvmStackTrace.cjs.map +1 -1
  100. package/dist/data/diagnostics/JvmStackTrace.d.ts +8 -1
  101. package/dist/data/diagnostics/JvmStackTrace.d.ts.map +1 -1
  102. package/dist/data/diagnostics/JvmStackTrace.js +26 -13
  103. package/dist/data/diagnostics/JvmStackTrace.js.map +1 -1
  104. package/dist/data/diagnostics/ThreadCard.cjs +9 -2
  105. package/dist/data/diagnostics/ThreadCard.cjs.map +1 -1
  106. package/dist/data/diagnostics/ThreadCard.d.ts +3 -1
  107. package/dist/data/diagnostics/ThreadCard.d.ts.map +1 -1
  108. package/dist/data/diagnostics/ThreadCard.js +9 -2
  109. package/dist/data/diagnostics/ThreadCard.js.map +1 -1
  110. package/dist/data/diagnostics/jvm-stacktrace.cjs +10 -1
  111. package/dist/data/diagnostics/jvm-stacktrace.cjs.map +1 -1
  112. package/dist/data/diagnostics/jvm-stacktrace.d.ts.map +1 -1
  113. package/dist/data/diagnostics/jvm-stacktrace.js +10 -1
  114. package/dist/data/diagnostics/jvm-stacktrace.js.map +1 -1
  115. package/dist/data/diagnostics/utils.cjs +0 -12
  116. package/dist/data/diagnostics/utils.cjs.map +1 -1
  117. package/dist/data/diagnostics/utils.d.ts +1 -1
  118. package/dist/data/diagnostics/utils.d.ts.map +1 -1
  119. package/dist/data/diagnostics/utils.js +0 -12
  120. package/dist/data/diagnostics/utils.js.map +1 -1
  121. package/dist/data/task-status.cjs +108 -0
  122. package/dist/data/task-status.cjs.map +1 -0
  123. package/dist/data/task-status.d.ts +21 -0
  124. package/dist/data/task-status.d.ts.map +1 -0
  125. package/dist/data/task-status.js +108 -0
  126. package/dist/data/task-status.js.map +1 -0
  127. package/dist/data.cjs +21 -0
  128. package/dist/data.cjs.map +1 -1
  129. package/dist/data.d.ts +7 -1
  130. package/dist/data.d.ts.map +1 -1
  131. package/dist/data.js +21 -0
  132. package/dist/data.js.map +1 -1
  133. package/dist/hooks/use-task-run.cjs +137 -0
  134. package/dist/hooks/use-task-run.cjs.map +1 -0
  135. package/dist/hooks/use-task-run.d.ts +35 -0
  136. package/dist/hooks/use-task-run.d.ts.map +1 -0
  137. package/dist/hooks/use-task-run.js +137 -0
  138. package/dist/hooks/use-task-run.js.map +1 -0
  139. package/dist/hooks.cjs +3 -0
  140. package/dist/hooks.cjs.map +1 -1
  141. package/dist/hooks.d.ts +1 -0
  142. package/dist/hooks.d.ts.map +1 -1
  143. package/dist/hooks.js +3 -0
  144. package/dist/hooks.js.map +1 -1
  145. package/dist/index.cjs +37 -0
  146. package/dist/index.cjs.map +1 -1
  147. package/dist/index.js +37 -0
  148. package/dist/index.js.map +1 -1
  149. package/dist/lib/color.cjs +33 -0
  150. package/dist/lib/color.cjs.map +1 -0
  151. package/dist/lib/color.d.ts +10 -0
  152. package/dist/lib/color.d.ts.map +1 -0
  153. package/dist/lib/color.js +33 -0
  154. package/dist/lib/color.js.map +1 -0
  155. package/dist/lib/format.cjs +77 -0
  156. package/dist/lib/format.cjs.map +1 -0
  157. package/dist/lib/format.d.ts +30 -0
  158. package/dist/lib/format.d.ts.map +1 -0
  159. package/dist/lib/format.js +77 -0
  160. package/dist/lib/format.js.map +1 -0
  161. package/dist/overlay/DropdownMenu.cjs +18 -2
  162. package/dist/overlay/DropdownMenu.cjs.map +1 -1
  163. package/dist/overlay/DropdownMenu.d.ts +3 -1
  164. package/dist/overlay/DropdownMenu.d.ts.map +1 -1
  165. package/dist/overlay/DropdownMenu.js +18 -2
  166. package/dist/overlay/DropdownMenu.js.map +1 -1
  167. package/dist/rpc/types.cjs.map +1 -1
  168. package/dist/rpc/types.d.ts +4 -0
  169. package/dist/rpc/types.d.ts.map +1 -1
  170. package/dist/rpc/types.js.map +1 -1
  171. package/dist/styles.css +1 -1
  172. package/dist/utils.cjs +6 -0
  173. package/dist/utils.cjs.map +1 -1
  174. package/dist/utils.d.ts +1 -0
  175. package/dist/utils.d.ts.map +1 -1
  176. package/dist/utils.js +6 -0
  177. package/dist/utils.js.map +1 -1
  178. package/package.json +4 -1
package/dist/index.js CHANGED
@@ -1,11 +1,15 @@
1
1
  import { cn } from "./lib/utils.js";
2
2
  import { AVATAR_PALETTE, fnv1a32, paletteClass } from "./lib/palette.js";
3
3
  import { SIZE_TOKENS, resolveSize } from "./lib/size.js";
4
+ import { formatBytes, formatBytesPerSecond, formatDuration, formatShort, formatUnit } from "./lib/format.js";
4
5
  import { ThemeProvider, useOptionalTheme, useResolvedTheme, useTheme } from "./hooks/use-theme.js";
5
6
  import { DensityProvider, useDensity, useDensityValue } from "./hooks/use-density.js";
6
7
  import { useSort } from "./hooks/use-sort.js";
7
8
  import { useHistoryRoute } from "./hooks/use-history-route.js";
9
+ import { useTaskRun, useTaskRuns } from "./hooks/use-task-run.js";
8
10
  import { Button, buttonVariants } from "./components/button.js";
11
+ import { SplitButton } from "./components/SplitButton.js";
12
+ import { CLICKY_FORMAT_OPTIONS, FormatOptionsDropdown } from "./components/FormatOptionsDropdown.js";
9
13
  import { DateField } from "./components/DateField.js";
10
14
  import { DatePicker } from "./components/DatePicker.js";
11
15
  import { DateTimePicker } from "./components/DateTimePicker.js";
@@ -14,6 +18,7 @@ import { RangeSlider } from "./components/RangeSlider.js";
14
18
  import { FilterBar } from "./components/FilterBar.js";
15
19
  import { MultiSelect } from "./components/MultiSelect.js";
16
20
  import { Select } from "./components/select.js";
21
+ import { Combobox } from "./components/Combobox.js";
17
22
  import { ThemeSwitcher } from "./components/theme-switcher.js";
18
23
  import { DensitySwitcher } from "./components/density-switcher.js";
19
24
  import { IconMenuPicker } from "./components/icon-menu-picker.js";
@@ -48,15 +53,21 @@ import { ProgressBar } from "./data/ProgressBar.js";
48
53
  import { SignedDeltaBar } from "./data/SignedDeltaBar.js";
49
54
  import { SortableHeader } from "./data/SortableHeader.js";
50
55
  import { TabButton } from "./data/TabButton.js";
56
+ import { TimeseriesPanel, resolveBreakdown, resolveSeries } from "./data/TimeseriesPanel.js";
51
57
  import { Tree } from "./data/Tree.js";
52
58
  import { TreeNode } from "./data/TreeNode.js";
53
59
  import { TreeGroupHeader } from "./data/TreeGroupHeader.js";
60
+ import { allGroupsTerminal, isTerminalStatus } from "./data/TaskSnapshot.js";
61
+ import { bucketTasks, logLevelColor, taskSegments, taskStatusBg, taskStatusColor, taskStatusIcon } from "./data/task-status.js";
62
+ import { TaskProgress } from "./data/TaskProgress.js";
63
+ import { TaskManager } from "./data/TaskManager.js";
54
64
  import { DiagnosticsTree } from "./data/diagnostics/DiagnosticsTree.js";
55
65
  import { DiagnosticsDetailPanel } from "./data/diagnostics/DiagnosticsDetailPanel.js";
56
66
  import { countProcesses, findProcessByPID, processLabel, processStateColor, processStateIcon } from "./data/diagnostics/utils.js";
57
67
  import { countGoroutinesByState, countStackByState, detectDumpFormat, parseGoroutineDump, parseStackDump } from "./data/diagnostics/stacktrace.js";
58
68
  import { countThreadsByState, parseJvmThreadDump } from "./data/diagnostics/jvm-stacktrace.js";
59
69
  import { JvmStackFrameRow, JvmStackTrace } from "./data/diagnostics/JvmStackTrace.js";
70
+ import { FrameSourceWindow, frameHasSource } from "./data/diagnostics/FrameSourceWindow.js";
60
71
  import { StackTrace } from "./data/diagnostics/RenderedStackTrace.js";
61
72
  import { parseJavaStackTrace } from "./data/diagnostics/stacktrace-parse.js";
62
73
  import { CopyBadge, ErrorDetails, PrettyStackTrace } from "./data/diagnostics/ErrorDetails.js";
@@ -346,10 +357,12 @@ export {
346
357
  AvatarBadge,
347
358
  Badge,
348
359
  Button,
360
+ CLICKY_FORMAT_OPTIONS,
349
361
  Clicky,
350
362
  ClickyNodeView,
351
363
  ClickyTable,
352
364
  CodeBlock,
365
+ Combobox,
353
366
  CommandForm,
354
367
  CommandOutput,
355
368
  CopyBadge,
@@ -371,6 +384,8 @@ export {
371
384
  FilterPill,
372
385
  FilterPillGroup,
373
386
  FilterSeparator,
387
+ FormatOptionsDropdown,
388
+ FrameSourceWindow,
374
389
  Gauge,
375
390
  HarPanel,
376
391
  HoverCard,
@@ -402,15 +417,19 @@ export {
402
417
  Select,
403
418
  SignedDeltaBar,
404
419
  SortableHeader,
420
+ SplitButton,
405
421
  SplitPane,
406
422
  StackTrace,
407
423
  StatusDot,
408
424
  TabButton,
409
425
  TagActionsProvider,
410
426
  TagList,
427
+ TaskManager,
428
+ TaskProgress,
411
429
  ThemeProvider,
412
430
  ThemeSwitcher,
413
431
  TimeRange,
432
+ TimeseriesPanel,
414
433
  Timestamp,
415
434
  Tree,
416
435
  TreeGroupHeader,
@@ -905,7 +924,9 @@ export {
905
924
  UiZoomOut,
906
925
  UiZoomOutFilled,
907
926
  VIEW_OPTIONS,
927
+ allGroupsTerminal,
908
928
  badgeVariants,
929
+ bucketTasks,
909
930
  buttonVariants,
910
931
  changeIconAliases,
911
932
  chooseTimestampFormat,
@@ -921,12 +942,20 @@ export {
921
942
  findListEndpoint,
922
943
  findProcessByPID,
923
944
  fnv1a32,
945
+ formatBytes,
946
+ formatBytesPerSecond,
947
+ formatDuration,
924
948
  formatPropertyLabel,
949
+ formatShort,
925
950
  formatTimestamp,
951
+ formatUnit,
952
+ frameHasSource,
926
953
  getChangeIcon,
927
954
  inferColumns,
928
955
  isApplicationStackFrame,
929
956
  isPositionalParam,
957
+ isTerminalStatus,
958
+ logLevelColor,
930
959
  modeToFormat,
931
960
  normalizeErrorDiagnostics,
932
961
  normalizeLogsTableRows,
@@ -945,11 +974,17 @@ export {
945
974
  processLabel,
946
975
  processStateColor,
947
976
  processStateIcon,
977
+ resolveBreakdown,
978
+ resolveSeries,
948
979
  resolveSize,
949
980
  setFallbackIconProvider,
950
981
  splitTagToken,
951
982
  tagActionsFromRecord,
952
983
  tagFilterTokens,
984
+ taskSegments,
985
+ taskStatusBg,
986
+ taskStatusColor,
987
+ taskStatusIcon,
953
988
  useDensity,
954
989
  useDensityValue,
955
990
  useHistoryRoute,
@@ -961,6 +996,8 @@ export {
961
996
  useSort,
962
997
  useTagActions,
963
998
  useTagActionsValue,
999
+ useTaskRun,
1000
+ useTaskRuns,
964
1001
  useTheme
965
1002
  };
966
1003
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const TAILWIND_COLOR_CLASS = /^(?:bg|text|border|fill|stroke)-/;
4
+ function isTailwindColorClass(color) {
5
+ return TAILWIND_COLOR_CLASS.test(color.trim());
6
+ }
7
+ const computedCache = /* @__PURE__ */ new Map();
8
+ function computeClassColor(cssClass) {
9
+ if (typeof document === "undefined") return void 0;
10
+ const probe = document.createElement("span");
11
+ probe.className = cssClass;
12
+ probe.style.position = "absolute";
13
+ probe.style.opacity = "0";
14
+ probe.style.pointerEvents = "none";
15
+ document.body.appendChild(probe);
16
+ const style = getComputedStyle(probe);
17
+ const usesText = /^(?:text|stroke|fill)-/.test(cssClass.trim());
18
+ const value = usesText ? style.color : style.backgroundColor;
19
+ document.body.removeChild(probe);
20
+ if (!value || value === "rgba(0, 0, 0, 0)" || value === "transparent") return void 0;
21
+ return value;
22
+ }
23
+ function resolveCssColor(color) {
24
+ if (!isTailwindColorClass(color)) return color;
25
+ const cached = computedCache.get(color);
26
+ if (cached !== void 0) return cached;
27
+ const computed = computeClassColor(color) ?? color;
28
+ computedCache.set(color, computed);
29
+ return computed;
30
+ }
31
+ exports.isTailwindColorClass = isTailwindColorClass;
32
+ exports.resolveCssColor = resolveCssColor;
33
+ //# sourceMappingURL=color.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.cjs","sources":["../../src/lib/color.ts"],"sourcesContent":["// Resolves a series/segment \"color\" that may be expressed either as a CSS color\n// value (e.g. \"#10b981\", \"rgb(16 185 129)\", \"var(--chart-3)\") or as a Tailwind\n// utility class (e.g. \"bg-emerald-500\", \"text-rose-500\"). Consumers that need a\n// real CSS value — recharts stroke/fill, inline backgroundColor — call\n// resolveCssColor; the class→value resolution is done once per class via a probe\n// element and cached.\n\nconst TAILWIND_COLOR_CLASS = /^(?:bg|text|border|fill|stroke)-/;\n\n/** True when `color` looks like a Tailwind color utility class rather than a CSS value. */\nexport function isTailwindColorClass(color: string): boolean {\n return TAILWIND_COLOR_CLASS.test(color.trim());\n}\n\nconst computedCache = new Map<string, string>();\n\n/**\n * Reads the rendered color of a Tailwind class by mounting an off-screen probe\n * and reading getComputedStyle. `text-`/`stroke-`/`fill-` classes resolve from\n * `color`; everything else (incl. `bg-`/`border-`) from `backgroundColor`.\n * Returns undefined when no document is available (SSR) or the property is\n * empty/transparent (so callers can fall back). No inline color is set on the\n * probe — an inline value outranks the class and would mask the class's color.\n */\nfunction computeClassColor(cssClass: string): string | undefined {\n if (typeof document === \"undefined\") return undefined;\n const probe = document.createElement(\"span\");\n probe.className = cssClass;\n probe.style.position = \"absolute\";\n probe.style.opacity = \"0\";\n probe.style.pointerEvents = \"none\";\n document.body.appendChild(probe);\n const style = getComputedStyle(probe);\n const usesText = /^(?:text|stroke|fill)-/.test(cssClass.trim());\n const value = usesText ? style.color : style.backgroundColor;\n document.body.removeChild(probe);\n if (!value || value === \"rgba(0, 0, 0, 0)\" || value === \"transparent\") return undefined;\n return value;\n}\n\n/**\n * Returns a usable CSS color value for `color`. CSS values pass through\n * unchanged; Tailwind color classes are resolved to their computed value\n * (memoized). Falls back to the original string when resolution is impossible\n * (SSR, unknown class) so callers still get something to render.\n */\nexport function resolveCssColor(color: string): string {\n if (!isTailwindColorClass(color)) return color;\n const cached = computedCache.get(color);\n if (cached !== undefined) return cached;\n const computed = computeClassColor(color) ?? color;\n computedCache.set(color, computed);\n return computed;\n}\n"],"names":[],"mappings":";;AAOA,MAAM,uBAAuB;AAGtB,SAAS,qBAAqB,OAAwB;AAC3D,SAAO,qBAAqB,KAAK,MAAM,KAAA,CAAM;AAC/C;AAEA,MAAM,oCAAoB,IAAA;AAU1B,SAAS,kBAAkB,UAAsC;AAC/D,MAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,QAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,QAAM,YAAY;AAClB,QAAM,MAAM,WAAW;AACvB,QAAM,MAAM,UAAU;AACtB,QAAM,MAAM,gBAAgB;AAC5B,WAAS,KAAK,YAAY,KAAK;AAC/B,QAAM,QAAQ,iBAAiB,KAAK;AACpC,QAAM,WAAW,yBAAyB,KAAK,SAAS,MAAM;AAC9D,QAAM,QAAQ,WAAW,MAAM,QAAQ,MAAM;AAC7C,WAAS,KAAK,YAAY,KAAK;AAC/B,MAAI,CAAC,SAAS,UAAU,sBAAsB,UAAU,cAAe,QAAO;AAC9E,SAAO;AACT;AAQO,SAAS,gBAAgB,OAAuB;AACrD,MAAI,CAAC,qBAAqB,KAAK,EAAG,QAAO;AACzC,QAAM,SAAS,cAAc,IAAI,KAAK;AACtC,MAAI,WAAW,OAAW,QAAO;AACjC,QAAM,WAAW,kBAAkB,KAAK,KAAK;AAC7C,gBAAc,IAAI,OAAO,QAAQ;AACjC,SAAO;AACT;;;"}
@@ -0,0 +1,10 @@
1
+ /** True when `color` looks like a Tailwind color utility class rather than a CSS value. */
2
+ export declare function isTailwindColorClass(color: string): boolean;
3
+ /**
4
+ * Returns a usable CSS color value for `color`. CSS values pass through
5
+ * unchanged; Tailwind color classes are resolved to their computed value
6
+ * (memoized). Falls back to the original string when resolution is impossible
7
+ * (SSR, unknown class) so callers still get something to render.
8
+ */
9
+ export declare function resolveCssColor(color: string): string;
10
+ //# sourceMappingURL=color.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../src/lib/color.ts"],"names":[],"mappings":"AASA,2FAA2F;AAC3F,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE3D;AA4BD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOrD"}
@@ -0,0 +1,33 @@
1
+ const TAILWIND_COLOR_CLASS = /^(?:bg|text|border|fill|stroke)-/;
2
+ function isTailwindColorClass(color) {
3
+ return TAILWIND_COLOR_CLASS.test(color.trim());
4
+ }
5
+ const computedCache = /* @__PURE__ */ new Map();
6
+ function computeClassColor(cssClass) {
7
+ if (typeof document === "undefined") return void 0;
8
+ const probe = document.createElement("span");
9
+ probe.className = cssClass;
10
+ probe.style.position = "absolute";
11
+ probe.style.opacity = "0";
12
+ probe.style.pointerEvents = "none";
13
+ document.body.appendChild(probe);
14
+ const style = getComputedStyle(probe);
15
+ const usesText = /^(?:text|stroke|fill)-/.test(cssClass.trim());
16
+ const value = usesText ? style.color : style.backgroundColor;
17
+ document.body.removeChild(probe);
18
+ if (!value || value === "rgba(0, 0, 0, 0)" || value === "transparent") return void 0;
19
+ return value;
20
+ }
21
+ function resolveCssColor(color) {
22
+ if (!isTailwindColorClass(color)) return color;
23
+ const cached = computedCache.get(color);
24
+ if (cached !== void 0) return cached;
25
+ const computed = computeClassColor(color) ?? color;
26
+ computedCache.set(color, computed);
27
+ return computed;
28
+ }
29
+ export {
30
+ isTailwindColorClass,
31
+ resolveCssColor
32
+ };
33
+ //# sourceMappingURL=color.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.js","sources":["../../src/lib/color.ts"],"sourcesContent":["// Resolves a series/segment \"color\" that may be expressed either as a CSS color\n// value (e.g. \"#10b981\", \"rgb(16 185 129)\", \"var(--chart-3)\") or as a Tailwind\n// utility class (e.g. \"bg-emerald-500\", \"text-rose-500\"). Consumers that need a\n// real CSS value — recharts stroke/fill, inline backgroundColor — call\n// resolveCssColor; the class→value resolution is done once per class via a probe\n// element and cached.\n\nconst TAILWIND_COLOR_CLASS = /^(?:bg|text|border|fill|stroke)-/;\n\n/** True when `color` looks like a Tailwind color utility class rather than a CSS value. */\nexport function isTailwindColorClass(color: string): boolean {\n return TAILWIND_COLOR_CLASS.test(color.trim());\n}\n\nconst computedCache = new Map<string, string>();\n\n/**\n * Reads the rendered color of a Tailwind class by mounting an off-screen probe\n * and reading getComputedStyle. `text-`/`stroke-`/`fill-` classes resolve from\n * `color`; everything else (incl. `bg-`/`border-`) from `backgroundColor`.\n * Returns undefined when no document is available (SSR) or the property is\n * empty/transparent (so callers can fall back). No inline color is set on the\n * probe — an inline value outranks the class and would mask the class's color.\n */\nfunction computeClassColor(cssClass: string): string | undefined {\n if (typeof document === \"undefined\") return undefined;\n const probe = document.createElement(\"span\");\n probe.className = cssClass;\n probe.style.position = \"absolute\";\n probe.style.opacity = \"0\";\n probe.style.pointerEvents = \"none\";\n document.body.appendChild(probe);\n const style = getComputedStyle(probe);\n const usesText = /^(?:text|stroke|fill)-/.test(cssClass.trim());\n const value = usesText ? style.color : style.backgroundColor;\n document.body.removeChild(probe);\n if (!value || value === \"rgba(0, 0, 0, 0)\" || value === \"transparent\") return undefined;\n return value;\n}\n\n/**\n * Returns a usable CSS color value for `color`. CSS values pass through\n * unchanged; Tailwind color classes are resolved to their computed value\n * (memoized). Falls back to the original string when resolution is impossible\n * (SSR, unknown class) so callers still get something to render.\n */\nexport function resolveCssColor(color: string): string {\n if (!isTailwindColorClass(color)) return color;\n const cached = computedCache.get(color);\n if (cached !== undefined) return cached;\n const computed = computeClassColor(color) ?? color;\n computedCache.set(color, computed);\n return computed;\n}\n"],"names":[],"mappings":"AAOA,MAAM,uBAAuB;AAGtB,SAAS,qBAAqB,OAAwB;AAC3D,SAAO,qBAAqB,KAAK,MAAM,KAAA,CAAM;AAC/C;AAEA,MAAM,oCAAoB,IAAA;AAU1B,SAAS,kBAAkB,UAAsC;AAC/D,MAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,QAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,QAAM,YAAY;AAClB,QAAM,MAAM,WAAW;AACvB,QAAM,MAAM,UAAU;AACtB,QAAM,MAAM,gBAAgB;AAC5B,WAAS,KAAK,YAAY,KAAK;AAC/B,QAAM,QAAQ,iBAAiB,KAAK;AACpC,QAAM,WAAW,yBAAyB,KAAK,SAAS,MAAM;AAC9D,QAAM,QAAQ,WAAW,MAAM,QAAQ,MAAM;AAC7C,WAAS,KAAK,YAAY,KAAK;AAC/B,MAAI,CAAC,SAAS,UAAU,sBAAsB,UAAU,cAAe,QAAO;AAC9E,SAAO;AACT;AAQO,SAAS,gBAAgB,OAAuB;AACrD,MAAI,CAAC,qBAAqB,KAAK,EAAG,QAAO;AACzC,QAAM,SAAS,cAAc,IAAI,KAAK;AACtC,MAAI,WAAW,OAAW,QAAO;AACjC,QAAM,WAAW,kBAAkB,KAAK,KAAK;AAC7C,gBAAc,IAAI,OAAO,QAAQ;AACjC,SAAO;AACT;"}
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const LOCALE = "en-US";
4
+ function intl(value, maxFractionDigits = 1) {
5
+ return new Intl.NumberFormat(LOCALE, { maximumFractionDigits: maxFractionDigits }).format(
6
+ Number.isFinite(value) ? value : 0
7
+ );
8
+ }
9
+ function trimZero(s) {
10
+ return s.endsWith(".0") ? s.slice(0, -2) : s;
11
+ }
12
+ const BYTE_UNITS = ["B", "KB", "MB", "GB", "TB", "PB"];
13
+ function formatBytes(bytes, opts) {
14
+ const suffix = (opts == null ? void 0 : opts.suffix) ?? "";
15
+ if (bytes === void 0 || !Number.isFinite(bytes) || bytes <= 0) return `0 B${suffix}`;
16
+ const base = (opts == null ? void 0 : opts.base) ?? 1024;
17
+ let size = bytes;
18
+ let unit = 0;
19
+ while (size >= base && unit < BYTE_UNITS.length - 1) {
20
+ size /= base;
21
+ unit++;
22
+ }
23
+ const rendered = size >= 10 || unit === 0 ? size.toFixed(0) : trimZero(size.toFixed(1));
24
+ return `${rendered} ${BYTE_UNITS[unit]}${suffix}`;
25
+ }
26
+ function formatBytesPerSecond(bytes) {
27
+ return formatBytes(bytes, { suffix: "/s" });
28
+ }
29
+ const SHORT_UNITS = ["", "K", "M", "B", "T"];
30
+ function formatShort(value) {
31
+ if (value === void 0 || !Number.isFinite(value)) return "0";
32
+ const sign = value < 0 ? "-" : "";
33
+ let n = Math.abs(value);
34
+ if (n < 1e3) return `${sign}${intl(n)}`;
35
+ let unit = 0;
36
+ while (n >= 1e3 && unit < SHORT_UNITS.length - 1) {
37
+ n /= 1e3;
38
+ unit++;
39
+ }
40
+ const rendered = n >= 10 ? n.toFixed(0) : trimZero(n.toFixed(1));
41
+ return `${sign}${rendered}${SHORT_UNITS[unit]}`;
42
+ }
43
+ function formatDuration(value, opts) {
44
+ if (value === void 0 || !Number.isFinite(value)) return "0 ms";
45
+ const ms = (opts == null ? void 0 : opts.from) === "s" ? value * 1e3 : value;
46
+ const abs = Math.abs(ms);
47
+ if (abs < 1e3) return `${intl(ms, 0)} ms`;
48
+ if (abs < 6e4) return `${intl(ms / 1e3)} s`;
49
+ if (abs < 36e5) return `${intl(ms / 6e4)} min`;
50
+ return `${intl(ms / 36e5)} h`;
51
+ }
52
+ const UNIT_FORMATTERS = {
53
+ none: (v) => formatShort(v),
54
+ short: (v) => formatShort(v),
55
+ percent: (v) => `${intl(v)}%`,
56
+ percentunit: (v) => `${intl(v * 100)}%`,
57
+ "%": (v) => `${intl(v)}%`,
58
+ bytes: (v) => formatBytes(v, { base: 1024 }),
59
+ decbytes: (v) => formatBytes(v, { base: 1e3 }),
60
+ Bps: (v) => formatBytesPerSecond(v),
61
+ binBps: (v) => formatBytesPerSecond(v),
62
+ ms: (v) => formatDuration(v, { from: "ms" }),
63
+ s: (v) => formatDuration(v, { from: "s" })
64
+ };
65
+ function formatUnit(value, unit) {
66
+ const v = Number.isFinite(value) ? value : 0;
67
+ if (!unit) return intl(v);
68
+ const fmt = UNIT_FORMATTERS[unit];
69
+ if (fmt) return fmt(v);
70
+ return `${intl(v)}${unit}`;
71
+ }
72
+ exports.formatBytes = formatBytes;
73
+ exports.formatBytesPerSecond = formatBytesPerSecond;
74
+ exports.formatDuration = formatDuration;
75
+ exports.formatShort = formatShort;
76
+ exports.formatUnit = formatUnit;
77
+ //# sourceMappingURL=format.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.cjs","sources":["../../src/lib/format.ts"],"sourcesContent":["// Value formatters and a Grafana-style unit registry, shared by TimeseriesPanel\n// (and any other metric display). Pure — no React — so it is trivially testable.\n//\n// `formatUnit(value, unit)` dispatches on a Grafana unit identifier (e.g.\n// \"bytes\", \"Bps\", \"percent\", \"ms\", \"short\"). Unknown unit strings are treated as\n// a raw suffix appended to the formatted number, preserving callers that pass a\n// literal like \"%\" or \"req\".\n\nconst LOCALE = \"en-US\";\n\nfunction intl(value: number, maxFractionDigits = 1): string {\n return new Intl.NumberFormat(LOCALE, { maximumFractionDigits: maxFractionDigits }).format(\n Number.isFinite(value) ? value : 0,\n );\n}\n\n/** Drops a trailing \".0\" so whole values read \"1 KB\" rather than \"1.0 KB\". */\nfunction trimZero(s: string): string {\n return s.endsWith(\".0\") ? s.slice(0, -2) : s;\n}\n\nexport interface FormatBytesOptions {\n /** 1024 (IEC, the default) or 1000 (SI/decimal). */\n base?: 1000 | 1024;\n /** Appended after the unit label, e.g. \"/s\" for a rate. */\n suffix?: string;\n}\n\nconst BYTE_UNITS = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\"] as const;\n\n/**\n * Human-readable byte size. 1024-based by default with B/KB/MB/GB/TB/PB labels,\n * matching the prior diagnostics formatter. Whole numbers and values >= 10 drop\n * the fraction; smaller fractional values keep one decimal.\n */\nexport function formatBytes(bytes?: number, opts?: FormatBytesOptions): string {\n const suffix = opts?.suffix ?? \"\";\n if (bytes === undefined || !Number.isFinite(bytes) || bytes <= 0) return `0 B${suffix}`;\n const base = opts?.base ?? 1024;\n let size = bytes;\n let unit = 0;\n while (size >= base && unit < BYTE_UNITS.length - 1) {\n size /= base;\n unit++;\n }\n const rendered = size >= 10 || unit === 0 ? size.toFixed(0) : trimZero(size.toFixed(1));\n return `${rendered} ${BYTE_UNITS[unit]}${suffix}`;\n}\n\n/** Byte rate, e.g. \"1.5 MB/s\". 1024-based. */\nexport function formatBytesPerSecond(bytes?: number): string {\n return formatBytes(bytes, { suffix: \"/s\" });\n}\n\nconst SHORT_UNITS = [\"\", \"K\", \"M\", \"B\", \"T\"] as const;\n\n/** SI abbreviation: 999 -> \"999\", 1200 -> \"1.2K\", 3_400_000 -> \"3.4M\". */\nexport function formatShort(value?: number): string {\n if (value === undefined || !Number.isFinite(value)) return \"0\";\n const sign = value < 0 ? \"-\" : \"\";\n let n = Math.abs(value);\n if (n < 1000) return `${sign}${intl(n)}`;\n let unit = 0;\n while (n >= 1000 && unit < SHORT_UNITS.length - 1) {\n n /= 1000;\n unit++;\n }\n const rendered = n >= 10 ? n.toFixed(0) : trimZero(n.toFixed(1));\n return `${sign}${rendered}${SHORT_UNITS[unit]}`;\n}\n\n/**\n * Duration rendered from a millisecond (default) or second input. Picks ms / s /\n * min / h by magnitude: 850 -> \"850 ms\", 1500 -> \"1.5 s\", 90_000 -> \"1.5 min\".\n */\nexport function formatDuration(value?: number, opts?: { from?: \"ms\" | \"s\" }): string {\n if (value === undefined || !Number.isFinite(value)) return \"0 ms\";\n const ms = opts?.from === \"s\" ? value * 1000 : value;\n const abs = Math.abs(ms);\n if (abs < 1000) return `${intl(ms, 0)} ms`;\n if (abs < 60_000) return `${intl(ms / 1000)} s`;\n if (abs < 3_600_000) return `${intl(ms / 60_000)} min`;\n return `${intl(ms / 3_600_000)} h`;\n}\n\n// formatters keyed by Grafana unit identifier. Keys not present here fall through\n// to the raw-suffix path in formatUnit.\nconst UNIT_FORMATTERS: Record<string, (v: number) => string> = {\n none: (v) => formatShort(v),\n short: (v) => formatShort(v),\n percent: (v) => `${intl(v)}%`,\n percentunit: (v) => `${intl(v * 100)}%`,\n \"%\": (v) => `${intl(v)}%`,\n bytes: (v) => formatBytes(v, { base: 1024 }),\n decbytes: (v) => formatBytes(v, { base: 1000 }),\n Bps: (v) => formatBytesPerSecond(v),\n binBps: (v) => formatBytesPerSecond(v),\n ms: (v) => formatDuration(v, { from: \"ms\" }),\n s: (v) => formatDuration(v, { from: \"s\" }),\n};\n\n/**\n * Formats a value for display per a Grafana-style unit key. Unknown units are\n * appended as a literal suffix (back-compat for callers passing \"%\", \"req\",\n * etc.); an absent unit yields the plain localized number.\n */\nexport function formatUnit(value: number, unit?: string): string {\n const v = Number.isFinite(value) ? value : 0;\n if (!unit) return intl(v);\n const fmt = UNIT_FORMATTERS[unit];\n if (fmt) return fmt(v);\n return `${intl(v)}${unit}`;\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ export interface FormatBytesOptions {
2
+ /** 1024 (IEC, the default) or 1000 (SI/decimal). */
3
+ base?: 1000 | 1024;
4
+ /** Appended after the unit label, e.g. "/s" for a rate. */
5
+ suffix?: string;
6
+ }
7
+ /**
8
+ * Human-readable byte size. 1024-based by default with B/KB/MB/GB/TB/PB labels,
9
+ * matching the prior diagnostics formatter. Whole numbers and values >= 10 drop
10
+ * the fraction; smaller fractional values keep one decimal.
11
+ */
12
+ export declare function formatBytes(bytes?: number, opts?: FormatBytesOptions): string;
13
+ /** Byte rate, e.g. "1.5 MB/s". 1024-based. */
14
+ export declare function formatBytesPerSecond(bytes?: number): string;
15
+ /** SI abbreviation: 999 -> "999", 1200 -> "1.2K", 3_400_000 -> "3.4M". */
16
+ export declare function formatShort(value?: number): string;
17
+ /**
18
+ * Duration rendered from a millisecond (default) or second input. Picks ms / s /
19
+ * min / h by magnitude: 850 -> "850 ms", 1500 -> "1.5 s", 90_000 -> "1.5 min".
20
+ */
21
+ export declare function formatDuration(value?: number, opts?: {
22
+ from?: "ms" | "s";
23
+ }): string;
24
+ /**
25
+ * Formats a value for display per a Grafana-style unit key. Unknown units are
26
+ * appended as a literal suffix (back-compat for callers passing "%", "req",
27
+ * etc.); an absent unit yields the plain localized number.
28
+ */
29
+ export declare function formatUnit(value: number, unit?: string): string;
30
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/lib/format.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,kBAAkB;IACjC,oDAAoD;IACpD,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAY7E;AAED,8CAA8C;AAC9C,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3D;AAID,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAYlD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,IAAI,GAAG,GAAG,CAAA;CAAE,GAAG,MAAM,CAQnF;AAkBD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAM/D"}
@@ -0,0 +1,77 @@
1
+ const LOCALE = "en-US";
2
+ function intl(value, maxFractionDigits = 1) {
3
+ return new Intl.NumberFormat(LOCALE, { maximumFractionDigits: maxFractionDigits }).format(
4
+ Number.isFinite(value) ? value : 0
5
+ );
6
+ }
7
+ function trimZero(s) {
8
+ return s.endsWith(".0") ? s.slice(0, -2) : s;
9
+ }
10
+ const BYTE_UNITS = ["B", "KB", "MB", "GB", "TB", "PB"];
11
+ function formatBytes(bytes, opts) {
12
+ const suffix = (opts == null ? void 0 : opts.suffix) ?? "";
13
+ if (bytes === void 0 || !Number.isFinite(bytes) || bytes <= 0) return `0 B${suffix}`;
14
+ const base = (opts == null ? void 0 : opts.base) ?? 1024;
15
+ let size = bytes;
16
+ let unit = 0;
17
+ while (size >= base && unit < BYTE_UNITS.length - 1) {
18
+ size /= base;
19
+ unit++;
20
+ }
21
+ const rendered = size >= 10 || unit === 0 ? size.toFixed(0) : trimZero(size.toFixed(1));
22
+ return `${rendered} ${BYTE_UNITS[unit]}${suffix}`;
23
+ }
24
+ function formatBytesPerSecond(bytes) {
25
+ return formatBytes(bytes, { suffix: "/s" });
26
+ }
27
+ const SHORT_UNITS = ["", "K", "M", "B", "T"];
28
+ function formatShort(value) {
29
+ if (value === void 0 || !Number.isFinite(value)) return "0";
30
+ const sign = value < 0 ? "-" : "";
31
+ let n = Math.abs(value);
32
+ if (n < 1e3) return `${sign}${intl(n)}`;
33
+ let unit = 0;
34
+ while (n >= 1e3 && unit < SHORT_UNITS.length - 1) {
35
+ n /= 1e3;
36
+ unit++;
37
+ }
38
+ const rendered = n >= 10 ? n.toFixed(0) : trimZero(n.toFixed(1));
39
+ return `${sign}${rendered}${SHORT_UNITS[unit]}`;
40
+ }
41
+ function formatDuration(value, opts) {
42
+ if (value === void 0 || !Number.isFinite(value)) return "0 ms";
43
+ const ms = (opts == null ? void 0 : opts.from) === "s" ? value * 1e3 : value;
44
+ const abs = Math.abs(ms);
45
+ if (abs < 1e3) return `${intl(ms, 0)} ms`;
46
+ if (abs < 6e4) return `${intl(ms / 1e3)} s`;
47
+ if (abs < 36e5) return `${intl(ms / 6e4)} min`;
48
+ return `${intl(ms / 36e5)} h`;
49
+ }
50
+ const UNIT_FORMATTERS = {
51
+ none: (v) => formatShort(v),
52
+ short: (v) => formatShort(v),
53
+ percent: (v) => `${intl(v)}%`,
54
+ percentunit: (v) => `${intl(v * 100)}%`,
55
+ "%": (v) => `${intl(v)}%`,
56
+ bytes: (v) => formatBytes(v, { base: 1024 }),
57
+ decbytes: (v) => formatBytes(v, { base: 1e3 }),
58
+ Bps: (v) => formatBytesPerSecond(v),
59
+ binBps: (v) => formatBytesPerSecond(v),
60
+ ms: (v) => formatDuration(v, { from: "ms" }),
61
+ s: (v) => formatDuration(v, { from: "s" })
62
+ };
63
+ function formatUnit(value, unit) {
64
+ const v = Number.isFinite(value) ? value : 0;
65
+ if (!unit) return intl(v);
66
+ const fmt = UNIT_FORMATTERS[unit];
67
+ if (fmt) return fmt(v);
68
+ return `${intl(v)}${unit}`;
69
+ }
70
+ export {
71
+ formatBytes,
72
+ formatBytesPerSecond,
73
+ formatDuration,
74
+ formatShort,
75
+ formatUnit
76
+ };
77
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sources":["../../src/lib/format.ts"],"sourcesContent":["// Value formatters and a Grafana-style unit registry, shared by TimeseriesPanel\n// (and any other metric display). Pure — no React — so it is trivially testable.\n//\n// `formatUnit(value, unit)` dispatches on a Grafana unit identifier (e.g.\n// \"bytes\", \"Bps\", \"percent\", \"ms\", \"short\"). Unknown unit strings are treated as\n// a raw suffix appended to the formatted number, preserving callers that pass a\n// literal like \"%\" or \"req\".\n\nconst LOCALE = \"en-US\";\n\nfunction intl(value: number, maxFractionDigits = 1): string {\n return new Intl.NumberFormat(LOCALE, { maximumFractionDigits: maxFractionDigits }).format(\n Number.isFinite(value) ? value : 0,\n );\n}\n\n/** Drops a trailing \".0\" so whole values read \"1 KB\" rather than \"1.0 KB\". */\nfunction trimZero(s: string): string {\n return s.endsWith(\".0\") ? s.slice(0, -2) : s;\n}\n\nexport interface FormatBytesOptions {\n /** 1024 (IEC, the default) or 1000 (SI/decimal). */\n base?: 1000 | 1024;\n /** Appended after the unit label, e.g. \"/s\" for a rate. */\n suffix?: string;\n}\n\nconst BYTE_UNITS = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\"] as const;\n\n/**\n * Human-readable byte size. 1024-based by default with B/KB/MB/GB/TB/PB labels,\n * matching the prior diagnostics formatter. Whole numbers and values >= 10 drop\n * the fraction; smaller fractional values keep one decimal.\n */\nexport function formatBytes(bytes?: number, opts?: FormatBytesOptions): string {\n const suffix = opts?.suffix ?? \"\";\n if (bytes === undefined || !Number.isFinite(bytes) || bytes <= 0) return `0 B${suffix}`;\n const base = opts?.base ?? 1024;\n let size = bytes;\n let unit = 0;\n while (size >= base && unit < BYTE_UNITS.length - 1) {\n size /= base;\n unit++;\n }\n const rendered = size >= 10 || unit === 0 ? size.toFixed(0) : trimZero(size.toFixed(1));\n return `${rendered} ${BYTE_UNITS[unit]}${suffix}`;\n}\n\n/** Byte rate, e.g. \"1.5 MB/s\". 1024-based. */\nexport function formatBytesPerSecond(bytes?: number): string {\n return formatBytes(bytes, { suffix: \"/s\" });\n}\n\nconst SHORT_UNITS = [\"\", \"K\", \"M\", \"B\", \"T\"] as const;\n\n/** SI abbreviation: 999 -> \"999\", 1200 -> \"1.2K\", 3_400_000 -> \"3.4M\". */\nexport function formatShort(value?: number): string {\n if (value === undefined || !Number.isFinite(value)) return \"0\";\n const sign = value < 0 ? \"-\" : \"\";\n let n = Math.abs(value);\n if (n < 1000) return `${sign}${intl(n)}`;\n let unit = 0;\n while (n >= 1000 && unit < SHORT_UNITS.length - 1) {\n n /= 1000;\n unit++;\n }\n const rendered = n >= 10 ? n.toFixed(0) : trimZero(n.toFixed(1));\n return `${sign}${rendered}${SHORT_UNITS[unit]}`;\n}\n\n/**\n * Duration rendered from a millisecond (default) or second input. Picks ms / s /\n * min / h by magnitude: 850 -> \"850 ms\", 1500 -> \"1.5 s\", 90_000 -> \"1.5 min\".\n */\nexport function formatDuration(value?: number, opts?: { from?: \"ms\" | \"s\" }): string {\n if (value === undefined || !Number.isFinite(value)) return \"0 ms\";\n const ms = opts?.from === \"s\" ? value * 1000 : value;\n const abs = Math.abs(ms);\n if (abs < 1000) return `${intl(ms, 0)} ms`;\n if (abs < 60_000) return `${intl(ms / 1000)} s`;\n if (abs < 3_600_000) return `${intl(ms / 60_000)} min`;\n return `${intl(ms / 3_600_000)} h`;\n}\n\n// formatters keyed by Grafana unit identifier. Keys not present here fall through\n// to the raw-suffix path in formatUnit.\nconst UNIT_FORMATTERS: Record<string, (v: number) => string> = {\n none: (v) => formatShort(v),\n short: (v) => formatShort(v),\n percent: (v) => `${intl(v)}%`,\n percentunit: (v) => `${intl(v * 100)}%`,\n \"%\": (v) => `${intl(v)}%`,\n bytes: (v) => formatBytes(v, { base: 1024 }),\n decbytes: (v) => formatBytes(v, { base: 1000 }),\n Bps: (v) => formatBytesPerSecond(v),\n binBps: (v) => formatBytesPerSecond(v),\n ms: (v) => formatDuration(v, { from: \"ms\" }),\n s: (v) => formatDuration(v, { from: \"s\" }),\n};\n\n/**\n * Formats a value for display per a Grafana-style unit key. Unknown units are\n * appended as a literal suffix (back-compat for callers passing \"%\", \"req\",\n * etc.); an absent unit yields the plain localized number.\n */\nexport function formatUnit(value: number, unit?: string): string {\n const v = Number.isFinite(value) ? value : 0;\n if (!unit) return intl(v);\n const fmt = UNIT_FORMATTERS[unit];\n if (fmt) return fmt(v);\n return `${intl(v)}${unit}`;\n}\n"],"names":[],"mappings":"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"}
@@ -16,12 +16,19 @@ function DropdownMenu({
16
16
  items,
17
17
  children,
18
18
  align = "right",
19
+ onOpenChange,
19
20
  title,
20
21
  className,
21
22
  menuClassName
22
23
  }) {
23
24
  const [open, setOpen] = react.useState(false);
24
25
  const wrapperRef = react.useRef(null);
26
+ const onOpenChangeRef = react.useRef(onOpenChange);
27
+ onOpenChangeRef.current = onOpenChange;
28
+ react.useEffect(() => {
29
+ var _a;
30
+ (_a = onOpenChangeRef.current) == null ? void 0 : _a.call(onOpenChangeRef, open);
31
+ }, [open]);
25
32
  react.useEffect(() => {
26
33
  if (!open) return;
27
34
  const onMouseDown = (e) => {
@@ -40,8 +47,17 @@ function DropdownMenu({
40
47
  }, [open]);
41
48
  const closeMenu = () => setOpen(false);
42
49
  const toggle = () => setOpen((v) => !v);
43
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("relative inline-block", className), ref: wrapperRef, children: [
44
- trigger ? /* @__PURE__ */ jsxRuntime.jsx("span", { onClick: toggle, "aria-haspopup": "menu", "aria-expanded": open, children: trigger }) : /* @__PURE__ */ jsxRuntime.jsxs(
50
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("relative inline-flex", className), ref: wrapperRef, children: [
51
+ trigger ? /* @__PURE__ */ jsxRuntime.jsx(
52
+ "span",
53
+ {
54
+ className: "contents",
55
+ onClick: toggle,
56
+ "aria-haspopup": "menu",
57
+ "aria-expanded": open,
58
+ children: trigger
59
+ }
60
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
45
61
  button.Button,
46
62
  {
47
63
  variant,
@@ -1 +1 @@
1
- {"version":3,"file":"DropdownMenu.cjs","sources":["../../src/overlay/DropdownMenu.tsx"],"sourcesContent":["import { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button, type ButtonProps } from \"../components/button\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiChevronDown } from \"../icons\";\n\nexport type DropdownMenuItem = {\n /** Visible label. */\n label: ReactNode;\n /** Called when the item is chosen. */\n onSelect: () => void;\n /** Iconify name or imported icon component rendered before the label. */\n icon?: string | StaticIconComponent;\n /** Browser tooltip for the item. */\n title?: string;\n /** Disable selection. */\n disabled?: boolean;\n};\n\nexport type DropdownMenuProps = {\n /** Trigger label. Ignored when `trigger` is provided. */\n label?: ReactNode;\n /** Icon shown before the trigger label. Ignored when `trigger` is provided. */\n icon?: string | StaticIconComponent;\n /** Hide the trailing chevron on the default trigger. */\n hideChevron?: boolean;\n /** Variant forwarded to the default Button trigger. */\n variant?: ButtonProps[\"variant\"];\n /** Size forwarded to the default Button trigger. */\n size?: ButtonProps[\"size\"];\n /** Fully custom trigger. Receives no props; the menu wraps it with open state. */\n trigger?: ReactNode;\n /** Declarative menu items. Provide this or `children`, not both. */\n items?: DropdownMenuItem[];\n /** Custom menu content. Use `closeMenu` to dismiss after a selection. */\n children?: (closeMenu: () => void) => ReactNode;\n /** Horizontal alignment of the menu relative to the trigger. */\n align?: \"left\" | \"right\";\n /** Browser tooltip / accessible label for the default trigger. */\n title?: string;\n /** Classes applied to the wrapper. */\n className?: string;\n /** Classes applied to the floating menu. */\n menuClassName?: string;\n};\n\nexport function DropdownMenu({\n label,\n icon,\n hideChevron = false,\n variant = \"outline\",\n size = \"sm\",\n trigger,\n items,\n children,\n align = \"right\",\n title,\n className,\n menuClassName,\n}: DropdownMenuProps) {\n const [open, setOpen] = useState(false);\n const wrapperRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!open) return;\n const onMouseDown = (e: MouseEvent) => {\n if (!wrapperRef.current?.contains(e.target as Node)) setOpen(false);\n };\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") setOpen(false);\n };\n document.addEventListener(\"mousedown\", onMouseDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"mousedown\", onMouseDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [open]);\n\n const closeMenu = () => setOpen(false);\n const toggle = () => setOpen((v) => !v);\n\n return (\n <div className={cn(\"relative inline-block\", className)} ref={wrapperRef}>\n {trigger ? (\n <span onClick={toggle} aria-haspopup=\"menu\" aria-expanded={open}>\n {trigger}\n </span>\n ) : (\n <Button\n variant={variant}\n size={size}\n onClick={toggle}\n title={title}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n >\n {icon && <Icon {...(typeof icon === \"string\" ? { name: icon } : { icon })} />}\n {label}\n {!hideChevron && <Icon icon={UiChevronDown} />}\n </Button>\n )}\n {open && (\n <div\n role=\"menu\"\n className={cn(\n \"absolute z-20 mt-1 min-w-[8rem] rounded-md border border-border bg-popover py-1 shadow-md\",\n align === \"right\" ? \"right-0\" : \"left-0\",\n menuClassName,\n )}\n >\n {children\n ? children(closeMenu)\n : items?.map((item, i) => (\n <button\n key={i}\n type=\"button\"\n role=\"menuitem\"\n disabled={item.disabled}\n title={item.title}\n onClick={() => {\n item.onSelect();\n closeMenu();\n }}\n className=\"flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-popover-foreground hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n {item.icon && (\n <Icon\n {...(typeof item.icon === \"string\"\n ? { name: item.icon }\n : { icon: item.icon })}\n />\n )}\n {item.label}\n </button>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"names":["useState","useRef","useEffect","jsxs","cn","jsx","Button","Icon","UiChevronDown"],"mappings":";;;;;;;;AA8CO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,UAAU;AAAA,EACV,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AACF,GAAsB;AACpB,QAAM,CAAC,MAAM,OAAO,IAAIA,MAAAA,SAAS,KAAK;AACtC,QAAM,aAAaC,MAAAA,OAAuB,IAAI;AAE9CC,QAAAA,UAAU,MAAM;AACd,QAAI,CAAC,KAAM;AACX,UAAM,cAAc,CAAC,MAAkB;;AACrC,UAAI,GAAC,gBAAW,YAAX,mBAAoB,SAAS,EAAE,kBAAyB,KAAK;AAAA,IACpE;AACA,UAAM,YAAY,CAAC,MAAqB;AACtC,UAAI,EAAE,QAAQ,SAAU,SAAQ,KAAK;AAAA,IACvC;AACA,aAAS,iBAAiB,aAAa,WAAW;AAClD,aAAS,iBAAiB,WAAW,SAAS;AAC9C,WAAO,MAAM;AACX,eAAS,oBAAoB,aAAa,WAAW;AACrD,eAAS,oBAAoB,WAAW,SAAS;AAAA,IACnD;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,YAAY,MAAM,QAAQ,KAAK;AACrC,QAAM,SAAS,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;AAEtC,SACEC,gCAAC,SAAI,WAAWC,MAAAA,GAAG,yBAAyB,SAAS,GAAG,KAAK,YAC1D,UAAA;AAAA,IAAA,UACCC,2BAAAA,IAAC,UAAK,SAAS,QAAQ,iBAAc,QAAO,iBAAe,MACxD,UAAA,QAAA,CACH,IAEAF,2BAAAA;AAAAA,MAACG,OAAAA;AAAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,SAAS;AAAA,QACT;AAAA,QACA,iBAAc;AAAA,QACd,iBAAe;AAAA,QAEd,UAAA;AAAA,UAAA,QAAQD,2BAAAA,IAACE,WAAA,EAAM,GAAI,OAAO,SAAS,WAAW,EAAE,MAAM,KAAA,IAAS,EAAE,KAAA,EAAK,CAAI;AAAA,UAC1E;AAAA,UACA,CAAC,eAAeF,2BAAAA,IAACE,KAAAA,MAAA,EAAK,MAAMC,cAAAA,cAAA,CAAe;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAG/C,QACCH,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAWD,MAAAA;AAAAA,UACT;AAAA,UACA,UAAU,UAAU,YAAY;AAAA,UAChC;AAAA,QAAA;AAAA,QAGD,UAAA,WACG,SAAS,SAAS,IAClB,+BAAO,IAAI,CAAC,MAAM,MAChBD,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YAEC,MAAK;AAAA,YACL,MAAK;AAAA,YACL,UAAU,KAAK;AAAA,YACf,OAAO,KAAK;AAAA,YACZ,SAAS,MAAM;AACb,mBAAK,SAAA;AACL,wBAAA;AAAA,YACF;AAAA,YACA,WAAU;AAAA,YAET,UAAA;AAAA,cAAA,KAAK,QACJE,2BAAAA;AAAAA,gBAACE,KAAAA;AAAAA,gBAAA;AAAA,kBACE,GAAI,OAAO,KAAK,SAAS,WACtB,EAAE,MAAM,KAAK,KAAA,IACb,EAAE,MAAM,KAAK,KAAA;AAAA,gBAAK;AAAA,cAAA;AAAA,cAGzB,KAAK;AAAA,YAAA;AAAA,UAAA;AAAA,UAlBD;AAAA,QAAA;AAAA,MAoBR;AAAA,IAAA;AAAA,EACP,GAEJ;AAEJ;;"}
1
+ {"version":3,"file":"DropdownMenu.cjs","sources":["../../src/overlay/DropdownMenu.tsx"],"sourcesContent":["import { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button, type ButtonProps } from \"../components/button\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiChevronDown } from \"../icons\";\n\nexport type DropdownMenuItem = {\n /** Visible label. */\n label: ReactNode;\n /** Called when the item is chosen. */\n onSelect: () => void;\n /** Iconify name or imported icon component rendered before the label. */\n icon?: string | StaticIconComponent;\n /** Browser tooltip for the item. */\n title?: string;\n /** Disable selection. */\n disabled?: boolean;\n};\n\nexport type DropdownMenuProps = {\n /** Trigger label. Ignored when `trigger` is provided. */\n label?: ReactNode;\n /** Icon shown before the trigger label. Ignored when `trigger` is provided. */\n icon?: string | StaticIconComponent;\n /** Hide the trailing chevron on the default trigger. */\n hideChevron?: boolean;\n /** Variant forwarded to the default Button trigger. */\n variant?: ButtonProps[\"variant\"];\n /** Size forwarded to the default Button trigger. */\n size?: ButtonProps[\"size\"];\n /** Fully custom trigger. Receives no props; the menu wraps it with open state. */\n trigger?: ReactNode;\n /** Declarative menu items. Provide this or `children`, not both. */\n items?: DropdownMenuItem[];\n /** Custom menu content. Use `closeMenu` to dismiss after a selection. */\n children?: (closeMenu: () => void) => ReactNode;\n /** Horizontal alignment of the menu relative to the trigger. */\n align?: \"left\" | \"right\";\n /** Notified whenever the menu opens or closes. */\n onOpenChange?: (open: boolean) => void;\n /** Browser tooltip / accessible label for the default trigger. */\n title?: string;\n /** Classes applied to the wrapper. */\n className?: string;\n /** Classes applied to the floating menu. */\n menuClassName?: string;\n};\n\nexport function DropdownMenu({\n label,\n icon,\n hideChevron = false,\n variant = \"outline\",\n size = \"sm\",\n trigger,\n items,\n children,\n align = \"right\",\n onOpenChange,\n title,\n className,\n menuClassName,\n}: DropdownMenuProps) {\n const [open, setOpen] = useState(false);\n const wrapperRef = useRef<HTMLDivElement>(null);\n\n const onOpenChangeRef = useRef(onOpenChange);\n onOpenChangeRef.current = onOpenChange;\n useEffect(() => {\n onOpenChangeRef.current?.(open);\n }, [open]);\n\n useEffect(() => {\n if (!open) return;\n const onMouseDown = (e: MouseEvent) => {\n if (!wrapperRef.current?.contains(e.target as Node)) setOpen(false);\n };\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") setOpen(false);\n };\n document.addEventListener(\"mousedown\", onMouseDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"mousedown\", onMouseDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [open]);\n\n const closeMenu = () => setOpen(false);\n const toggle = () => setOpen((v) => !v);\n\n return (\n <div className={cn(\"relative inline-flex\", className)} ref={wrapperRef}>\n {trigger ? (\n <span\n className=\"contents\"\n onClick={toggle}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n >\n {trigger}\n </span>\n ) : (\n <Button\n variant={variant}\n size={size}\n onClick={toggle}\n title={title}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n >\n {icon && <Icon {...(typeof icon === \"string\" ? { name: icon } : { icon })} />}\n {label}\n {!hideChevron && <Icon icon={UiChevronDown} />}\n </Button>\n )}\n {open && (\n <div\n role=\"menu\"\n className={cn(\n \"absolute z-20 mt-1 min-w-[8rem] rounded-md border border-border bg-popover py-1 shadow-md\",\n align === \"right\" ? \"right-0\" : \"left-0\",\n menuClassName,\n )}\n >\n {children\n ? children(closeMenu)\n : items?.map((item, i) => (\n <button\n key={i}\n type=\"button\"\n role=\"menuitem\"\n disabled={item.disabled}\n title={item.title}\n onClick={() => {\n item.onSelect();\n closeMenu();\n }}\n className=\"flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-popover-foreground hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n {item.icon && (\n <Icon\n {...(typeof item.icon === \"string\"\n ? { name: item.icon }\n : { icon: item.icon })}\n />\n )}\n {item.label}\n </button>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"names":["useState","useRef","useEffect","jsxs","cn","jsx","Button","Icon","UiChevronDown"],"mappings":"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"}
@@ -32,6 +32,8 @@ export type DropdownMenuProps = {
32
32
  children?: (closeMenu: () => void) => ReactNode;
33
33
  /** Horizontal alignment of the menu relative to the trigger. */
34
34
  align?: "left" | "right";
35
+ /** Notified whenever the menu opens or closes. */
36
+ onOpenChange?: (open: boolean) => void;
35
37
  /** Browser tooltip / accessible label for the default trigger. */
36
38
  title?: string;
37
39
  /** Classes applied to the wrapper. */
@@ -39,5 +41,5 @@ export type DropdownMenuProps = {
39
41
  /** Classes applied to the floating menu. */
40
42
  menuClassName?: string;
41
43
  };
42
- export declare function DropdownMenu({ label, icon, hideChevron, variant, size, trigger, items, children, align, title, className, menuClassName, }: DropdownMenuProps): import("react/jsx-runtime").JSX.Element;
44
+ export declare function DropdownMenu({ label, icon, hideChevron, variant, size, trigger, items, children, align, onOpenChange, title, className, menuClassName, }: DropdownMenuProps): import("react/jsx-runtime").JSX.Element;
43
45
  //# sourceMappingURL=DropdownMenu.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DropdownMenu.d.ts","sourceRoot":"","sources":["../../src/overlay/DropdownMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEpE,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAG9D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,qBAAqB;IACrB,KAAK,EAAE,SAAS,CAAC;IACjB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,yDAAyD;IACzD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,+EAA+E;IAC/E,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,wDAAwD;IACxD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uDAAuD;IACvD,OAAO,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IACjC,oDAAoD;IACpD,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,kFAAkF;IAClF,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,oEAAoE;IACpE,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,IAAI,KAAK,SAAS,CAAC;IAChD,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,IAAI,EACJ,WAAmB,EACnB,OAAmB,EACnB,IAAW,EACX,OAAO,EACP,KAAK,EACL,QAAQ,EACR,KAAe,EACf,KAAK,EACL,SAAS,EACT,aAAa,GACd,EAAE,iBAAiB,2CAiFnB"}
1
+ {"version":3,"file":"DropdownMenu.d.ts","sourceRoot":"","sources":["../../src/overlay/DropdownMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEpE,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAG9D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,qBAAqB;IACrB,KAAK,EAAE,SAAS,CAAC;IACjB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,yDAAyD;IACzD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,+EAA+E;IAC/E,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,wDAAwD;IACxD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uDAAuD;IACvD,OAAO,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IACjC,oDAAoD;IACpD,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,kFAAkF;IAClF,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,oEAAoE;IACpE,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,IAAI,KAAK,SAAS,CAAC;IAChD,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,kDAAkD;IAClD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,IAAI,EACJ,WAAmB,EACnB,OAAmB,EACnB,IAAW,EACX,OAAO,EACP,KAAK,EACL,QAAQ,EACR,KAAe,EACf,YAAY,EACZ,KAAK,EACL,SAAS,EACT,aAAa,GACd,EAAE,iBAAiB,2CA4FnB"}
@@ -14,12 +14,19 @@ function DropdownMenu({
14
14
  items,
15
15
  children,
16
16
  align = "right",
17
+ onOpenChange,
17
18
  title,
18
19
  className,
19
20
  menuClassName
20
21
  }) {
21
22
  const [open, setOpen] = useState(false);
22
23
  const wrapperRef = useRef(null);
24
+ const onOpenChangeRef = useRef(onOpenChange);
25
+ onOpenChangeRef.current = onOpenChange;
26
+ useEffect(() => {
27
+ var _a;
28
+ (_a = onOpenChangeRef.current) == null ? void 0 : _a.call(onOpenChangeRef, open);
29
+ }, [open]);
23
30
  useEffect(() => {
24
31
  if (!open) return;
25
32
  const onMouseDown = (e) => {
@@ -38,8 +45,17 @@ function DropdownMenu({
38
45
  }, [open]);
39
46
  const closeMenu = () => setOpen(false);
40
47
  const toggle = () => setOpen((v) => !v);
41
- return /* @__PURE__ */ jsxs("div", { className: cn("relative inline-block", className), ref: wrapperRef, children: [
42
- trigger ? /* @__PURE__ */ jsx("span", { onClick: toggle, "aria-haspopup": "menu", "aria-expanded": open, children: trigger }) : /* @__PURE__ */ jsxs(
48
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative inline-flex", className), ref: wrapperRef, children: [
49
+ trigger ? /* @__PURE__ */ jsx(
50
+ "span",
51
+ {
52
+ className: "contents",
53
+ onClick: toggle,
54
+ "aria-haspopup": "menu",
55
+ "aria-expanded": open,
56
+ children: trigger
57
+ }
58
+ ) : /* @__PURE__ */ jsxs(
43
59
  Button,
44
60
  {
45
61
  variant,