@flanksource/clicky-ui 0.2.4 → 0.2.6

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 (189) 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 +1 -1
  26. package/dist/components/icon-menu-picker.cjs.map +1 -1
  27. package/dist/components/icon-menu-picker.js +1 -1
  28. package/dist/components/icon-menu-picker.js.map +1 -1
  29. package/dist/components.cjs +7 -0
  30. package/dist/components.cjs.map +1 -1
  31. package/dist/components.d.ts +3 -0
  32. package/dist/components.d.ts.map +1 -1
  33. package/dist/components.js +7 -0
  34. package/dist/components.js.map +1 -1
  35. package/dist/data/Badge.d.ts +1 -1
  36. package/dist/data/ProgressBar.cjs +4 -2
  37. package/dist/data/ProgressBar.cjs.map +1 -1
  38. package/dist/data/ProgressBar.d.ts +1 -1
  39. package/dist/data/ProgressBar.d.ts.map +1 -1
  40. package/dist/data/ProgressBar.js +4 -2
  41. package/dist/data/ProgressBar.js.map +1 -1
  42. package/dist/data/StatusBreakdown.cjs +80 -0
  43. package/dist/data/StatusBreakdown.cjs.map +1 -0
  44. package/dist/data/StatusBreakdown.d.ts +36 -0
  45. package/dist/data/StatusBreakdown.d.ts.map +1 -0
  46. package/dist/data/StatusBreakdown.js +80 -0
  47. package/dist/data/StatusBreakdown.js.map +1 -0
  48. package/dist/data/TaskManager.cjs +162 -0
  49. package/dist/data/TaskManager.cjs.map +1 -0
  50. package/dist/data/TaskManager.d.ts +21 -0
  51. package/dist/data/TaskManager.d.ts.map +1 -0
  52. package/dist/data/TaskManager.js +162 -0
  53. package/dist/data/TaskManager.js.map +1 -0
  54. package/dist/data/TaskProgress.cjs +125 -0
  55. package/dist/data/TaskProgress.cjs.map +1 -0
  56. package/dist/data/TaskProgress.d.ts +20 -0
  57. package/dist/data/TaskProgress.d.ts.map +1 -0
  58. package/dist/data/TaskProgress.js +125 -0
  59. package/dist/data/TaskProgress.js.map +1 -0
  60. package/dist/data/TaskSnapshot.cjs +23 -0
  61. package/dist/data/TaskSnapshot.cjs.map +1 -0
  62. package/dist/data/TaskSnapshot.d.ts +49 -0
  63. package/dist/data/TaskSnapshot.d.ts.map +1 -0
  64. package/dist/data/TaskSnapshot.js +23 -0
  65. package/dist/data/TaskSnapshot.js.map +1 -0
  66. package/dist/data/TimeseriesGauge.cjs +144 -0
  67. package/dist/data/TimeseriesGauge.cjs.map +1 -0
  68. package/dist/data/TimeseriesGauge.d.ts +56 -0
  69. package/dist/data/TimeseriesGauge.d.ts.map +1 -0
  70. package/dist/data/TimeseriesGauge.js +144 -0
  71. package/dist/data/TimeseriesGauge.js.map +1 -0
  72. package/dist/data/TimeseriesPanel.axes.cjs +39 -0
  73. package/dist/data/TimeseriesPanel.axes.cjs.map +1 -0
  74. package/dist/data/TimeseriesPanel.axes.d.ts +28 -0
  75. package/dist/data/TimeseriesPanel.axes.d.ts.map +1 -0
  76. package/dist/data/TimeseriesPanel.axes.js +39 -0
  77. package/dist/data/TimeseriesPanel.axes.js.map +1 -0
  78. package/dist/data/TimeseriesPanel.cjs +324 -0
  79. package/dist/data/TimeseriesPanel.cjs.map +1 -0
  80. package/dist/data/TimeseriesPanel.d.ts +141 -0
  81. package/dist/data/TimeseriesPanel.d.ts.map +1 -0
  82. package/dist/data/TimeseriesPanel.js +324 -0
  83. package/dist/data/TimeseriesPanel.js.map +1 -0
  84. package/dist/data/Tree.cjs.map +1 -1
  85. package/dist/data/Tree.d.ts.map +1 -1
  86. package/dist/data/Tree.js.map +1 -1
  87. package/dist/data/TreeNode.cjs +44 -7
  88. package/dist/data/TreeNode.cjs.map +1 -1
  89. package/dist/data/TreeNode.d.ts +19 -1
  90. package/dist/data/TreeNode.d.ts.map +1 -1
  91. package/dist/data/TreeNode.js +44 -7
  92. package/dist/data/TreeNode.js.map +1 -1
  93. package/dist/data/diagnostics/DiagnosticsDetailPanel.cjs +66 -44
  94. package/dist/data/diagnostics/DiagnosticsDetailPanel.cjs.map +1 -1
  95. package/dist/data/diagnostics/DiagnosticsDetailPanel.d.ts +21 -1
  96. package/dist/data/diagnostics/DiagnosticsDetailPanel.d.ts.map +1 -1
  97. package/dist/data/diagnostics/DiagnosticsDetailPanel.js +65 -43
  98. package/dist/data/diagnostics/DiagnosticsDetailPanel.js.map +1 -1
  99. package/dist/data/diagnostics/DiagnosticsTree.cjs +2 -1
  100. package/dist/data/diagnostics/DiagnosticsTree.cjs.map +1 -1
  101. package/dist/data/diagnostics/DiagnosticsTree.js +2 -1
  102. package/dist/data/diagnostics/DiagnosticsTree.js.map +1 -1
  103. package/dist/data/diagnostics/FrameSourceWindow.cjs +37 -0
  104. package/dist/data/diagnostics/FrameSourceWindow.cjs.map +1 -0
  105. package/dist/data/diagnostics/FrameSourceWindow.d.ts +15 -0
  106. package/dist/data/diagnostics/FrameSourceWindow.d.ts.map +1 -0
  107. package/dist/data/diagnostics/FrameSourceWindow.js +37 -0
  108. package/dist/data/diagnostics/FrameSourceWindow.js.map +1 -0
  109. package/dist/data/diagnostics/JvmStackTrace.cjs +25 -12
  110. package/dist/data/diagnostics/JvmStackTrace.cjs.map +1 -1
  111. package/dist/data/diagnostics/JvmStackTrace.d.ts +8 -1
  112. package/dist/data/diagnostics/JvmStackTrace.d.ts.map +1 -1
  113. package/dist/data/diagnostics/JvmStackTrace.js +26 -13
  114. package/dist/data/diagnostics/JvmStackTrace.js.map +1 -1
  115. package/dist/data/diagnostics/ThreadCard.cjs +9 -2
  116. package/dist/data/diagnostics/ThreadCard.cjs.map +1 -1
  117. package/dist/data/diagnostics/ThreadCard.d.ts +3 -1
  118. package/dist/data/diagnostics/ThreadCard.d.ts.map +1 -1
  119. package/dist/data/diagnostics/ThreadCard.js +9 -2
  120. package/dist/data/diagnostics/ThreadCard.js.map +1 -1
  121. package/dist/data/diagnostics/jvm-stacktrace.cjs +10 -1
  122. package/dist/data/diagnostics/jvm-stacktrace.cjs.map +1 -1
  123. package/dist/data/diagnostics/jvm-stacktrace.d.ts.map +1 -1
  124. package/dist/data/diagnostics/jvm-stacktrace.js +10 -1
  125. package/dist/data/diagnostics/jvm-stacktrace.js.map +1 -1
  126. package/dist/data/diagnostics/utils.cjs +0 -12
  127. package/dist/data/diagnostics/utils.cjs.map +1 -1
  128. package/dist/data/diagnostics/utils.d.ts +1 -1
  129. package/dist/data/diagnostics/utils.d.ts.map +1 -1
  130. package/dist/data/diagnostics/utils.js +0 -12
  131. package/dist/data/diagnostics/utils.js.map +1 -1
  132. package/dist/data/task-status.cjs +108 -0
  133. package/dist/data/task-status.cjs.map +1 -0
  134. package/dist/data/task-status.d.ts +21 -0
  135. package/dist/data/task-status.d.ts.map +1 -0
  136. package/dist/data/task-status.js +108 -0
  137. package/dist/data/task-status.js.map +1 -0
  138. package/dist/data.cjs +27 -0
  139. package/dist/data.cjs.map +1 -1
  140. package/dist/data.d.ts +9 -1
  141. package/dist/data.d.ts.map +1 -1
  142. package/dist/data.js +27 -0
  143. package/dist/data.js.map +1 -1
  144. package/dist/hooks/use-task-run.cjs +137 -0
  145. package/dist/hooks/use-task-run.cjs.map +1 -0
  146. package/dist/hooks/use-task-run.d.ts +35 -0
  147. package/dist/hooks/use-task-run.d.ts.map +1 -0
  148. package/dist/hooks/use-task-run.js +137 -0
  149. package/dist/hooks/use-task-run.js.map +1 -0
  150. package/dist/hooks.cjs +3 -0
  151. package/dist/hooks.cjs.map +1 -1
  152. package/dist/hooks.d.ts +1 -0
  153. package/dist/hooks.d.ts.map +1 -1
  154. package/dist/hooks.js +3 -0
  155. package/dist/hooks.js.map +1 -1
  156. package/dist/index.cjs +43 -0
  157. package/dist/index.cjs.map +1 -1
  158. package/dist/index.js +43 -0
  159. package/dist/index.js.map +1 -1
  160. package/dist/lib/color.cjs +33 -0
  161. package/dist/lib/color.cjs.map +1 -0
  162. package/dist/lib/color.d.ts +10 -0
  163. package/dist/lib/color.d.ts.map +1 -0
  164. package/dist/lib/color.js +33 -0
  165. package/dist/lib/color.js.map +1 -0
  166. package/dist/lib/format.cjs +77 -0
  167. package/dist/lib/format.cjs.map +1 -0
  168. package/dist/lib/format.d.ts +30 -0
  169. package/dist/lib/format.d.ts.map +1 -0
  170. package/dist/lib/format.js +77 -0
  171. package/dist/lib/format.js.map +1 -0
  172. package/dist/overlay/DropdownMenu.cjs +18 -2
  173. package/dist/overlay/DropdownMenu.cjs.map +1 -1
  174. package/dist/overlay/DropdownMenu.d.ts +3 -1
  175. package/dist/overlay/DropdownMenu.d.ts.map +1 -1
  176. package/dist/overlay/DropdownMenu.js +18 -2
  177. package/dist/overlay/DropdownMenu.js.map +1 -1
  178. package/dist/rpc/types.cjs.map +1 -1
  179. package/dist/rpc/types.d.ts +4 -0
  180. package/dist/rpc/types.d.ts.map +1 -1
  181. package/dist/rpc/types.js.map +1 -1
  182. package/dist/styles.css +1 -1
  183. package/dist/utils.cjs +6 -0
  184. package/dist/utils.cjs.map +1 -1
  185. package/dist/utils.d.ts +1 -0
  186. package/dist/utils.d.ts.map +1 -1
  187. package/dist/utils.js +6 -0
  188. package/dist/utils.js.map +1 -1
  189. 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,23 @@ 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";
57
+ import { TimeseriesGauge } from "./data/TimeseriesGauge.js";
58
+ import { StackedStatusBar, StatusRows, segment } from "./data/StatusBreakdown.js";
51
59
  import { Tree } from "./data/Tree.js";
52
60
  import { TreeNode } from "./data/TreeNode.js";
53
61
  import { TreeGroupHeader } from "./data/TreeGroupHeader.js";
62
+ import { allGroupsTerminal, isTerminalStatus } from "./data/TaskSnapshot.js";
63
+ import { bucketTasks, logLevelColor, taskSegments, taskStatusBg, taskStatusColor, taskStatusIcon } from "./data/task-status.js";
64
+ import { TaskProgress } from "./data/TaskProgress.js";
65
+ import { TaskManager } from "./data/TaskManager.js";
54
66
  import { DiagnosticsTree } from "./data/diagnostics/DiagnosticsTree.js";
55
67
  import { DiagnosticsDetailPanel } from "./data/diagnostics/DiagnosticsDetailPanel.js";
56
68
  import { countProcesses, findProcessByPID, processLabel, processStateColor, processStateIcon } from "./data/diagnostics/utils.js";
57
69
  import { countGoroutinesByState, countStackByState, detectDumpFormat, parseGoroutineDump, parseStackDump } from "./data/diagnostics/stacktrace.js";
58
70
  import { countThreadsByState, parseJvmThreadDump } from "./data/diagnostics/jvm-stacktrace.js";
59
71
  import { JvmStackFrameRow, JvmStackTrace } from "./data/diagnostics/JvmStackTrace.js";
72
+ import { FrameSourceWindow, frameHasSource } from "./data/diagnostics/FrameSourceWindow.js";
60
73
  import { StackTrace } from "./data/diagnostics/RenderedStackTrace.js";
61
74
  import { parseJavaStackTrace } from "./data/diagnostics/stacktrace-parse.js";
62
75
  import { CopyBadge, ErrorDetails, PrettyStackTrace } from "./data/diagnostics/ErrorDetails.js";
@@ -346,10 +359,12 @@ export {
346
359
  AvatarBadge,
347
360
  Badge,
348
361
  Button,
362
+ CLICKY_FORMAT_OPTIONS,
349
363
  Clicky,
350
364
  ClickyNodeView,
351
365
  ClickyTable,
352
366
  CodeBlock,
367
+ Combobox,
353
368
  CommandForm,
354
369
  CommandOutput,
355
370
  CopyBadge,
@@ -371,6 +386,8 @@ export {
371
386
  FilterPill,
372
387
  FilterPillGroup,
373
388
  FilterSeparator,
389
+ FormatOptionsDropdown,
390
+ FrameSourceWindow,
374
391
  Gauge,
375
392
  HarPanel,
376
393
  HoverCard,
@@ -402,15 +419,22 @@ export {
402
419
  Select,
403
420
  SignedDeltaBar,
404
421
  SortableHeader,
422
+ SplitButton,
405
423
  SplitPane,
406
424
  StackTrace,
425
+ StackedStatusBar,
407
426
  StatusDot,
427
+ StatusRows,
408
428
  TabButton,
409
429
  TagActionsProvider,
410
430
  TagList,
431
+ TaskManager,
432
+ TaskProgress,
411
433
  ThemeProvider,
412
434
  ThemeSwitcher,
413
435
  TimeRange,
436
+ TimeseriesGauge,
437
+ TimeseriesPanel,
414
438
  Timestamp,
415
439
  Tree,
416
440
  TreeGroupHeader,
@@ -905,7 +929,9 @@ export {
905
929
  UiZoomOut,
906
930
  UiZoomOutFilled,
907
931
  VIEW_OPTIONS,
932
+ allGroupsTerminal,
908
933
  badgeVariants,
934
+ bucketTasks,
909
935
  buttonVariants,
910
936
  changeIconAliases,
911
937
  chooseTimestampFormat,
@@ -921,12 +947,20 @@ export {
921
947
  findListEndpoint,
922
948
  findProcessByPID,
923
949
  fnv1a32,
950
+ formatBytes,
951
+ formatBytesPerSecond,
952
+ formatDuration,
924
953
  formatPropertyLabel,
954
+ formatShort,
925
955
  formatTimestamp,
956
+ formatUnit,
957
+ frameHasSource,
926
958
  getChangeIcon,
927
959
  inferColumns,
928
960
  isApplicationStackFrame,
929
961
  isPositionalParam,
962
+ isTerminalStatus,
963
+ logLevelColor,
930
964
  modeToFormat,
931
965
  normalizeErrorDiagnostics,
932
966
  normalizeLogsTableRows,
@@ -945,11 +979,18 @@ export {
945
979
  processLabel,
946
980
  processStateColor,
947
981
  processStateIcon,
982
+ resolveBreakdown,
983
+ resolveSeries,
948
984
  resolveSize,
985
+ segment,
949
986
  setFallbackIconProvider,
950
987
  splitTagToken,
951
988
  tagActionsFromRecord,
952
989
  tagFilterTokens,
990
+ taskSegments,
991
+ taskStatusBg,
992
+ taskStatusColor,
993
+ taskStatusIcon,
953
994
  useDensity,
954
995
  useDensityValue,
955
996
  useHistoryRoute,
@@ -961,6 +1002,8 @@ export {
961
1002
  useSort,
962
1003
  useTagActions,
963
1004
  useTagActionsValue,
1005
+ useTaskRun,
1006
+ useTaskRuns,
964
1007
  useTheme
965
1008
  };
966
1009
  //# 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,