@veltrixsecops/app-sdk 2.0.0 → 2.2.0

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.
@@ -0,0 +1,662 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/ui/index.tsx
31
+ var ui_exports = {};
32
+ __export(ui_exports, {
33
+ Badge: () => Badge,
34
+ Button: () => Button,
35
+ Card: () => Card,
36
+ CardBody: () => CardBody,
37
+ CardFooter: () => CardFooter,
38
+ CardHeader: () => CardHeader,
39
+ Checkbox: () => Checkbox,
40
+ DataTable: () => DataTable,
41
+ EmptyState: () => EmptyState,
42
+ FormDialog: () => FormDialog,
43
+ FormField: () => FormField,
44
+ Input: () => Input,
45
+ Select: () => Select,
46
+ Skeleton: () => Skeleton,
47
+ SkeletonCard: () => SkeletonCard,
48
+ SkeletonText: () => SkeletonText,
49
+ Spinner: () => Spinner,
50
+ StatsCard: () => StatsCard,
51
+ Tabs: () => Tabs,
52
+ Textarea: () => Textarea,
53
+ Tooltip: () => Tooltip,
54
+ useConfirmDialog: () => useConfirmDialog,
55
+ useToast: () => useToast
56
+ });
57
+ module.exports = __toCommonJS(ui_exports);
58
+ var React = __toESM(require("react"), 1);
59
+
60
+ // src/client/index.ts
61
+ var HOST_RUNTIME_GLOBAL = "__VELTRIX_APP_RUNTIME__";
62
+ function getHostRuntime() {
63
+ const runtime = globalThis[HOST_RUNTIME_GLOBAL];
64
+ return runtime ?? null;
65
+ }
66
+
67
+ // src/ui/index.tsx
68
+ var import_jsx_runtime = require("react/jsx-runtime");
69
+ function getHostUi(name) {
70
+ return getHostRuntime()?.ui?.[name];
71
+ }
72
+ var fallbackNote = { fontFamily: "inherit" };
73
+ var Button = React.forwardRef((props, ref) => {
74
+ const HostButton = getHostUi("Button");
75
+ if (HostButton) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostButton, { ref, ...props });
76
+ const {
77
+ variant: _variant,
78
+ size: _size,
79
+ isLoading,
80
+ loadingText,
81
+ fullWidth,
82
+ leftIcon,
83
+ rightIcon,
84
+ children,
85
+ disabled,
86
+ type,
87
+ style,
88
+ ...rest
89
+ } = props;
90
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
91
+ "button",
92
+ {
93
+ ref,
94
+ type: type ?? "button",
95
+ disabled: disabled || isLoading,
96
+ "aria-busy": isLoading || void 0,
97
+ style: {
98
+ ...fallbackNote,
99
+ display: "inline-flex",
100
+ alignItems: "center",
101
+ gap: 6,
102
+ padding: "6px 14px",
103
+ borderRadius: 6,
104
+ border: "1px solid currentColor",
105
+ background: "transparent",
106
+ cursor: disabled || isLoading ? "not-allowed" : "pointer",
107
+ width: fullWidth ? "100%" : void 0,
108
+ opacity: disabled || isLoading ? 0.6 : 1,
109
+ justifyContent: "center",
110
+ ...style
111
+ },
112
+ ...rest,
113
+ children: [
114
+ leftIcon,
115
+ isLoading ? loadingText ?? children : children,
116
+ rightIcon
117
+ ]
118
+ }
119
+ );
120
+ });
121
+ Button.displayName = "Button";
122
+ var BADGE_FALLBACK_COLORS = {
123
+ default: "#6b7280",
124
+ primary: "#4f46e5",
125
+ secondary: "#6b7280",
126
+ success: "#16a34a",
127
+ danger: "#dc2626",
128
+ warning: "#d97706",
129
+ info: "#0284c7"
130
+ };
131
+ var Badge = ({ variant = "default", size = "md", rounded, dot, children, style, ...rest }) => {
132
+ const HostBadge = getHostUi("Badge");
133
+ if (HostBadge) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostBadge, { variant, size, rounded, dot, style, ...rest, children });
134
+ const color = BADGE_FALLBACK_COLORS[variant];
135
+ const fontSize = size === "sm" ? 11 : size === "lg" ? 14 : 12;
136
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
137
+ "span",
138
+ {
139
+ style: {
140
+ ...fallbackNote,
141
+ display: "inline-flex",
142
+ alignItems: "center",
143
+ gap: 4,
144
+ padding: size === "sm" ? "1px 6px" : size === "lg" ? "3px 10px" : "2px 8px",
145
+ borderRadius: rounded ? 999 : 4,
146
+ border: `1px solid ${color}`,
147
+ color,
148
+ fontSize,
149
+ fontWeight: 500,
150
+ ...style
151
+ },
152
+ ...rest,
153
+ children: [
154
+ dot && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": "true", style: { width: 6, height: 6, borderRadius: "50%", background: color } }),
155
+ children
156
+ ]
157
+ }
158
+ );
159
+ };
160
+ Badge.displayName = "Badge";
161
+ var Card = ({ children, style, ...rest }) => {
162
+ const HostCard = getHostUi("Card");
163
+ if (HostCard) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostCard, { style, ...rest, children });
164
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...fallbackNote, border: "1px solid #d1d5db", borderRadius: 8, overflow: "hidden", ...style }, ...rest, children });
165
+ };
166
+ Card.displayName = "Card";
167
+ var CardHeader = ({ actions, children, style, ...rest }) => {
168
+ const HostCardHeader = getHostUi("CardHeader");
169
+ if (HostCardHeader) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostCardHeader, { actions, style, ...rest, children });
170
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
171
+ "div",
172
+ {
173
+ style: {
174
+ ...fallbackNote,
175
+ padding: "12px 16px",
176
+ borderBottom: "1px solid #d1d5db",
177
+ display: "flex",
178
+ alignItems: "center",
179
+ justifyContent: "space-between",
180
+ gap: 12,
181
+ ...style
182
+ },
183
+ ...rest,
184
+ children: [
185
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flex: 1, minWidth: 0 }, children }),
186
+ actions && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: actions })
187
+ ]
188
+ }
189
+ );
190
+ };
191
+ CardHeader.displayName = "CardHeader";
192
+ var CardBody = ({ children, style, ...rest }) => {
193
+ const HostCardBody = getHostUi("CardBody");
194
+ if (HostCardBody) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostCardBody, { style, ...rest, children });
195
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...fallbackNote, padding: "12px 16px", ...style }, ...rest, children });
196
+ };
197
+ CardBody.displayName = "CardBody";
198
+ var CardFooter = ({ children, style, ...rest }) => {
199
+ const HostCardFooter = getHostUi("CardFooter");
200
+ if (HostCardFooter) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostCardFooter, { style, ...rest, children });
201
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...fallbackNote, padding: "12px 16px", borderTop: "1px solid #d1d5db", ...style }, ...rest, children });
202
+ };
203
+ CardFooter.displayName = "CardFooter";
204
+ var Input = React.forwardRef((props, ref) => {
205
+ const HostInput = getHostUi("Input");
206
+ if (HostInput) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostInput, { ref, ...props });
207
+ const { label, error, helperText, leftIcon, rightIcon, fullWidth = true, id, style, ...rest } = props;
208
+ const inputId = id ?? (label ? `vx-input-${label.toLowerCase().replace(/\s+/g, "-")}` : void 0);
209
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...fallbackNote, width: fullWidth ? "100%" : void 0 }, children: [
210
+ label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: inputId, style: { display: "block", fontSize: 13, fontWeight: 500, marginBottom: 4 }, children: label }),
211
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
212
+ leftIcon,
213
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
214
+ "input",
215
+ {
216
+ ref,
217
+ id: inputId,
218
+ "aria-invalid": !!error || void 0,
219
+ style: { flex: 1, padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af", ...style },
220
+ ...rest
221
+ }
222
+ ),
223
+ rightIcon
224
+ ] }),
225
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { marginTop: 4, fontSize: 12, color: "#dc2626" }, children: error }),
226
+ helperText && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 4, fontSize: 12, color: "#6b7280" }, children: helperText })
227
+ ] });
228
+ });
229
+ Input.displayName = "Input";
230
+ var Textarea = React.forwardRef((props, ref) => {
231
+ const HostTextarea = getHostUi("Textarea");
232
+ if (HostTextarea) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostTextarea, { ref, ...props });
233
+ const { label, error, helperText, fullWidth = true, id, rows = 4, style, ...rest } = props;
234
+ const textareaId = id ?? (label ? `vx-textarea-${label.toLowerCase().replace(/\s+/g, "-")}` : void 0);
235
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...fallbackNote, width: fullWidth ? "100%" : void 0 }, children: [
236
+ label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: textareaId, style: { display: "block", fontSize: 13, fontWeight: 500, marginBottom: 4 }, children: label }),
237
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
238
+ "textarea",
239
+ {
240
+ ref,
241
+ id: textareaId,
242
+ rows,
243
+ "aria-invalid": !!error || void 0,
244
+ style: { width: "100%", padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af", ...style },
245
+ ...rest
246
+ }
247
+ ),
248
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { marginTop: 4, fontSize: 12, color: "#dc2626" }, children: error }),
249
+ helperText && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 4, fontSize: 12, color: "#6b7280" }, children: helperText })
250
+ ] });
251
+ });
252
+ Textarea.displayName = "Textarea";
253
+ var Checkbox = React.forwardRef((props, ref) => {
254
+ const HostCheckbox = getHostUi("Checkbox");
255
+ if (HostCheckbox) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostCheckbox, { ref, ...props });
256
+ const { label, error, helperText, id, disabled, ...rest } = props;
257
+ const generatedId = React.useId();
258
+ const inputId = id ?? generatedId;
259
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: fallbackNote, children: [
260
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
261
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { ref, id: inputId, type: "checkbox", disabled, "aria-invalid": !!error || void 0, ...rest }),
262
+ label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: inputId, style: { fontSize: 13, cursor: disabled ? "not-allowed" : "pointer", opacity: disabled ? 0.6 : 1 }, children: label })
263
+ ] }),
264
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { marginTop: 4, fontSize: 12, color: "#dc2626" }, children: error }),
265
+ helperText && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 4, fontSize: 12, color: "#6b7280" }, children: helperText })
266
+ ] });
267
+ });
268
+ Checkbox.displayName = "Checkbox";
269
+ var Select = (props) => {
270
+ const HostSelect = getHostUi("Select");
271
+ if (HostSelect) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSelect, { ...props });
272
+ const { options, value, onChange, placeholder = "Select\u2026", label, error, helperText, disabled, fullWidth = true, id, name, className } = props;
273
+ const selectId = id ?? (label ? `vx-select-${label.toLowerCase().replace(/\s+/g, "-")}` : void 0);
274
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: { ...fallbackNote, width: fullWidth ? "100%" : void 0 }, children: [
275
+ label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: selectId, style: { display: "block", fontSize: 13, fontWeight: 500, marginBottom: 4 }, children: label }),
276
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
277
+ "select",
278
+ {
279
+ id: selectId,
280
+ name,
281
+ "aria-label": props["aria-label"],
282
+ "aria-invalid": !!error || void 0,
283
+ disabled,
284
+ value: value ?? "",
285
+ onChange: (event) => onChange?.(event.target.value),
286
+ style: { width: "100%", padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af" },
287
+ children: [
288
+ placeholder && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", disabled: true, children: placeholder }),
289
+ options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value))
290
+ ]
291
+ }
292
+ ),
293
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { marginTop: 4, fontSize: 12, color: "#dc2626" }, children: error }),
294
+ helperText && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 4, fontSize: 12, color: "#6b7280" }, children: helperText })
295
+ ] });
296
+ };
297
+ Select.displayName = "Select";
298
+ var FormField = (props) => {
299
+ const HostFormField = getHostUi("FormField");
300
+ if (HostFormField) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostFormField, { ...props });
301
+ const { label, htmlFor, error, hint, required, className, children } = props;
302
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: fallbackNote, children: [
303
+ label && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { htmlFor, style: { display: "block", fontSize: 13, fontWeight: 500, marginBottom: 4 }, children: [
304
+ label,
305
+ required && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": "true", style: { color: "#dc2626", marginLeft: 2 }, children: "*" })
306
+ ] }),
307
+ children,
308
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { marginTop: 4, fontSize: 12, color: "#dc2626" }, children: error }),
309
+ hint && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 4, fontSize: 12, color: "#6b7280" }, children: hint })
310
+ ] });
311
+ };
312
+ FormField.displayName = "FormField";
313
+ var Tabs = (props) => {
314
+ const HostTabs = getHostUi("Tabs");
315
+ if (HostTabs) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostTabs, { ...props });
316
+ const { tabs, defaultActiveIndex = 0, activeIndex, onTabChange, children, className } = props;
317
+ const [internalIndex, setInternalIndex] = React.useState(defaultActiveIndex);
318
+ const isControlled = activeIndex !== void 0;
319
+ const currentIndex = isControlled ? activeIndex : internalIndex;
320
+ const activeTab = tabs[currentIndex];
321
+ const selectTab = (index) => {
322
+ if (tabs[index]?.disabled) return;
323
+ if (!isControlled) setInternalIndex(index);
324
+ onTabChange?.(index);
325
+ };
326
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: fallbackNote, children: [
327
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { role: "tablist", style: { display: "flex", gap: 4, borderBottom: "1px solid #d1d5db" }, children: tabs.map((tab, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
328
+ "button",
329
+ {
330
+ type: "button",
331
+ role: "tab",
332
+ "aria-selected": index === currentIndex,
333
+ disabled: tab.disabled,
334
+ onClick: () => selectTab(index),
335
+ style: {
336
+ padding: "6px 12px",
337
+ border: "none",
338
+ borderBottom: index === currentIndex ? "2px solid currentColor" : "2px solid transparent",
339
+ background: "transparent",
340
+ cursor: tab.disabled ? "not-allowed" : "pointer",
341
+ fontWeight: index === currentIndex ? 600 : 400,
342
+ opacity: tab.disabled ? 0.5 : 1
343
+ },
344
+ children: tab.label
345
+ },
346
+ tab.key ?? index
347
+ )) }),
348
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { role: "tabpanel", style: { padding: 12 }, children: children || activeTab?.content })
349
+ ] });
350
+ };
351
+ Tabs.displayName = "Tabs";
352
+ var EmptyState = (props) => {
353
+ const HostEmptyState = getHostUi("EmptyState");
354
+ if (HostEmptyState) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostEmptyState, { ...props });
355
+ const { icon, title, description, action, className } = props;
356
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: { ...fallbackNote, textAlign: "center", padding: "32px 16px" }, children: [
357
+ icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 12 }, children: icon }),
358
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontWeight: 600, marginBottom: description ? 4 : 0 }, children: title }),
359
+ description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: 13, color: "#6b7280", marginBottom: action ? 12 : 0 }, children: description }),
360
+ action
361
+ ] });
362
+ };
363
+ EmptyState.displayName = "EmptyState";
364
+ var skeletonRadius = { text: 4, circular: "50%", rectangular: 8 };
365
+ var Skeleton = (props) => {
366
+ const HostSkeleton = getHostUi("Skeleton");
367
+ if (HostSkeleton) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSkeleton, { ...props });
368
+ const { variant = "text", width, height, style, ...rest } = props;
369
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
370
+ "div",
371
+ {
372
+ "aria-hidden": "true",
373
+ style: {
374
+ background: "#e5e7eb",
375
+ borderRadius: skeletonRadius[variant],
376
+ width: width ?? (variant === "text" ? "100%" : void 0),
377
+ height: height ?? (variant === "text" ? 14 : void 0),
378
+ ...style
379
+ },
380
+ ...rest
381
+ }
382
+ );
383
+ };
384
+ Skeleton.displayName = "Skeleton";
385
+ var SkeletonText = (props) => {
386
+ const HostSkeletonText = getHostUi("SkeletonText");
387
+ if (HostSkeletonText) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSkeletonText, { ...props });
388
+ const { lines = 3, width = "100%", lastLineWidth = "80%", className } = props;
389
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, style: { display: "flex", flexDirection: "column", gap: 8 }, children: Array.from({ length: lines }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { variant: "text", width: index === lines - 1 ? lastLineWidth : width }, index)) });
390
+ };
391
+ SkeletonText.displayName = "SkeletonText";
392
+ var SkeletonCard = (props) => {
393
+ const HostSkeletonCard = getHostUi("SkeletonCard");
394
+ if (HostSkeletonCard) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSkeletonCard, { ...props });
395
+ const { hasAvatar, hasActions, className } = props;
396
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: { border: "1px solid #e5e7eb", borderRadius: 8, padding: 16 }, children: [
397
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: 12 }, children: [
398
+ hasAvatar && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { variant: "circular", width: 40, height: 40 }),
399
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1 }, children: [
400
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { variant: "text", width: "60%" }),
401
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginTop: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SkeletonText, { lines: 2 }) })
402
+ ] })
403
+ ] }),
404
+ hasActions && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: 8, marginTop: 12 }, children: [
405
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { variant: "rectangular", width: 72, height: 30 }),
406
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { variant: "rectangular", width: 72, height: 30 })
407
+ ] })
408
+ ] });
409
+ };
410
+ SkeletonCard.displayName = "SkeletonCard";
411
+ var Tooltip = (props) => {
412
+ const HostTooltip = getHostUi("Tooltip");
413
+ if (HostTooltip) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostTooltip, { ...props });
414
+ const { content, disabled, className, children } = props;
415
+ const titleText = typeof content === "string" ? content : void 0;
416
+ if (disabled || !titleText) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
417
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className, title: titleText, style: { display: "inline-flex" }, children });
418
+ };
419
+ Tooltip.displayName = "Tooltip";
420
+ var spinnerDiameter = { sm: 16, md: 24, lg: 32 };
421
+ var Spinner = (props) => {
422
+ const HostSpinner = getHostUi("Spinner");
423
+ if (HostSpinner) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSpinner, { ...props });
424
+ const { size = "md", className, label } = props;
425
+ const diameter = spinnerDiameter[size];
426
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, role: "status", "aria-live": "polite", style: { display: "flex", flexDirection: "column", alignItems: "center" }, children: [
427
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
428
+ "div",
429
+ {
430
+ "aria-hidden": "true",
431
+ style: {
432
+ width: diameter,
433
+ height: diameter,
434
+ borderRadius: "50%",
435
+ border: "2px solid currentColor",
436
+ borderTopColor: "transparent",
437
+ animation: "vx-sdk-spin 0.8s linear infinite"
438
+ }
439
+ }
440
+ ),
441
+ label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 8, fontSize: 12, color: "#6b7280" }, children: label }),
442
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)" }, children: label || "Loading" }),
443
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: "@keyframes vx-sdk-spin { to { transform: rotate(360deg) } }" })
444
+ ] });
445
+ };
446
+ Spinner.displayName = "Spinner";
447
+ function FallbackDataTable(props) {
448
+ const { columns, data, rowKey, isLoading, emptyState, onRowClick, rowActions, className } = props;
449
+ const showEmpty = !isLoading && data.length === 0;
450
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, style: fallbackNote, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { style: { width: "100%", borderCollapse: "collapse" }, children: [
451
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
452
+ columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: { textAlign: column.align ?? "left", padding: "8px 10px", borderBottom: "1px solid #d1d5db" }, children: column.header }, column.key)),
453
+ rowActions && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: { padding: "8px 10px", borderBottom: "1px solid #d1d5db" } })
454
+ ] }) }),
455
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tbody", { children: [
456
+ isLoading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { colSpan: Math.max(columns.length + (rowActions ? 1 : 0), 1), style: { padding: "12px 10px" }, children: "Loading\u2026" }) }),
457
+ showEmpty && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { colSpan: Math.max(columns.length + (rowActions ? 1 : 0), 1), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyState, { title: emptyState?.title ?? "No data", description: emptyState?.description, icon: emptyState?.icon, action: emptyState?.action }) }) }),
458
+ !isLoading && !showEmpty && data.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
459
+ "tr",
460
+ {
461
+ onClick: onRowClick ? () => onRowClick(row) : void 0,
462
+ style: { cursor: onRowClick ? "pointer" : void 0, borderBottom: "1px solid #e5e7eb" },
463
+ children: [
464
+ columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: { textAlign: column.align ?? "left", padding: "8px 10px" }, children: column.render ? column.render(row) : String(row[column.key] ?? "") }, column.key)),
465
+ rowActions && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: { padding: "8px 10px" }, onClick: (event) => event.stopPropagation(), children: rowActions(row) })
466
+ ]
467
+ },
468
+ rowKey(row)
469
+ ))
470
+ ] })
471
+ ] }) });
472
+ }
473
+ function DataTable(props) {
474
+ const HostDataTable = getHostUi("DataTable");
475
+ if (HostDataTable) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostDataTable, { ...props });
476
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FallbackDataTable, { ...props });
477
+ }
478
+ var DELTA_FALLBACK_COLOR = {
479
+ up: "#16a34a",
480
+ down: "#dc2626",
481
+ neutral: "#6b7280"
482
+ };
483
+ var StatsCard = (props) => {
484
+ const HostStatsCard = getHostUi("StatsCard");
485
+ if (HostStatsCard) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostStatsCard, { ...props });
486
+ const { label, value, icon, delta, isLoading, onClick, className } = props;
487
+ const isClickable = Boolean(onClick);
488
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
489
+ "div",
490
+ {
491
+ role: isClickable ? "button" : void 0,
492
+ tabIndex: isClickable ? 0 : void 0,
493
+ onClick,
494
+ onKeyDown: isClickable ? (event) => {
495
+ if (event.key === "Enter" || event.key === " ") {
496
+ event.preventDefault();
497
+ onClick?.();
498
+ }
499
+ } : void 0,
500
+ className,
501
+ style: {
502
+ ...fallbackNote,
503
+ border: "1px solid #d1d5db",
504
+ borderRadius: 8,
505
+ padding: 16,
506
+ display: "flex",
507
+ alignItems: "flex-start",
508
+ justifyContent: "space-between",
509
+ gap: 12,
510
+ cursor: isClickable ? "pointer" : void 0
511
+ },
512
+ children: [
513
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { minWidth: 0, flex: 1 }, children: [
514
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: 13, color: "#6b7280", margin: 0 }, children: label }),
515
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: 28, fontWeight: 700, margin: "4px 0 0" }, children: isLoading ? "\u2026" : value }),
516
+ delta && !isLoading && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: { fontSize: 13, color: DELTA_FALLBACK_COLOR[delta.direction], margin: "4px 0 0" }, children: [
517
+ delta.value,
518
+ " ",
519
+ delta.label
520
+ ] })
521
+ ] }),
522
+ icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "aria-hidden": "true", children: icon })
523
+ ]
524
+ }
525
+ );
526
+ };
527
+ StatsCard.displayName = "StatsCard";
528
+ var FORM_DIALOG_MAX_WIDTH = { sm: 420, md: 520, lg: 680 };
529
+ var FormDialog = (props) => {
530
+ const HostFormDialog = getHostUi("FormDialog");
531
+ if (HostFormDialog) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostFormDialog, { ...props });
532
+ const {
533
+ isOpen,
534
+ onClose,
535
+ title,
536
+ description,
537
+ children,
538
+ onSubmit,
539
+ submitText = "Save",
540
+ cancelText = "Cancel",
541
+ isSubmitting = false,
542
+ error = null,
543
+ size = "md",
544
+ disableBackdropClose = false,
545
+ submitDisabled = false
546
+ } = props;
547
+ React.useEffect(() => {
548
+ if (!isOpen) return;
549
+ const handleKeyDown = (event) => {
550
+ if (event.key === "Escape" && !isSubmitting) onClose();
551
+ };
552
+ document.addEventListener("keydown", handleKeyDown);
553
+ return () => document.removeEventListener("keydown", handleKeyDown);
554
+ }, [isOpen, isSubmitting, onClose]);
555
+ if (!isOpen) return null;
556
+ const requestClose = () => {
557
+ if (!isSubmitting) onClose();
558
+ };
559
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
560
+ "div",
561
+ {
562
+ style: { position: "fixed", inset: 0, background: "rgba(0,0,0,0.5)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 50 },
563
+ onClick: disableBackdropClose ? void 0 : requestClose,
564
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
565
+ "div",
566
+ {
567
+ role: "dialog",
568
+ "aria-modal": "true",
569
+ "aria-label": title,
570
+ style: { background: "white", color: "black", borderRadius: 8, width: "100%", maxWidth: FORM_DIALOG_MAX_WIDTH[size], maxHeight: "80vh", overflow: "auto", ...fallbackNote },
571
+ onClick: (event) => event.stopPropagation(),
572
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
573
+ "form",
574
+ {
575
+ onSubmit: (event) => {
576
+ event.preventDefault();
577
+ void onSubmit();
578
+ },
579
+ children: [
580
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, borderBottom: "1px solid #d1d5db" }, children: [
581
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { style: { margin: 0, fontSize: 16, fontWeight: 600 }, children: title }),
582
+ description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { margin: "4px 0 0", fontSize: 13, color: "#6b7280" }, children: description })
583
+ ] }),
584
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, display: "flex", flexDirection: "column", gap: 12 }, children: [
585
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { margin: 0, fontSize: 13, color: "#dc2626" }, children: error }),
586
+ children
587
+ ] }),
588
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, borderTop: "1px solid #d1d5db", display: "flex", justifyContent: "flex-end", gap: 8 }, children: [
589
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, { type: "button", variant: "secondary", onClick: requestClose, disabled: isSubmitting, children: cancelText }),
590
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, { type: "submit", variant: "primary", isLoading: isSubmitting, disabled: submitDisabled, children: submitText })
591
+ ] })
592
+ ]
593
+ }
594
+ )
595
+ }
596
+ )
597
+ }
598
+ );
599
+ };
600
+ FormDialog.displayName = "FormDialog";
601
+ function fallbackToast(message, options) {
602
+ console.warn(`[@veltrixsecops/app-sdk/ui] Toast (${options?.variant ?? "info"}): ${message}`);
603
+ return "fallback-toast";
604
+ }
605
+ var fallbackToastContext = {
606
+ toasts: [],
607
+ toast: fallbackToast,
608
+ success: (message) => fallbackToast(message, { variant: "success" }),
609
+ error: (message) => fallbackToast(message, { variant: "error" }),
610
+ warning: (message) => fallbackToast(message, { variant: "warning" }),
611
+ info: (message) => fallbackToast(message, { variant: "info" }),
612
+ promise: (promise) => promise,
613
+ dismiss: () => {
614
+ },
615
+ dismissAll: () => {
616
+ }
617
+ };
618
+ function useToast() {
619
+ const hostUseToast = getHostUi("useToast");
620
+ if (hostUseToast) return hostUseToast();
621
+ return fallbackToastContext;
622
+ }
623
+ var fallbackConfirmationContext = {
624
+ confirm: (options) => {
625
+ console.warn(
626
+ `[@veltrixsecops/app-sdk/ui] ConfirmationDialog is only available inside the Veltrix platform \u2014 "${options.title}" auto-resolved to "not confirmed" (fail closed).`
627
+ );
628
+ return Promise.resolve(false);
629
+ }
630
+ };
631
+ function useConfirmDialog() {
632
+ const hostUseConfirmDialog = getHostUi("useConfirmDialog");
633
+ if (hostUseConfirmDialog) return hostUseConfirmDialog();
634
+ return fallbackConfirmationContext;
635
+ }
636
+ // Annotate the CommonJS export names for ESM import in node:
637
+ 0 && (module.exports = {
638
+ Badge,
639
+ Button,
640
+ Card,
641
+ CardBody,
642
+ CardFooter,
643
+ CardHeader,
644
+ Checkbox,
645
+ DataTable,
646
+ EmptyState,
647
+ FormDialog,
648
+ FormField,
649
+ Input,
650
+ Select,
651
+ Skeleton,
652
+ SkeletonCard,
653
+ SkeletonText,
654
+ Spinner,
655
+ StatsCard,
656
+ Tabs,
657
+ Textarea,
658
+ Tooltip,
659
+ useConfirmDialog,
660
+ useToast
661
+ });
662
+ //# sourceMappingURL=index.cjs.map