@lablup/ui-common 0.1.0-alpha.2 → 0.1.0-alpha.20
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/README.md +1 -1
- package/dist/assets/components/BaseCard/BaseCard.css +1 -1
- package/dist/assets/components/Button/Button.css +1 -1
- package/dist/assets/components/DataTable/DataTable.css +1 -1
- package/dist/assets/components/Drawer/Drawer.css +1 -1
- package/dist/assets/components/EmptyState/EmptyState.css +1 -1
- package/dist/assets/components/ErrorState/ErrorState.css +1 -1
- package/dist/assets/components/PageHeader/PageHeader.css +1 -1
- package/dist/assets/components/Select/Select.css +1 -0
- package/dist/assets/components/StatCard/StatCard.css +1 -1
- package/dist/assets/components/Tabs/Tabs.css +1 -1
- package/dist/assets/components/Tooltip/Tooltip.css +1 -1
- package/dist/components/DataTable/DataTable.d.ts +30 -6
- package/dist/components/DataTable/DataTable2.js +85 -92
- package/dist/components/DataTable/DataTable2.js.map +1 -1
- package/dist/components/Drawer/Drawer.d.ts +12 -1
- package/dist/components/Drawer/Drawer2.js +69 -51
- package/dist/components/Drawer/Drawer2.js.map +1 -1
- package/dist/components/EmptyState/EmptyState.d.ts +10 -3
- package/dist/components/EmptyState/EmptyState2.js +31 -48
- package/dist/components/EmptyState/EmptyState2.js.map +1 -1
- package/dist/components/EmptyState/index.d.ts +6 -1
- package/dist/components/ErrorState/ErrorState.d.ts +17 -4
- package/dist/components/ErrorState/ErrorState2.js +20 -23
- package/dist/components/ErrorState/ErrorState2.js.map +1 -1
- package/dist/components/ErrorState/index.d.ts +1 -1
- package/dist/components/PageHeader/PageHeader.d.ts +14 -1
- package/dist/components/PageHeader/PageHeader2.js +40 -24
- package/dist/components/PageHeader/PageHeader2.js.map +1 -1
- package/dist/components/Select/Select.d.ts +38 -0
- package/dist/components/Select/Select2.js +313 -0
- package/dist/components/Select/Select2.js.map +1 -0
- package/dist/components/Select/index.d.ts +2 -0
- package/dist/components/Select/index.js +2 -0
- package/dist/components/Skeleton/Skeleton.d.ts +11 -1
- package/dist/components/Skeleton/Skeleton2.js +8 -6
- package/dist/components/Skeleton/Skeleton2.js.map +1 -1
- package/dist/components/Skeleton/SkeletonCard.d.ts +3 -1
- package/dist/components/Skeleton/SkeletonCard2.js +13 -3
- package/dist/components/Skeleton/SkeletonCard2.js.map +1 -1
- package/dist/components/Skeleton/SkeletonChart2.js +5 -0
- package/dist/components/Skeleton/SkeletonChart2.js.map +1 -1
- package/dist/components/Skeleton/SkeletonRow.d.ts +3 -1
- package/dist/components/Skeleton/SkeletonRow2.js +9 -3
- package/dist/components/Skeleton/SkeletonRow2.js.map +1 -1
- package/dist/components/Skeleton/SkeletonText.d.ts +3 -1
- package/dist/components/Skeleton/SkeletonText2.js +7 -5
- package/dist/components/Skeleton/SkeletonText2.js.map +1 -1
- package/dist/components/StatCard/StatCard.d.ts +10 -1
- package/dist/components/StatCard/StatCard2.js +30 -30
- package/dist/components/StatCard/StatCard2.js.map +1 -1
- package/dist/components/Tabs/Tabs.d.ts +10 -23
- package/dist/components/Tabs/Tabs2.js +127 -146
- package/dist/components/Tabs/Tabs2.js.map +1 -1
- package/dist/components/Tabs/index.d.ts +2 -2
- package/dist/components/Tabs/index.js +2 -3
- package/dist/components/Tooltip/Tooltip.d.ts +15 -1
- package/dist/components/Tooltip/Tooltip2.js +77 -47
- package/dist/components/Tooltip/Tooltip2.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -6
- package/dist/styles/base.css +101 -10
- package/dist/styles/themes/orange-dark.css +3 -0
- package/dist/styles/themes/orange-light.css +6 -1
- package/package.json +1 -1
- package/dist/components/EmptyState/illustrations.d.ts +0 -51
- package/dist/components/EmptyState/illustrations.js +0 -571
- package/dist/components/EmptyState/illustrations.js.map +0 -1
- package/dist/components/Tabs/tabTags.d.ts +0 -9
- package/dist/components/Tabs/tabTags.js +0 -11
- package/dist/components/Tabs/tabTags.js.map +0 -1
|
@@ -1,76 +1,94 @@
|
|
|
1
1
|
/* empty css */
|
|
2
|
-
import { useCallback as e, useEffect as t,
|
|
3
|
-
import { jsx as
|
|
2
|
+
import { useCallback as e, useEffect as t, useId as n, useRef as r, useState as i } from "react";
|
|
3
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
4
4
|
//#region src/components/Drawer/Drawer.tsx
|
|
5
|
-
var
|
|
5
|
+
var s = {
|
|
6
6
|
narrow: "400px",
|
|
7
7
|
medium: "520px",
|
|
8
8
|
wide: "900px"
|
|
9
9
|
};
|
|
10
|
-
function
|
|
11
|
-
let
|
|
10
|
+
function c({ isOpen: c, onClose: l, title: u, subtitle: d, width: f = "medium", children: p, footer: m, className: h = "", ariaLabelledBy: g, ariaDescribedBy: _, closeLabel: v = "Close", preventDismiss: y = !1, onDismissAttempt: b }) {
|
|
11
|
+
let x = r(null), S = n(), [C, w] = i(!1), T = r(y);
|
|
12
|
+
T.current = y;
|
|
13
|
+
let E = r(b);
|
|
14
|
+
E.current = b;
|
|
15
|
+
let D = r(l);
|
|
16
|
+
D.current = l;
|
|
17
|
+
let O = e(() => {
|
|
18
|
+
D.current();
|
|
19
|
+
}, []), k = e(() => T.current ? (w(!0), E.current?.(), !0) : !1, []), A = e((e) => {
|
|
20
|
+
e.animationName === "drawer-shake" && w(!1);
|
|
21
|
+
}, []), j = r(null), M = r(null), [N, P] = i(!1), F = r(!1);
|
|
12
22
|
t(() => {
|
|
13
|
-
|
|
23
|
+
c && !F.current ? (F.current = !0, requestAnimationFrame(() => {
|
|
14
24
|
requestAnimationFrame(() => {
|
|
15
|
-
|
|
25
|
+
P(!0);
|
|
16
26
|
});
|
|
17
|
-
})) :
|
|
18
|
-
}, [
|
|
19
|
-
let
|
|
27
|
+
})) : P(!!c);
|
|
28
|
+
}, [c]);
|
|
29
|
+
let I = c && N, L = f in s ? s[f] : f, R = g ?? `${S}-title`, z = _ ?? (d ? `${S}-subtitle` : void 0);
|
|
20
30
|
t(() => {
|
|
21
|
-
if (!
|
|
22
|
-
|
|
23
|
-
let e =
|
|
31
|
+
if (!I) return;
|
|
32
|
+
j.current = document.activeElement;
|
|
33
|
+
let e = x.current;
|
|
24
34
|
if (!e) return;
|
|
25
|
-
|
|
26
|
-
b.current?.focus();
|
|
27
|
-
});
|
|
35
|
+
M.current?.focus();
|
|
28
36
|
let t = e.querySelectorAll("button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])"), n = t[0], r = t[t.length - 1], i = (e) => {
|
|
29
37
|
if (e.key === "Escape") {
|
|
30
|
-
|
|
38
|
+
if (k()) return;
|
|
39
|
+
O();
|
|
31
40
|
return;
|
|
32
41
|
}
|
|
33
42
|
e.key === "Tab" && (e.shiftKey ? document.activeElement === n && (e.preventDefault(), r?.focus()) : document.activeElement === r && (e.preventDefault(), n?.focus()));
|
|
34
43
|
};
|
|
35
44
|
return e.addEventListener("keydown", i), () => {
|
|
36
|
-
e.removeEventListener("keydown", i),
|
|
45
|
+
e.removeEventListener("keydown", i), j.current && j.current.focus();
|
|
37
46
|
};
|
|
38
|
-
}, [
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
}, [
|
|
48
|
+
I,
|
|
49
|
+
O,
|
|
50
|
+
k
|
|
51
|
+
]);
|
|
52
|
+
let B = e((e) => {
|
|
53
|
+
if (e.target === e.currentTarget) {
|
|
54
|
+
if (k()) return;
|
|
55
|
+
O();
|
|
56
|
+
}
|
|
57
|
+
}, [O, k]);
|
|
58
|
+
return /* @__PURE__ */ a("div", {
|
|
59
|
+
className: `drawer__backdrop ${I ? "drawer__backdrop--open" : ""}`,
|
|
60
|
+
onClick: B,
|
|
45
61
|
role: "presentation",
|
|
46
|
-
"aria-hidden": !
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
62
|
+
"aria-hidden": !c,
|
|
63
|
+
inert: !c,
|
|
64
|
+
children: /* @__PURE__ */ o("aside", {
|
|
65
|
+
ref: x,
|
|
66
|
+
className: `drawer ${I ? "drawer--open" : ""} ${C ? "drawer--shaking" : ""} ${h}`,
|
|
50
67
|
style: {
|
|
51
|
-
width:
|
|
68
|
+
width: L,
|
|
52
69
|
maxWidth: "100vw"
|
|
53
70
|
},
|
|
54
71
|
role: "dialog",
|
|
55
72
|
"aria-modal": "true",
|
|
56
|
-
"aria-labelledby":
|
|
57
|
-
"aria-describedby":
|
|
73
|
+
"aria-labelledby": R,
|
|
74
|
+
"aria-describedby": z,
|
|
75
|
+
onAnimationEnd: A,
|
|
58
76
|
children: [
|
|
59
|
-
/* @__PURE__ */
|
|
77
|
+
/* @__PURE__ */ o("header", {
|
|
60
78
|
className: "drawer__header",
|
|
61
|
-
children: [/* @__PURE__ */
|
|
79
|
+
children: [/* @__PURE__ */ o("div", {
|
|
62
80
|
className: "drawer__title-row",
|
|
63
|
-
children: [/* @__PURE__ */
|
|
64
|
-
id:
|
|
81
|
+
children: [/* @__PURE__ */ a("h2", {
|
|
82
|
+
id: R,
|
|
65
83
|
className: "drawer__title",
|
|
66
|
-
children:
|
|
67
|
-
}), /* @__PURE__ */
|
|
68
|
-
ref:
|
|
84
|
+
children: u
|
|
85
|
+
}), /* @__PURE__ */ a("button", {
|
|
86
|
+
ref: M,
|
|
69
87
|
type: "button",
|
|
70
88
|
className: "drawer__close-btn",
|
|
71
|
-
onClick:
|
|
72
|
-
"aria-label":
|
|
73
|
-
children: /* @__PURE__ */
|
|
89
|
+
onClick: O,
|
|
90
|
+
"aria-label": v,
|
|
91
|
+
children: /* @__PURE__ */ o("svg", {
|
|
74
92
|
width: "20",
|
|
75
93
|
height: "20",
|
|
76
94
|
viewBox: "0 0 24 24",
|
|
@@ -79,29 +97,29 @@ function s({ isOpen: s, onClose: c, title: l, subtitle: u, width: d = "medium",
|
|
|
79
97
|
strokeWidth: "2",
|
|
80
98
|
strokeLinecap: "round",
|
|
81
99
|
strokeLinejoin: "round",
|
|
82
|
-
children: [/* @__PURE__ */
|
|
100
|
+
children: [/* @__PURE__ */ a("path", { d: "M18 6L6 18" }), /* @__PURE__ */ a("path", { d: "M6 6l12 12" })]
|
|
83
101
|
})
|
|
84
102
|
})]
|
|
85
|
-
}),
|
|
86
|
-
id:
|
|
103
|
+
}), d && /* @__PURE__ */ a("p", {
|
|
104
|
+
id: z,
|
|
87
105
|
className: "drawer__subtitle",
|
|
88
|
-
children:
|
|
106
|
+
children: d
|
|
89
107
|
})]
|
|
90
108
|
}),
|
|
91
|
-
/* @__PURE__ */
|
|
109
|
+
/* @__PURE__ */ a("div", {
|
|
92
110
|
className: "drawer__content",
|
|
93
|
-
children:
|
|
111
|
+
children: p
|
|
94
112
|
}),
|
|
95
|
-
|
|
113
|
+
m && /* @__PURE__ */ a("footer", {
|
|
96
114
|
className: "drawer__footer",
|
|
97
|
-
children:
|
|
115
|
+
children: m
|
|
98
116
|
})
|
|
99
117
|
]
|
|
100
118
|
})
|
|
101
119
|
});
|
|
102
120
|
}
|
|
103
121
|
//#endregion
|
|
104
|
-
export {
|
|
122
|
+
export { c as Drawer };
|
|
105
123
|
|
|
106
124
|
//# sourceMappingURL=Drawer2.js.map
|
|
107
125
|
import "../../assets/components/Drawer/Drawer.css";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Drawer2.js","names":[],"sources":["../../../src/components/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * Drawer Component\n *\n * A reusable right-side drawer panel with backdrop, animations, and accessibility features.\n * Consolidates common patterns from all drawer implementations.\n */\n\nimport { useEffect, useRef, useCallback, useState } from \"react\";\nimport \"./Drawer.css\";\n\nexport interface DrawerProps {\n /** Whether the drawer is open */\n isOpen: boolean;\n /** Callback when drawer should close */\n onClose: () => void;\n /** Drawer title */\n title: React.ReactNode;\n /** Optional subtitle */\n subtitle?: React.ReactNode;\n /** Drawer width preset or custom CSS width value */\n width?: \"narrow\" | \"medium\" | \"wide\" | (string & Record<never, never>);\n /** Drawer content */\n children: React.ReactNode;\n /** Optional footer content */\n footer?: React.ReactNode;\n /** Additional CSS class for drawer panel */\n className?: string;\n /** ARIA labelledby ID (defaults to internal title ID) */\n ariaLabelledBy?: string;\n /** ARIA describedby ID (defaults to internal subtitle ID if subtitle exists) */\n ariaDescribedBy?: string;\n /** Accessible label for the close button. Default: \"Close\" */\n closeLabel?: string;\n}\n\nconst WIDTH_PRESETS = {\n narrow: \"400px\",\n medium: \"520px\",\n wide: \"900px\",\n};\n\nexport function Drawer({\n isOpen,\n onClose,\n title,\n subtitle,\n width = \"medium\",\n children,\n footer,\n className = \"\",\n ariaLabelledBy,\n ariaDescribedBy,\n closeLabel = \"Close\",\n}: DrawerProps) {\n const drawerRef = useRef<HTMLDivElement>(null);\n const previousActiveElementRef = useRef<HTMLElement | null>(null);\n const closeButtonRef = useRef<HTMLButtonElement>(null);\n\n // Track if this is the first render to enable animation on initial open\n // Without this, when the component mounts with isOpen=true, there's no\n // transition because CSS transitions only apply to state changes, not initial renders.\n const [shouldAnimate, setShouldAnimate] = useState(false);\n const hasInitialized = useRef(false);\n\n useEffect(() => {\n if (isOpen && !hasInitialized.current) {\n // First time opening - delay adding open class to trigger animation\n hasInitialized.current = true;\n // Use double rAF to ensure the closed state is painted first\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n setShouldAnimate(true);\n });\n });\n } else if (isOpen) {\n setShouldAnimate(true);\n } else {\n setShouldAnimate(false);\n }\n }, [isOpen]);\n\n // Compute the actual open state for CSS classes\n const isVisuallyOpen = isOpen && shouldAnimate;\n\n // Compute width value\n const widthValue =\n width in WIDTH_PRESETS ? WIDTH_PRESETS[width as keyof typeof WIDTH_PRESETS] : width;\n\n // Internal IDs for ARIA\n const titleId = ariaLabelledBy || \"drawer-title\";\n const subtitleId = ariaDescribedBy || (subtitle ? \"drawer-subtitle\" : undefined);\n\n // Focus management and trap\n useEffect(() => {\n if (!isOpen) return;\n\n // Store previously focused element\n previousActiveElementRef.current = document.activeElement as HTMLElement;\n\n const drawer = drawerRef.current;\n if (!drawer) return;\n\n // Focus close button when drawer opens\n requestAnimationFrame(() => {\n closeButtonRef.current?.focus();\n });\n\n // Setup focus trap\n const focusableElements = drawer.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n );\n\n const firstFocusable = focusableElements[0];\n const lastFocusable = focusableElements[focusableElements.length - 1];\n\n const handleKeyDown = (e: KeyboardEvent) => {\n // Escape to close\n if (e.key === \"Escape\") {\n onClose();\n return;\n }\n\n // Tab key focus trap\n if (e.key === \"Tab\") {\n if (e.shiftKey) {\n // Shift + Tab\n if (document.activeElement === firstFocusable) {\n e.preventDefault();\n lastFocusable?.focus();\n }\n } else {\n // Tab\n if (document.activeElement === lastFocusable) {\n e.preventDefault();\n firstFocusable?.focus();\n }\n }\n }\n };\n\n drawer.addEventListener(\"keydown\", handleKeyDown);\n\n return () => {\n drawer.removeEventListener(\"keydown\", handleKeyDown);\n // Restore focus when drawer closes\n if (previousActiveElementRef.current) {\n previousActiveElementRef.current.focus();\n }\n };\n }, [isOpen, onClose]);\n\n // Handle backdrop click\n const handleBackdropClick = useCallback(\n (e: React.MouseEvent) => {\n if (e.target === e.currentTarget) {\n onClose();\n }\n },\n [onClose],\n );\n\n return (\n <div\n className={`drawer__backdrop ${isVisuallyOpen ? \"drawer__backdrop--open\" : \"\"}`}\n onClick={handleBackdropClick}\n role=\"presentation\"\n aria-hidden={!isOpen}\n >\n <aside\n ref={drawerRef}\n className={`drawer ${isVisuallyOpen ? \"drawer--open\" : \"\"} ${className}`}\n style={{ width: widthValue, maxWidth: \"100vw\" }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n aria-describedby={subtitleId}\n >\n {/* Header */}\n <header className=\"drawer__header\">\n <div className=\"drawer__title-row\">\n <h2 id={titleId} className=\"drawer__title\">\n {title}\n </h2>\n <button\n ref={closeButtonRef}\n type=\"button\"\n className=\"drawer__close-btn\"\n onClick={onClose}\n aria-label={closeLabel}\n >\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M18 6L6 18\" />\n <path d=\"M6 6l12 12\" />\n </svg>\n </button>\n </div>\n {subtitle && (\n <p id={subtitleId} className=\"drawer__subtitle\">\n {subtitle}\n </p>\n )}\n </header>\n\n {/* Content */}\n <div className=\"drawer__content\">{children}</div>\n\n {/* Footer */}\n {footer && <footer className=\"drawer__footer\">{footer}</footer>}\n </aside>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Drawer2.js","names":[],"sources":["../../../src/components/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * Drawer Component\n *\n * A reusable right-side drawer panel with backdrop, animations, and accessibility features.\n * Consolidates common patterns from all drawer implementations.\n */\n\nimport { useEffect, useRef, useCallback, useId, useState } from \"react\";\nimport \"./Drawer.css\";\n\nexport interface DrawerProps {\n /** Whether the drawer is open */\n isOpen: boolean;\n /** Callback when drawer should close */\n onClose: () => void;\n /** Drawer title */\n title: React.ReactNode;\n /** Optional subtitle */\n subtitle?: React.ReactNode;\n /** Drawer width preset or custom CSS width value */\n width?: \"narrow\" | \"medium\" | \"wide\" | (string & Record<never, never>);\n /** Drawer content */\n children: React.ReactNode;\n /** Optional footer content */\n footer?: React.ReactNode;\n /** Additional CSS class for drawer panel */\n className?: string;\n /** ARIA labelledby ID (defaults to internal title ID) */\n ariaLabelledBy?: string;\n /** ARIA describedby ID (defaults to internal subtitle ID if subtitle exists) */\n ariaDescribedBy?: string;\n /** Accessible label for the close button. Default: \"Close\" */\n closeLabel?: string;\n /**\n * Refuse dismissal by Escape or a backdrop click, shaking the panel instead.\n * The close button and any footer control still call `onClose` directly, so\n * route those through the same guard when they also need confirming.\n */\n preventDismiss?: boolean;\n /**\n * Called when `preventDismiss` blocked a dismissal, so the consumer can offer\n * an explicit way out (a discard confirmation, for instance).\n */\n onDismissAttempt?: () => void;\n}\n\nconst WIDTH_PRESETS = {\n narrow: \"400px\",\n medium: \"520px\",\n wide: \"900px\",\n};\n\nexport function Drawer({\n isOpen,\n onClose,\n title,\n subtitle,\n width = \"medium\",\n children,\n footer,\n className = \"\",\n ariaLabelledBy,\n ariaDescribedBy,\n closeLabel = \"Close\",\n preventDismiss = false,\n onDismissAttempt,\n}: DrawerProps) {\n const drawerRef = useRef<HTMLDivElement>(null);\n const generatedId = useId();\n\n const [isShaking, setIsShaking] = useState(false);\n\n // Read the guard through refs so `blockDismiss` keeps a stable identity. The\n // focus-management effect depends on it, and if the identity changed the\n // moment the consumer marks the drawer dirty, that effect would re-run on the\n // first keystroke and steal focus from the field being typed into.\n const preventDismissRef = useRef(preventDismiss);\n preventDismissRef.current = preventDismiss;\n const onDismissAttemptRef = useRef(onDismissAttempt);\n onDismissAttemptRef.current = onDismissAttempt;\n const onCloseRef = useRef(onClose);\n onCloseRef.current = onClose;\n\n const requestClose = useCallback(() => {\n onCloseRef.current();\n }, []);\n\n /** Blocks a dismissal when the guard is on. Returns whether it blocked. */\n const blockDismiss = useCallback(() => {\n if (!preventDismissRef.current) return false;\n setIsShaking(true);\n onDismissAttemptRef.current?.();\n return true;\n }, []);\n\n // Guarded on the animation name so an unrelated animation on the panel does\n // not clear the flag early.\n const handleAnimationEnd = useCallback((e: React.AnimationEvent) => {\n if (e.animationName === \"drawer-shake\") setIsShaking(false);\n }, []);\n const previousActiveElementRef = useRef<HTMLElement | null>(null);\n const closeButtonRef = useRef<HTMLButtonElement>(null);\n\n // Track if this is the first render to enable animation on initial open\n // Without this, when the component mounts with isOpen=true, there's no\n // transition because CSS transitions only apply to state changes, not initial renders.\n const [shouldAnimate, setShouldAnimate] = useState(false);\n const hasInitialized = useRef(false);\n\n useEffect(() => {\n if (isOpen && !hasInitialized.current) {\n // First time opening - delay adding open class to trigger animation\n hasInitialized.current = true;\n // Use double rAF to ensure the closed state is painted first\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n setShouldAnimate(true);\n });\n });\n } else if (isOpen) {\n setShouldAnimate(true);\n } else {\n setShouldAnimate(false);\n }\n }, [isOpen]);\n\n // Compute the actual open state for CSS classes\n const isVisuallyOpen = isOpen && shouldAnimate;\n\n // Compute width value\n const widthValue =\n width in WIDTH_PRESETS ? WIDTH_PRESETS[width as keyof typeof WIDTH_PRESETS] : width;\n\n // Internal IDs for ARIA\n const titleId = ariaLabelledBy ?? `${generatedId}-title`;\n const subtitleId =\n ariaDescribedBy ?? (subtitle ? `${generatedId}-subtitle` : undefined);\n\n // Focus management and trap\n useEffect(() => {\n // Wait until the open classes have committed so focus never enters a\n // backdrop that is still hidden and non-interactive.\n if (!isVisuallyOpen) return;\n\n // Store previously focused element\n previousActiveElementRef.current = document.activeElement as HTMLElement;\n\n const drawer = drawerRef.current;\n if (!drawer) return;\n\n // Focus close button when drawer opens\n closeButtonRef.current?.focus();\n\n // Setup focus trap\n const focusableElements = drawer.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n );\n\n const firstFocusable = focusableElements[0];\n const lastFocusable = focusableElements[focusableElements.length - 1];\n\n const handleKeyDown = (e: KeyboardEvent) => {\n // Escape to close, unless the guard blocks it\n if (e.key === \"Escape\") {\n if (blockDismiss()) return;\n requestClose();\n return;\n }\n\n // Tab key focus trap\n if (e.key === \"Tab\") {\n if (e.shiftKey) {\n // Shift + Tab\n if (document.activeElement === firstFocusable) {\n e.preventDefault();\n lastFocusable?.focus();\n }\n } else {\n // Tab\n if (document.activeElement === lastFocusable) {\n e.preventDefault();\n firstFocusable?.focus();\n }\n }\n }\n };\n\n drawer.addEventListener(\"keydown\", handleKeyDown);\n\n return () => {\n drawer.removeEventListener(\"keydown\", handleKeyDown);\n // Restore focus when drawer closes\n if (previousActiveElementRef.current) {\n previousActiveElementRef.current.focus();\n }\n };\n }, [isVisuallyOpen, requestClose, blockDismiss]);\n\n // Handle backdrop click\n const handleBackdropClick = useCallback(\n (e: React.MouseEvent) => {\n if (e.target === e.currentTarget) {\n if (blockDismiss()) return;\n requestClose();\n }\n },\n [requestClose, blockDismiss],\n );\n\n return (\n <div\n className={`drawer__backdrop ${isVisuallyOpen ? \"drawer__backdrop--open\" : \"\"}`}\n onClick={handleBackdropClick}\n role=\"presentation\"\n aria-hidden={!isOpen}\n // `aria-hidden` takes the closed drawer out of the accessibility tree but\n // leaves its focusable children in the tab sequence, so a keyboard user\n // tabs into an invisible form. `inert` removes both.\n inert={!isOpen}\n >\n <aside\n ref={drawerRef}\n className={`drawer ${isVisuallyOpen ? \"drawer--open\" : \"\"} ${\n isShaking ? \"drawer--shaking\" : \"\"\n } ${className}`}\n style={{ width: widthValue, maxWidth: \"100vw\" }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n aria-describedby={subtitleId}\n onAnimationEnd={handleAnimationEnd}\n >\n {/* Header */}\n <header className=\"drawer__header\">\n <div className=\"drawer__title-row\">\n <h2 id={titleId} className=\"drawer__title\">\n {title}\n </h2>\n <button\n ref={closeButtonRef}\n type=\"button\"\n className=\"drawer__close-btn\"\n onClick={requestClose}\n aria-label={closeLabel}\n >\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M18 6L6 18\" />\n <path d=\"M6 6l12 12\" />\n </svg>\n </button>\n </div>\n {subtitle && (\n <p id={subtitleId} className=\"drawer__subtitle\">\n {subtitle}\n </p>\n )}\n </header>\n\n {/* Content */}\n <div className=\"drawer__content\">{children}</div>\n\n {/* Footer */}\n {footer && <footer className=\"drawer__footer\">{footer}</footer>}\n </aside>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
export type IllustrationType = "chat" | "models" | "creations" | "text" | "benchmark" | "logs" | "statistics" | "error" | "schedule" | "generic";
|
|
3
2
|
export interface EmptyStateAction {
|
|
4
3
|
label: string;
|
|
5
4
|
onClick: () => void;
|
|
@@ -10,8 +9,16 @@ export interface EmptyStateSecondaryAction {
|
|
|
10
9
|
onClick?: () => void;
|
|
11
10
|
}
|
|
12
11
|
export interface EmptyStateProps {
|
|
13
|
-
/**
|
|
14
|
-
|
|
12
|
+
/**
|
|
13
|
+
* The drawing shown above the text.
|
|
14
|
+
*
|
|
15
|
+
* This used to be one of ten names: chat, models, creations, benchmark,
|
|
16
|
+
* logs, statistics, schedule and so on, each resolving to an SVG shipped
|
|
17
|
+
* inside this package. Those names are one product's information
|
|
18
|
+
* architecture, and no other consumer has a "creations" screen to draw for.
|
|
19
|
+
* A consumer passes its own artwork and keeps its own vocabulary.
|
|
20
|
+
*/
|
|
21
|
+
illustration?: ReactNode;
|
|
15
22
|
/** Main heading */
|
|
16
23
|
title: string;
|
|
17
24
|
/** Descriptive text */
|
|
@@ -1,79 +1,62 @@
|
|
|
1
1
|
import { Button as e } from "../Button/Button2.js";
|
|
2
|
-
import { BenchmarkIllustration as t, ChatIllustration as n, CreationsIllustration as r, ErrorIllustration as i, GenericIllustration as a, LogsIllustration as o, ModelsIllustration as s, ScheduleIllustration as c, StatisticsIllustration as l, TextIllustration as u } from "./illustrations.js";
|
|
3
2
|
/* empty css */
|
|
4
|
-
import { memo as
|
|
5
|
-
import { Fragment as
|
|
3
|
+
import { memo as t, useCallback as n, useMemo as r } from "react";
|
|
4
|
+
import { Fragment as i, jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
6
5
|
//#region src/components/EmptyState/EmptyState.tsx
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
error: i,
|
|
16
|
-
schedule: c,
|
|
17
|
-
generic: a
|
|
18
|
-
};
|
|
19
|
-
function v({ illustration: t, title: n, description: r, primaryAction: i, secondaryAction: a, children: o, className: s = "", showIllustration: c = !0 }) {
|
|
20
|
-
let l = i?.onClick, u = a?.onClick, d = f(() => {
|
|
21
|
-
l?.();
|
|
22
|
-
}, [l]), v = f(() => {
|
|
23
|
-
u?.();
|
|
24
|
-
}, [u]), y = p(() => _[t], [t]), b = p(() => [
|
|
25
|
-
"empty-state",
|
|
26
|
-
`empty-state--${t}`,
|
|
27
|
-
s
|
|
28
|
-
].filter(Boolean).join(" "), [t, s]);
|
|
29
|
-
return /* @__PURE__ */ g("div", {
|
|
30
|
-
className: b,
|
|
6
|
+
function s({ illustration: t, title: s, description: c, primaryAction: l, secondaryAction: u, children: d, className: f = "", showIllustration: p = !0 }) {
|
|
7
|
+
let m = l?.onClick, h = u?.onClick, g = n(() => {
|
|
8
|
+
m?.();
|
|
9
|
+
}, [m]), _ = n(() => {
|
|
10
|
+
h?.();
|
|
11
|
+
}, [h]), v = r(() => ["empty-state", f].filter(Boolean).join(" "), [f]);
|
|
12
|
+
return /* @__PURE__ */ o("div", {
|
|
13
|
+
className: v,
|
|
31
14
|
role: "status",
|
|
32
15
|
"aria-live": "polite",
|
|
33
16
|
children: [
|
|
34
|
-
|
|
17
|
+
p && t && /* @__PURE__ */ a("div", {
|
|
35
18
|
className: "empty-state__illustration",
|
|
36
|
-
children:
|
|
19
|
+
children: t
|
|
37
20
|
}),
|
|
38
|
-
/* @__PURE__ */
|
|
21
|
+
/* @__PURE__ */ o("div", {
|
|
39
22
|
className: "empty-state__content",
|
|
40
23
|
children: [
|
|
41
|
-
/* @__PURE__ */
|
|
24
|
+
/* @__PURE__ */ a("h3", {
|
|
42
25
|
className: "empty-state__title",
|
|
43
|
-
children:
|
|
26
|
+
children: s
|
|
44
27
|
}),
|
|
45
|
-
/* @__PURE__ */
|
|
28
|
+
/* @__PURE__ */ a("p", {
|
|
46
29
|
className: "empty-state__description",
|
|
47
|
-
children:
|
|
30
|
+
children: c
|
|
48
31
|
}),
|
|
49
|
-
|
|
32
|
+
d
|
|
50
33
|
]
|
|
51
34
|
}),
|
|
52
|
-
(
|
|
35
|
+
(l || u) && /* @__PURE__ */ o("div", {
|
|
53
36
|
className: "empty-state__actions",
|
|
54
|
-
children: [
|
|
37
|
+
children: [l && /* @__PURE__ */ a(e, {
|
|
55
38
|
variant: "primary",
|
|
56
|
-
onClick:
|
|
39
|
+
onClick: g,
|
|
57
40
|
className: "empty-state__action-btn empty-state__action-btn--primary",
|
|
58
|
-
children:
|
|
59
|
-
}),
|
|
60
|
-
href:
|
|
41
|
+
children: l.label
|
|
42
|
+
}), u && /* @__PURE__ */ a(i, { children: u.href ? /* @__PURE__ */ a("a", {
|
|
43
|
+
href: u.href,
|
|
61
44
|
className: "empty-state__action-link",
|
|
62
|
-
onClick:
|
|
63
|
-
children:
|
|
64
|
-
}) : /* @__PURE__ */
|
|
45
|
+
onClick: _,
|
|
46
|
+
children: u.label
|
|
47
|
+
}) : /* @__PURE__ */ a(e, {
|
|
65
48
|
variant: "ghost",
|
|
66
|
-
onClick:
|
|
49
|
+
onClick: _,
|
|
67
50
|
className: "empty-state__action-btn empty-state__action-btn--secondary",
|
|
68
|
-
children:
|
|
51
|
+
children: u.label
|
|
69
52
|
}) })]
|
|
70
53
|
})
|
|
71
54
|
]
|
|
72
55
|
});
|
|
73
56
|
}
|
|
74
|
-
var
|
|
57
|
+
var c = t(s);
|
|
75
58
|
//#endregion
|
|
76
|
-
export {
|
|
59
|
+
export { c as EmptyState };
|
|
77
60
|
|
|
78
61
|
//# sourceMappingURL=EmptyState2.js.map
|
|
79
62
|
import "../../assets/components/EmptyState/EmptyState.css";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmptyState2.js","names":[],"sources":["../../../src/components/EmptyState/EmptyState.tsx"],"sourcesContent":["/**\n * EmptyState Component\n *\n * Unified empty state component with consistent layout and styling.\n * Provides configurable illustration, title, description, and call-to-action buttons.\n *\n * Features:\n * - Multiple illustration types for different contexts\n * - Primary and secondary action buttons, plus a `children` slot for an\n * onboarding state that needs more than two (issue #3903)\n * - Accessible (ARIA attributes, semantic HTML)\n * - Dark/light theme support via design tokens\n * - Fade-in animation with reduced motion support\n * - Responsive design\n * - Memoized for optimal performance\n */\n\nimport { memo, useCallback, useMemo, type ReactNode } from \"react\";\nimport { Button } from \"../Button\";\nimport
|
|
1
|
+
{"version":3,"file":"EmptyState2.js","names":[],"sources":["../../../src/components/EmptyState/EmptyState.tsx"],"sourcesContent":["/**\n * EmptyState Component\n *\n * Unified empty state component with consistent layout and styling.\n * Provides configurable illustration, title, description, and call-to-action buttons.\n *\n * Features:\n * - Multiple illustration types for different contexts\n * - Primary and secondary action buttons, plus a `children` slot for an\n * onboarding state that needs more than two (issue #3903)\n * - Accessible (ARIA attributes, semantic HTML)\n * - Dark/light theme support via design tokens\n * - Fade-in animation with reduced motion support\n * - Responsive design\n * - Memoized for optimal performance\n */\n\nimport { memo, useCallback, useMemo, type ReactNode } from \"react\";\nimport { Button } from \"../Button\";\nimport \"./EmptyState.css\";\n\nexport interface EmptyStateAction {\n label: string;\n onClick: () => void;\n}\n\nexport interface EmptyStateSecondaryAction {\n label: string;\n href?: string;\n onClick?: () => void;\n}\n\nexport interface EmptyStateProps {\n /**\n * The drawing shown above the text.\n *\n * This used to be one of ten names: chat, models, creations, benchmark,\n * logs, statistics, schedule and so on, each resolving to an SVG shipped\n * inside this package. Those names are one product's information\n * architecture, and no other consumer has a \"creations\" screen to draw for.\n * A consumer passes its own artwork and keeps its own vocabulary.\n */\n illustration?: ReactNode;\n /** Main heading */\n title: string;\n /** Descriptive text */\n description: string;\n /** Primary action button */\n primaryAction?: EmptyStateAction;\n /** Secondary action (link or button) */\n secondaryAction?: EmptyStateSecondaryAction;\n /**\n * Extra content rendered under the description, inside the same text column.\n *\n * The two action props cover the common one-or-two-button case. An\n * onboarding state that has to offer more than that (e.g. the Data Hub's\n * three ingest paths, issue #3903) renders them here instead of growing a\n * page-local copy of this component.\n */\n children?: ReactNode;\n /** Additional CSS classes */\n className?: string;\n /** Whether to show illustration (default: true) */\n showIllustration?: boolean;\n}\n\n/**\n * EmptyState Component\n *\n * Displays an empty state with illustration, text, and optional actions.\n * Memoized to prevent unnecessary re-renders.\n */\nfunction EmptyStateComponent({\n illustration,\n title,\n description,\n primaryAction,\n secondaryAction,\n children,\n className = \"\",\n showIllustration = true,\n}: EmptyStateProps) {\n // Use stable callback references by extracting onClick functions\n const primaryOnClick = primaryAction?.onClick;\n const secondaryOnClick = secondaryAction?.onClick;\n\n const handlePrimaryAction = useCallback(() => {\n primaryOnClick?.();\n }, [primaryOnClick]);\n\n const handleSecondaryAction = useCallback(() => {\n secondaryOnClick?.();\n }, [secondaryOnClick]);\n\n // Memoize class name computation\n const containerClass = useMemo(\n () => [\"empty-state\", className].filter(Boolean).join(\" \"),\n [className],\n );\n\n return (\n <div className={containerClass} role=\"status\" aria-live=\"polite\">\n {showIllustration && illustration && (\n <div className=\"empty-state__illustration\">{illustration}</div>\n )}\n\n <div className=\"empty-state__content\">\n <h3 className=\"empty-state__title\">{title}</h3>\n <p className=\"empty-state__description\">{description}</p>\n {children}\n </div>\n\n {(primaryAction || secondaryAction) && (\n <div className=\"empty-state__actions\">\n {primaryAction && (\n <Button\n variant=\"primary\"\n onClick={handlePrimaryAction}\n className=\"empty-state__action-btn empty-state__action-btn--primary\"\n >\n {primaryAction.label}\n </Button>\n )}\n\n {secondaryAction && (\n <>\n {secondaryAction.href ? (\n <a\n href={secondaryAction.href}\n className=\"empty-state__action-link\"\n onClick={handleSecondaryAction}\n >\n {secondaryAction.label}\n </a>\n ) : (\n <Button\n variant=\"ghost\"\n onClick={handleSecondaryAction}\n className=\"empty-state__action-btn empty-state__action-btn--secondary\"\n >\n {secondaryAction.label}\n </Button>\n )}\n </>\n )}\n </div>\n )}\n </div>\n );\n}\n\nexport const EmptyState = memo(EmptyStateComponent);\n"],"mappings":";;;;;AAwEA,SAAS,EAAoB,EAC3B,iBACA,UACA,gBACA,kBACA,oBACA,aACA,eAAY,IACZ,sBAAmB,MACD;CAElB,IAAM,IAAiB,GAAe,SAChC,IAAmB,GAAiB,SAEpC,IAAsB,QAAkB;EAC5C,IAAiB;CACnB,GAAG,CAAC,CAAc,CAAC,GAEb,IAAwB,QAAkB;EAC9C,IAAmB;CACrB,GAAG,CAAC,CAAgB,CAAC,GAGf,IAAiB,QACf,CAAC,eAAe,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,GACzD,CAAC,CAAS,CACZ;CAEA,OACE,kBAAC,OAAD;EAAK,WAAW;EAAgB,MAAK;EAAS,aAAU;EAAxD,UAAA;GACG,KAAoB,KACnB,kBAAC,OAAD;IAAK,WAAU;IAA6B,UAAA;GAAkB,CAAA;GAGhE,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,kBAAC,MAAD;MAAI,WAAU;MAAsB,UAAA;KAAU,CAAA;KAC9C,kBAAC,KAAD;MAAG,WAAU;MAA4B,UAAA;KAAe,CAAA;KACvD;IACE;;IAEH,KAAiB,MACjB,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACG,KACC,kBAAC,GAAD;KACE,SAAQ;KACR,SAAS;KACT,WAAU;KAET,UAAA,EAAc;IACT,CAAA,GAGT,KACC,kBAAA,GAAA,EAAA,UACG,EAAgB,OACf,kBAAC,KAAD;KACE,MAAM,EAAgB;KACtB,WAAU;KACV,SAAS;KAER,UAAA,EAAgB;IAChB,CAAA,IAEH,kBAAC,GAAD;KACE,SAAQ;KACR,SAAS;KACT,WAAU;KAET,UAAA,EAAgB;IACX,CAAA,EAEV,CAAA,CAED;;EAEJ;;AAET;AAEA,IAAa,IAAa,EAAK,CAAmB"}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* EmptyState Component - Barrel Export
|
|
3
|
+
*
|
|
4
|
+
* The ten illustrations that used to ship here were one product's information
|
|
5
|
+
* architecture: chat, models, creations, benchmark, logs, statistics,
|
|
6
|
+
* schedule. No other consumer has a "creations" screen to draw for. A consumer
|
|
7
|
+
* passes its own artwork through `illustration` and keeps its own vocabulary.
|
|
3
8
|
*/
|
|
4
9
|
export { EmptyState } from './EmptyState';
|
|
5
|
-
export type { EmptyStateProps, EmptyStateAction, EmptyStateSecondaryAction,
|
|
10
|
+
export type { EmptyStateProps, EmptyStateAction, EmptyStateSecondaryAction, } from './EmptyState';
|
|
@@ -1,11 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* How the error reads, not what it is about.
|
|
4
|
+
*
|
|
5
|
+
* This prop used to be a union of five names: network, configuration, model,
|
|
6
|
+
* permission, generic. Two of those, and "model" in particular, are one
|
|
7
|
+
* product's categories rather than anything a shared component can reason
|
|
8
|
+
* about, and all five resolved to three colours anyway: network and permission
|
|
9
|
+
* were the same amber, model and generic the same red. A consumer decides
|
|
10
|
+
* which of its own error categories reads as which tone.
|
|
11
|
+
*/
|
|
12
|
+
export type ErrorTone = "danger" | "warning" | "accent";
|
|
2
13
|
export interface ErrorAction {
|
|
3
14
|
label: string;
|
|
4
15
|
onClick: () => void;
|
|
5
16
|
}
|
|
6
17
|
export interface ErrorStateProps {
|
|
7
|
-
/**
|
|
8
|
-
|
|
18
|
+
/** How the error reads. Defaults to `danger`. */
|
|
19
|
+
tone?: ErrorTone;
|
|
20
|
+
/** Replaces the default alert icon. */
|
|
21
|
+
icon?: ReactNode;
|
|
9
22
|
/** Error title - main heading */
|
|
10
23
|
title: string;
|
|
11
24
|
/** Detailed error message */
|
|
@@ -22,4 +35,4 @@ export interface ErrorStateProps {
|
|
|
22
35
|
/**
|
|
23
36
|
* ErrorState Component
|
|
24
37
|
*/
|
|
25
|
-
export declare function ErrorState({
|
|
38
|
+
export declare function ErrorState({ tone, icon, title, message, primaryAction, secondaryAction, className, showIcon, }: ErrorStateProps): import("react").JSX.Element;
|
|
@@ -4,56 +4,53 @@ import { AlertCircleIcon as t } from "../../icons/AlertCircleIcon.js";
|
|
|
4
4
|
import { useCallback as n } from "react";
|
|
5
5
|
import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
6
6
|
//#region src/components/ErrorState/ErrorState.tsx
|
|
7
|
-
function a(
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
function o({ type: t = "generic", title: o, message: s, primaryAction: c, secondaryAction: l, className: u = "", showIcon: d = !0 }) {
|
|
11
|
-
let f = n(() => {
|
|
12
|
-
c?.onClick();
|
|
13
|
-
}, [c]), p = n(() => {
|
|
7
|
+
function a({ tone: a = "danger", icon: o, title: s, message: c, primaryAction: l, secondaryAction: u, className: d = "", showIcon: f = !0 }) {
|
|
8
|
+
let p = n(() => {
|
|
14
9
|
l?.onClick();
|
|
15
|
-
}, [l]), m =
|
|
10
|
+
}, [l]), m = n(() => {
|
|
11
|
+
u?.onClick();
|
|
12
|
+
}, [u]), h = [
|
|
16
13
|
"error-state",
|
|
17
|
-
`error-state--${
|
|
18
|
-
|
|
14
|
+
`error-state--${a}`,
|
|
15
|
+
d
|
|
19
16
|
].filter(Boolean).join(" ");
|
|
20
17
|
return /* @__PURE__ */ i("div", {
|
|
21
|
-
className:
|
|
18
|
+
className: h,
|
|
22
19
|
role: "alert",
|
|
23
20
|
"aria-live": "polite",
|
|
24
21
|
children: [
|
|
25
|
-
|
|
22
|
+
f && /* @__PURE__ */ r("div", {
|
|
26
23
|
className: "error-state__icon",
|
|
27
24
|
"aria-hidden": "true",
|
|
28
|
-
children:
|
|
25
|
+
children: o ?? /* @__PURE__ */ r(t, { size: 48 })
|
|
29
26
|
}),
|
|
30
27
|
/* @__PURE__ */ r("h2", {
|
|
31
28
|
className: "error-state__title",
|
|
32
|
-
children:
|
|
29
|
+
children: s
|
|
33
30
|
}),
|
|
34
31
|
/* @__PURE__ */ r("p", {
|
|
35
32
|
className: "error-state__message",
|
|
36
|
-
children:
|
|
33
|
+
children: c
|
|
37
34
|
}),
|
|
38
|
-
(
|
|
35
|
+
(l || u) && /* @__PURE__ */ i("div", {
|
|
39
36
|
className: "error-state__actions",
|
|
40
|
-
children: [
|
|
37
|
+
children: [l && /* @__PURE__ */ r(e, {
|
|
41
38
|
variant: "primary",
|
|
42
|
-
onClick:
|
|
39
|
+
onClick: p,
|
|
43
40
|
className: "error-state__action-btn error-state__action-btn--primary",
|
|
44
|
-
children:
|
|
45
|
-
}),
|
|
41
|
+
children: l.label
|
|
42
|
+
}), u && /* @__PURE__ */ r(e, {
|
|
46
43
|
variant: "secondary",
|
|
47
|
-
onClick:
|
|
44
|
+
onClick: m,
|
|
48
45
|
className: "error-state__action-btn error-state__action-btn--secondary",
|
|
49
|
-
children:
|
|
46
|
+
children: u.label
|
|
50
47
|
})]
|
|
51
48
|
})
|
|
52
49
|
]
|
|
53
50
|
});
|
|
54
51
|
}
|
|
55
52
|
//#endregion
|
|
56
|
-
export {
|
|
53
|
+
export { a as ErrorState };
|
|
57
54
|
|
|
58
55
|
//# sourceMappingURL=ErrorState2.js.map
|
|
59
56
|
import "../../assets/components/ErrorState/ErrorState.css";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorState2.js","names":[],"sources":["../../../src/components/ErrorState/ErrorState.tsx"],"sourcesContent":["/**\n * ErrorState Component\n *\n * Unified error state component with recovery actions.\n * Provides consistent error presentation across all pages with actionable buttons.\n *\n * Features:\n * -
|
|
1
|
+
{"version":3,"file":"ErrorState2.js","names":[],"sources":["../../../src/components/ErrorState/ErrorState.tsx"],"sourcesContent":["/**\n * ErrorState Component\n *\n * Unified error state component with recovery actions.\n * Provides consistent error presentation across all pages with actionable buttons.\n *\n * Features:\n * - Three visual tones\n * - Primary and secondary action buttons\n * - Accessible (ARIA attributes, focus management)\n * - Dark/light theme support\n */\n\nimport type { ReactNode } from \"react\";\nimport { useCallback } from \"react\";\nimport { AlertCircleIcon } from \"../../icons/AlertCircleIcon\";\nimport { Button } from \"../Button\";\nimport \"./ErrorState.css\";\n\n/**\n * How the error reads, not what it is about.\n *\n * This prop used to be a union of five names: network, configuration, model,\n * permission, generic. Two of those, and \"model\" in particular, are one\n * product's categories rather than anything a shared component can reason\n * about, and all five resolved to three colours anyway: network and permission\n * were the same amber, model and generic the same red. A consumer decides\n * which of its own error categories reads as which tone.\n */\nexport type ErrorTone = \"danger\" | \"warning\" | \"accent\";\n\nexport interface ErrorAction {\n label: string;\n onClick: () => void;\n}\n\nexport interface ErrorStateProps {\n /** How the error reads. Defaults to `danger`. */\n tone?: ErrorTone;\n /** Replaces the default alert icon. */\n icon?: ReactNode;\n /** Error title - main heading */\n title: string;\n /** Detailed error message */\n message: string;\n /** Primary action button (e.g., Retry, Go to Settings) */\n primaryAction?: ErrorAction;\n /** Secondary action button (e.g., View Logs, Report Issue) */\n secondaryAction?: ErrorAction;\n /** Additional CSS classes */\n className?: string;\n /** Whether to show error icon */\n showIcon?: boolean;\n}\n\n/**\n * ErrorState Component\n */\nexport function ErrorState({\n tone = \"danger\",\n icon,\n title,\n message,\n primaryAction,\n secondaryAction,\n className = \"\",\n showIcon = true,\n}: ErrorStateProps) {\n const handlePrimaryAction = useCallback(() => {\n primaryAction?.onClick();\n }, [primaryAction]);\n\n const handleSecondaryAction = useCallback(() => {\n secondaryAction?.onClick();\n }, [secondaryAction]);\n\n const containerClass = [\"error-state\", `error-state--${tone}`, className]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div className={containerClass} role=\"alert\" aria-live=\"polite\">\n {showIcon && (\n <div className=\"error-state__icon\" aria-hidden=\"true\">\n {icon ?? <AlertCircleIcon size={48} />}\n </div>\n )}\n\n <h2 className=\"error-state__title\">{title}</h2>\n\n <p className=\"error-state__message\">{message}</p>\n\n {(primaryAction || secondaryAction) && (\n <div className=\"error-state__actions\">\n {primaryAction && (\n <Button\n variant=\"primary\"\n onClick={handlePrimaryAction}\n className=\"error-state__action-btn error-state__action-btn--primary\"\n >\n {primaryAction.label}\n </Button>\n )}\n\n {secondaryAction && (\n <Button\n variant=\"secondary\"\n onClick={handleSecondaryAction}\n className=\"error-state__action-btn error-state__action-btn--secondary\"\n >\n {secondaryAction.label}\n </Button>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;AA0DA,SAAgB,EAAW,EACzB,UAAO,UACP,SACA,UACA,YACA,kBACA,oBACA,eAAY,IACZ,cAAW,MACO;CAClB,IAAM,IAAsB,QAAkB;EAC5C,GAAe,QAAQ;CACzB,GAAG,CAAC,CAAa,CAAC,GAEZ,IAAwB,QAAkB;EAC9C,GAAiB,QAAQ;CAC3B,GAAG,CAAC,CAAe,CAAC,GAEd,IAAiB;EAAC;EAAe,gBAAgB;EAAQ;CAAS,CAAC,CACtE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CAEX,OACE,kBAAC,OAAD;EAAK,WAAW;EAAgB,MAAK;EAAQ,aAAU;EAAvD,UAAA;GACG,KACC,kBAAC,OAAD;IAAK,WAAU;IAAoB,eAAY;IAC5C,UAAA,KAAQ,kBAAC,GAAD,EAAiB,MAAM,GAAK,CAAA;GAClC,CAAA;GAGP,kBAAC,MAAD;IAAI,WAAU;IAAsB,UAAA;GAAU,CAAA;GAE9C,kBAAC,KAAD;IAAG,WAAU;IAAwB,UAAA;GAAW,CAAA;IAE9C,KAAiB,MACjB,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACG,KACC,kBAAC,GAAD;KACE,SAAQ;KACR,SAAS;KACT,WAAU;KAET,UAAA,EAAc;IACT,CAAA,GAGT,KACC,kBAAC,GAAD;KACE,SAAQ;KACR,SAAS;KACT,WAAU;KAET,UAAA,EAAgB;IACX,CAAA,CAEP;;EAEJ;;AAET"}
|
|
@@ -8,6 +8,19 @@ export interface PageHeaderProps {
|
|
|
8
8
|
actions?: ReactNode;
|
|
9
9
|
/** Error message to display below the header */
|
|
10
10
|
error?: string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Secondary line under the message, for the raw detail a server returned.
|
|
13
|
+
* Kept separate so the message stays readable when the detail is long.
|
|
14
|
+
*/
|
|
15
|
+
errorDetail?: string | null;
|
|
16
|
+
/**
|
|
17
|
+
* Callback for the Retry button, rendered before the dismiss control when
|
|
18
|
+
* set. Dismissal is a pure dismissal and must not retry, which is why these
|
|
19
|
+
* are two props rather than one.
|
|
20
|
+
*/
|
|
21
|
+
onRetry?: () => void;
|
|
22
|
+
/** Label for the Retry button. Default: "Retry" */
|
|
23
|
+
retryLabel?: string;
|
|
11
24
|
/** Callback when error is dismissed */
|
|
12
25
|
onErrorDismiss?: () => void;
|
|
13
26
|
/** Additional CSS class names */
|
|
@@ -15,4 +28,4 @@ export interface PageHeaderProps {
|
|
|
15
28
|
/** Accessible label for the error-dismiss button. Default: "Dismiss error" */
|
|
16
29
|
dismissErrorLabel?: string;
|
|
17
30
|
}
|
|
18
|
-
export declare function PageHeader({ title, description, actions, error, onErrorDismiss, className, dismissErrorLabel, }: PageHeaderProps): import("react").JSX.Element;
|
|
31
|
+
export declare function PageHeader({ title, description, actions, error, onErrorDismiss, className, errorDetail, onRetry, retryLabel, dismissErrorLabel, }: PageHeaderProps): import("react").JSX.Element;
|