@flanksource/clicky-ui 0.2.4 → 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.
- package/dist/components/Combobox.cjs +291 -0
- package/dist/components/Combobox.cjs.map +1 -0
- package/dist/components/Combobox.d.ts +59 -0
- package/dist/components/Combobox.d.ts.map +1 -0
- package/dist/components/Combobox.js +291 -0
- package/dist/components/Combobox.js.map +1 -0
- package/dist/components/FilterBar.cjs +153 -124
- package/dist/components/FilterBar.cjs.map +1 -1
- package/dist/components/FilterBar.d.ts +25 -1
- package/dist/components/FilterBar.d.ts.map +1 -1
- package/dist/components/FilterBar.js +153 -124
- package/dist/components/FilterBar.js.map +1 -1
- package/dist/components/FormatOptionsDropdown.cjs +62 -0
- package/dist/components/FormatOptionsDropdown.cjs.map +1 -0
- package/dist/components/FormatOptionsDropdown.d.ts +37 -0
- package/dist/components/FormatOptionsDropdown.d.ts.map +1 -0
- package/dist/components/FormatOptionsDropdown.js +62 -0
- package/dist/components/FormatOptionsDropdown.js.map +1 -0
- package/dist/components/SplitButton.cjs +87 -0
- package/dist/components/SplitButton.cjs.map +1 -0
- package/dist/components/SplitButton.d.ts +32 -0
- package/dist/components/SplitButton.d.ts.map +1 -0
- package/dist/components/SplitButton.js +87 -0
- package/dist/components/SplitButton.js.map +1 -0
- package/dist/components/icon-menu-picker.cjs +1 -1
- package/dist/components/icon-menu-picker.cjs.map +1 -1
- package/dist/components/icon-menu-picker.js +1 -1
- package/dist/components/icon-menu-picker.js.map +1 -1
- package/dist/components.cjs +7 -0
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.ts +3 -0
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +7 -0
- package/dist/components.js.map +1 -1
- package/dist/data/Badge.d.ts +1 -1
- package/dist/data/ProgressBar.cjs +4 -2
- package/dist/data/ProgressBar.cjs.map +1 -1
- package/dist/data/ProgressBar.d.ts +1 -1
- package/dist/data/ProgressBar.d.ts.map +1 -1
- package/dist/data/ProgressBar.js +4 -2
- package/dist/data/ProgressBar.js.map +1 -1
- package/dist/data/TaskManager.cjs +162 -0
- package/dist/data/TaskManager.cjs.map +1 -0
- package/dist/data/TaskManager.d.ts +21 -0
- package/dist/data/TaskManager.d.ts.map +1 -0
- package/dist/data/TaskManager.js +162 -0
- package/dist/data/TaskManager.js.map +1 -0
- package/dist/data/TaskProgress.cjs +125 -0
- package/dist/data/TaskProgress.cjs.map +1 -0
- package/dist/data/TaskProgress.d.ts +20 -0
- package/dist/data/TaskProgress.d.ts.map +1 -0
- package/dist/data/TaskProgress.js +125 -0
- package/dist/data/TaskProgress.js.map +1 -0
- package/dist/data/TaskSnapshot.cjs +23 -0
- package/dist/data/TaskSnapshot.cjs.map +1 -0
- package/dist/data/TaskSnapshot.d.ts +49 -0
- package/dist/data/TaskSnapshot.d.ts.map +1 -0
- package/dist/data/TaskSnapshot.js +23 -0
- package/dist/data/TaskSnapshot.js.map +1 -0
- package/dist/data/TimeseriesPanel.axes.cjs +39 -0
- package/dist/data/TimeseriesPanel.axes.cjs.map +1 -0
- package/dist/data/TimeseriesPanel.axes.d.ts +28 -0
- package/dist/data/TimeseriesPanel.axes.d.ts.map +1 -0
- package/dist/data/TimeseriesPanel.axes.js +39 -0
- package/dist/data/TimeseriesPanel.axes.js.map +1 -0
- package/dist/data/TimeseriesPanel.cjs +324 -0
- package/dist/data/TimeseriesPanel.cjs.map +1 -0
- package/dist/data/TimeseriesPanel.d.ts +141 -0
- package/dist/data/TimeseriesPanel.d.ts.map +1 -0
- package/dist/data/TimeseriesPanel.js +324 -0
- package/dist/data/TimeseriesPanel.js.map +1 -0
- package/dist/data/Tree.cjs.map +1 -1
- package/dist/data/Tree.d.ts.map +1 -1
- package/dist/data/Tree.js.map +1 -1
- package/dist/data/TreeNode.cjs +44 -7
- package/dist/data/TreeNode.cjs.map +1 -1
- package/dist/data/TreeNode.d.ts +19 -1
- package/dist/data/TreeNode.d.ts.map +1 -1
- package/dist/data/TreeNode.js +44 -7
- package/dist/data/TreeNode.js.map +1 -1
- package/dist/data/diagnostics/DiagnosticsDetailPanel.cjs +66 -44
- package/dist/data/diagnostics/DiagnosticsDetailPanel.cjs.map +1 -1
- package/dist/data/diagnostics/DiagnosticsDetailPanel.d.ts +21 -1
- package/dist/data/diagnostics/DiagnosticsDetailPanel.d.ts.map +1 -1
- package/dist/data/diagnostics/DiagnosticsDetailPanel.js +65 -43
- package/dist/data/diagnostics/DiagnosticsDetailPanel.js.map +1 -1
- package/dist/data/diagnostics/DiagnosticsTree.cjs +2 -1
- package/dist/data/diagnostics/DiagnosticsTree.cjs.map +1 -1
- package/dist/data/diagnostics/DiagnosticsTree.js +2 -1
- package/dist/data/diagnostics/DiagnosticsTree.js.map +1 -1
- package/dist/data/diagnostics/FrameSourceWindow.cjs +37 -0
- package/dist/data/diagnostics/FrameSourceWindow.cjs.map +1 -0
- package/dist/data/diagnostics/FrameSourceWindow.d.ts +15 -0
- package/dist/data/diagnostics/FrameSourceWindow.d.ts.map +1 -0
- package/dist/data/diagnostics/FrameSourceWindow.js +37 -0
- package/dist/data/diagnostics/FrameSourceWindow.js.map +1 -0
- package/dist/data/diagnostics/JvmStackTrace.cjs +25 -12
- package/dist/data/diagnostics/JvmStackTrace.cjs.map +1 -1
- package/dist/data/diagnostics/JvmStackTrace.d.ts +8 -1
- package/dist/data/diagnostics/JvmStackTrace.d.ts.map +1 -1
- package/dist/data/diagnostics/JvmStackTrace.js +26 -13
- package/dist/data/diagnostics/JvmStackTrace.js.map +1 -1
- package/dist/data/diagnostics/ThreadCard.cjs +9 -2
- package/dist/data/diagnostics/ThreadCard.cjs.map +1 -1
- package/dist/data/diagnostics/ThreadCard.d.ts +3 -1
- package/dist/data/diagnostics/ThreadCard.d.ts.map +1 -1
- package/dist/data/diagnostics/ThreadCard.js +9 -2
- package/dist/data/diagnostics/ThreadCard.js.map +1 -1
- package/dist/data/diagnostics/jvm-stacktrace.cjs +10 -1
- package/dist/data/diagnostics/jvm-stacktrace.cjs.map +1 -1
- package/dist/data/diagnostics/jvm-stacktrace.d.ts.map +1 -1
- package/dist/data/diagnostics/jvm-stacktrace.js +10 -1
- package/dist/data/diagnostics/jvm-stacktrace.js.map +1 -1
- package/dist/data/diagnostics/utils.cjs +0 -12
- package/dist/data/diagnostics/utils.cjs.map +1 -1
- package/dist/data/diagnostics/utils.d.ts +1 -1
- package/dist/data/diagnostics/utils.d.ts.map +1 -1
- package/dist/data/diagnostics/utils.js +0 -12
- package/dist/data/diagnostics/utils.js.map +1 -1
- package/dist/data/task-status.cjs +108 -0
- package/dist/data/task-status.cjs.map +1 -0
- package/dist/data/task-status.d.ts +21 -0
- package/dist/data/task-status.d.ts.map +1 -0
- package/dist/data/task-status.js +108 -0
- package/dist/data/task-status.js.map +1 -0
- package/dist/data.cjs +21 -0
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +7 -1
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +21 -0
- package/dist/data.js.map +1 -1
- package/dist/hooks/use-task-run.cjs +137 -0
- package/dist/hooks/use-task-run.cjs.map +1 -0
- package/dist/hooks/use-task-run.d.ts +35 -0
- package/dist/hooks/use-task-run.d.ts.map +1 -0
- package/dist/hooks/use-task-run.js +137 -0
- package/dist/hooks/use-task-run.js.map +1 -0
- package/dist/hooks.cjs +3 -0
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.ts +1 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +3 -0
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +37 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/color.cjs +33 -0
- package/dist/lib/color.cjs.map +1 -0
- package/dist/lib/color.d.ts +10 -0
- package/dist/lib/color.d.ts.map +1 -0
- package/dist/lib/color.js +33 -0
- package/dist/lib/color.js.map +1 -0
- package/dist/lib/format.cjs +77 -0
- package/dist/lib/format.cjs.map +1 -0
- package/dist/lib/format.d.ts +30 -0
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/lib/format.js +77 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/overlay/DropdownMenu.cjs +18 -2
- package/dist/overlay/DropdownMenu.cjs.map +1 -1
- package/dist/overlay/DropdownMenu.d.ts +3 -1
- package/dist/overlay/DropdownMenu.d.ts.map +1 -1
- package/dist/overlay/DropdownMenu.js +18 -2
- package/dist/overlay/DropdownMenu.js.map +1 -1
- package/dist/rpc/types.cjs.map +1 -1
- package/dist/rpc/types.d.ts +4 -0
- package/dist/rpc/types.d.ts.map +1 -1
- package/dist/rpc/types.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/utils.cjs +6 -0
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +6 -0
- package/dist/utils.js.map +1 -1
- 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":"AAQA,MAAM,SAAS;AAEf,SAAS,KAAK,OAAe,oBAAoB,GAAW;AAC1D,SAAO,IAAI,KAAK,aAAa,QAAQ,EAAE,uBAAuB,kBAAA,CAAmB,EAAE;AAAA,IACjF,OAAO,SAAS,KAAK,IAAI,QAAQ;AAAA,EAAA;AAErC;AAGA,SAAS,SAAS,GAAmB;AACnC,SAAO,EAAE,SAAS,IAAI,IAAI,EAAE,MAAM,GAAG,EAAE,IAAI;AAC7C;AASA,MAAM,aAAa,CAAC,KAAK,MAAM,MAAM,MAAM,MAAM,IAAI;AAO9C,SAAS,YAAY,OAAgB,MAAmC;AAC7E,QAAM,UAAS,6BAAM,WAAU;AAC/B,MAAI,UAAU,UAAa,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,EAAG,QAAO,MAAM,MAAM;AACrF,QAAM,QAAO,6BAAM,SAAQ;AAC3B,MAAI,OAAO;AACX,MAAI,OAAO;AACX,SAAO,QAAQ,QAAQ,OAAO,WAAW,SAAS,GAAG;AACnD,YAAQ;AACR;AAAA,EACF;AACA,QAAM,WAAW,QAAQ,MAAM,SAAS,IAAI,KAAK,QAAQ,CAAC,IAAI,SAAS,KAAK,QAAQ,CAAC,CAAC;AACtF,SAAO,GAAG,QAAQ,IAAI,WAAW,IAAI,CAAC,GAAG,MAAM;AACjD;AAGO,SAAS,qBAAqB,OAAwB;AAC3D,SAAO,YAAY,OAAO,EAAE,QAAQ,MAAM;AAC5C;AAEA,MAAM,cAAc,CAAC,IAAI,KAAK,KAAK,KAAK,GAAG;AAGpC,SAAS,YAAY,OAAwB;AAClD,MAAI,UAAU,UAAa,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AAC3D,QAAM,OAAO,QAAQ,IAAI,MAAM;AAC/B,MAAI,IAAI,KAAK,IAAI,KAAK;AACtB,MAAI,IAAI,IAAM,QAAO,GAAG,IAAI,GAAG,KAAK,CAAC,CAAC;AACtC,MAAI,OAAO;AACX,SAAO,KAAK,OAAQ,OAAO,YAAY,SAAS,GAAG;AACjD,SAAK;AACL;AAAA,EACF;AACA,QAAM,WAAW,KAAK,KAAK,EAAE,QAAQ,CAAC,IAAI,SAAS,EAAE,QAAQ,CAAC,CAAC;AAC/D,SAAO,GAAG,IAAI,GAAG,QAAQ,GAAG,YAAY,IAAI,CAAC;AAC/C;AAMO,SAAS,eAAe,OAAgB,MAAsC;AACnF,MAAI,UAAU,UAAa,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AAC3D,QAAM,MAAK,6BAAM,UAAS,MAAM,QAAQ,MAAO;AAC/C,QAAM,MAAM,KAAK,IAAI,EAAE;AACvB,MAAI,MAAM,IAAM,QAAO,GAAG,KAAK,IAAI,CAAC,CAAC;AACrC,MAAI,MAAM,IAAQ,QAAO,GAAG,KAAK,KAAK,GAAI,CAAC;AAC3C,MAAI,MAAM,KAAW,QAAO,GAAG,KAAK,KAAK,GAAM,CAAC;AAChD,SAAO,GAAG,KAAK,KAAK,IAAS,CAAC;AAChC;AAIA,MAAM,kBAAyD;AAAA,EAC7D,MAAM,CAAC,MAAM,YAAY,CAAC;AAAA,EAC1B,OAAO,CAAC,MAAM,YAAY,CAAC;AAAA,EAC3B,SAAS,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;AAAA,EAC1B,aAAa,CAAC,MAAM,GAAG,KAAK,IAAI,GAAG,CAAC;AAAA,EACpC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;AAAA,EACtB,OAAO,CAAC,MAAM,YAAY,GAAG,EAAE,MAAM,MAAM;AAAA,EAC3C,UAAU,CAAC,MAAM,YAAY,GAAG,EAAE,MAAM,KAAM;AAAA,EAC9C,KAAK,CAAC,MAAM,qBAAqB,CAAC;AAAA,EAClC,QAAQ,CAAC,MAAM,qBAAqB,CAAC;AAAA,EACrC,IAAI,CAAC,MAAM,eAAe,GAAG,EAAE,MAAM,MAAM;AAAA,EAC3C,GAAG,CAAC,MAAM,eAAe,GAAG,EAAE,MAAM,KAAK;AAC3C;AAOO,SAAS,WAAW,OAAe,MAAuB;AAC/D,QAAM,IAAI,OAAO,SAAS,KAAK,IAAI,QAAQ;AAC3C,MAAI,CAAC,KAAM,QAAO,KAAK,CAAC;AACxB,QAAM,MAAM,gBAAgB,IAAI;AAChC,MAAI,IAAK,QAAO,IAAI,CAAC;AACrB,SAAO,GAAG,KAAK,CAAC,CAAC,GAAG,IAAI;AAC1B;"}
|
|
@@ -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-
|
|
44
|
-
trigger ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
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-
|
|
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,
|
|
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-
|
|
42
|
-
trigger ? /* @__PURE__ */ jsx(
|
|
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,
|