@flanksource/clicky-ui 0.2.20 → 0.3.1
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 +5 -2
- package/dist/components/Combobox.cjs.map +1 -1
- package/dist/components/Combobox.d.ts.map +1 -1
- package/dist/components/Combobox.js +5 -2
- package/dist/components/Combobox.js.map +1 -1
- package/dist/components/FilterBar.cjs +280 -253
- package/dist/components/FilterBar.cjs.map +1 -1
- package/dist/components/FilterBar.d.ts.map +1 -1
- package/dist/components/FilterBar.js +280 -253
- package/dist/components/FilterBar.js.map +1 -1
- package/dist/components/JsonSchemaForm.cjs +3 -1
- package/dist/components/JsonSchemaForm.cjs.map +1 -1
- package/dist/components/JsonSchemaForm.d.ts.map +1 -1
- package/dist/components/JsonSchemaForm.js +4 -2
- package/dist/components/JsonSchemaForm.js.map +1 -1
- package/dist/components/MultiSelect.cjs +29 -32
- package/dist/components/MultiSelect.cjs.map +1 -1
- package/dist/components/MultiSelect.d.ts.map +1 -1
- package/dist/components/MultiSelect.js +30 -33
- package/dist/components/MultiSelect.js.map +1 -1
- package/dist/components/TimeRange.cjs +29 -37
- package/dist/components/TimeRange.cjs.map +1 -1
- package/dist/components/TimeRange.d.ts.map +1 -1
- package/dist/components/TimeRange.js +30 -38
- package/dist/components/TimeRange.js.map +1 -1
- package/dist/components/TreePickerField.cjs +6 -3
- package/dist/components/TreePickerField.cjs.map +1 -1
- package/dist/components/TreePickerField.d.ts.map +1 -1
- package/dist/components/TreePickerField.js +6 -3
- package/dist/components/TreePickerField.js.map +1 -1
- package/dist/components/icon-menu-picker.cjs +9 -3
- package/dist/components/icon-menu-picker.cjs.map +1 -1
- package/dist/components/icon-menu-picker.d.ts.map +1 -1
- package/dist/components/icon-menu-picker.js +9 -3
- package/dist/components/icon-menu-picker.js.map +1 -1
- package/dist/components/json-schema-form-fields.cjs +80 -2
- package/dist/components/json-schema-form-fields.cjs.map +1 -1
- package/dist/components/json-schema-form-fields.d.ts +15 -0
- package/dist/components/json-schema-form-fields.d.ts.map +1 -1
- package/dist/components/json-schema-form-fields.js +81 -3
- package/dist/components/json-schema-form-fields.js.map +1 -1
- package/dist/components/json-schema-form-refs.cjs +60 -0
- package/dist/components/json-schema-form-refs.cjs.map +1 -0
- package/dist/components/json-schema-form-refs.d.ts +3 -0
- package/dist/components/json-schema-form-refs.d.ts.map +1 -0
- package/dist/components/json-schema-form-refs.js +60 -0
- package/dist/components/json-schema-form-refs.js.map +1 -0
- package/dist/components/json-schema-form-render.cjs +14 -2
- package/dist/components/json-schema-form-render.cjs.map +1 -1
- package/dist/components/json-schema-form-render.d.ts.map +1 -1
- package/dist/components/json-schema-form-render.js +15 -3
- package/dist/components/json-schema-form-render.js.map +1 -1
- package/dist/components/json-schema-form-resolve.cjs +5 -0
- package/dist/components/json-schema-form-resolve.cjs.map +1 -1
- package/dist/components/json-schema-form-resolve.d.ts.map +1 -1
- package/dist/components/json-schema-form-resolve.js +5 -0
- package/dist/components/json-schema-form-resolve.js.map +1 -1
- package/dist/components/json-schema-form-template-menu.cjs +83 -0
- package/dist/components/json-schema-form-template-menu.cjs.map +1 -0
- package/dist/components/json-schema-form-template-menu.d.ts +15 -0
- package/dist/components/json-schema-form-template-menu.d.ts.map +1 -0
- package/dist/components/json-schema-form-template-menu.js +83 -0
- package/dist/components/json-schema-form-template-menu.js.map +1 -0
- package/dist/components/json-schema-form-template.cjs +18 -0
- package/dist/components/json-schema-form-template.cjs.map +1 -0
- package/dist/components/json-schema-form-template.d.ts +13 -0
- package/dist/components/json-schema-form-template.d.ts.map +1 -0
- package/dist/components/json-schema-form-template.js +18 -0
- package/dist/components/json-schema-form-template.js.map +1 -0
- package/dist/components/json-schema-form-types.d.ts +5 -1
- package/dist/components/json-schema-form-types.d.ts.map +1 -1
- package/dist/components.cjs +4 -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 +4 -0
- package/dist/components.js.map +1 -1
- package/dist/data/Clicky.cjs +6 -9
- package/dist/data/Clicky.cjs.map +1 -1
- package/dist/data/Clicky.d.ts.map +1 -1
- package/dist/data/Clicky.js +6 -9
- package/dist/data/Clicky.js.map +1 -1
- package/dist/data/DataTable.cjs +23 -17
- package/dist/data/DataTable.cjs.map +1 -1
- package/dist/data/DataTable.d.ts +7 -1
- package/dist/data/DataTable.d.ts.map +1 -1
- package/dist/data/DataTable.js +24 -18
- package/dist/data/DataTable.js.map +1 -1
- package/dist/data/Gauge.cjs +37 -4
- package/dist/data/Gauge.cjs.map +1 -1
- package/dist/data/Gauge.d.ts +6 -1
- package/dist/data/Gauge.d.ts.map +1 -1
- package/dist/data/Gauge.js +37 -4
- package/dist/data/Gauge.js.map +1 -1
- package/dist/data/TimeseriesCoreBars.cjs +127 -29
- package/dist/data/TimeseriesCoreBars.cjs.map +1 -1
- package/dist/data/TimeseriesCoreBars.d.ts +11 -1
- package/dist/data/TimeseriesCoreBars.d.ts.map +1 -1
- package/dist/data/TimeseriesCoreBars.js +128 -30
- package/dist/data/TimeseriesCoreBars.js.map +1 -1
- package/dist/data/TimeseriesGauge.cjs +105 -47
- package/dist/data/TimeseriesGauge.cjs.map +1 -1
- package/dist/data/TimeseriesGauge.d.ts +6 -1
- package/dist/data/TimeseriesGauge.d.ts.map +1 -1
- package/dist/data/TimeseriesGauge.js +105 -47
- package/dist/data/TimeseriesGauge.js.map +1 -1
- package/dist/data/WorkloadCard.cjs +371 -0
- package/dist/data/WorkloadCard.cjs.map +1 -0
- package/dist/data/WorkloadCard.d.ts +24 -0
- package/dist/data/WorkloadCard.d.ts.map +1 -0
- package/dist/data/WorkloadCard.js +371 -0
- package/dist/data/WorkloadCard.js.map +1 -0
- package/dist/data/WorkloadCard.model.cjs +51 -0
- package/dist/data/WorkloadCard.model.cjs.map +1 -0
- package/dist/data/WorkloadCard.model.d.ts +53 -0
- package/dist/data/WorkloadCard.model.d.ts.map +1 -0
- package/dist/data/WorkloadCard.model.js +51 -0
- package/dist/data/WorkloadCard.model.js.map +1 -0
- package/dist/data/ai/ChatFab.cjs +3 -1
- package/dist/data/ai/ChatFab.cjs.map +1 -1
- package/dist/data/ai/ChatFab.d.ts.map +1 -1
- package/dist/data/ai/ChatFab.js +3 -1
- package/dist/data/ai/ChatFab.js.map +1 -1
- package/dist/data/ai/ChatWindow.cjs +25 -7
- package/dist/data/ai/ChatWindow.cjs.map +1 -1
- package/dist/data/ai/ChatWindow.d.ts +11 -3
- package/dist/data/ai/ChatWindow.d.ts.map +1 -1
- package/dist/data/ai/ChatWindow.js +26 -8
- package/dist/data/ai/ChatWindow.js.map +1 -1
- package/dist/data/ai/chat-window-context.cjs +2 -1
- package/dist/data/ai/chat-window-context.cjs.map +1 -1
- package/dist/data/ai/chat-window-context.d.ts +1 -1
- package/dist/data/ai/chat-window-context.d.ts.map +1 -1
- package/dist/data/ai/chat-window-context.js +2 -1
- package/dist/data/ai/chat-window-context.js.map +1 -1
- package/dist/data/ai/context.cjs.map +1 -1
- package/dist/data/ai/context.d.ts +3 -0
- package/dist/data/ai/context.d.ts.map +1 -1
- package/dist/data/ai/context.js.map +1 -1
- package/dist/data/chat/Chat.cjs +13 -2
- package/dist/data/chat/Chat.cjs.map +1 -1
- package/dist/data/chat/Chat.d.ts +4 -2
- package/dist/data/chat/Chat.d.ts.map +1 -1
- package/dist/data/chat/Chat.js +14 -3
- package/dist/data/chat/Chat.js.map +1 -1
- package/dist/data/chat/Conversation.cjs +47 -2
- package/dist/data/chat/Conversation.cjs.map +1 -1
- package/dist/data/chat/Conversation.d.ts +8 -2
- package/dist/data/chat/Conversation.d.ts.map +1 -1
- package/dist/data/chat/Conversation.js +47 -2
- package/dist/data/chat/Conversation.js.map +1 -1
- package/dist/data/chat/Message.cjs +13 -4
- package/dist/data/chat/Message.cjs.map +1 -1
- package/dist/data/chat/Message.d.ts +4 -2
- package/dist/data/chat/Message.d.ts.map +1 -1
- package/dist/data/chat/Message.js +13 -4
- package/dist/data/chat/Message.js.map +1 -1
- package/dist/data/chat/ToolCall.cjs +9 -4
- package/dist/data/chat/ToolCall.cjs.map +1 -1
- package/dist/data/chat/ToolCall.d.ts +3 -2
- package/dist/data/chat/ToolCall.d.ts.map +1 -1
- package/dist/data/chat/ToolCall.js +9 -4
- package/dist/data/chat/ToolCall.js.map +1 -1
- package/dist/data/chat/index.d.ts +1 -1
- package/dist/data/chat/index.d.ts.map +1 -1
- package/dist/data/chat/types.cjs.map +1 -1
- package/dist/data/chat/types.d.ts +7 -0
- package/dist/data/chat/types.d.ts.map +1 -1
- package/dist/data/chat/types.js.map +1 -1
- package/dist/data/version-info.cjs +3 -3
- package/dist/data/version-info.js +3 -3
- package/dist/data.cjs +7 -0
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +5 -3
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +8 -1
- package/dist/data.js.map +1 -1
- package/dist/hooks/use-sort.cjs +19 -10
- package/dist/hooks/use-sort.cjs.map +1 -1
- package/dist/hooks/use-sort.d.ts +3 -0
- package/dist/hooks/use-sort.d.ts.map +1 -1
- package/dist/hooks/use-sort.js +19 -10
- package/dist/hooks/use-sort.js.map +1 -1
- package/dist/index.cjs +11 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/layout/AppShell.cjs +6 -10
- package/dist/layout/AppShell.cjs.map +1 -1
- package/dist/layout/AppShell.d.ts +2 -0
- package/dist/layout/AppShell.d.ts.map +1 -1
- package/dist/layout/AppShell.js +6 -10
- package/dist/layout/AppShell.js.map +1 -1
- package/dist/overlay/DropdownMenu.cjs +50 -38
- package/dist/overlay/DropdownMenu.cjs.map +1 -1
- package/dist/overlay/DropdownMenu.d.ts +5 -1
- package/dist/overlay/DropdownMenu.d.ts.map +1 -1
- package/dist/overlay/DropdownMenu.js +50 -38
- package/dist/overlay/DropdownMenu.js.map +1 -1
- package/dist/overlay/HoverCard.cjs +7 -2
- package/dist/overlay/HoverCard.cjs.map +1 -1
- package/dist/overlay/HoverCard.d.ts.map +1 -1
- package/dist/overlay/HoverCard.js +7 -2
- package/dist/overlay/HoverCard.js.map +1 -1
- package/dist/overlay/Modal.cjs +20 -17
- package/dist/overlay/Modal.cjs.map +1 -1
- package/dist/overlay/Modal.d.ts.map +1 -1
- package/dist/overlay/Modal.js +21 -18
- package/dist/overlay/Modal.js.map +1 -1
- package/dist/overlay/Toast.cjs +9 -1
- package/dist/overlay/Toast.cjs.map +1 -1
- package/dist/overlay/Toast.d.ts.map +1 -1
- package/dist/overlay/Toast.js +9 -1
- package/dist/overlay/Toast.js.map +1 -1
- package/dist/overlay/modalStack.cjs +53 -1
- package/dist/overlay/modalStack.cjs.map +1 -1
- package/dist/overlay/modalStack.d.ts +12 -0
- package/dist/overlay/modalStack.d.ts.map +1 -1
- package/dist/overlay/modalStack.js +54 -2
- package/dist/overlay/modalStack.js.map +1 -1
- package/dist/overlay/zIndex.cjs +20 -0
- package/dist/overlay/zIndex.cjs.map +1 -0
- package/dist/overlay/zIndex.d.ts +39 -0
- package/dist/overlay/zIndex.d.ts.map +1 -0
- package/dist/overlay/zIndex.js +20 -0
- package/dist/overlay/zIndex.js.map +1 -0
- package/dist/rpc/AcceptPicker.cjs +28 -26
- package/dist/rpc/AcceptPicker.cjs.map +1 -1
- package/dist/rpc/AcceptPicker.d.ts.map +1 -1
- package/dist/rpc/AcceptPicker.js +29 -27
- package/dist/rpc/AcceptPicker.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/data/Gauge.cjs
CHANGED
|
@@ -20,10 +20,37 @@ function Gauge({
|
|
|
20
20
|
icon,
|
|
21
21
|
subtitle,
|
|
22
22
|
meta,
|
|
23
|
+
variant = "default",
|
|
24
|
+
showLabel = true,
|
|
23
25
|
className
|
|
24
26
|
}) {
|
|
25
27
|
const pct = max > 0 ? Math.round(value / max * 100) : 0;
|
|
26
28
|
const displayValue = max === 100 ? value : pct;
|
|
29
|
+
const labelText = typeof label === "string" || typeof label === "number" ? String(label) : void 0;
|
|
30
|
+
const valueNode = /* @__PURE__ */ jsxRuntime.jsxs("span", { className: utils.cn("font-semibold tabular-nums", toneClass[tone]), children: [
|
|
31
|
+
displayValue,
|
|
32
|
+
suffix ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-0.5 text-[0.85em] opacity-60", children: suffix }) : null
|
|
33
|
+
] });
|
|
34
|
+
if (variant === "cell") {
|
|
35
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
36
|
+
"span",
|
|
37
|
+
{
|
|
38
|
+
className: utils.cn(
|
|
39
|
+
"inline-flex min-w-0 items-center gap-1.5 whitespace-nowrap align-middle text-xs",
|
|
40
|
+
className
|
|
41
|
+
),
|
|
42
|
+
title: labelText,
|
|
43
|
+
"aria-label": !showLabel ? labelText : void 0,
|
|
44
|
+
children: [
|
|
45
|
+
icon ? /* @__PURE__ */ jsxRuntime.jsx(GaugeIcon, { icon }) : null,
|
|
46
|
+
showLabel ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate text-muted-foreground", children: label }) : null,
|
|
47
|
+
valueNode,
|
|
48
|
+
meta ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 truncate text-muted-foreground", children: meta }) : null
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
const showHeader = icon || showLabel || meta;
|
|
27
54
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
28
55
|
"div",
|
|
29
56
|
{
|
|
@@ -31,18 +58,24 @@ function Gauge({
|
|
|
31
58
|
"flex min-h-28 w-[15.5rem] shrink-0 flex-col rounded-lg border border-border bg-card p-3",
|
|
32
59
|
className
|
|
33
60
|
),
|
|
61
|
+
title: !showLabel ? labelText : void 0,
|
|
62
|
+
"aria-label": !showLabel ? labelText : void 0,
|
|
34
63
|
children: [
|
|
35
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 text-xs text-muted-foreground", children: [
|
|
64
|
+
showHeader ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 text-xs text-muted-foreground", children: [
|
|
36
65
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex min-w-0 items-center gap-1.5", children: [
|
|
37
66
|
icon ? /* @__PURE__ */ jsxRuntime.jsx(GaugeIcon, { icon }) : null,
|
|
38
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: label })
|
|
67
|
+
showLabel ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: label }) : null
|
|
39
68
|
] }),
|
|
40
69
|
meta ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 truncate", children: meta }) : null
|
|
41
|
-
] }),
|
|
70
|
+
] }) : null,
|
|
42
71
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
43
72
|
"span",
|
|
44
73
|
{
|
|
45
|
-
className: utils.cn(
|
|
74
|
+
className: utils.cn(
|
|
75
|
+
showHeader && "mt-2",
|
|
76
|
+
"text-2xl font-semibold tabular-nums tracking-tight",
|
|
77
|
+
toneClass[tone]
|
|
78
|
+
),
|
|
46
79
|
children: [
|
|
47
80
|
displayValue,
|
|
48
81
|
suffix ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-0.5 text-sm opacity-60", children: suffix }) : null
|
package/dist/data/Gauge.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Gauge.cjs","sources":["../../src/data/Gauge.tsx"],"sourcesContent":["import { isValidElement, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\n\nexport type GaugeTone = \"neutral\" | \"success\" | \"warning\" | \"danger\" | \"info\";\n\nexport type GaugeProps = {\n label: ReactNode;\n value: number;\n max?: number;\n tone?: GaugeTone;\n suffix?: string;\n icon?: string | StaticIconComponent | ReactNode;\n subtitle?: ReactNode;\n meta?: ReactNode;\n className?: string;\n};\n\nconst toneClass: Record<GaugeTone, string> = {\n neutral: \"text-foreground\",\n success: \"text-green-600 dark:text-green-400\",\n warning: \"text-yellow-600 dark:text-yellow-400\",\n danger: \"text-red-600 dark:text-red-400\",\n info: \"text-blue-600 dark:text-blue-400\",\n};\n\nexport function Gauge({\n label,\n value,\n max = 100,\n tone = \"neutral\",\n suffix = \"%\",\n icon,\n subtitle,\n meta,\n className,\n}: GaugeProps) {\n const pct = max > 0 ? Math.round((value / max) * 100) : 0;\n const displayValue = max === 100 ? value : pct;\n return (\n <div\n className={cn(\n \"flex min-h-28 w-[15.5rem] shrink-0 flex-col rounded-lg border border-border bg-card p-3\",\n className,\n )}\n >\n <div className=\"flex items-center justify-between gap-2 text-xs text-muted-foreground\">\n
|
|
1
|
+
{"version":3,"file":"Gauge.cjs","sources":["../../src/data/Gauge.tsx"],"sourcesContent":["import { isValidElement, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\n\nexport type GaugeTone = \"neutral\" | \"success\" | \"warning\" | \"danger\" | \"info\";\nexport type GaugeVariant = \"default\" | \"cell\";\n\nexport type GaugeProps = {\n label: ReactNode;\n value: number;\n max?: number;\n tone?: GaugeTone;\n suffix?: string;\n icon?: string | StaticIconComponent | ReactNode;\n subtitle?: ReactNode;\n meta?: ReactNode;\n /** Visual density/layout. `cell` is a compact inline form for table/grid cells. */\n variant?: GaugeVariant;\n /** Show the text label. Icons and values remain visible when false. */\n showLabel?: boolean;\n className?: string;\n};\n\nconst toneClass: Record<GaugeTone, string> = {\n neutral: \"text-foreground\",\n success: \"text-green-600 dark:text-green-400\",\n warning: \"text-yellow-600 dark:text-yellow-400\",\n danger: \"text-red-600 dark:text-red-400\",\n info: \"text-blue-600 dark:text-blue-400\",\n};\n\nexport function Gauge({\n label,\n value,\n max = 100,\n tone = \"neutral\",\n suffix = \"%\",\n icon,\n subtitle,\n meta,\n variant = \"default\",\n showLabel = true,\n className,\n}: GaugeProps) {\n const pct = max > 0 ? Math.round((value / max) * 100) : 0;\n const displayValue = max === 100 ? value : pct;\n const labelText =\n typeof label === \"string\" || typeof label === \"number\" ? String(label) : undefined;\n const valueNode = (\n <span className={cn(\"font-semibold tabular-nums\", toneClass[tone])}>\n {displayValue}\n {suffix ? <span className=\"ml-0.5 text-[0.85em] opacity-60\">{suffix}</span> : null}\n </span>\n );\n\n if (variant === \"cell\") {\n return (\n <span\n className={cn(\n \"inline-flex min-w-0 items-center gap-1.5 whitespace-nowrap align-middle text-xs\",\n className,\n )}\n title={labelText}\n aria-label={!showLabel ? labelText : undefined}\n >\n {icon ? <GaugeIcon icon={icon} /> : null}\n {showLabel ? (\n <span className=\"min-w-0 truncate text-muted-foreground\">{label}</span>\n ) : null}\n {valueNode}\n {meta ? <span className=\"shrink-0 truncate text-muted-foreground\">{meta}</span> : null}\n </span>\n );\n }\n\n const showHeader = icon || showLabel || meta;\n\n return (\n <div\n className={cn(\n \"flex min-h-28 w-[15.5rem] shrink-0 flex-col rounded-lg border border-border bg-card p-3\",\n className,\n )}\n title={!showLabel ? labelText : undefined}\n aria-label={!showLabel ? labelText : undefined}\n >\n {showHeader ? (\n <div className=\"flex items-center justify-between gap-2 text-xs text-muted-foreground\">\n <span className=\"inline-flex min-w-0 items-center gap-1.5\">\n {icon ? <GaugeIcon icon={icon} /> : null}\n {showLabel ? <span className=\"truncate\">{label}</span> : null}\n </span>\n {meta ? <span className=\"shrink-0 truncate\">{meta}</span> : null}\n </div>\n ) : null}\n <span\n className={cn(\n showHeader && \"mt-2\",\n \"text-2xl font-semibold tabular-nums tracking-tight\",\n toneClass[tone],\n )}\n >\n {displayValue}\n {suffix ? <span className=\"ml-0.5 text-sm opacity-60\">{suffix}</span> : null}\n </span>\n {subtitle ? (\n <span className=\"mt-1 truncate text-xs text-muted-foreground\">{subtitle}</span>\n ) : null}\n </div>\n );\n}\n\nfunction GaugeIcon({ icon }: { icon: string | StaticIconComponent | ReactNode }) {\n if (typeof icon === \"string\") {\n return <Icon name={icon} width={16} height={16} className=\"text-muted-foreground\" />;\n }\n if (isValidElement(icon)) {\n return (\n <span className=\"inline-flex h-4 w-4 shrink-0 items-center justify-center text-muted-foreground [&>svg]:h-4 [&>svg]:w-4\">\n {icon}\n </span>\n );\n }\n\n return (\n <Icon\n icon={icon as StaticIconComponent}\n width={16}\n height={16}\n className=\"text-muted-foreground\"\n />\n );\n}\n"],"names":["cn","jsx","jsxs","Icon","isValidElement"],"mappings":"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"}
|
package/dist/data/Gauge.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { StaticIconComponent } from './Icon';
|
|
3
3
|
export type GaugeTone = "neutral" | "success" | "warning" | "danger" | "info";
|
|
4
|
+
export type GaugeVariant = "default" | "cell";
|
|
4
5
|
export type GaugeProps = {
|
|
5
6
|
label: ReactNode;
|
|
6
7
|
value: number;
|
|
@@ -10,7 +11,11 @@ export type GaugeProps = {
|
|
|
10
11
|
icon?: string | StaticIconComponent | ReactNode;
|
|
11
12
|
subtitle?: ReactNode;
|
|
12
13
|
meta?: ReactNode;
|
|
14
|
+
/** Visual density/layout. `cell` is a compact inline form for table/grid cells. */
|
|
15
|
+
variant?: GaugeVariant;
|
|
16
|
+
/** Show the text label. Icons and values remain visible when false. */
|
|
17
|
+
showLabel?: boolean;
|
|
13
18
|
className?: string;
|
|
14
19
|
};
|
|
15
|
-
export declare function Gauge({ label, value, max, tone, suffix, icon, subtitle, meta, className, }: GaugeProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export declare function Gauge({ label, value, max, tone, suffix, icon, subtitle, meta, variant, showLabel, className, }: GaugeProps): import("react/jsx-runtime").JSX.Element;
|
|
16
21
|
//# sourceMappingURL=Gauge.d.ts.map
|
package/dist/data/Gauge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Gauge.d.ts","sourceRoot":"","sources":["../../src/data/Gauge.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvD,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAExD,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"Gauge.d.ts","sourceRoot":"","sources":["../../src/data/Gauge.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvD,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAExD,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;AAC9E,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,MAAM,CAAC;AAE9C,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAAC;IAChD,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,mFAAmF;IACnF,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,uEAAuE;IACvE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAUF,wBAAgB,KAAK,CAAC,EACpB,KAAK,EACL,KAAK,EACL,GAAS,EACT,IAAgB,EAChB,MAAY,EACZ,IAAI,EACJ,QAAQ,EACR,IAAI,EACJ,OAAmB,EACnB,SAAgB,EAChB,SAAS,GACV,EAAE,UAAU,2CAmEZ"}
|
package/dist/data/Gauge.js
CHANGED
|
@@ -18,10 +18,37 @@ function Gauge({
|
|
|
18
18
|
icon,
|
|
19
19
|
subtitle,
|
|
20
20
|
meta,
|
|
21
|
+
variant = "default",
|
|
22
|
+
showLabel = true,
|
|
21
23
|
className
|
|
22
24
|
}) {
|
|
23
25
|
const pct = max > 0 ? Math.round(value / max * 100) : 0;
|
|
24
26
|
const displayValue = max === 100 ? value : pct;
|
|
27
|
+
const labelText = typeof label === "string" || typeof label === "number" ? String(label) : void 0;
|
|
28
|
+
const valueNode = /* @__PURE__ */ jsxs("span", { className: cn("font-semibold tabular-nums", toneClass[tone]), children: [
|
|
29
|
+
displayValue,
|
|
30
|
+
suffix ? /* @__PURE__ */ jsx("span", { className: "ml-0.5 text-[0.85em] opacity-60", children: suffix }) : null
|
|
31
|
+
] });
|
|
32
|
+
if (variant === "cell") {
|
|
33
|
+
return /* @__PURE__ */ jsxs(
|
|
34
|
+
"span",
|
|
35
|
+
{
|
|
36
|
+
className: cn(
|
|
37
|
+
"inline-flex min-w-0 items-center gap-1.5 whitespace-nowrap align-middle text-xs",
|
|
38
|
+
className
|
|
39
|
+
),
|
|
40
|
+
title: labelText,
|
|
41
|
+
"aria-label": !showLabel ? labelText : void 0,
|
|
42
|
+
children: [
|
|
43
|
+
icon ? /* @__PURE__ */ jsx(GaugeIcon, { icon }) : null,
|
|
44
|
+
showLabel ? /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate text-muted-foreground", children: label }) : null,
|
|
45
|
+
valueNode,
|
|
46
|
+
meta ? /* @__PURE__ */ jsx("span", { className: "shrink-0 truncate text-muted-foreground", children: meta }) : null
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
const showHeader = icon || showLabel || meta;
|
|
25
52
|
return /* @__PURE__ */ jsxs(
|
|
26
53
|
"div",
|
|
27
54
|
{
|
|
@@ -29,18 +56,24 @@ function Gauge({
|
|
|
29
56
|
"flex min-h-28 w-[15.5rem] shrink-0 flex-col rounded-lg border border-border bg-card p-3",
|
|
30
57
|
className
|
|
31
58
|
),
|
|
59
|
+
title: !showLabel ? labelText : void 0,
|
|
60
|
+
"aria-label": !showLabel ? labelText : void 0,
|
|
32
61
|
children: [
|
|
33
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 text-xs text-muted-foreground", children: [
|
|
62
|
+
showHeader ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 text-xs text-muted-foreground", children: [
|
|
34
63
|
/* @__PURE__ */ jsxs("span", { className: "inline-flex min-w-0 items-center gap-1.5", children: [
|
|
35
64
|
icon ? /* @__PURE__ */ jsx(GaugeIcon, { icon }) : null,
|
|
36
|
-
/* @__PURE__ */ jsx("span", { className: "truncate", children: label })
|
|
65
|
+
showLabel ? /* @__PURE__ */ jsx("span", { className: "truncate", children: label }) : null
|
|
37
66
|
] }),
|
|
38
67
|
meta ? /* @__PURE__ */ jsx("span", { className: "shrink-0 truncate", children: meta }) : null
|
|
39
|
-
] }),
|
|
68
|
+
] }) : null,
|
|
40
69
|
/* @__PURE__ */ jsxs(
|
|
41
70
|
"span",
|
|
42
71
|
{
|
|
43
|
-
className: cn(
|
|
72
|
+
className: cn(
|
|
73
|
+
showHeader && "mt-2",
|
|
74
|
+
"text-2xl font-semibold tabular-nums tracking-tight",
|
|
75
|
+
toneClass[tone]
|
|
76
|
+
),
|
|
44
77
|
children: [
|
|
45
78
|
displayValue,
|
|
46
79
|
suffix ? /* @__PURE__ */ jsx("span", { className: "ml-0.5 text-sm opacity-60", children: suffix }) : null
|
package/dist/data/Gauge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Gauge.js","sources":["../../src/data/Gauge.tsx"],"sourcesContent":["import { isValidElement, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\n\nexport type GaugeTone = \"neutral\" | \"success\" | \"warning\" | \"danger\" | \"info\";\n\nexport type GaugeProps = {\n label: ReactNode;\n value: number;\n max?: number;\n tone?: GaugeTone;\n suffix?: string;\n icon?: string | StaticIconComponent | ReactNode;\n subtitle?: ReactNode;\n meta?: ReactNode;\n className?: string;\n};\n\nconst toneClass: Record<GaugeTone, string> = {\n neutral: \"text-foreground\",\n success: \"text-green-600 dark:text-green-400\",\n warning: \"text-yellow-600 dark:text-yellow-400\",\n danger: \"text-red-600 dark:text-red-400\",\n info: \"text-blue-600 dark:text-blue-400\",\n};\n\nexport function Gauge({\n label,\n value,\n max = 100,\n tone = \"neutral\",\n suffix = \"%\",\n icon,\n subtitle,\n meta,\n className,\n}: GaugeProps) {\n const pct = max > 0 ? Math.round((value / max) * 100) : 0;\n const displayValue = max === 100 ? value : pct;\n return (\n <div\n className={cn(\n \"flex min-h-28 w-[15.5rem] shrink-0 flex-col rounded-lg border border-border bg-card p-3\",\n className,\n )}\n >\n <div className=\"flex items-center justify-between gap-2 text-xs text-muted-foreground\">\n
|
|
1
|
+
{"version":3,"file":"Gauge.js","sources":["../../src/data/Gauge.tsx"],"sourcesContent":["import { isValidElement, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\n\nexport type GaugeTone = \"neutral\" | \"success\" | \"warning\" | \"danger\" | \"info\";\nexport type GaugeVariant = \"default\" | \"cell\";\n\nexport type GaugeProps = {\n label: ReactNode;\n value: number;\n max?: number;\n tone?: GaugeTone;\n suffix?: string;\n icon?: string | StaticIconComponent | ReactNode;\n subtitle?: ReactNode;\n meta?: ReactNode;\n /** Visual density/layout. `cell` is a compact inline form for table/grid cells. */\n variant?: GaugeVariant;\n /** Show the text label. Icons and values remain visible when false. */\n showLabel?: boolean;\n className?: string;\n};\n\nconst toneClass: Record<GaugeTone, string> = {\n neutral: \"text-foreground\",\n success: \"text-green-600 dark:text-green-400\",\n warning: \"text-yellow-600 dark:text-yellow-400\",\n danger: \"text-red-600 dark:text-red-400\",\n info: \"text-blue-600 dark:text-blue-400\",\n};\n\nexport function Gauge({\n label,\n value,\n max = 100,\n tone = \"neutral\",\n suffix = \"%\",\n icon,\n subtitle,\n meta,\n variant = \"default\",\n showLabel = true,\n className,\n}: GaugeProps) {\n const pct = max > 0 ? Math.round((value / max) * 100) : 0;\n const displayValue = max === 100 ? value : pct;\n const labelText =\n typeof label === \"string\" || typeof label === \"number\" ? String(label) : undefined;\n const valueNode = (\n <span className={cn(\"font-semibold tabular-nums\", toneClass[tone])}>\n {displayValue}\n {suffix ? <span className=\"ml-0.5 text-[0.85em] opacity-60\">{suffix}</span> : null}\n </span>\n );\n\n if (variant === \"cell\") {\n return (\n <span\n className={cn(\n \"inline-flex min-w-0 items-center gap-1.5 whitespace-nowrap align-middle text-xs\",\n className,\n )}\n title={labelText}\n aria-label={!showLabel ? labelText : undefined}\n >\n {icon ? <GaugeIcon icon={icon} /> : null}\n {showLabel ? (\n <span className=\"min-w-0 truncate text-muted-foreground\">{label}</span>\n ) : null}\n {valueNode}\n {meta ? <span className=\"shrink-0 truncate text-muted-foreground\">{meta}</span> : null}\n </span>\n );\n }\n\n const showHeader = icon || showLabel || meta;\n\n return (\n <div\n className={cn(\n \"flex min-h-28 w-[15.5rem] shrink-0 flex-col rounded-lg border border-border bg-card p-3\",\n className,\n )}\n title={!showLabel ? labelText : undefined}\n aria-label={!showLabel ? labelText : undefined}\n >\n {showHeader ? (\n <div className=\"flex items-center justify-between gap-2 text-xs text-muted-foreground\">\n <span className=\"inline-flex min-w-0 items-center gap-1.5\">\n {icon ? <GaugeIcon icon={icon} /> : null}\n {showLabel ? <span className=\"truncate\">{label}</span> : null}\n </span>\n {meta ? <span className=\"shrink-0 truncate\">{meta}</span> : null}\n </div>\n ) : null}\n <span\n className={cn(\n showHeader && \"mt-2\",\n \"text-2xl font-semibold tabular-nums tracking-tight\",\n toneClass[tone],\n )}\n >\n {displayValue}\n {suffix ? <span className=\"ml-0.5 text-sm opacity-60\">{suffix}</span> : null}\n </span>\n {subtitle ? (\n <span className=\"mt-1 truncate text-xs text-muted-foreground\">{subtitle}</span>\n ) : null}\n </div>\n );\n}\n\nfunction GaugeIcon({ icon }: { icon: string | StaticIconComponent | ReactNode }) {\n if (typeof icon === \"string\") {\n return <Icon name={icon} width={16} height={16} className=\"text-muted-foreground\" />;\n }\n if (isValidElement(icon)) {\n return (\n <span className=\"inline-flex h-4 w-4 shrink-0 items-center justify-center text-muted-foreground [&>svg]:h-4 [&>svg]:w-4\">\n {icon}\n </span>\n );\n }\n\n return (\n <Icon\n icon={icon as StaticIconComponent}\n width={16}\n height={16}\n className=\"text-muted-foreground\"\n />\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -24,9 +24,25 @@ function toneClass(pct, [warn, danger]) {
|
|
|
24
24
|
}
|
|
25
25
|
function CoreBarsIcon({ icon }) {
|
|
26
26
|
if (typeof icon === "string") {
|
|
27
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
27
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
28
|
+
Icon.Icon,
|
|
29
|
+
{
|
|
30
|
+
name: icon,
|
|
31
|
+
width: 14,
|
|
32
|
+
height: 14,
|
|
33
|
+
className: "text-muted-foreground"
|
|
34
|
+
}
|
|
35
|
+
);
|
|
28
36
|
}
|
|
29
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
37
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
38
|
+
Icon.Icon,
|
|
39
|
+
{
|
|
40
|
+
icon,
|
|
41
|
+
width: 14,
|
|
42
|
+
height: 14,
|
|
43
|
+
className: "text-muted-foreground"
|
|
44
|
+
}
|
|
45
|
+
);
|
|
30
46
|
}
|
|
31
47
|
function formatCores(cores) {
|
|
32
48
|
return Number.isInteger(cores) ? String(cores) : cores.toFixed(1);
|
|
@@ -40,6 +56,10 @@ function TimeseriesCoreBars({
|
|
|
40
56
|
range = "1h",
|
|
41
57
|
refreshMs = 5e3,
|
|
42
58
|
thresholds = [75, 90],
|
|
59
|
+
variant = "default",
|
|
60
|
+
showLabel = true,
|
|
61
|
+
showValue = true,
|
|
62
|
+
orientation,
|
|
43
63
|
fetcher = defaultFetcher,
|
|
44
64
|
className
|
|
45
65
|
}) {
|
|
@@ -77,33 +97,111 @@ function TimeseriesCoreBars({
|
|
|
77
97
|
const model = TimeseriesCoreBars_model.deriveCoreBars(usage, limit);
|
|
78
98
|
const tone = toneClass(model.pct, thresholds);
|
|
79
99
|
const caption = model.hasUsage ? `${formatCores(model.usageCores)} / ${model.limitCores !== void 0 ? formatCores(model.limitCores) : "?"} cores` : "—";
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
100
|
+
const compactCaption = model.hasUsage ? `${formatCores(model.usageCores)}/${model.limitCores !== void 0 ? formatCores(model.limitCores) : "?"} cores` : "—";
|
|
101
|
+
const titleText = `${title}: ${compactCaption}`;
|
|
102
|
+
const isCell = variant === "cell";
|
|
103
|
+
const resolvedOrientation = orientation ?? "vertical";
|
|
104
|
+
const isDense = model.coreCount > 4;
|
|
105
|
+
const valueText = isCell ? compactCaption : caption;
|
|
106
|
+
const hiddenAccessibleText = !showLabel || !showValue ? titleText : void 0;
|
|
107
|
+
const bars = /* @__PURE__ */ jsxRuntime.jsx(
|
|
108
|
+
"span",
|
|
109
|
+
{
|
|
110
|
+
className: utils.cn(
|
|
111
|
+
"flex shrink-0",
|
|
112
|
+
resolvedOrientation === "vertical" ? utils.cn("items-end", isCell ? "h-4 gap-px" : "h-10 gap-0.5") : utils.cn(
|
|
113
|
+
"flex-col justify-center",
|
|
114
|
+
isCell ? "h-4 w-10 gap-px" : "h-10 w-16 gap-0.5"
|
|
115
|
+
)
|
|
116
|
+
),
|
|
117
|
+
"aria-hidden": isCell ? "true" : void 0,
|
|
118
|
+
"data-orientation": resolvedOrientation,
|
|
119
|
+
"data-core-count": model.coreCount,
|
|
120
|
+
"data-core-density": isDense ? "compact" : "default",
|
|
121
|
+
children: model.bars.map((bar, i) => {
|
|
122
|
+
const corePct = Math.round(bar.fill * 100);
|
|
123
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
124
|
+
"span",
|
|
125
|
+
{
|
|
126
|
+
className: utils.cn(
|
|
127
|
+
"relative overflow-hidden bg-muted",
|
|
128
|
+
resolvedOrientation === "vertical" ? utils.cn(
|
|
129
|
+
"h-full",
|
|
130
|
+
isCell ? isDense ? "w-[3px] rounded-[2px]" : "w-1.5 rounded-[2px]" : isDense ? "w-1 rounded-sm" : "w-2 rounded-sm"
|
|
131
|
+
) : utils.cn("min-h-px w-full flex-1 rounded-[2px]")
|
|
132
|
+
),
|
|
133
|
+
title: `core ${i + 1}: ${corePct}%`,
|
|
134
|
+
"aria-label": !isCell ? `core ${i + 1}: ${corePct}%` : void 0,
|
|
135
|
+
"data-fill": bar.fill,
|
|
136
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
137
|
+
"span",
|
|
138
|
+
{
|
|
139
|
+
className: utils.cn(
|
|
140
|
+
"absolute bottom-0 left-0 transition-all duration-300",
|
|
141
|
+
tone
|
|
142
|
+
),
|
|
143
|
+
style: resolvedOrientation === "vertical" ? { height: `${corePct}%`, right: 0 } : { width: `${corePct}%`, top: 0 }
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
},
|
|
147
|
+
i
|
|
148
|
+
);
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
const valueNode = showValue ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
153
|
+
"span",
|
|
154
|
+
{
|
|
155
|
+
className: utils.cn(
|
|
156
|
+
"shrink-0 font-semibold tabular-nums text-foreground",
|
|
157
|
+
isCell ? null : "text-xs"
|
|
158
|
+
),
|
|
159
|
+
children: valueText
|
|
160
|
+
}
|
|
161
|
+
) : null;
|
|
162
|
+
const label = icon || showLabel ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
163
|
+
"span",
|
|
164
|
+
{
|
|
165
|
+
className: utils.cn(
|
|
166
|
+
"inline-flex min-w-0 items-center gap-1 text-xs text-muted-foreground"
|
|
167
|
+
),
|
|
168
|
+
children: [
|
|
169
|
+
icon ? /* @__PURE__ */ jsxRuntime.jsx(CoreBarsIcon, { icon }) : null,
|
|
170
|
+
showLabel ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: title }) : null
|
|
171
|
+
]
|
|
172
|
+
}
|
|
173
|
+
) : null;
|
|
174
|
+
if (isCell) {
|
|
175
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
176
|
+
"span",
|
|
177
|
+
{
|
|
178
|
+
className: utils.cn(
|
|
179
|
+
"inline-flex min-w-0 items-center gap-1.5 whitespace-nowrap align-middle text-xs",
|
|
180
|
+
className
|
|
181
|
+
),
|
|
182
|
+
title: titleText,
|
|
183
|
+
"aria-label": hiddenAccessibleText,
|
|
184
|
+
children: [
|
|
185
|
+
label,
|
|
186
|
+
bars,
|
|
187
|
+
valueNode
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
193
|
+
"div",
|
|
194
|
+
{
|
|
195
|
+
className: utils.cn("flex flex-col items-center gap-1", className),
|
|
196
|
+
title: hiddenAccessibleText,
|
|
197
|
+
"aria-label": hiddenAccessibleText,
|
|
198
|
+
children: [
|
|
199
|
+
bars,
|
|
200
|
+
valueNode,
|
|
201
|
+
label
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
);
|
|
107
205
|
}
|
|
108
206
|
exports.TimeseriesCoreBars = TimeseriesCoreBars;
|
|
109
207
|
//# sourceMappingURL=TimeseriesCoreBars.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeseriesCoreBars.cjs","sources":["../../src/data/TimeseriesCoreBars.tsx"],"sourcesContent":["import { useQueries } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\nimport type { GaugeSeries } from \"./TimeseriesGauge\";\nimport type { TimeseriesResponse } from \"./TimeseriesPanel\";\nimport { deriveCoreBars } from \"./TimeseriesCoreBars.model\";\n\nexport interface TimeseriesCoreBarsProps {\n /** Common prefix; the value/max requests are `baseUrl + id`. */\n baseUrl?: string;\n /** The metric whose latest value (CPU millicores) fills the bars. */\n value: GaugeSeries;\n /**\n * The CPU capacity. Either a `.limit`-style metric series (its latest value in\n * millicores) or a fixed number of millicores. Determines the number of bars\n * (one per core). When omitted or 0, the bar count falls back to the ceiling of\n * the usage in cores so an unbounded reading still renders.\n */\n max?: GaugeSeries | number;\n title: string;\n /** Iconify name or static icon component shown beside the label. */\n icon?: string | StaticIconComponent;\n /** Look-back window passed as ?since=; defaults to \"1h\". */\n range?: string;\n /** Poll interval in ms; defaults to 5000. Pass 0 to disable polling. */\n refreshMs?: number;\n /**\n * Utilisation thresholds (percent of capacity) at which the fill turns amber\n * then red. Defaults to [75, 90].\n */\n thresholds?: [warning: number, danger: number];\n /** Override the default fetch (e.g. to route through an app's API client). */\n fetcher?: (url: string) => Promise<TimeseriesResponse>;\n className?: string;\n}\n\nconst defaultFetcher = async (url: string): Promise<TimeseriesResponse> => {\n const res = await fetch(url);\n if (!res.ok) throw new Error(`metrics request failed: ${res.status}`);\n return res.json();\n};\n\nfunction latestValue(resp: TimeseriesResponse | undefined): number | undefined {\n const points = resp?.points;\n if (!points || points.length === 0) return undefined;\n return points[points.length - 1]?.value;\n}\n\nfunction toneClass(pct: number, [warn, danger]: [number, number]): string {\n if (pct >= danger) return \"bg-red-500\";\n if (pct >= warn) return \"bg-amber-500\";\n return \"bg-emerald-500\";\n}\n\nfunction CoreBarsIcon({ icon }: { icon: string | StaticIconComponent }) {\n if (typeof icon === \"string\") {\n return <Icon name={icon} width={14} height={14} className=\"text-muted-foreground\" />;\n }\n return <Icon icon={icon} width={14} height={14} className=\"text-muted-foreground\" />;\n}\n\nfunction formatCores(cores: number): string {\n return Number.isInteger(cores) ? String(cores) : cores.toFixed(1);\n}\n\n/**\n * TimeseriesCoreBars renders CPU utilisation as a row of vertical bars — one bar\n * per core (the ceiling of the CPU limit) — filled left to right by the latest\n * usage. The bar that straddles the usage boundary is partially filled (\"blocked\n * out\") to show the fractional core in use, and the trailing bars stay empty to\n * show headroom. Both usage and limit are read live from the timeseries store,\n * mirroring TimeseriesGauge's data plumbing; the bar colour crosses warning and\n * danger thresholds. A \"<used> / <cores>\" caption sits below with the icon+title.\n */\nexport function TimeseriesCoreBars({\n baseUrl = \"\",\n value,\n max,\n title,\n icon,\n range = \"1h\",\n refreshMs = 5000,\n thresholds = [75, 90],\n fetcher = defaultFetcher,\n className,\n}: TimeseriesCoreBarsProps) {\n const maxIsSeries = typeof max === \"object\";\n const maxSeries = maxIsSeries ? max : undefined;\n const ids = useMemo(() => {\n const list = [value.id];\n if (maxSeries) list.push(maxSeries.id);\n return list;\n }, [value.id, maxSeries]);\n\n const results = useQueries({\n queries: ids.map((id) => {\n const u = new URL(baseUrl + id, window.location.origin);\n if (range) u.searchParams.set(\"since\", range);\n const requestUrl = u.pathname + u.search;\n return {\n queryKey: [\"timeseries\", requestUrl],\n queryFn: () => fetcher(requestUrl),\n refetchInterval: refreshMs > 0 ? refreshMs : false,\n staleTime: 0,\n retry: 0,\n };\n }),\n });\n\n const rawValue = latestValue(results[0]?.data);\n const usage = rawValue === undefined ? undefined : value.transform ? value.transform(rawValue) : rawValue;\n\n let limit: number | undefined;\n if (typeof max === \"number\") {\n limit = max;\n } else if (maxSeries) {\n const rawMax = latestValue(results[1]?.data);\n limit = rawMax === undefined ? undefined : maxSeries.transform ? maxSeries.transform(rawMax) : rawMax;\n }\n\n const model = deriveCoreBars(usage, limit);\n const tone = toneClass(model.pct, thresholds);\n const caption = model.hasUsage\n ? `${formatCores(model.usageCores)} / ${model.limitCores !== undefined ? formatCores(model.limitCores) : \"?\"} cores`\n : \"—\";\n\n return (\n <div className={cn(\"flex flex-col items-center gap-1\", className)}>\n <div className=\"flex h-10 items-end gap-0.5\">\n {model.bars.map((bar, i) => {\n const corePct = Math.round(bar.fill * 100);\n return (\n <div\n key={i}\n className=\"relative h-full w-2 overflow-hidden rounded-sm bg-muted\"\n title={`core ${i + 1}: ${corePct}%`}\n aria-label={`core ${i + 1}: ${corePct}%`}\n data-fill={bar.fill}\n >\n <div\n className={cn(\"absolute inset-x-0 bottom-0 transition-all duration-300\", tone)}\n style={{ height: `${corePct}%` }}\n />\n </div>\n );\n })}\n </div>\n <span className=\"text-xs font-semibold text-foreground\">{caption}</span>\n <div className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n {icon ? <CoreBarsIcon icon={icon} /> : null}\n <span>{title}</span>\n </div>\n </div>\n );\n}\n"],"names":["jsx","Icon","useMemo","useQueries","deriveCoreBars","cn","jsxs"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TimeseriesCoreBars.cjs","sources":["../../src/data/TimeseriesCoreBars.tsx"],"sourcesContent":["import { useQueries } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\nimport type { GaugeSeries } from \"./TimeseriesGauge\";\nimport type { TimeseriesResponse } from \"./TimeseriesPanel\";\nimport { deriveCoreBars } from \"./TimeseriesCoreBars.model\";\n\nexport type TimeseriesCoreBarsVariant = \"default\" | \"cell\";\nexport type TimeseriesCoreBarsOrientation = \"horizontal\" | \"vertical\";\n\nexport interface TimeseriesCoreBarsProps {\n /** Common prefix; the value/max requests are `baseUrl + id`. */\n baseUrl?: string;\n /** The metric whose latest value (CPU millicores) fills the bars. */\n value: GaugeSeries;\n /**\n * The CPU capacity. Either a `.limit`-style metric series (its latest value in\n * millicores) or a fixed number of millicores. Determines the number of bars\n * (one per core). When omitted or 0, the bar count falls back to the ceiling of\n * the usage in cores so an unbounded reading still renders.\n */\n max?: GaugeSeries | number;\n title: string;\n /** Iconify name or static icon component shown beside the label. */\n icon?: string | StaticIconComponent;\n /** Look-back window passed as ?since=; defaults to \"1h\". */\n range?: string;\n /** Poll interval in ms; defaults to 5000. Pass 0 to disable polling. */\n refreshMs?: number;\n /**\n * Utilisation thresholds (percent of capacity) at which the fill turns amber\n * then red. Defaults to [75, 90].\n */\n thresholds?: [warning: number, danger: number];\n /** Visual density/layout. `cell` is a compact inline form for table/grid cells. */\n variant?: TimeseriesCoreBarsVariant;\n /** Show the title text. Icons and values remain visible unless separately hidden. */\n showLabel?: boolean;\n /** Show the value/capacity caption. Icons and bars remain visible when false. */\n showValue?: boolean;\n /** Bar direction. Vertical bars fill bottom-up; horizontal bars fill left-to-right. */\n orientation?: TimeseriesCoreBarsOrientation;\n /** Override the default fetch (e.g. to route through an app's API client). */\n fetcher?: (url: string) => Promise<TimeseriesResponse>;\n className?: string;\n}\n\nconst defaultFetcher = async (url: string): Promise<TimeseriesResponse> => {\n const res = await fetch(url);\n if (!res.ok) throw new Error(`metrics request failed: ${res.status}`);\n return res.json();\n};\n\nfunction latestValue(resp: TimeseriesResponse | undefined): number | undefined {\n const points = resp?.points;\n if (!points || points.length === 0) return undefined;\n return points[points.length - 1]?.value;\n}\n\nfunction toneClass(pct: number, [warn, danger]: [number, number]): string {\n if (pct >= danger) return \"bg-red-500\";\n if (pct >= warn) return \"bg-amber-500\";\n return \"bg-emerald-500\";\n}\n\nfunction CoreBarsIcon({ icon }: { icon: string | StaticIconComponent }) {\n if (typeof icon === \"string\") {\n return (\n <Icon\n name={icon}\n width={14}\n height={14}\n className=\"text-muted-foreground\"\n />\n );\n }\n return (\n <Icon\n icon={icon}\n width={14}\n height={14}\n className=\"text-muted-foreground\"\n />\n );\n}\n\nfunction formatCores(cores: number): string {\n return Number.isInteger(cores) ? String(cores) : cores.toFixed(1);\n}\n\n/**\n * TimeseriesCoreBars renders CPU utilisation as a row of vertical bars — one bar\n * per core (the ceiling of the CPU limit) — filled left to right by the latest\n * usage. The bar that straddles the usage boundary is partially filled (\"blocked\n * out\") to show the fractional core in use, and the trailing bars stay empty to\n * show headroom. Both usage and limit are read live from the timeseries store,\n * mirroring TimeseriesGauge's data plumbing; the bar colour crosses warning and\n * danger thresholds. A \"<used> / <cores>\" caption sits below with the icon+title.\n */\nexport function TimeseriesCoreBars({\n baseUrl = \"\",\n value,\n max,\n title,\n icon,\n range = \"1h\",\n refreshMs = 5000,\n thresholds = [75, 90],\n variant = \"default\",\n showLabel = true,\n showValue = true,\n orientation,\n fetcher = defaultFetcher,\n className,\n}: TimeseriesCoreBarsProps) {\n const maxIsSeries = typeof max === \"object\";\n const maxSeries = maxIsSeries ? max : undefined;\n const ids = useMemo(() => {\n const list = [value.id];\n if (maxSeries) list.push(maxSeries.id);\n return list;\n }, [value.id, maxSeries]);\n\n const results = useQueries({\n queries: ids.map((id) => {\n const u = new URL(baseUrl + id, window.location.origin);\n if (range) u.searchParams.set(\"since\", range);\n const requestUrl = u.pathname + u.search;\n return {\n queryKey: [\"timeseries\", requestUrl],\n queryFn: () => fetcher(requestUrl),\n refetchInterval: refreshMs > 0 ? refreshMs : false,\n staleTime: 0,\n retry: 0,\n };\n }),\n });\n\n const rawValue = latestValue(results[0]?.data);\n const usage =\n rawValue === undefined\n ? undefined\n : value.transform\n ? value.transform(rawValue)\n : rawValue;\n\n let limit: number | undefined;\n if (typeof max === \"number\") {\n limit = max;\n } else if (maxSeries) {\n const rawMax = latestValue(results[1]?.data);\n limit =\n rawMax === undefined\n ? undefined\n : maxSeries.transform\n ? maxSeries.transform(rawMax)\n : rawMax;\n }\n\n const model = deriveCoreBars(usage, limit);\n const tone = toneClass(model.pct, thresholds);\n const caption = model.hasUsage\n ? `${formatCores(model.usageCores)} / ${model.limitCores !== undefined ? formatCores(model.limitCores) : \"?\"} cores`\n : \"—\";\n const compactCaption = model.hasUsage\n ? `${formatCores(model.usageCores)}/${model.limitCores !== undefined ? formatCores(model.limitCores) : \"?\"} cores`\n : \"—\";\n const titleText = `${title}: ${compactCaption}`;\n const isCell = variant === \"cell\";\n const resolvedOrientation = orientation ?? \"vertical\";\n const isDense = model.coreCount > 4;\n const valueText = isCell ? compactCaption : caption;\n const hiddenAccessibleText = !showLabel || !showValue ? titleText : undefined;\n const bars = (\n <span\n className={cn(\n \"flex shrink-0\",\n resolvedOrientation === \"vertical\"\n ? cn(\"items-end\", isCell ? \"h-4 gap-px\" : \"h-10 gap-0.5\")\n : cn(\n \"flex-col justify-center\",\n isCell ? \"h-4 w-10 gap-px\" : \"h-10 w-16 gap-0.5\",\n ),\n )}\n aria-hidden={isCell ? \"true\" : undefined}\n data-orientation={resolvedOrientation}\n data-core-count={model.coreCount}\n data-core-density={isDense ? \"compact\" : \"default\"}\n >\n {model.bars.map((bar, i) => {\n const corePct = Math.round(bar.fill * 100);\n return (\n <span\n key={i}\n className={cn(\n \"relative overflow-hidden bg-muted\",\n resolvedOrientation === \"vertical\"\n ? cn(\n \"h-full\",\n isCell\n ? isDense\n ? \"w-[3px] rounded-[2px]\"\n : \"w-1.5 rounded-[2px]\"\n : isDense\n ? \"w-1 rounded-sm\"\n : \"w-2 rounded-sm\",\n )\n : cn(\"min-h-px w-full flex-1 rounded-[2px]\"),\n )}\n title={`core ${i + 1}: ${corePct}%`}\n aria-label={!isCell ? `core ${i + 1}: ${corePct}%` : undefined}\n data-fill={bar.fill}\n >\n <span\n className={cn(\n \"absolute bottom-0 left-0 transition-all duration-300\",\n tone,\n )}\n style={\n resolvedOrientation === \"vertical\"\n ? { height: `${corePct}%`, right: 0 }\n : { width: `${corePct}%`, top: 0 }\n }\n />\n </span>\n );\n })}\n </span>\n );\n const valueNode = showValue ? (\n <span\n className={cn(\n \"shrink-0 font-semibold tabular-nums text-foreground\",\n isCell ? null : \"text-xs\",\n )}\n >\n {valueText}\n </span>\n ) : null;\n const label =\n icon || showLabel ? (\n <span\n className={cn(\n \"inline-flex min-w-0 items-center gap-1 text-xs text-muted-foreground\",\n )}\n >\n {icon ? <CoreBarsIcon icon={icon} /> : null}\n {showLabel ? <span className=\"min-w-0 truncate\">{title}</span> : null}\n </span>\n ) : null;\n\n if (isCell) {\n return (\n <span\n className={cn(\n \"inline-flex min-w-0 items-center gap-1.5 whitespace-nowrap align-middle text-xs\",\n className,\n )}\n title={titleText}\n aria-label={hiddenAccessibleText}\n >\n {label}\n {bars}\n {valueNode}\n </span>\n );\n }\n\n return (\n <div\n className={cn(\"flex flex-col items-center gap-1\", className)}\n title={hiddenAccessibleText}\n aria-label={hiddenAccessibleText}\n >\n {bars}\n {valueNode}\n {label}\n </div>\n );\n}\n"],"names":["jsx","Icon","useMemo","useQueries","deriveCoreBars","cn","jsxs"],"mappings":"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"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { StaticIconComponent } from './Icon';
|
|
2
2
|
import { GaugeSeries } from './TimeseriesGauge';
|
|
3
3
|
import { TimeseriesResponse } from './TimeseriesPanel';
|
|
4
|
+
export type TimeseriesCoreBarsVariant = "default" | "cell";
|
|
5
|
+
export type TimeseriesCoreBarsOrientation = "horizontal" | "vertical";
|
|
4
6
|
export interface TimeseriesCoreBarsProps {
|
|
5
7
|
/** Common prefix; the value/max requests are `baseUrl + id`. */
|
|
6
8
|
baseUrl?: string;
|
|
@@ -25,6 +27,14 @@ export interface TimeseriesCoreBarsProps {
|
|
|
25
27
|
* then red. Defaults to [75, 90].
|
|
26
28
|
*/
|
|
27
29
|
thresholds?: [warning: number, danger: number];
|
|
30
|
+
/** Visual density/layout. `cell` is a compact inline form for table/grid cells. */
|
|
31
|
+
variant?: TimeseriesCoreBarsVariant;
|
|
32
|
+
/** Show the title text. Icons and values remain visible unless separately hidden. */
|
|
33
|
+
showLabel?: boolean;
|
|
34
|
+
/** Show the value/capacity caption. Icons and bars remain visible when false. */
|
|
35
|
+
showValue?: boolean;
|
|
36
|
+
/** Bar direction. Vertical bars fill bottom-up; horizontal bars fill left-to-right. */
|
|
37
|
+
orientation?: TimeseriesCoreBarsOrientation;
|
|
28
38
|
/** Override the default fetch (e.g. to route through an app's API client). */
|
|
29
39
|
fetcher?: (url: string) => Promise<TimeseriesResponse>;
|
|
30
40
|
className?: string;
|
|
@@ -38,5 +48,5 @@ export interface TimeseriesCoreBarsProps {
|
|
|
38
48
|
* mirroring TimeseriesGauge's data plumbing; the bar colour crosses warning and
|
|
39
49
|
* danger thresholds. A "<used> / <cores>" caption sits below with the icon+title.
|
|
40
50
|
*/
|
|
41
|
-
export declare function TimeseriesCoreBars({ baseUrl, value, max, title, icon, range, refreshMs, thresholds, fetcher, className, }: TimeseriesCoreBarsProps): import("react/jsx-runtime").JSX.Element;
|
|
51
|
+
export declare function TimeseriesCoreBars({ baseUrl, value, max, title, icon, range, refreshMs, thresholds, variant, showLabel, showValue, orientation, fetcher, className, }: TimeseriesCoreBarsProps): import("react/jsx-runtime").JSX.Element;
|
|
42
52
|
//# sourceMappingURL=TimeseriesCoreBars.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeseriesCoreBars.d.ts","sourceRoot":"","sources":["../../src/data/TimeseriesCoreBars.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,MAAM,WAAW,uBAAuB;IACtC,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,KAAK,EAAE,WAAW,CAAC;IACnB;;;;;OAKG;IACH,GAAG,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,8EAA8E;IAC9E,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"TimeseriesCoreBars.d.ts","sourceRoot":"","sources":["../../src/data/TimeseriesCoreBars.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,MAAM,MAAM,yBAAyB,GAAG,SAAS,GAAG,MAAM,CAAC;AAC3D,MAAM,MAAM,6BAA6B,GAAG,YAAY,GAAG,UAAU,CAAC;AAEtE,MAAM,WAAW,uBAAuB;IACtC,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,KAAK,EAAE,WAAW,CAAC;IACnB;;;;;OAKG;IACH,GAAG,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,mFAAmF;IACnF,OAAO,CAAC,EAAE,yBAAyB,CAAC;IACpC,qFAAqF;IACrF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iFAAiF;IACjF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uFAAuF;IACvF,WAAW,CAAC,EAAE,6BAA6B,CAAC;IAC5C,8EAA8E;IAC9E,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA6CD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,OAAY,EACZ,KAAK,EACL,GAAG,EACH,KAAK,EACL,IAAI,EACJ,KAAY,EACZ,SAAgB,EAChB,UAAqB,EACrB,OAAmB,EACnB,SAAgB,EAChB,SAAgB,EAChB,WAAW,EACX,OAAwB,EACxB,SAAS,GACV,EAAE,uBAAuB,2CAqKzB"}
|