@camstack/ui-library 0.1.25
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/assets/brand/stacked-lens-current.svg +14 -0
- package/assets/brand/stacked-lens-light.svg +13 -0
- package/assets/brand/stacked-lens-mono.svg +13 -0
- package/assets/brand/stacked-lens-primary.svg +13 -0
- package/dist/index.cjs +2086 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +415 -0
- package/dist/index.d.ts +415 -0
- package/dist/index.js +2043 -0
- package/dist/index.js.map +1 -0
- package/dist/theme/index.cjs +306 -0
- package/dist/theme/index.cjs.map +1 -0
- package/dist/theme/index.d.cts +128 -0
- package/dist/theme/index.d.ts +128 -0
- package/dist/theme/index.js +273 -0
- package/dist/theme/index.js.map +1 -0
- package/package.json +58 -0
- package/src/tailwind/camstack-theme.css +96 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2086 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
AppShell: () => AppShell,
|
|
24
|
+
Badge: () => Badge,
|
|
25
|
+
Button: () => Button,
|
|
26
|
+
Card: () => Card,
|
|
27
|
+
Checkbox: () => Checkbox,
|
|
28
|
+
CodeBlock: () => CodeBlock,
|
|
29
|
+
ConfirmDialog: () => ConfirmDialog,
|
|
30
|
+
DataTable: () => DataTable,
|
|
31
|
+
DeviceCard: () => DeviceCard,
|
|
32
|
+
DeviceGrid: () => DeviceGrid,
|
|
33
|
+
Dialog: () => Dialog,
|
|
34
|
+
DialogContent: () => DialogContent,
|
|
35
|
+
DialogDescription: () => DialogDescription,
|
|
36
|
+
DialogFooter: () => DialogFooter,
|
|
37
|
+
DialogHeader: () => DialogHeader,
|
|
38
|
+
DialogTitle: () => DialogTitle,
|
|
39
|
+
DialogTrigger: () => DialogTrigger,
|
|
40
|
+
Dropdown: () => Dropdown,
|
|
41
|
+
DropdownContent: () => DropdownContent,
|
|
42
|
+
DropdownItem: () => DropdownItem,
|
|
43
|
+
DropdownTrigger: () => DropdownTrigger,
|
|
44
|
+
EmptyState: () => EmptyState,
|
|
45
|
+
FilterBar: () => FilterBar,
|
|
46
|
+
FloatingPanel: () => FloatingPanel,
|
|
47
|
+
FormField: () => FormField,
|
|
48
|
+
IconButton: () => IconButton,
|
|
49
|
+
Input: () => Input,
|
|
50
|
+
KeyValueList: () => KeyValueList,
|
|
51
|
+
Label: () => Label,
|
|
52
|
+
PageHeader: () => PageHeader,
|
|
53
|
+
Popover: () => Popover,
|
|
54
|
+
PopoverContent: () => PopoverContent,
|
|
55
|
+
PopoverTrigger: () => PopoverTrigger,
|
|
56
|
+
ProviderBadge: () => ProviderBadge,
|
|
57
|
+
ScrollArea: () => ScrollArea,
|
|
58
|
+
Select: () => Select,
|
|
59
|
+
Separator: () => Separator,
|
|
60
|
+
Sidebar: () => Sidebar,
|
|
61
|
+
SidebarItem: () => SidebarItem,
|
|
62
|
+
Skeleton: () => Skeleton,
|
|
63
|
+
StatCard: () => StatCard,
|
|
64
|
+
StatusBadge: () => StatusBadge,
|
|
65
|
+
Switch: () => Switch,
|
|
66
|
+
Tabs: () => Tabs,
|
|
67
|
+
TabsContent: () => TabsContent,
|
|
68
|
+
TabsList: () => TabsList,
|
|
69
|
+
TabsTrigger: () => TabsTrigger,
|
|
70
|
+
ThemeProvider: () => ThemeProvider,
|
|
71
|
+
Tooltip: () => Tooltip,
|
|
72
|
+
TooltipContent: () => TooltipContent,
|
|
73
|
+
TooltipTrigger: () => TooltipTrigger,
|
|
74
|
+
cn: () => cn,
|
|
75
|
+
createTheme: () => createTheme,
|
|
76
|
+
darkColors: () => darkColors,
|
|
77
|
+
defaultTheme: () => defaultTheme,
|
|
78
|
+
lightColors: () => lightColors,
|
|
79
|
+
providerIcons: () => providerIcons,
|
|
80
|
+
statusIcons: () => statusIcons,
|
|
81
|
+
themeToCss: () => themeToCss,
|
|
82
|
+
useThemeMode: () => useThemeMode
|
|
83
|
+
});
|
|
84
|
+
module.exports = __toCommonJS(src_exports);
|
|
85
|
+
|
|
86
|
+
// src/theme/defaults.ts
|
|
87
|
+
var providerColors = {
|
|
88
|
+
frigate: "#3b82f6",
|
|
89
|
+
scrypted: "#a855f7",
|
|
90
|
+
reolink: "#06b6d4",
|
|
91
|
+
homeAssistant: "#22d3ee",
|
|
92
|
+
rtsp: "#78716c"
|
|
93
|
+
};
|
|
94
|
+
var darkColors = {
|
|
95
|
+
primary: "#f59e42",
|
|
96
|
+
primaryForeground: "#0c0a09",
|
|
97
|
+
background: "#0c0a09",
|
|
98
|
+
backgroundElevated: "#1c1917",
|
|
99
|
+
surface: "#1c1917",
|
|
100
|
+
surfaceHover: "#292524",
|
|
101
|
+
border: "#292524",
|
|
102
|
+
borderSubtle: "#1c1917",
|
|
103
|
+
foreground: "#fafaf9",
|
|
104
|
+
foregroundMuted: "#a8a29e",
|
|
105
|
+
foregroundSubtle: "#78716c",
|
|
106
|
+
foregroundDisabled: "#57534e",
|
|
107
|
+
success: "#4ade80",
|
|
108
|
+
warning: "#fbbf24",
|
|
109
|
+
danger: "#f87171",
|
|
110
|
+
info: "#60a5fa",
|
|
111
|
+
provider: providerColors
|
|
112
|
+
};
|
|
113
|
+
var lightColors = {
|
|
114
|
+
primary: "#e67e22",
|
|
115
|
+
primaryForeground: "#ffffff",
|
|
116
|
+
background: "#fafaf9",
|
|
117
|
+
backgroundElevated: "#ffffff",
|
|
118
|
+
surface: "#f5f5f4",
|
|
119
|
+
surfaceHover: "#e7e5e4",
|
|
120
|
+
border: "#d6d3d1",
|
|
121
|
+
borderSubtle: "#e7e5e4",
|
|
122
|
+
foreground: "#1c1917",
|
|
123
|
+
foregroundMuted: "#57534e",
|
|
124
|
+
foregroundSubtle: "#78716c",
|
|
125
|
+
foregroundDisabled: "#a8a29e",
|
|
126
|
+
success: "#16a34a",
|
|
127
|
+
warning: "#d97706",
|
|
128
|
+
danger: "#dc2626",
|
|
129
|
+
info: "#2563eb",
|
|
130
|
+
provider: providerColors
|
|
131
|
+
};
|
|
132
|
+
var defaultTheme = {
|
|
133
|
+
colors: {
|
|
134
|
+
dark: darkColors,
|
|
135
|
+
light: lightColors
|
|
136
|
+
},
|
|
137
|
+
spacing: {
|
|
138
|
+
xs: 2,
|
|
139
|
+
sm: 4,
|
|
140
|
+
md: 8,
|
|
141
|
+
lg: 12,
|
|
142
|
+
xl: 16,
|
|
143
|
+
"2xl": 24,
|
|
144
|
+
"3xl": 32
|
|
145
|
+
},
|
|
146
|
+
radius: {
|
|
147
|
+
sm: 4,
|
|
148
|
+
md: 6,
|
|
149
|
+
lg: 8,
|
|
150
|
+
xl: 12
|
|
151
|
+
},
|
|
152
|
+
typography: {
|
|
153
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
154
|
+
sizes: {
|
|
155
|
+
xs: { fontSize: 10, lineHeight: 14 },
|
|
156
|
+
sm: { fontSize: 11, lineHeight: 16 },
|
|
157
|
+
base: { fontSize: 12, lineHeight: 18 },
|
|
158
|
+
lg: { fontSize: 13, lineHeight: 18 },
|
|
159
|
+
xl: { fontSize: 14, lineHeight: 20 },
|
|
160
|
+
"2xl": { fontSize: 16, lineHeight: 22 },
|
|
161
|
+
"3xl": { fontSize: 20, lineHeight: 28 }
|
|
162
|
+
},
|
|
163
|
+
weights: {
|
|
164
|
+
regular: 400,
|
|
165
|
+
medium: 500,
|
|
166
|
+
semibold: 600,
|
|
167
|
+
bold: 700
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
table: {
|
|
171
|
+
rowHeight: 28,
|
|
172
|
+
headerHeight: 24,
|
|
173
|
+
cellPaddingX: 8,
|
|
174
|
+
cellPaddingY: 6
|
|
175
|
+
},
|
|
176
|
+
sidebar: {
|
|
177
|
+
width: 176,
|
|
178
|
+
itemHeight: 28,
|
|
179
|
+
iconSize: 14
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// src/theme/create-theme.ts
|
|
184
|
+
function deepMerge(target, source) {
|
|
185
|
+
const result = { ...target };
|
|
186
|
+
for (const key in source) {
|
|
187
|
+
const sourceVal = source[key];
|
|
188
|
+
const targetVal = target[key];
|
|
189
|
+
if (sourceVal !== void 0 && typeof sourceVal === "object" && sourceVal !== null && !Array.isArray(sourceVal) && typeof targetVal === "object" && targetVal !== null) {
|
|
190
|
+
result[key] = deepMerge(
|
|
191
|
+
targetVal,
|
|
192
|
+
sourceVal
|
|
193
|
+
);
|
|
194
|
+
} else if (sourceVal !== void 0) {
|
|
195
|
+
result[key] = sourceVal;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
function createTheme(overrides) {
|
|
201
|
+
if (!overrides) return structuredClone(defaultTheme);
|
|
202
|
+
return deepMerge(structuredClone(defaultTheme), overrides);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/theme/theme-to-css.ts
|
|
206
|
+
function camelToKebab(str) {
|
|
207
|
+
return str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
208
|
+
}
|
|
209
|
+
function colorTokenToCssVar(key) {
|
|
210
|
+
return `--color-${camelToKebab(key)}`;
|
|
211
|
+
}
|
|
212
|
+
function generateColorBlock(colors) {
|
|
213
|
+
const lines = [];
|
|
214
|
+
for (const [key, value] of Object.entries(colors)) {
|
|
215
|
+
if (key === "provider") continue;
|
|
216
|
+
lines.push(` ${colorTokenToCssVar(key)}: ${value};`);
|
|
217
|
+
}
|
|
218
|
+
return lines.join("\n");
|
|
219
|
+
}
|
|
220
|
+
function generateProviderColors(provider) {
|
|
221
|
+
const lines = [];
|
|
222
|
+
for (const [key, value] of Object.entries(provider)) {
|
|
223
|
+
lines.push(` --color-provider-${camelToKebab(key)}: ${value};`);
|
|
224
|
+
}
|
|
225
|
+
return lines.join("\n");
|
|
226
|
+
}
|
|
227
|
+
function generateSpacingTokens(spacing) {
|
|
228
|
+
const lines = [];
|
|
229
|
+
for (const [key, value] of Object.entries(spacing)) {
|
|
230
|
+
lines.push(` --spacing-${key}: ${value}px;`);
|
|
231
|
+
}
|
|
232
|
+
return lines.join("\n");
|
|
233
|
+
}
|
|
234
|
+
function generateRadiusTokens(radius) {
|
|
235
|
+
const lines = [];
|
|
236
|
+
for (const [key, value] of Object.entries(radius)) {
|
|
237
|
+
lines.push(` --radius-${key}: ${value}px;`);
|
|
238
|
+
}
|
|
239
|
+
return lines.join("\n");
|
|
240
|
+
}
|
|
241
|
+
function themeToCss(theme) {
|
|
242
|
+
const darkColorBlock = generateColorBlock(theme.colors.dark);
|
|
243
|
+
const lightColorBlock = generateColorBlock(theme.colors.light);
|
|
244
|
+
const providerBlock = generateProviderColors(theme.colors.dark.provider);
|
|
245
|
+
const spacingBlock = generateSpacingTokens(theme.spacing);
|
|
246
|
+
const radiusBlock = generateRadiusTokens(theme.radius);
|
|
247
|
+
return `@theme {
|
|
248
|
+
${providerBlock}
|
|
249
|
+
${spacingBlock}
|
|
250
|
+
${radiusBlock}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.dark {
|
|
254
|
+
${darkColorBlock}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.light {
|
|
258
|
+
${lightColorBlock}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
@media (prefers-color-scheme: dark) {
|
|
262
|
+
:root {
|
|
263
|
+
${darkColorBlock.replace(/^ /gm, " ")}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
@media (prefers-color-scheme: light) {
|
|
268
|
+
:root {
|
|
269
|
+
${lightColorBlock.replace(/^ /gm, " ")}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
`;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// src/theme/theme-provider.tsx
|
|
276
|
+
var import_react = require("react");
|
|
277
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
278
|
+
var ThemeContext = (0, import_react.createContext)(null);
|
|
279
|
+
var TOGGLE_ORDER = ["dark", "light", "system"];
|
|
280
|
+
function getSystemPreference() {
|
|
281
|
+
if (typeof window === "undefined") return "dark";
|
|
282
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
283
|
+
}
|
|
284
|
+
function getInitialMode(storageKey, defaultMode) {
|
|
285
|
+
if (typeof window === "undefined") return defaultMode;
|
|
286
|
+
const stored = localStorage.getItem(storageKey);
|
|
287
|
+
if (stored === "dark" || stored === "light" || stored === "system") {
|
|
288
|
+
return stored;
|
|
289
|
+
}
|
|
290
|
+
return defaultMode;
|
|
291
|
+
}
|
|
292
|
+
function resolveMode(mode) {
|
|
293
|
+
if (mode === "system") return getSystemPreference();
|
|
294
|
+
return mode;
|
|
295
|
+
}
|
|
296
|
+
function ThemeProvider({
|
|
297
|
+
children,
|
|
298
|
+
defaultMode = "system",
|
|
299
|
+
storageKey = "camstack-theme-mode"
|
|
300
|
+
}) {
|
|
301
|
+
const [mode, setModeState] = (0, import_react.useState)(() => getInitialMode(storageKey, defaultMode));
|
|
302
|
+
const [resolvedMode, setResolvedMode] = (0, import_react.useState)(() => resolveMode(mode));
|
|
303
|
+
const setMode = (0, import_react.useCallback)(
|
|
304
|
+
(newMode) => {
|
|
305
|
+
setModeState(newMode);
|
|
306
|
+
setResolvedMode(resolveMode(newMode));
|
|
307
|
+
if (typeof window !== "undefined") {
|
|
308
|
+
localStorage.setItem(storageKey, newMode);
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
[storageKey]
|
|
312
|
+
);
|
|
313
|
+
const toggleMode = (0, import_react.useCallback)(() => {
|
|
314
|
+
const currentIndex = TOGGLE_ORDER.indexOf(mode);
|
|
315
|
+
const nextIndex = (currentIndex + 1) % TOGGLE_ORDER.length;
|
|
316
|
+
setMode(TOGGLE_ORDER[nextIndex] ?? "dark");
|
|
317
|
+
}, [mode, setMode]);
|
|
318
|
+
(0, import_react.useEffect)(() => {
|
|
319
|
+
if (typeof document === "undefined") return;
|
|
320
|
+
const root = document.documentElement;
|
|
321
|
+
root.classList.remove("dark", "light");
|
|
322
|
+
root.classList.add(resolvedMode);
|
|
323
|
+
}, [mode, resolvedMode]);
|
|
324
|
+
(0, import_react.useEffect)(() => {
|
|
325
|
+
if (typeof window === "undefined" || mode !== "system") return;
|
|
326
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
327
|
+
const handleChange = () => {
|
|
328
|
+
setResolvedMode(getSystemPreference());
|
|
329
|
+
};
|
|
330
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
331
|
+
return () => mediaQuery.removeEventListener("change", handleChange);
|
|
332
|
+
}, [mode]);
|
|
333
|
+
const value = (0, import_react.useMemo)(
|
|
334
|
+
() => ({ mode, resolvedMode, setMode, toggleMode }),
|
|
335
|
+
[mode, resolvedMode, setMode, toggleMode]
|
|
336
|
+
);
|
|
337
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeContext.Provider, { value, children });
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// src/theme/use-theme-mode.ts
|
|
341
|
+
var import_react2 = require("react");
|
|
342
|
+
function useThemeMode() {
|
|
343
|
+
const context = (0, import_react2.useContext)(ThemeContext);
|
|
344
|
+
if (!context) {
|
|
345
|
+
throw new Error("useThemeMode must be used within a ThemeProvider");
|
|
346
|
+
}
|
|
347
|
+
return context;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// src/lib/cn.ts
|
|
351
|
+
var import_clsx = require("clsx");
|
|
352
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
353
|
+
function cn(...inputs) {
|
|
354
|
+
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// src/icons/provider-icons.ts
|
|
358
|
+
var import_lucide_react = require("lucide-react");
|
|
359
|
+
var providerIcons = {
|
|
360
|
+
frigate: import_lucide_react.Ship,
|
|
361
|
+
scrypted: import_lucide_react.Shield,
|
|
362
|
+
reolink: import_lucide_react.Radio,
|
|
363
|
+
homeAssistant: import_lucide_react.Home,
|
|
364
|
+
rtsp: import_lucide_react.Cast
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
// src/icons/status-icons.ts
|
|
368
|
+
var import_lucide_react2 = require("lucide-react");
|
|
369
|
+
var statusIcons = {
|
|
370
|
+
online: import_lucide_react2.CircleCheck,
|
|
371
|
+
offline: import_lucide_react2.CircleX,
|
|
372
|
+
degraded: import_lucide_react2.CircleAlert,
|
|
373
|
+
unknown: import_lucide_react2.CircleHelp
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
// src/primitives/button.tsx
|
|
377
|
+
var import_react3 = require("react");
|
|
378
|
+
var import_class_variance_authority = require("class-variance-authority");
|
|
379
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
380
|
+
var buttonVariants = (0, import_class_variance_authority.cva)(
|
|
381
|
+
"inline-flex items-center justify-center rounded-md font-medium transition-colors disabled:opacity-50 disabled:pointer-events-none",
|
|
382
|
+
{
|
|
383
|
+
variants: {
|
|
384
|
+
variant: {
|
|
385
|
+
primary: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
386
|
+
secondary: "bg-surface text-foreground hover:bg-surface-hover",
|
|
387
|
+
ghost: "hover:bg-surface-hover text-foreground-muted",
|
|
388
|
+
danger: "bg-danger text-white hover:bg-danger/90",
|
|
389
|
+
outline: "border border-border bg-transparent hover:bg-surface-hover"
|
|
390
|
+
},
|
|
391
|
+
size: {
|
|
392
|
+
sm: "h-7 px-2.5 text-xs gap-1.5",
|
|
393
|
+
md: "h-8 px-3 text-sm gap-2",
|
|
394
|
+
lg: "h-9 px-4 text-sm gap-2"
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
defaultVariants: {
|
|
398
|
+
variant: "primary",
|
|
399
|
+
size: "sm"
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
);
|
|
403
|
+
var Button = (0, import_react3.forwardRef)(
|
|
404
|
+
({ className, variant, size, ...props }, ref) => {
|
|
405
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
406
|
+
"button",
|
|
407
|
+
{
|
|
408
|
+
ref,
|
|
409
|
+
className: cn(buttonVariants({ variant, size }), className),
|
|
410
|
+
...props
|
|
411
|
+
}
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
);
|
|
415
|
+
Button.displayName = "Button";
|
|
416
|
+
|
|
417
|
+
// src/primitives/icon-button.tsx
|
|
418
|
+
var import_react4 = require("react");
|
|
419
|
+
var import_class_variance_authority2 = require("class-variance-authority");
|
|
420
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
421
|
+
var iconButtonVariants = (0, import_class_variance_authority2.cva)(
|
|
422
|
+
"inline-flex items-center justify-center rounded-md font-medium transition-colors disabled:opacity-50 disabled:pointer-events-none",
|
|
423
|
+
{
|
|
424
|
+
variants: {
|
|
425
|
+
variant: {
|
|
426
|
+
primary: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
427
|
+
secondary: "bg-surface text-foreground hover:bg-surface-hover",
|
|
428
|
+
ghost: "hover:bg-surface-hover text-foreground-muted",
|
|
429
|
+
danger: "bg-danger text-white hover:bg-danger/90",
|
|
430
|
+
outline: "border border-border bg-transparent hover:bg-surface-hover"
|
|
431
|
+
},
|
|
432
|
+
size: {
|
|
433
|
+
sm: "h-7 w-7",
|
|
434
|
+
md: "h-8 w-8",
|
|
435
|
+
lg: "h-9 w-9"
|
|
436
|
+
}
|
|
437
|
+
},
|
|
438
|
+
defaultVariants: {
|
|
439
|
+
variant: "primary",
|
|
440
|
+
size: "sm"
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
);
|
|
444
|
+
var IconButton = (0, import_react4.forwardRef)(
|
|
445
|
+
({ className, variant, size, icon: Icon, ...props }, ref) => {
|
|
446
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
447
|
+
"button",
|
|
448
|
+
{
|
|
449
|
+
ref,
|
|
450
|
+
className: cn(iconButtonVariants({ variant, size }), className),
|
|
451
|
+
...props,
|
|
452
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { className: "h-4 w-4" })
|
|
453
|
+
}
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
);
|
|
457
|
+
IconButton.displayName = "IconButton";
|
|
458
|
+
|
|
459
|
+
// src/primitives/badge.tsx
|
|
460
|
+
var import_react5 = require("react");
|
|
461
|
+
var import_class_variance_authority3 = require("class-variance-authority");
|
|
462
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
463
|
+
var badgeVariants = (0, import_class_variance_authority3.cva)(
|
|
464
|
+
"inline-flex items-center rounded-full text-xs font-medium px-2 py-0.5",
|
|
465
|
+
{
|
|
466
|
+
variants: {
|
|
467
|
+
variant: {
|
|
468
|
+
default: "bg-surface-hover text-foreground",
|
|
469
|
+
success: "bg-success/15 text-success",
|
|
470
|
+
warning: "bg-warning/15 text-warning",
|
|
471
|
+
danger: "bg-danger/15 text-danger",
|
|
472
|
+
info: "bg-info/15 text-info"
|
|
473
|
+
},
|
|
474
|
+
styleVariant: {
|
|
475
|
+
solid: "",
|
|
476
|
+
outline: "border bg-transparent"
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
defaultVariants: {
|
|
480
|
+
variant: "default",
|
|
481
|
+
styleVariant: "solid"
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
);
|
|
485
|
+
var Badge = (0, import_react5.forwardRef)(
|
|
486
|
+
({ className, variant, style, ...props }, ref) => {
|
|
487
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
488
|
+
"span",
|
|
489
|
+
{
|
|
490
|
+
ref,
|
|
491
|
+
className: cn(badgeVariants({ variant, styleVariant: style }), className),
|
|
492
|
+
...props
|
|
493
|
+
}
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
);
|
|
497
|
+
Badge.displayName = "Badge";
|
|
498
|
+
|
|
499
|
+
// src/primitives/card.tsx
|
|
500
|
+
var import_react6 = require("react");
|
|
501
|
+
var import_class_variance_authority4 = require("class-variance-authority");
|
|
502
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
503
|
+
var cardVariants = (0, import_class_variance_authority4.cva)("rounded-lg p-3", {
|
|
504
|
+
variants: {
|
|
505
|
+
variant: {
|
|
506
|
+
flat: "bg-surface",
|
|
507
|
+
bordered: "bg-surface border border-border"
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
defaultVariants: {
|
|
511
|
+
variant: "bordered"
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
var Card = (0, import_react6.forwardRef)(
|
|
515
|
+
({ className, variant, ...props }, ref) => {
|
|
516
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
517
|
+
"div",
|
|
518
|
+
{
|
|
519
|
+
ref,
|
|
520
|
+
className: cn(cardVariants({ variant }), className),
|
|
521
|
+
...props
|
|
522
|
+
}
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
);
|
|
526
|
+
Card.displayName = "Card";
|
|
527
|
+
|
|
528
|
+
// src/primitives/label.tsx
|
|
529
|
+
var import_react7 = require("react");
|
|
530
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
531
|
+
var Label = (0, import_react7.forwardRef)(
|
|
532
|
+
({ className, ...props }, ref) => {
|
|
533
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
534
|
+
"label",
|
|
535
|
+
{
|
|
536
|
+
ref,
|
|
537
|
+
className: cn(
|
|
538
|
+
"text-[10px] uppercase tracking-wider text-foreground-muted font-medium",
|
|
539
|
+
className
|
|
540
|
+
),
|
|
541
|
+
...props
|
|
542
|
+
}
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
);
|
|
546
|
+
Label.displayName = "Label";
|
|
547
|
+
|
|
548
|
+
// src/primitives/separator.tsx
|
|
549
|
+
var import_react8 = require("react");
|
|
550
|
+
var import_class_variance_authority5 = require("class-variance-authority");
|
|
551
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
552
|
+
var separatorVariants = (0, import_class_variance_authority5.cva)("", {
|
|
553
|
+
variants: {
|
|
554
|
+
orientation: {
|
|
555
|
+
horizontal: "h-px w-full bg-border-subtle",
|
|
556
|
+
vertical: "w-px h-full bg-border-subtle"
|
|
557
|
+
}
|
|
558
|
+
},
|
|
559
|
+
defaultVariants: {
|
|
560
|
+
orientation: "horizontal"
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
var Separator = (0, import_react8.forwardRef)(
|
|
564
|
+
({ className, orientation, ...props }, ref) => {
|
|
565
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
566
|
+
"div",
|
|
567
|
+
{
|
|
568
|
+
ref,
|
|
569
|
+
role: "separator",
|
|
570
|
+
className: cn(separatorVariants({ orientation }), className),
|
|
571
|
+
...props
|
|
572
|
+
}
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
);
|
|
576
|
+
Separator.displayName = "Separator";
|
|
577
|
+
|
|
578
|
+
// src/primitives/skeleton.tsx
|
|
579
|
+
var import_react9 = require("react");
|
|
580
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
581
|
+
var Skeleton = (0, import_react9.forwardRef)(
|
|
582
|
+
({ className, ...props }, ref) => {
|
|
583
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
584
|
+
"div",
|
|
585
|
+
{
|
|
586
|
+
ref,
|
|
587
|
+
className: cn("animate-pulse bg-surface-hover rounded-md h-4 w-full", className),
|
|
588
|
+
...props
|
|
589
|
+
}
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
);
|
|
593
|
+
Skeleton.displayName = "Skeleton";
|
|
594
|
+
|
|
595
|
+
// src/primitives/input.tsx
|
|
596
|
+
var import_react10 = require("react");
|
|
597
|
+
var import_class_variance_authority6 = require("class-variance-authority");
|
|
598
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
599
|
+
var inputVariants = (0, import_class_variance_authority6.cva)(
|
|
600
|
+
"h-7 w-full px-2.5 text-xs bg-surface border rounded-md text-foreground placeholder:text-foreground-subtle focus:outline-none focus:ring-1 focus:ring-primary transition-colors",
|
|
601
|
+
{
|
|
602
|
+
variants: {
|
|
603
|
+
state: {
|
|
604
|
+
default: "border-border",
|
|
605
|
+
error: "border-danger"
|
|
606
|
+
}
|
|
607
|
+
},
|
|
608
|
+
defaultVariants: {
|
|
609
|
+
state: "default"
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
);
|
|
613
|
+
var Input = (0, import_react10.forwardRef)(
|
|
614
|
+
({ className, state, leftSlot, rightSlot, ...props }, ref) => {
|
|
615
|
+
if (leftSlot || rightSlot) {
|
|
616
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "relative flex items-center w-full", children: [
|
|
617
|
+
leftSlot && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "absolute left-2.5 flex items-center pointer-events-none", children: leftSlot }),
|
|
618
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
619
|
+
"input",
|
|
620
|
+
{
|
|
621
|
+
ref,
|
|
622
|
+
className: cn(
|
|
623
|
+
inputVariants({ state }),
|
|
624
|
+
leftSlot ? "pl-7" : "",
|
|
625
|
+
rightSlot ? "pr-7" : "",
|
|
626
|
+
className
|
|
627
|
+
),
|
|
628
|
+
...props
|
|
629
|
+
}
|
|
630
|
+
),
|
|
631
|
+
rightSlot && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "absolute right-2.5 flex items-center pointer-events-none", children: rightSlot })
|
|
632
|
+
] });
|
|
633
|
+
}
|
|
634
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
635
|
+
"input",
|
|
636
|
+
{
|
|
637
|
+
ref,
|
|
638
|
+
className: cn(inputVariants({ state }), className),
|
|
639
|
+
...props
|
|
640
|
+
}
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
);
|
|
644
|
+
Input.displayName = "Input";
|
|
645
|
+
|
|
646
|
+
// src/primitives/select.tsx
|
|
647
|
+
var import_react11 = require("react");
|
|
648
|
+
var import_lucide_react3 = require("lucide-react");
|
|
649
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
650
|
+
var Select = (0, import_react11.forwardRef)(
|
|
651
|
+
({ className, options, ...props }, ref) => {
|
|
652
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "relative w-full", children: [
|
|
653
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
654
|
+
"select",
|
|
655
|
+
{
|
|
656
|
+
ref,
|
|
657
|
+
className: cn(
|
|
658
|
+
"h-7 w-full px-2.5 text-xs bg-surface border border-border rounded-md text-foreground appearance-none pr-7 focus:outline-none focus:ring-1 focus:ring-primary",
|
|
659
|
+
className
|
|
660
|
+
),
|
|
661
|
+
...props,
|
|
662
|
+
children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: option.value, children: option.label }, option.value))
|
|
663
|
+
}
|
|
664
|
+
),
|
|
665
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none text-foreground-muted", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react3.ChevronDown, { className: "h-3.5 w-3.5" }) })
|
|
666
|
+
] });
|
|
667
|
+
}
|
|
668
|
+
);
|
|
669
|
+
Select.displayName = "Select";
|
|
670
|
+
|
|
671
|
+
// src/primitives/checkbox.tsx
|
|
672
|
+
var import_react12 = require("react");
|
|
673
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
674
|
+
var Checkbox = (0, import_react12.forwardRef)(
|
|
675
|
+
({ className, ...props }, ref) => {
|
|
676
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
677
|
+
"input",
|
|
678
|
+
{
|
|
679
|
+
ref,
|
|
680
|
+
type: "checkbox",
|
|
681
|
+
className: cn(
|
|
682
|
+
"w-3.5 h-3.5 rounded-sm border border-border accent-primary",
|
|
683
|
+
className
|
|
684
|
+
),
|
|
685
|
+
...props
|
|
686
|
+
}
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
);
|
|
690
|
+
Checkbox.displayName = "Checkbox";
|
|
691
|
+
|
|
692
|
+
// src/primitives/switch.tsx
|
|
693
|
+
var import_react13 = require("react");
|
|
694
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
695
|
+
var Switch = (0, import_react13.forwardRef)(
|
|
696
|
+
({ className, checked, onCheckedChange, label, ...props }, ref) => {
|
|
697
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
698
|
+
"button",
|
|
699
|
+
{
|
|
700
|
+
ref,
|
|
701
|
+
role: "switch",
|
|
702
|
+
"aria-checked": checked,
|
|
703
|
+
type: "button",
|
|
704
|
+
onClick: () => onCheckedChange(!checked),
|
|
705
|
+
className: cn("inline-flex items-center gap-2", className),
|
|
706
|
+
...props,
|
|
707
|
+
children: [
|
|
708
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
709
|
+
"span",
|
|
710
|
+
{
|
|
711
|
+
className: cn(
|
|
712
|
+
"w-8 h-4 rounded-full transition-colors",
|
|
713
|
+
checked ? "bg-primary" : "bg-surface-hover"
|
|
714
|
+
),
|
|
715
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
716
|
+
"span",
|
|
717
|
+
{
|
|
718
|
+
className: cn(
|
|
719
|
+
"block w-3.5 h-3.5 rounded-full bg-white transition-transform",
|
|
720
|
+
checked ? "translate-x-4" : "translate-x-0.5"
|
|
721
|
+
)
|
|
722
|
+
}
|
|
723
|
+
)
|
|
724
|
+
}
|
|
725
|
+
),
|
|
726
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-xs text-foreground", children: label })
|
|
727
|
+
]
|
|
728
|
+
}
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
);
|
|
732
|
+
Switch.displayName = "Switch";
|
|
733
|
+
|
|
734
|
+
// src/primitives/dialog.tsx
|
|
735
|
+
var import_react14 = require("react");
|
|
736
|
+
var import_class_variance_authority7 = require("class-variance-authority");
|
|
737
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
738
|
+
var DialogContext = (0, import_react14.createContext)(null);
|
|
739
|
+
function useDialogContext() {
|
|
740
|
+
const ctx = (0, import_react14.useContext)(DialogContext);
|
|
741
|
+
if (!ctx) throw new Error("Dialog compound components must be used within <Dialog>");
|
|
742
|
+
return ctx;
|
|
743
|
+
}
|
|
744
|
+
function Dialog({ children, open: controlledOpen, onOpenChange }) {
|
|
745
|
+
const [uncontrolledOpen, setUncontrolledOpen] = (0, import_react14.useState)(false);
|
|
746
|
+
const open = controlledOpen ?? uncontrolledOpen;
|
|
747
|
+
const contentId = (0, import_react14.useId)();
|
|
748
|
+
const setOpen = (0, import_react14.useCallback)(
|
|
749
|
+
(next) => {
|
|
750
|
+
onOpenChange?.(next);
|
|
751
|
+
if (controlledOpen === void 0) setUncontrolledOpen(next);
|
|
752
|
+
},
|
|
753
|
+
[controlledOpen, onOpenChange]
|
|
754
|
+
);
|
|
755
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogContext.Provider, { value: { open, setOpen, contentId }, children });
|
|
756
|
+
}
|
|
757
|
+
function DialogTrigger({ children, ...props }) {
|
|
758
|
+
const { setOpen } = useDialogContext();
|
|
759
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", onClick: () => setOpen(true), ...props, children });
|
|
760
|
+
}
|
|
761
|
+
var contentVariants = (0, import_class_variance_authority7.cva)(
|
|
762
|
+
"bg-background-elevated border border-border rounded-lg p-4 backdrop:bg-black/50 backdrop:backdrop-blur-sm",
|
|
763
|
+
{
|
|
764
|
+
variants: {
|
|
765
|
+
width: {
|
|
766
|
+
sm: "max-w-sm",
|
|
767
|
+
md: "max-w-md",
|
|
768
|
+
lg: "max-w-lg"
|
|
769
|
+
}
|
|
770
|
+
},
|
|
771
|
+
defaultVariants: { width: "md" }
|
|
772
|
+
}
|
|
773
|
+
);
|
|
774
|
+
var DialogContent = (0, import_react14.forwardRef)(
|
|
775
|
+
({ className, width, children, ...props }, ref) => {
|
|
776
|
+
const { open, setOpen, contentId } = useDialogContext();
|
|
777
|
+
const innerRef = (0, import_react14.useRef)(null);
|
|
778
|
+
const dialogRef = ref ?? innerRef;
|
|
779
|
+
(0, import_react14.useEffect)(() => {
|
|
780
|
+
const el = dialogRef.current;
|
|
781
|
+
if (!el) return;
|
|
782
|
+
if (open && !el.open) el.showModal();
|
|
783
|
+
if (!open && el.open) el.close();
|
|
784
|
+
}, [open, dialogRef]);
|
|
785
|
+
const handleClick = (e) => {
|
|
786
|
+
if (e.target === e.currentTarget) setOpen(false);
|
|
787
|
+
};
|
|
788
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
789
|
+
"dialog",
|
|
790
|
+
{
|
|
791
|
+
ref: dialogRef,
|
|
792
|
+
id: contentId,
|
|
793
|
+
className: cn(contentVariants({ width }), "w-full", className),
|
|
794
|
+
onClick: handleClick,
|
|
795
|
+
onClose: () => setOpen(false),
|
|
796
|
+
...props,
|
|
797
|
+
children
|
|
798
|
+
}
|
|
799
|
+
);
|
|
800
|
+
}
|
|
801
|
+
);
|
|
802
|
+
DialogContent.displayName = "DialogContent";
|
|
803
|
+
function DialogHeader({ className, ...props }) {
|
|
804
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn("flex flex-col gap-1 mb-3", className), ...props });
|
|
805
|
+
}
|
|
806
|
+
function DialogFooter({ className, ...props }) {
|
|
807
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn("flex justify-end gap-2 mt-4", className), ...props });
|
|
808
|
+
}
|
|
809
|
+
function DialogTitle({ className, ...props }) {
|
|
810
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h2", { className: cn("text-sm font-semibold text-foreground", className), ...props });
|
|
811
|
+
}
|
|
812
|
+
function DialogDescription({ className, ...props }) {
|
|
813
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: cn("text-xs text-foreground-muted", className), ...props });
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// src/primitives/dropdown.tsx
|
|
817
|
+
var import_react15 = require("react");
|
|
818
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
819
|
+
var DropdownContext = (0, import_react15.createContext)(null);
|
|
820
|
+
function useDropdownContext() {
|
|
821
|
+
const ctx = (0, import_react15.useContext)(DropdownContext);
|
|
822
|
+
if (!ctx) throw new Error("Dropdown compound components must be used within <Dropdown>");
|
|
823
|
+
return ctx;
|
|
824
|
+
}
|
|
825
|
+
function Dropdown({ children }) {
|
|
826
|
+
const [open, setOpen] = (0, import_react15.useState)(false);
|
|
827
|
+
const triggerId = (0, import_react15.useId)();
|
|
828
|
+
const contentId = (0, import_react15.useId)();
|
|
829
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DropdownContext.Provider, { value: { open, setOpen, triggerId, contentId }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "relative inline-block", children }) });
|
|
830
|
+
}
|
|
831
|
+
function DropdownTrigger({ children, ...props }) {
|
|
832
|
+
const { open, setOpen, triggerId, contentId } = useDropdownContext();
|
|
833
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
834
|
+
"button",
|
|
835
|
+
{
|
|
836
|
+
type: "button",
|
|
837
|
+
id: triggerId,
|
|
838
|
+
"aria-haspopup": "menu",
|
|
839
|
+
"aria-expanded": open,
|
|
840
|
+
"aria-controls": open ? contentId : void 0,
|
|
841
|
+
onClick: () => setOpen(!open),
|
|
842
|
+
...props,
|
|
843
|
+
children
|
|
844
|
+
}
|
|
845
|
+
);
|
|
846
|
+
}
|
|
847
|
+
function DropdownContent({ className, children, ...props }) {
|
|
848
|
+
const { open, setOpen, contentId, triggerId } = useDropdownContext();
|
|
849
|
+
const ref = (0, import_react15.useRef)(null);
|
|
850
|
+
(0, import_react15.useEffect)(() => {
|
|
851
|
+
if (!open) return;
|
|
852
|
+
const handler = (e) => {
|
|
853
|
+
const el = ref.current;
|
|
854
|
+
const trigger = document.getElementById(triggerId);
|
|
855
|
+
if (el && !el.contains(e.target) && !trigger?.contains(e.target)) {
|
|
856
|
+
setOpen(false);
|
|
857
|
+
}
|
|
858
|
+
};
|
|
859
|
+
const escHandler = (e) => {
|
|
860
|
+
if (e.key === "Escape") setOpen(false);
|
|
861
|
+
};
|
|
862
|
+
document.addEventListener("mousedown", handler);
|
|
863
|
+
document.addEventListener("keydown", escHandler);
|
|
864
|
+
return () => {
|
|
865
|
+
document.removeEventListener("mousedown", handler);
|
|
866
|
+
document.removeEventListener("keydown", escHandler);
|
|
867
|
+
};
|
|
868
|
+
}, [open, setOpen, triggerId]);
|
|
869
|
+
if (!open) return null;
|
|
870
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
871
|
+
"div",
|
|
872
|
+
{
|
|
873
|
+
ref,
|
|
874
|
+
id: contentId,
|
|
875
|
+
role: "menu",
|
|
876
|
+
"aria-labelledby": triggerId,
|
|
877
|
+
className: cn(
|
|
878
|
+
"absolute left-0 top-full z-50 mt-1 bg-background-elevated border border-border rounded-md shadow-lg py-1 min-w-[160px]",
|
|
879
|
+
className
|
|
880
|
+
),
|
|
881
|
+
...props,
|
|
882
|
+
children
|
|
883
|
+
}
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
function DropdownItem({
|
|
887
|
+
className,
|
|
888
|
+
icon: Icon,
|
|
889
|
+
variant = "default",
|
|
890
|
+
children,
|
|
891
|
+
onClick,
|
|
892
|
+
...props
|
|
893
|
+
}) {
|
|
894
|
+
const { setOpen } = useDropdownContext();
|
|
895
|
+
const handleClick = (0, import_react15.useCallback)(
|
|
896
|
+
(e) => {
|
|
897
|
+
onClick?.(e);
|
|
898
|
+
setOpen(false);
|
|
899
|
+
},
|
|
900
|
+
[onClick, setOpen]
|
|
901
|
+
);
|
|
902
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
903
|
+
"button",
|
|
904
|
+
{
|
|
905
|
+
type: "button",
|
|
906
|
+
role: "menuitem",
|
|
907
|
+
className: cn(
|
|
908
|
+
"h-7 text-xs px-2 w-full text-left flex items-center gap-2",
|
|
909
|
+
variant === "danger" ? "text-danger hover:bg-danger/10" : "text-foreground hover:bg-surface-hover",
|
|
910
|
+
className
|
|
911
|
+
),
|
|
912
|
+
onClick: handleClick,
|
|
913
|
+
...props,
|
|
914
|
+
children: [
|
|
915
|
+
Icon && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { size: 14 }),
|
|
916
|
+
children
|
|
917
|
+
]
|
|
918
|
+
}
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// src/primitives/tooltip.tsx
|
|
923
|
+
var import_react16 = require("react");
|
|
924
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
925
|
+
var TooltipContext = (0, import_react16.createContext)(null);
|
|
926
|
+
function useTooltipContext() {
|
|
927
|
+
const ctx = (0, import_react16.useContext)(TooltipContext);
|
|
928
|
+
if (!ctx) throw new Error("Tooltip compound components must be used within <Tooltip>");
|
|
929
|
+
return ctx;
|
|
930
|
+
}
|
|
931
|
+
function Tooltip({ children }) {
|
|
932
|
+
const [open, setOpen] = (0, import_react16.useState)(false);
|
|
933
|
+
const timerRef = (0, import_react16.useRef)(null);
|
|
934
|
+
const tooltipId = (0, import_react16.useId)();
|
|
935
|
+
const show = () => {
|
|
936
|
+
timerRef.current = setTimeout(() => setOpen(true), 300);
|
|
937
|
+
};
|
|
938
|
+
const hide = () => {
|
|
939
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
940
|
+
setOpen(false);
|
|
941
|
+
};
|
|
942
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TooltipContext.Provider, { value: { open, show, hide, tooltipId }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "relative inline-block", children }) });
|
|
943
|
+
}
|
|
944
|
+
function TooltipTrigger({ children, ...props }) {
|
|
945
|
+
const { show, hide, tooltipId, open } = useTooltipContext();
|
|
946
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
947
|
+
"div",
|
|
948
|
+
{
|
|
949
|
+
onMouseEnter: show,
|
|
950
|
+
onMouseLeave: hide,
|
|
951
|
+
onFocus: show,
|
|
952
|
+
onBlur: hide,
|
|
953
|
+
"aria-describedby": open ? tooltipId : void 0,
|
|
954
|
+
...props,
|
|
955
|
+
children
|
|
956
|
+
}
|
|
957
|
+
);
|
|
958
|
+
}
|
|
959
|
+
function TooltipContent({ className, children, ...props }) {
|
|
960
|
+
const { open, tooltipId } = useTooltipContext();
|
|
961
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
962
|
+
"div",
|
|
963
|
+
{
|
|
964
|
+
id: tooltipId,
|
|
965
|
+
role: "tooltip",
|
|
966
|
+
className: cn(
|
|
967
|
+
"absolute bottom-full left-1/2 -translate-x-1/2 mb-2 bg-foreground text-background text-xs px-2 py-1 rounded-md shadow-md whitespace-nowrap pointer-events-none transition-opacity duration-150",
|
|
968
|
+
open ? "opacity-100" : "opacity-0",
|
|
969
|
+
className
|
|
970
|
+
),
|
|
971
|
+
...props,
|
|
972
|
+
children
|
|
973
|
+
}
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// src/primitives/popover.tsx
|
|
978
|
+
var import_react17 = require("react");
|
|
979
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
980
|
+
var PopoverContext = (0, import_react17.createContext)(null);
|
|
981
|
+
function usePopoverContext() {
|
|
982
|
+
const ctx = (0, import_react17.useContext)(PopoverContext);
|
|
983
|
+
if (!ctx) throw new Error("Popover compound components must be used within <Popover>");
|
|
984
|
+
return ctx;
|
|
985
|
+
}
|
|
986
|
+
function Popover({ children, open: controlledOpen, onOpenChange }) {
|
|
987
|
+
const [uncontrolledOpen, setUncontrolledOpen] = (0, import_react17.useState)(false);
|
|
988
|
+
const open = controlledOpen ?? uncontrolledOpen;
|
|
989
|
+
const triggerId = (0, import_react17.useId)();
|
|
990
|
+
const contentId = (0, import_react17.useId)();
|
|
991
|
+
const setOpen = (0, import_react17.useCallback)(
|
|
992
|
+
(next) => {
|
|
993
|
+
onOpenChange?.(next);
|
|
994
|
+
if (controlledOpen === void 0) setUncontrolledOpen(next);
|
|
995
|
+
},
|
|
996
|
+
[controlledOpen, onOpenChange]
|
|
997
|
+
);
|
|
998
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PopoverContext.Provider, { value: { open, setOpen, triggerId, contentId }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "relative inline-block", children }) });
|
|
999
|
+
}
|
|
1000
|
+
function PopoverTrigger({ children, ...props }) {
|
|
1001
|
+
const { open, setOpen, triggerId, contentId } = usePopoverContext();
|
|
1002
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1003
|
+
"button",
|
|
1004
|
+
{
|
|
1005
|
+
type: "button",
|
|
1006
|
+
id: triggerId,
|
|
1007
|
+
"aria-haspopup": "dialog",
|
|
1008
|
+
"aria-expanded": open,
|
|
1009
|
+
"aria-controls": open ? contentId : void 0,
|
|
1010
|
+
onClick: () => setOpen(!open),
|
|
1011
|
+
...props,
|
|
1012
|
+
children
|
|
1013
|
+
}
|
|
1014
|
+
);
|
|
1015
|
+
}
|
|
1016
|
+
function PopoverContent({ className, children, ...props }) {
|
|
1017
|
+
const { open, setOpen, contentId, triggerId } = usePopoverContext();
|
|
1018
|
+
const ref = (0, import_react17.useRef)(null);
|
|
1019
|
+
(0, import_react17.useEffect)(() => {
|
|
1020
|
+
if (!open) return;
|
|
1021
|
+
const handler = (e) => {
|
|
1022
|
+
const el = ref.current;
|
|
1023
|
+
const trigger = document.getElementById(triggerId);
|
|
1024
|
+
if (el && !el.contains(e.target) && !trigger?.contains(e.target)) {
|
|
1025
|
+
setOpen(false);
|
|
1026
|
+
}
|
|
1027
|
+
};
|
|
1028
|
+
const escHandler = (e) => {
|
|
1029
|
+
if (e.key === "Escape") setOpen(false);
|
|
1030
|
+
};
|
|
1031
|
+
document.addEventListener("mousedown", handler);
|
|
1032
|
+
document.addEventListener("keydown", escHandler);
|
|
1033
|
+
return () => {
|
|
1034
|
+
document.removeEventListener("mousedown", handler);
|
|
1035
|
+
document.removeEventListener("keydown", escHandler);
|
|
1036
|
+
};
|
|
1037
|
+
}, [open, setOpen, triggerId]);
|
|
1038
|
+
if (!open) return null;
|
|
1039
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1040
|
+
"div",
|
|
1041
|
+
{
|
|
1042
|
+
ref,
|
|
1043
|
+
id: contentId,
|
|
1044
|
+
role: "dialog",
|
|
1045
|
+
"aria-labelledby": triggerId,
|
|
1046
|
+
className: cn(
|
|
1047
|
+
"absolute left-0 top-full z-50 mt-1 bg-background-elevated border border-border rounded-lg shadow-lg p-3",
|
|
1048
|
+
className
|
|
1049
|
+
),
|
|
1050
|
+
...props,
|
|
1051
|
+
children
|
|
1052
|
+
}
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
// src/primitives/tabs.tsx
|
|
1057
|
+
var import_react18 = require("react");
|
|
1058
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1059
|
+
var TabsContext = (0, import_react18.createContext)(null);
|
|
1060
|
+
function useTabsContext() {
|
|
1061
|
+
const ctx = (0, import_react18.useContext)(TabsContext);
|
|
1062
|
+
if (!ctx) throw new Error("Tabs compound components must be used within <Tabs>");
|
|
1063
|
+
return ctx;
|
|
1064
|
+
}
|
|
1065
|
+
function Tabs({
|
|
1066
|
+
value: controlledValue,
|
|
1067
|
+
onValueChange,
|
|
1068
|
+
defaultValue = "",
|
|
1069
|
+
className,
|
|
1070
|
+
...props
|
|
1071
|
+
}) {
|
|
1072
|
+
const [uncontrolledValue, setUncontrolledValue] = (0, import_react18.useState)(defaultValue);
|
|
1073
|
+
const value = controlledValue ?? uncontrolledValue;
|
|
1074
|
+
const setValue = (0, import_react18.useCallback)(
|
|
1075
|
+
(next) => {
|
|
1076
|
+
onValueChange?.(next);
|
|
1077
|
+
if (controlledValue === void 0) setUncontrolledValue(next);
|
|
1078
|
+
},
|
|
1079
|
+
[controlledValue, onValueChange]
|
|
1080
|
+
);
|
|
1081
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(TabsContext.Provider, { value: { value, setValue }, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className, ...props }) });
|
|
1082
|
+
}
|
|
1083
|
+
function TabsList({ className, ...props }) {
|
|
1084
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1085
|
+
"div",
|
|
1086
|
+
{
|
|
1087
|
+
role: "tablist",
|
|
1088
|
+
className: cn("flex flex-row border-b border-border-subtle", className),
|
|
1089
|
+
...props
|
|
1090
|
+
}
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
function TabsTrigger({ value, className, ...props }) {
|
|
1094
|
+
const { value: activeValue, setValue } = useTabsContext();
|
|
1095
|
+
const isActive = value === activeValue;
|
|
1096
|
+
const panelId = `tabpanel-${value}`;
|
|
1097
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1098
|
+
"button",
|
|
1099
|
+
{
|
|
1100
|
+
type: "button",
|
|
1101
|
+
role: "tab",
|
|
1102
|
+
"aria-selected": isActive,
|
|
1103
|
+
"aria-controls": panelId,
|
|
1104
|
+
tabIndex: isActive ? 0 : -1,
|
|
1105
|
+
className: cn(
|
|
1106
|
+
"h-7 text-xs px-3 transition-colors",
|
|
1107
|
+
isActive ? "border-b-2 border-primary text-foreground font-medium" : "text-foreground-muted hover:text-foreground",
|
|
1108
|
+
className
|
|
1109
|
+
),
|
|
1110
|
+
onClick: () => setValue(value),
|
|
1111
|
+
...props
|
|
1112
|
+
}
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
function TabsContent({ value, className, ...props }) {
|
|
1116
|
+
const { value: activeValue } = useTabsContext();
|
|
1117
|
+
if (value !== activeValue) return null;
|
|
1118
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1119
|
+
"div",
|
|
1120
|
+
{
|
|
1121
|
+
role: "tabpanel",
|
|
1122
|
+
id: `tabpanel-${value}`,
|
|
1123
|
+
className: cn("pt-3", className),
|
|
1124
|
+
...props
|
|
1125
|
+
}
|
|
1126
|
+
);
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
// src/primitives/scroll-area.tsx
|
|
1130
|
+
var import_react19 = require("react");
|
|
1131
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1132
|
+
var ScrollArea = (0, import_react19.forwardRef)(
|
|
1133
|
+
({ className, ...props }, ref) => {
|
|
1134
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1135
|
+
"div",
|
|
1136
|
+
{
|
|
1137
|
+
ref,
|
|
1138
|
+
className: cn(
|
|
1139
|
+
"overflow-auto [&::-webkit-scrollbar]:w-1 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:bg-surface-hover [&::-webkit-scrollbar-thumb]:rounded-full",
|
|
1140
|
+
className
|
|
1141
|
+
),
|
|
1142
|
+
...props
|
|
1143
|
+
}
|
|
1144
|
+
);
|
|
1145
|
+
}
|
|
1146
|
+
);
|
|
1147
|
+
ScrollArea.displayName = "ScrollArea";
|
|
1148
|
+
|
|
1149
|
+
// src/primitives/floating-panel.tsx
|
|
1150
|
+
var import_react20 = require("react");
|
|
1151
|
+
var import_lucide_react4 = require("lucide-react");
|
|
1152
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1153
|
+
function FloatingPanel({
|
|
1154
|
+
title,
|
|
1155
|
+
onClose,
|
|
1156
|
+
children,
|
|
1157
|
+
defaultWidth = 360,
|
|
1158
|
+
defaultHeight = 280,
|
|
1159
|
+
minWidth = 280,
|
|
1160
|
+
minHeight = 160,
|
|
1161
|
+
offsetIndex = 0,
|
|
1162
|
+
className
|
|
1163
|
+
}) {
|
|
1164
|
+
const [pos, setPos] = (0, import_react20.useState)({ x: 80 + offsetIndex * 30, y: 80 + offsetIndex * 30 });
|
|
1165
|
+
const [size, setSize] = (0, import_react20.useState)({ w: defaultWidth, h: defaultHeight });
|
|
1166
|
+
const [minimized, setMinimized] = (0, import_react20.useState)(false);
|
|
1167
|
+
const dragging = (0, import_react20.useRef)(false);
|
|
1168
|
+
const resizing = (0, import_react20.useRef)(false);
|
|
1169
|
+
const offset = (0, import_react20.useRef)({ x: 0, y: 0 });
|
|
1170
|
+
const onDragStart = (0, import_react20.useCallback)((e) => {
|
|
1171
|
+
e.preventDefault();
|
|
1172
|
+
dragging.current = true;
|
|
1173
|
+
offset.current = { x: e.clientX - pos.x, y: e.clientY - pos.y };
|
|
1174
|
+
}, [pos]);
|
|
1175
|
+
const onResizeStart = (0, import_react20.useCallback)((e) => {
|
|
1176
|
+
e.preventDefault();
|
|
1177
|
+
e.stopPropagation();
|
|
1178
|
+
resizing.current = true;
|
|
1179
|
+
offset.current = { x: e.clientX, y: e.clientY };
|
|
1180
|
+
}, []);
|
|
1181
|
+
(0, import_react20.useEffect)(() => {
|
|
1182
|
+
const onMouseMove = (e) => {
|
|
1183
|
+
if (dragging.current) setPos({ x: e.clientX - offset.current.x, y: e.clientY - offset.current.y });
|
|
1184
|
+
if (resizing.current) {
|
|
1185
|
+
const dx = e.clientX - offset.current.x;
|
|
1186
|
+
const dy = e.clientY - offset.current.y;
|
|
1187
|
+
offset.current = { x: e.clientX, y: e.clientY };
|
|
1188
|
+
setSize((prev) => ({ w: Math.max(minWidth, prev.w + dx), h: Math.max(minHeight, prev.h + dy) }));
|
|
1189
|
+
}
|
|
1190
|
+
};
|
|
1191
|
+
const onMouseUp = () => {
|
|
1192
|
+
dragging.current = false;
|
|
1193
|
+
resizing.current = false;
|
|
1194
|
+
};
|
|
1195
|
+
window.addEventListener("mousemove", onMouseMove);
|
|
1196
|
+
window.addEventListener("mouseup", onMouseUp);
|
|
1197
|
+
return () => {
|
|
1198
|
+
window.removeEventListener("mousemove", onMouseMove);
|
|
1199
|
+
window.removeEventListener("mouseup", onMouseUp);
|
|
1200
|
+
};
|
|
1201
|
+
}, [minWidth, minHeight]);
|
|
1202
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1203
|
+
"div",
|
|
1204
|
+
{
|
|
1205
|
+
className: cn(
|
|
1206
|
+
"fixed z-50 rounded-lg border border-border bg-background-elevated shadow-2xl flex flex-col overflow-hidden",
|
|
1207
|
+
className
|
|
1208
|
+
),
|
|
1209
|
+
style: { left: pos.x, top: pos.y, width: minimized ? 280 : size.w, height: minimized ? "auto" : size.h },
|
|
1210
|
+
children: [
|
|
1211
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1212
|
+
"div",
|
|
1213
|
+
{
|
|
1214
|
+
onMouseDown: onDragStart,
|
|
1215
|
+
className: "flex items-center justify-between gap-2 px-3 py-2 border-b border-border cursor-move select-none shrink-0 bg-surface",
|
|
1216
|
+
children: [
|
|
1217
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2 min-w-0", children: [
|
|
1218
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react4.GripHorizontal, { size: 12, className: "text-foreground-subtle shrink-0" }),
|
|
1219
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-[11px] font-medium truncate", children: title })
|
|
1220
|
+
] }),
|
|
1221
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-1 shrink-0", children: [
|
|
1222
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1223
|
+
"button",
|
|
1224
|
+
{
|
|
1225
|
+
onClick: () => setMinimized(!minimized),
|
|
1226
|
+
className: "p-0.5 rounded hover:bg-surface-hover text-foreground-muted transition-colors",
|
|
1227
|
+
title: minimized ? "Restore" : "Minimize",
|
|
1228
|
+
children: minimized ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react4.Maximize2, { size: 12 }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react4.Minimize2, { size: 12 })
|
|
1229
|
+
}
|
|
1230
|
+
),
|
|
1231
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1232
|
+
"button",
|
|
1233
|
+
{
|
|
1234
|
+
onClick: onClose,
|
|
1235
|
+
className: "p-0.5 rounded hover:bg-danger/20 text-foreground-muted hover:text-danger transition-colors",
|
|
1236
|
+
title: "Close",
|
|
1237
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react4.X, { size: 12 })
|
|
1238
|
+
}
|
|
1239
|
+
)
|
|
1240
|
+
] })
|
|
1241
|
+
]
|
|
1242
|
+
}
|
|
1243
|
+
),
|
|
1244
|
+
!minimized && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex-1 min-h-0 overflow-y-auto relative", children: [
|
|
1245
|
+
children,
|
|
1246
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1247
|
+
"div",
|
|
1248
|
+
{
|
|
1249
|
+
onMouseDown: onResizeStart,
|
|
1250
|
+
className: "absolute bottom-0 right-0 w-4 h-4 cursor-nwse-resize",
|
|
1251
|
+
style: { background: "linear-gradient(135deg, transparent 50%, var(--color-foreground-subtle) 50%)", opacity: 0.4 }
|
|
1252
|
+
}
|
|
1253
|
+
)
|
|
1254
|
+
] })
|
|
1255
|
+
]
|
|
1256
|
+
}
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
// src/composites/status-badge.tsx
|
|
1261
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1262
|
+
var statusConfig = {
|
|
1263
|
+
online: { colorClass: "bg-success", label: "Online" },
|
|
1264
|
+
offline: { colorClass: "bg-danger", label: "Offline" },
|
|
1265
|
+
degraded: { colorClass: "bg-warning", label: "Degraded" },
|
|
1266
|
+
unknown: { colorClass: "bg-foreground-subtle", label: "Unknown" }
|
|
1267
|
+
};
|
|
1268
|
+
function StatusBadge({
|
|
1269
|
+
status,
|
|
1270
|
+
showDot = true,
|
|
1271
|
+
showLabel = true,
|
|
1272
|
+
size = "sm",
|
|
1273
|
+
className
|
|
1274
|
+
}) {
|
|
1275
|
+
const config = statusConfig[status];
|
|
1276
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
1277
|
+
"span",
|
|
1278
|
+
{
|
|
1279
|
+
className: cn(
|
|
1280
|
+
"inline-flex items-center gap-1.5",
|
|
1281
|
+
size === "sm" ? "text-xs" : "text-sm",
|
|
1282
|
+
className
|
|
1283
|
+
),
|
|
1284
|
+
children: [
|
|
1285
|
+
showDot && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
1286
|
+
"span",
|
|
1287
|
+
{
|
|
1288
|
+
className: cn("h-1.5 w-1.5 shrink-0 rounded-full", config.colorClass),
|
|
1289
|
+
"aria-hidden": "true"
|
|
1290
|
+
}
|
|
1291
|
+
),
|
|
1292
|
+
showLabel && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-foreground", children: config.label })
|
|
1293
|
+
]
|
|
1294
|
+
}
|
|
1295
|
+
);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
// src/composites/provider-badge.tsx
|
|
1299
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1300
|
+
var providerConfig = {
|
|
1301
|
+
frigate: { colorClass: "bg-provider-frigate", label: "Frigate" },
|
|
1302
|
+
scrypted: { colorClass: "bg-provider-scrypted", label: "Scrypted" },
|
|
1303
|
+
reolink: { colorClass: "bg-provider-reolink", label: "Reolink" },
|
|
1304
|
+
homeAssistant: { colorClass: "bg-provider-homeAssistant", label: "Home Assistant" },
|
|
1305
|
+
rtsp: { colorClass: "bg-provider-rtsp", label: "RTSP" }
|
|
1306
|
+
};
|
|
1307
|
+
function ProviderBadge({
|
|
1308
|
+
provider,
|
|
1309
|
+
showLabel = true,
|
|
1310
|
+
className
|
|
1311
|
+
}) {
|
|
1312
|
+
const config = providerConfig[provider];
|
|
1313
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: cn("inline-flex items-center gap-1.5 text-xs", className), children: [
|
|
1314
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1315
|
+
"span",
|
|
1316
|
+
{
|
|
1317
|
+
className: cn("h-1.5 w-1.5 shrink-0 rounded-sm", config.colorClass),
|
|
1318
|
+
"aria-hidden": "true"
|
|
1319
|
+
}
|
|
1320
|
+
),
|
|
1321
|
+
showLabel && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-foreground", children: config.label })
|
|
1322
|
+
] });
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// src/composites/form-field.tsx
|
|
1326
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
1327
|
+
function FormField({
|
|
1328
|
+
label,
|
|
1329
|
+
description,
|
|
1330
|
+
error,
|
|
1331
|
+
required,
|
|
1332
|
+
children,
|
|
1333
|
+
orientation = "vertical",
|
|
1334
|
+
className
|
|
1335
|
+
}) {
|
|
1336
|
+
const isHorizontal = orientation === "horizontal";
|
|
1337
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
1338
|
+
"div",
|
|
1339
|
+
{
|
|
1340
|
+
className: cn(
|
|
1341
|
+
"flex gap-2",
|
|
1342
|
+
isHorizontal ? "flex-row items-center justify-between" : "flex-col",
|
|
1343
|
+
className
|
|
1344
|
+
),
|
|
1345
|
+
children: [
|
|
1346
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: cn(isHorizontal ? "flex-1" : ""), children: [
|
|
1347
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Label, { children: [
|
|
1348
|
+
label,
|
|
1349
|
+
required && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-danger ml-0.5", children: "*" })
|
|
1350
|
+
] }),
|
|
1351
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-foreground-subtle text-xs mt-0.5", children: description })
|
|
1352
|
+
] }),
|
|
1353
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn(isHorizontal ? "shrink-0" : ""), children }),
|
|
1354
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-danger text-xs", children: error })
|
|
1355
|
+
]
|
|
1356
|
+
}
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
// src/composites/page-header.tsx
|
|
1361
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1362
|
+
function PageHeader({ title, subtitle, actions, className }) {
|
|
1363
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: cn("flex items-center justify-between mb-3", className), children: [
|
|
1364
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { children: [
|
|
1365
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h1", { className: "text-sm font-semibold text-foreground", children: title }),
|
|
1366
|
+
subtitle && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-foreground-subtle text-xs", children: subtitle })
|
|
1367
|
+
] }),
|
|
1368
|
+
actions && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex items-center gap-2", children: actions })
|
|
1369
|
+
] });
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
// src/composites/empty-state.tsx
|
|
1373
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1374
|
+
function EmptyState({
|
|
1375
|
+
icon: Icon,
|
|
1376
|
+
title,
|
|
1377
|
+
description,
|
|
1378
|
+
action,
|
|
1379
|
+
className
|
|
1380
|
+
}) {
|
|
1381
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: cn("flex flex-col items-center justify-center gap-3 py-12", className), children: [
|
|
1382
|
+
Icon && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Icon, { className: "h-12 w-12 text-foreground-subtle", "aria-hidden": "true" }),
|
|
1383
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col items-center gap-1 text-center", children: [
|
|
1384
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-foreground-muted text-sm font-medium", children: title }),
|
|
1385
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-foreground-subtle text-xs max-w-xs", children: description })
|
|
1386
|
+
] }),
|
|
1387
|
+
action && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "mt-1", children: action })
|
|
1388
|
+
] });
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
// src/composites/confirm-dialog.tsx
|
|
1392
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1393
|
+
function ConfirmDialog({
|
|
1394
|
+
title,
|
|
1395
|
+
message,
|
|
1396
|
+
confirmLabel = "Confirm",
|
|
1397
|
+
cancelLabel = "Cancel",
|
|
1398
|
+
onConfirm,
|
|
1399
|
+
onCancel,
|
|
1400
|
+
variant = "default",
|
|
1401
|
+
open,
|
|
1402
|
+
onOpenChange
|
|
1403
|
+
}) {
|
|
1404
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Dialog, { open, onOpenChange, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DialogContent, { children: [
|
|
1405
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DialogHeader, { children: [
|
|
1406
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(DialogTitle, { children: title }),
|
|
1407
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(DialogDescription, { children: message })
|
|
1408
|
+
] }),
|
|
1409
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DialogFooter, { children: [
|
|
1410
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Button, { variant: "ghost", onClick: onCancel, children: cancelLabel }),
|
|
1411
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1412
|
+
Button,
|
|
1413
|
+
{
|
|
1414
|
+
variant: variant === "danger" ? "danger" : "primary",
|
|
1415
|
+
onClick: onConfirm,
|
|
1416
|
+
children: confirmLabel
|
|
1417
|
+
}
|
|
1418
|
+
)
|
|
1419
|
+
] })
|
|
1420
|
+
] }) });
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
// src/composites/stat-card.tsx
|
|
1424
|
+
var import_lucide_react5 = require("lucide-react");
|
|
1425
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1426
|
+
function StatCard({ value, label, trend, className }) {
|
|
1427
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Card, { className: cn("flex flex-col gap-1", className), children: [
|
|
1428
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-baseline gap-2", children: [
|
|
1429
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-2xl font-semibold text-foreground", children: value }),
|
|
1430
|
+
trend && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
1431
|
+
"span",
|
|
1432
|
+
{
|
|
1433
|
+
className: cn(
|
|
1434
|
+
"inline-flex items-center gap-0.5 text-xs font-medium",
|
|
1435
|
+
trend.direction === "up" ? "text-success" : "text-danger"
|
|
1436
|
+
),
|
|
1437
|
+
children: [
|
|
1438
|
+
trend.direction === "up" ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react5.TrendingUp, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react5.TrendingDown, { className: "h-3 w-3" }),
|
|
1439
|
+
trend.value,
|
|
1440
|
+
"%"
|
|
1441
|
+
]
|
|
1442
|
+
}
|
|
1443
|
+
)
|
|
1444
|
+
] }),
|
|
1445
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-xs text-foreground-muted", children: label })
|
|
1446
|
+
] });
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
// src/composites/key-value-list.tsx
|
|
1450
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1451
|
+
function KeyValueList({ items, className }) {
|
|
1452
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("dl", { className: cn("flex flex-col", className), children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
1453
|
+
"div",
|
|
1454
|
+
{
|
|
1455
|
+
className: "flex items-center h-7",
|
|
1456
|
+
children: [
|
|
1457
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("dt", { className: "text-foreground-subtle text-xs w-1/3 shrink-0", children: item.key }),
|
|
1458
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("dd", { className: "text-foreground text-xs", children: item.value })
|
|
1459
|
+
]
|
|
1460
|
+
},
|
|
1461
|
+
item.key
|
|
1462
|
+
)) });
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
// src/composites/code-block.tsx
|
|
1466
|
+
var import_react21 = require("react");
|
|
1467
|
+
var import_lucide_react6 = require("lucide-react");
|
|
1468
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1469
|
+
function CodeBlock({ children, maxHeight = 300, className }) {
|
|
1470
|
+
const [copied, setCopied] = (0, import_react21.useState)(false);
|
|
1471
|
+
const handleCopy = (0, import_react21.useCallback)(() => {
|
|
1472
|
+
navigator.clipboard.writeText(children).then(() => {
|
|
1473
|
+
setCopied(true);
|
|
1474
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
1475
|
+
});
|
|
1476
|
+
}, [children]);
|
|
1477
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: cn("relative group", className), children: [
|
|
1478
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ScrollArea, { style: { maxHeight }, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("pre", { className: "font-mono text-xs bg-surface p-3 rounded-md border border-border-subtle", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("code", { children }) }) }),
|
|
1479
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1480
|
+
IconButton,
|
|
1481
|
+
{
|
|
1482
|
+
icon: copied ? import_lucide_react6.Check : import_lucide_react6.Copy,
|
|
1483
|
+
"aria-label": "Copy code",
|
|
1484
|
+
variant: "ghost",
|
|
1485
|
+
size: "sm",
|
|
1486
|
+
onClick: handleCopy
|
|
1487
|
+
}
|
|
1488
|
+
) })
|
|
1489
|
+
] });
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
// src/composites/filter-bar.tsx
|
|
1493
|
+
var import_lucide_react7 = require("lucide-react");
|
|
1494
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1495
|
+
function FilterBar({ filters, values, onChange, className }) {
|
|
1496
|
+
const handleChange = (key, value) => {
|
|
1497
|
+
onChange({ ...values, [key]: value });
|
|
1498
|
+
};
|
|
1499
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: cn("flex items-center gap-2 flex-wrap", className), children: filters.map((filter) => {
|
|
1500
|
+
switch (filter.type) {
|
|
1501
|
+
case "search":
|
|
1502
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1503
|
+
Input,
|
|
1504
|
+
{
|
|
1505
|
+
placeholder: filter.placeholder ?? "Search...",
|
|
1506
|
+
value: values[filter.key] ?? "",
|
|
1507
|
+
onChange: (e) => handleChange(filter.key, e.target.value),
|
|
1508
|
+
leftSlot: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react7.Search, { className: "h-3 w-3 text-foreground-subtle" }),
|
|
1509
|
+
className: "w-48"
|
|
1510
|
+
},
|
|
1511
|
+
filter.key
|
|
1512
|
+
);
|
|
1513
|
+
case "select":
|
|
1514
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1515
|
+
Select,
|
|
1516
|
+
{
|
|
1517
|
+
options: filter.options,
|
|
1518
|
+
value: values[filter.key] ?? "",
|
|
1519
|
+
onChange: (e) => handleChange(filter.key, e.target.value),
|
|
1520
|
+
className: "w-36"
|
|
1521
|
+
},
|
|
1522
|
+
filter.key
|
|
1523
|
+
);
|
|
1524
|
+
case "badge-toggle":
|
|
1525
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "flex items-center gap-1", children: filter.options.map((option) => {
|
|
1526
|
+
const currentValue = values[filter.key];
|
|
1527
|
+
const isActive = currentValue === option.value;
|
|
1528
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1529
|
+
"button",
|
|
1530
|
+
{
|
|
1531
|
+
type: "button",
|
|
1532
|
+
onClick: () => handleChange(
|
|
1533
|
+
filter.key,
|
|
1534
|
+
isActive ? void 0 : option.value
|
|
1535
|
+
),
|
|
1536
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1537
|
+
Badge,
|
|
1538
|
+
{
|
|
1539
|
+
variant: isActive ? "info" : "default",
|
|
1540
|
+
className: "cursor-pointer",
|
|
1541
|
+
children: option.label
|
|
1542
|
+
}
|
|
1543
|
+
)
|
|
1544
|
+
},
|
|
1545
|
+
option.value
|
|
1546
|
+
);
|
|
1547
|
+
}) }, filter.key);
|
|
1548
|
+
default:
|
|
1549
|
+
return null;
|
|
1550
|
+
}
|
|
1551
|
+
}) });
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
// src/composites/app-shell/sidebar-item.tsx
|
|
1555
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1556
|
+
function SidebarItem({
|
|
1557
|
+
label,
|
|
1558
|
+
icon: Icon,
|
|
1559
|
+
href,
|
|
1560
|
+
badge,
|
|
1561
|
+
active = false,
|
|
1562
|
+
className
|
|
1563
|
+
}) {
|
|
1564
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
1565
|
+
"a",
|
|
1566
|
+
{
|
|
1567
|
+
href,
|
|
1568
|
+
className: cn(
|
|
1569
|
+
"flex items-center gap-2 h-7 px-2 text-[11px] transition-colors",
|
|
1570
|
+
active ? "border-l-2 border-primary bg-primary/[0.08] text-foreground rounded-r-md" : "text-foreground-subtle hover:bg-surface-hover rounded-md",
|
|
1571
|
+
className
|
|
1572
|
+
),
|
|
1573
|
+
children: [
|
|
1574
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
1575
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "truncate flex-1", children: label }),
|
|
1576
|
+
badge !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Badge, { className: "ml-auto text-[10px] px-1.5 py-0", children: badge })
|
|
1577
|
+
]
|
|
1578
|
+
}
|
|
1579
|
+
);
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
// src/composites/app-shell/sidebar.tsx
|
|
1583
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1584
|
+
function Sidebar({ logo, sections, footer, className }) {
|
|
1585
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
1586
|
+
"nav",
|
|
1587
|
+
{
|
|
1588
|
+
className: cn(
|
|
1589
|
+
"w-44 bg-surface border-r border-border h-full flex flex-col",
|
|
1590
|
+
className
|
|
1591
|
+
),
|
|
1592
|
+
children: [
|
|
1593
|
+
logo && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "px-3 py-2 shrink-0", children: logo }),
|
|
1594
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "flex-1 overflow-auto px-1 py-1", children: sections.map((section, sectionIndex) => /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: cn(sectionIndex > 0 ? "mt-3" : ""), children: [
|
|
1595
|
+
section.label && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "text-[10px] text-foreground-disabled uppercase tracking-wider px-2 mb-1 block", children: section.label }),
|
|
1596
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "flex flex-col gap-0.5", children: section.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(SidebarItem, { ...item }, item.href)) })
|
|
1597
|
+
] }, sectionIndex)) }),
|
|
1598
|
+
footer && footer.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "shrink-0 px-1 pb-1", children: [
|
|
1599
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Separator, { className: "mb-1" }),
|
|
1600
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "flex flex-col gap-0.5", children: footer.map((item) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(SidebarItem, { ...item }, item.href)) })
|
|
1601
|
+
] })
|
|
1602
|
+
]
|
|
1603
|
+
}
|
|
1604
|
+
);
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
// src/composites/app-shell/app-shell.tsx
|
|
1608
|
+
var import_lucide_react8 = require("lucide-react");
|
|
1609
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1610
|
+
function AppShell({ sidebar, header, children, className }) {
|
|
1611
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: cn("flex h-screen", className), children: [
|
|
1612
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Sidebar, { ...sidebar }),
|
|
1613
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-1 flex-col min-w-0", children: [
|
|
1614
|
+
header && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("header", { className: "flex items-center h-10 border-b border-border px-4 shrink-0", children: [
|
|
1615
|
+
header.breadcrumbs && header.breadcrumbs.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("nav", { className: "flex items-center gap-1 text-xs flex-1 min-w-0", children: header.breadcrumbs.map((crumb, index) => {
|
|
1616
|
+
const isLast = index === header.breadcrumbs.length - 1;
|
|
1617
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: "flex items-center gap-1", children: [
|
|
1618
|
+
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react8.ChevronRight, { className: "h-3 w-3 text-foreground-subtle shrink-0" }),
|
|
1619
|
+
crumb.href && !isLast ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
1620
|
+
"a",
|
|
1621
|
+
{
|
|
1622
|
+
href: crumb.href,
|
|
1623
|
+
className: "text-foreground-subtle hover:text-foreground transition-colors truncate",
|
|
1624
|
+
children: crumb.label
|
|
1625
|
+
}
|
|
1626
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-foreground truncate", children: crumb.label })
|
|
1627
|
+
] }, index);
|
|
1628
|
+
}) }),
|
|
1629
|
+
header.actions && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "flex items-center gap-2 ml-auto shrink-0", children: header.actions })
|
|
1630
|
+
] }),
|
|
1631
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("main", { className: "flex-1 overflow-auto p-4", children })
|
|
1632
|
+
] })
|
|
1633
|
+
] });
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
// src/composites/data-table/data-table.tsx
|
|
1637
|
+
var import_react22 = require("react");
|
|
1638
|
+
var import_react_table = require("@tanstack/react-table");
|
|
1639
|
+
|
|
1640
|
+
// src/composites/data-table/data-table-header.tsx
|
|
1641
|
+
var import_lucide_react9 = require("lucide-react");
|
|
1642
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1643
|
+
function DataTableHeader({
|
|
1644
|
+
headerGroups,
|
|
1645
|
+
onSortingChange,
|
|
1646
|
+
stickyHeader,
|
|
1647
|
+
flexRender: render
|
|
1648
|
+
}) {
|
|
1649
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1650
|
+
"thead",
|
|
1651
|
+
{
|
|
1652
|
+
className: cn(
|
|
1653
|
+
stickyHeader && "sticky top-0 z-10 bg-background"
|
|
1654
|
+
),
|
|
1655
|
+
children: headerGroups.map((headerGroup) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("tr", { className: "h-6", children: headerGroup.headers.map((header) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1656
|
+
HeaderCell,
|
|
1657
|
+
{
|
|
1658
|
+
header,
|
|
1659
|
+
sortable: header.column.getCanSort() && !!onSortingChange,
|
|
1660
|
+
flexRender: render
|
|
1661
|
+
},
|
|
1662
|
+
header.id
|
|
1663
|
+
)) }, headerGroup.id))
|
|
1664
|
+
}
|
|
1665
|
+
);
|
|
1666
|
+
}
|
|
1667
|
+
function HeaderCell({ header, sortable, flexRender: render }) {
|
|
1668
|
+
const sorted = header.column.getIsSorted();
|
|
1669
|
+
const SortIcon = sorted === "asc" ? import_lucide_react9.ArrowUp : sorted === "desc" ? import_lucide_react9.ArrowDown : import_lucide_react9.ArrowUpDown;
|
|
1670
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1671
|
+
"th",
|
|
1672
|
+
{
|
|
1673
|
+
className: cn(
|
|
1674
|
+
"px-2 py-1 text-left text-[10px] text-foreground-subtle uppercase tracking-wider font-medium",
|
|
1675
|
+
sortable && "cursor-pointer select-none"
|
|
1676
|
+
),
|
|
1677
|
+
onClick: sortable ? header.column.getToggleSortingHandler() : void 0,
|
|
1678
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
|
|
1679
|
+
header.isPlaceholder ? null : render(header.column.columnDef.header, header.getContext()),
|
|
1680
|
+
sortable && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(SortIcon, { className: "h-3 w-3" })
|
|
1681
|
+
] })
|
|
1682
|
+
}
|
|
1683
|
+
);
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
// src/composites/data-table/data-table-row.tsx
|
|
1687
|
+
var import_lucide_react10 = require("lucide-react");
|
|
1688
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1689
|
+
function DataTableRow({
|
|
1690
|
+
row,
|
|
1691
|
+
onRowClick,
|
|
1692
|
+
rowActions,
|
|
1693
|
+
flexRender: render
|
|
1694
|
+
}) {
|
|
1695
|
+
const actions = rowActions ? rowActions(row.original) : [];
|
|
1696
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
|
|
1697
|
+
"tr",
|
|
1698
|
+
{
|
|
1699
|
+
className: cn(
|
|
1700
|
+
"h-7 border-b border-border/50",
|
|
1701
|
+
onRowClick && "cursor-pointer",
|
|
1702
|
+
"hover:bg-surface-hover"
|
|
1703
|
+
),
|
|
1704
|
+
onClick: onRowClick ? () => onRowClick(row.original) : void 0,
|
|
1705
|
+
children: [
|
|
1706
|
+
row.getVisibleCells().map((cell) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(DataTableCell, { cell, flexRender: render }, cell.id)),
|
|
1707
|
+
actions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("td", { className: "px-2 py-1.5 w-8", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(Dropdown, { children: [
|
|
1708
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
1709
|
+
DropdownTrigger,
|
|
1710
|
+
{
|
|
1711
|
+
className: "p-0.5 rounded hover:bg-surface-hover",
|
|
1712
|
+
onClick: (e) => e.stopPropagation(),
|
|
1713
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react10.MoreHorizontal, { className: "h-3.5 w-3.5 text-foreground-muted" })
|
|
1714
|
+
}
|
|
1715
|
+
),
|
|
1716
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(DropdownContent, { className: "right-0 left-auto", children: actions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
1717
|
+
DropdownItem,
|
|
1718
|
+
{
|
|
1719
|
+
icon: action.icon,
|
|
1720
|
+
variant: action.variant,
|
|
1721
|
+
onClick: (e) => {
|
|
1722
|
+
e.stopPropagation();
|
|
1723
|
+
action.onClick();
|
|
1724
|
+
},
|
|
1725
|
+
children: action.label
|
|
1726
|
+
},
|
|
1727
|
+
action.label
|
|
1728
|
+
)) })
|
|
1729
|
+
] }) })
|
|
1730
|
+
]
|
|
1731
|
+
}
|
|
1732
|
+
);
|
|
1733
|
+
}
|
|
1734
|
+
function DataTableCell({ cell, flexRender: render }) {
|
|
1735
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("td", { className: "px-2 py-1.5 text-xs text-foreground", children: render(cell.column.columnDef.cell, cell.getContext()) });
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
// src/composites/data-table/data-table-pagination.tsx
|
|
1739
|
+
var import_lucide_react11 = require("lucide-react");
|
|
1740
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
1741
|
+
var PAGE_SIZE_OPTIONS = [
|
|
1742
|
+
{ value: "10", label: "10" },
|
|
1743
|
+
{ value: "25", label: "25" },
|
|
1744
|
+
{ value: "50", label: "50" },
|
|
1745
|
+
{ value: "100", label: "100" }
|
|
1746
|
+
];
|
|
1747
|
+
function DataTablePagination({
|
|
1748
|
+
page,
|
|
1749
|
+
pageSize,
|
|
1750
|
+
total,
|
|
1751
|
+
onPaginationChange
|
|
1752
|
+
}) {
|
|
1753
|
+
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
1754
|
+
const currentPage = page + 1;
|
|
1755
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex items-center justify-between px-2 py-2 text-xs text-foreground-muted", children: [
|
|
1756
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1757
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { children: "Rows per page" }),
|
|
1758
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "w-16", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1759
|
+
Select,
|
|
1760
|
+
{
|
|
1761
|
+
options: PAGE_SIZE_OPTIONS,
|
|
1762
|
+
value: String(pageSize),
|
|
1763
|
+
onChange: (e) => onPaginationChange?.({
|
|
1764
|
+
pageIndex: 0,
|
|
1765
|
+
pageSize: Number(e.target.value)
|
|
1766
|
+
})
|
|
1767
|
+
}
|
|
1768
|
+
) })
|
|
1769
|
+
] }),
|
|
1770
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1771
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { children: [
|
|
1772
|
+
"Page ",
|
|
1773
|
+
currentPage,
|
|
1774
|
+
" of ",
|
|
1775
|
+
totalPages
|
|
1776
|
+
] }),
|
|
1777
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1778
|
+
IconButton,
|
|
1779
|
+
{
|
|
1780
|
+
icon: import_lucide_react11.ChevronLeft,
|
|
1781
|
+
"aria-label": "Previous page",
|
|
1782
|
+
variant: "ghost",
|
|
1783
|
+
size: "sm",
|
|
1784
|
+
disabled: page <= 0,
|
|
1785
|
+
onClick: () => onPaginationChange?.({ pageIndex: page - 1, pageSize })
|
|
1786
|
+
}
|
|
1787
|
+
),
|
|
1788
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1789
|
+
IconButton,
|
|
1790
|
+
{
|
|
1791
|
+
icon: import_lucide_react11.ChevronRight,
|
|
1792
|
+
"aria-label": "Next page",
|
|
1793
|
+
variant: "ghost",
|
|
1794
|
+
size: "sm",
|
|
1795
|
+
disabled: currentPage >= totalPages,
|
|
1796
|
+
onClick: () => onPaginationChange?.({ pageIndex: page + 1, pageSize })
|
|
1797
|
+
}
|
|
1798
|
+
)
|
|
1799
|
+
] })
|
|
1800
|
+
] });
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
// src/composites/data-table/data-table.tsx
|
|
1804
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
1805
|
+
function DataTable({
|
|
1806
|
+
data,
|
|
1807
|
+
columns: userColumns,
|
|
1808
|
+
sorting,
|
|
1809
|
+
onSortingChange,
|
|
1810
|
+
filtering,
|
|
1811
|
+
onFilteringChange,
|
|
1812
|
+
pagination,
|
|
1813
|
+
onPaginationChange,
|
|
1814
|
+
loading = false,
|
|
1815
|
+
emptyState,
|
|
1816
|
+
rowActions,
|
|
1817
|
+
onRowClick,
|
|
1818
|
+
selectable = false,
|
|
1819
|
+
compact = true,
|
|
1820
|
+
stickyHeader = false,
|
|
1821
|
+
className
|
|
1822
|
+
}) {
|
|
1823
|
+
const columns = (0, import_react22.useMemo)(() => {
|
|
1824
|
+
if (!selectable) return userColumns;
|
|
1825
|
+
const selectColumn = {
|
|
1826
|
+
id: "__select",
|
|
1827
|
+
header: ({ table: table2 }) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
1828
|
+
Checkbox,
|
|
1829
|
+
{
|
|
1830
|
+
checked: table2.getIsAllPageRowsSelected(),
|
|
1831
|
+
onChange: table2.getToggleAllPageRowsSelectedHandler(),
|
|
1832
|
+
"aria-label": "Select all"
|
|
1833
|
+
}
|
|
1834
|
+
),
|
|
1835
|
+
cell: ({ row }) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
1836
|
+
Checkbox,
|
|
1837
|
+
{
|
|
1838
|
+
checked: row.getIsSelected(),
|
|
1839
|
+
onChange: row.getToggleSelectedHandler(),
|
|
1840
|
+
"aria-label": "Select row"
|
|
1841
|
+
}
|
|
1842
|
+
),
|
|
1843
|
+
enableSorting: false
|
|
1844
|
+
};
|
|
1845
|
+
return [selectColumn, ...userColumns];
|
|
1846
|
+
}, [userColumns, selectable]);
|
|
1847
|
+
const table = (0, import_react_table.useReactTable)({
|
|
1848
|
+
data,
|
|
1849
|
+
columns,
|
|
1850
|
+
state: {
|
|
1851
|
+
...sorting !== void 0 && { sorting },
|
|
1852
|
+
...filtering !== void 0 && { columnFilters: filtering },
|
|
1853
|
+
...pagination !== void 0 && {
|
|
1854
|
+
pagination: { pageIndex: pagination.page, pageSize: pagination.pageSize }
|
|
1855
|
+
}
|
|
1856
|
+
},
|
|
1857
|
+
onSortingChange: onSortingChange ? (updater) => {
|
|
1858
|
+
const next = typeof updater === "function" ? updater(sorting ?? []) : updater;
|
|
1859
|
+
onSortingChange(next);
|
|
1860
|
+
} : void 0,
|
|
1861
|
+
onColumnFiltersChange: onFilteringChange ? (updater) => {
|
|
1862
|
+
const next = typeof updater === "function" ? updater(filtering ?? []) : updater;
|
|
1863
|
+
onFilteringChange(next);
|
|
1864
|
+
} : void 0,
|
|
1865
|
+
getCoreRowModel: (0, import_react_table.getCoreRowModel)(),
|
|
1866
|
+
getSortedRowModel: (0, import_react_table.getSortedRowModel)(),
|
|
1867
|
+
getFilteredRowModel: (0, import_react_table.getFilteredRowModel)(),
|
|
1868
|
+
getPaginationRowModel: pagination ? (0, import_react_table.getPaginationRowModel)() : void 0,
|
|
1869
|
+
manualPagination: pagination !== void 0,
|
|
1870
|
+
pageCount: pagination ? Math.ceil(pagination.total / pagination.pageSize) : void 0
|
|
1871
|
+
});
|
|
1872
|
+
const hasActions = !!rowActions;
|
|
1873
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: cn("overflow-auto", className), children: [
|
|
1874
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("table", { className: "w-full border-collapse", children: [
|
|
1875
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
1876
|
+
DataTableHeader,
|
|
1877
|
+
{
|
|
1878
|
+
headerGroups: table.getHeaderGroups(),
|
|
1879
|
+
onSortingChange,
|
|
1880
|
+
stickyHeader,
|
|
1881
|
+
flexRender: import_react_table.flexRender
|
|
1882
|
+
}
|
|
1883
|
+
),
|
|
1884
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("tbody", { children: loading ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(LoadingRows, { colSpan: columns.length + (hasActions ? 1 : 0), compact }) : table.getRowModel().rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
1885
|
+
"td",
|
|
1886
|
+
{
|
|
1887
|
+
colSpan: columns.length + (hasActions ? 1 : 0),
|
|
1888
|
+
className: "text-center py-8 text-xs text-foreground-muted",
|
|
1889
|
+
children: emptyState ?? "No data"
|
|
1890
|
+
}
|
|
1891
|
+
) }) : table.getRowModel().rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
1892
|
+
DataTableRow,
|
|
1893
|
+
{
|
|
1894
|
+
row,
|
|
1895
|
+
onRowClick,
|
|
1896
|
+
rowActions,
|
|
1897
|
+
flexRender: import_react_table.flexRender
|
|
1898
|
+
},
|
|
1899
|
+
row.id
|
|
1900
|
+
)) })
|
|
1901
|
+
] }),
|
|
1902
|
+
pagination && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
1903
|
+
DataTablePagination,
|
|
1904
|
+
{
|
|
1905
|
+
page: pagination.page,
|
|
1906
|
+
pageSize: pagination.pageSize,
|
|
1907
|
+
total: pagination.total,
|
|
1908
|
+
onPaginationChange
|
|
1909
|
+
}
|
|
1910
|
+
)
|
|
1911
|
+
] });
|
|
1912
|
+
}
|
|
1913
|
+
function LoadingRows({ colSpan, compact }) {
|
|
1914
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_jsx_runtime36.Fragment, { children: Array.from({ length: 5 }).map((_, rowIdx) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("tr", { className: compact ? "h-7" : "h-9", children: Array.from({ length: colSpan }).map((_2, colIdx) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "px-2 py-1.5", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Skeleton, { className: "h-3 w-full" }) }, colIdx)) }, rowIdx)) });
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
// src/composites/device-card.tsx
|
|
1918
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
1919
|
+
var STATUS_COLORS = {
|
|
1920
|
+
online: "bg-success",
|
|
1921
|
+
offline: "bg-danger",
|
|
1922
|
+
warning: "bg-warning",
|
|
1923
|
+
unknown: "bg-foreground-subtle"
|
|
1924
|
+
};
|
|
1925
|
+
function DeviceCard({
|
|
1926
|
+
title,
|
|
1927
|
+
subtitle,
|
|
1928
|
+
status,
|
|
1929
|
+
selected,
|
|
1930
|
+
onClick,
|
|
1931
|
+
badges,
|
|
1932
|
+
actions,
|
|
1933
|
+
offlineAction,
|
|
1934
|
+
className
|
|
1935
|
+
}) {
|
|
1936
|
+
const isOffline = status === "offline";
|
|
1937
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
1938
|
+
"div",
|
|
1939
|
+
{
|
|
1940
|
+
onClick,
|
|
1941
|
+
className: cn(
|
|
1942
|
+
"w-full rounded-lg border p-3 text-left transition-colors",
|
|
1943
|
+
onClick && "cursor-pointer",
|
|
1944
|
+
selected ? "border-primary bg-primary/10" : "border-border bg-surface hover:bg-surface-hover",
|
|
1945
|
+
isOffline && !selected && "opacity-50",
|
|
1946
|
+
className
|
|
1947
|
+
),
|
|
1948
|
+
children: [
|
|
1949
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex items-center justify-between mb-2", children: [
|
|
1950
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "text-sm font-medium truncate", children: title }),
|
|
1951
|
+
status && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: cn("h-2 w-2 rounded-full shrink-0", STATUS_COLORS[status]) })
|
|
1952
|
+
] }),
|
|
1953
|
+
subtitle && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "text-[11px] text-foreground-muted", children: subtitle }),
|
|
1954
|
+
badges && badges.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "flex flex-wrap gap-1 mt-2", children: badges.map((badge, i) => {
|
|
1955
|
+
const cls = cn(
|
|
1956
|
+
"rounded px-1.5 py-0.5 text-[10px] flex items-center gap-0.5",
|
|
1957
|
+
selected ? "bg-primary/20" : "bg-surface-hover",
|
|
1958
|
+
badge.onClick && "hover:opacity-80 transition-opacity cursor-pointer"
|
|
1959
|
+
);
|
|
1960
|
+
return badge.onClick ? /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
1961
|
+
"button",
|
|
1962
|
+
{
|
|
1963
|
+
onClick: (e) => {
|
|
1964
|
+
e.stopPropagation();
|
|
1965
|
+
badge.onClick();
|
|
1966
|
+
},
|
|
1967
|
+
className: cls,
|
|
1968
|
+
children: [
|
|
1969
|
+
badge.icon,
|
|
1970
|
+
badge.label
|
|
1971
|
+
]
|
|
1972
|
+
},
|
|
1973
|
+
i
|
|
1974
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: cls, children: [
|
|
1975
|
+
badge.icon,
|
|
1976
|
+
badge.label
|
|
1977
|
+
] }, i);
|
|
1978
|
+
}) }),
|
|
1979
|
+
!isOffline && actions && actions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "flex items-center gap-0.5 mt-2 -mb-1", children: actions.map((action, i) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
1980
|
+
"button",
|
|
1981
|
+
{
|
|
1982
|
+
onClick: (e) => {
|
|
1983
|
+
e.stopPropagation();
|
|
1984
|
+
action.onClick();
|
|
1985
|
+
},
|
|
1986
|
+
className: "p-1 rounded hover:bg-surface-hover text-foreground-subtle hover:text-foreground transition-colors",
|
|
1987
|
+
title: action.label,
|
|
1988
|
+
"aria-label": action.label,
|
|
1989
|
+
children: action.icon
|
|
1990
|
+
},
|
|
1991
|
+
i
|
|
1992
|
+
)) }),
|
|
1993
|
+
isOffline && offlineAction && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "mt-2", onClick: (e) => e.stopPropagation(), children: offlineAction })
|
|
1994
|
+
]
|
|
1995
|
+
}
|
|
1996
|
+
);
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
// src/composites/device-grid.tsx
|
|
2000
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2001
|
+
function DeviceGrid({
|
|
2002
|
+
children,
|
|
2003
|
+
minCardWidth = 220,
|
|
2004
|
+
gap = 3,
|
|
2005
|
+
className
|
|
2006
|
+
}) {
|
|
2007
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2008
|
+
"div",
|
|
2009
|
+
{
|
|
2010
|
+
className: cn(
|
|
2011
|
+
"p-4 overflow-y-auto flex-1 content-start",
|
|
2012
|
+
className
|
|
2013
|
+
),
|
|
2014
|
+
style: {
|
|
2015
|
+
display: "grid",
|
|
2016
|
+
gridTemplateColumns: `repeat(auto-fill, minmax(${minCardWidth}px, 1fr))`,
|
|
2017
|
+
gap: `${gap * 4}px`
|
|
2018
|
+
},
|
|
2019
|
+
children
|
|
2020
|
+
}
|
|
2021
|
+
);
|
|
2022
|
+
}
|
|
2023
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
2024
|
+
0 && (module.exports = {
|
|
2025
|
+
AppShell,
|
|
2026
|
+
Badge,
|
|
2027
|
+
Button,
|
|
2028
|
+
Card,
|
|
2029
|
+
Checkbox,
|
|
2030
|
+
CodeBlock,
|
|
2031
|
+
ConfirmDialog,
|
|
2032
|
+
DataTable,
|
|
2033
|
+
DeviceCard,
|
|
2034
|
+
DeviceGrid,
|
|
2035
|
+
Dialog,
|
|
2036
|
+
DialogContent,
|
|
2037
|
+
DialogDescription,
|
|
2038
|
+
DialogFooter,
|
|
2039
|
+
DialogHeader,
|
|
2040
|
+
DialogTitle,
|
|
2041
|
+
DialogTrigger,
|
|
2042
|
+
Dropdown,
|
|
2043
|
+
DropdownContent,
|
|
2044
|
+
DropdownItem,
|
|
2045
|
+
DropdownTrigger,
|
|
2046
|
+
EmptyState,
|
|
2047
|
+
FilterBar,
|
|
2048
|
+
FloatingPanel,
|
|
2049
|
+
FormField,
|
|
2050
|
+
IconButton,
|
|
2051
|
+
Input,
|
|
2052
|
+
KeyValueList,
|
|
2053
|
+
Label,
|
|
2054
|
+
PageHeader,
|
|
2055
|
+
Popover,
|
|
2056
|
+
PopoverContent,
|
|
2057
|
+
PopoverTrigger,
|
|
2058
|
+
ProviderBadge,
|
|
2059
|
+
ScrollArea,
|
|
2060
|
+
Select,
|
|
2061
|
+
Separator,
|
|
2062
|
+
Sidebar,
|
|
2063
|
+
SidebarItem,
|
|
2064
|
+
Skeleton,
|
|
2065
|
+
StatCard,
|
|
2066
|
+
StatusBadge,
|
|
2067
|
+
Switch,
|
|
2068
|
+
Tabs,
|
|
2069
|
+
TabsContent,
|
|
2070
|
+
TabsList,
|
|
2071
|
+
TabsTrigger,
|
|
2072
|
+
ThemeProvider,
|
|
2073
|
+
Tooltip,
|
|
2074
|
+
TooltipContent,
|
|
2075
|
+
TooltipTrigger,
|
|
2076
|
+
cn,
|
|
2077
|
+
createTheme,
|
|
2078
|
+
darkColors,
|
|
2079
|
+
defaultTheme,
|
|
2080
|
+
lightColors,
|
|
2081
|
+
providerIcons,
|
|
2082
|
+
statusIcons,
|
|
2083
|
+
themeToCss,
|
|
2084
|
+
useThemeMode
|
|
2085
|
+
});
|
|
2086
|
+
//# sourceMappingURL=index.cjs.map
|