@seedgrid/fe-components 2026.3.9 → 2026.3.11-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/buttons/SgButton.d.ts +2 -0
  2. package/dist/buttons/SgButton.d.ts.map +1 -1
  3. package/dist/buttons/SgButton.js +4 -3
  4. package/dist/digits/segment-digit/SgSegmentDigit.d.ts +15 -0
  5. package/dist/digits/segment-digit/SgSegmentDigit.d.ts.map +1 -0
  6. package/dist/digits/segment-digit/SgSegmentDigit.js +96 -0
  7. package/dist/digits/segment-digit/index.d.ts +3 -0
  8. package/dist/digits/segment-digit/index.d.ts.map +1 -0
  9. package/dist/digits/segment-digit/index.js +1 -0
  10. package/dist/digits/seven-segment-digit/SgSevenSegmentDigit.d.ts +36 -0
  11. package/dist/digits/seven-segment-digit/SgSevenSegmentDigit.d.ts.map +1 -0
  12. package/dist/digits/seven-segment-digit/SgSevenSegmentDigit.js +123 -0
  13. package/dist/digits/seven-segment-digit/index.d.ts +3 -0
  14. package/dist/digits/seven-segment-digit/index.d.ts.map +1 -0
  15. package/dist/digits/seven-segment-digit/index.js +1 -0
  16. package/dist/gadgets/calendar/SgCalendar.d.ts +29 -0
  17. package/dist/gadgets/calendar/SgCalendar.d.ts.map +1 -0
  18. package/dist/gadgets/calendar/SgCalendar.js +248 -0
  19. package/dist/gadgets/calendar/index.d.ts +3 -0
  20. package/dist/gadgets/calendar/index.d.ts.map +1 -0
  21. package/dist/gadgets/calendar/index.js +1 -0
  22. package/dist/gadgets/clock/SgClock.d.ts +4 -1
  23. package/dist/gadgets/clock/SgClock.d.ts.map +1 -1
  24. package/dist/gadgets/clock/SgClock.js +74 -115
  25. package/dist/i18n/en-US.d.ts.map +1 -1
  26. package/dist/i18n/en-US.js +11 -1
  27. package/dist/i18n/es.d.ts.map +1 -1
  28. package/dist/i18n/es.js +11 -1
  29. package/dist/i18n/pt-BR.d.ts.map +1 -1
  30. package/dist/i18n/pt-BR.js +11 -1
  31. package/dist/i18n/pt-PT.d.ts.map +1 -1
  32. package/dist/i18n/pt-PT.js +11 -1
  33. package/dist/index.d.ts +11 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +5 -0
  36. package/dist/inputs/SgDatatable.d.ts +104 -0
  37. package/dist/inputs/SgDatatable.d.ts.map +1 -0
  38. package/dist/inputs/SgDatatable.js +441 -0
  39. package/dist/layout/SgCard.d.ts +12 -0
  40. package/dist/layout/SgCard.d.ts.map +1 -1
  41. package/dist/layout/SgCard.js +228 -12
  42. package/dist/overlay/SgConfirmationDialog.d.ts +34 -0
  43. package/dist/overlay/SgConfirmationDialog.d.ts.map +1 -0
  44. package/dist/overlay/SgConfirmationDialog.js +81 -0
  45. package/dist/overlay/SgDialog.d.ts +5 -0
  46. package/dist/overlay/SgDialog.d.ts.map +1 -1
  47. package/dist/overlay/SgDialog.js +28 -3
  48. package/dist/sandbox.cjs +83 -83
  49. package/package.json +1 -1
@@ -1,16 +1,152 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
+ import { useHasSgEnvironmentProvider, useSgPersistence } from "../environment/SgEnvironmentProvider";
4
5
  function cn(...parts) {
5
6
  return parts.filter(Boolean).join(" ");
6
7
  }
7
- function DefaultCaret({ open }) {
8
- return (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", className: cn("shrink-0 transition-transform duration-200", open ? "rotate-90" : "rotate-0"), "aria-hidden": "true", children: _jsx("path", { fill: "currentColor", d: "M9.29 6.71a1 1 0 0 0 0 1.41L13.17 12l-3.88 3.88a1 1 0 1 0 1.41 1.41l4.59-4.59a1 1 0 0 0 0-1.41L10.7 6.7a1 1 0 0 0-1.41.01Z" }) }));
8
+ function parseStoredCardPosition(raw) {
9
+ const value = typeof raw === "string"
10
+ ? (() => {
11
+ try {
12
+ return JSON.parse(raw);
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ })()
18
+ : raw;
19
+ if (!value ||
20
+ typeof value !== "object" ||
21
+ typeof value.x !== "number" ||
22
+ typeof value.y !== "number" ||
23
+ !Number.isFinite(value.x) ||
24
+ !Number.isFinite(value.y)) {
25
+ return null;
26
+ }
27
+ return {
28
+ x: value.x,
29
+ y: value.y
30
+ };
31
+ }
32
+ function mergeTransforms(baseTransform, extraTransform) {
33
+ if (typeof baseTransform === "string" && baseTransform.trim().length > 0) {
34
+ return `${baseTransform} ${extraTransform}`;
35
+ }
36
+ return extraTransform;
37
+ }
38
+ function DefaultCaret({ open, size }) {
39
+ return (_jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", className: cn("shrink-0 transition-transform duration-200", open ? "rotate-90" : "rotate-0"), "aria-hidden": "true", children: _jsx("path", { fill: "currentColor", d: "M9.29 6.71a1 1 0 0 0 0 1.41L13.17 12l-3.88 3.88a1 1 0 1 0 1.41 1.41l4.59-4.59a1 1 0 0 0 0-1.41L10.7 6.7a1 1 0 0 0-1.41.01Z" }) }));
9
40
  }
10
41
  export function SgCard(props) {
11
- const { className, headerClassName, bodyClassName, footerClassName, cardStyle = "default", size = "md", leading, trailing, trailer, title, description, actions, header, footer, clickable = false, disabled = false, collapsible = false, defaultOpen = true, open: controlledOpen, onOpenChange, collapseIcon, collapseToggleAlign = "left", toggleOnHeaderClick = true, onClick, children, ...rest } = props;
12
- const isInteractive = (clickable || typeof onClick === "function") && !collapsible;
42
+ const { id, className, headerClassName, bodyClassName, footerClassName, bgColor, bgColorTitle, bgColorFooter, cardStyle = "default", size = "md", leading, trailing, trailer, title, description, actions, header, footer, clickable = false, disabled = false, collapsible = false, defaultOpen = true, open: controlledOpen, onOpenChange, collapseIcon, collapseIconSize = 18, collapseToggleAlign = "left", toggleOnHeaderClick = true, draggable = false, defaultPosition, dragPersistKey, onPositionChange, style, onClick, children, ...rest } = props;
43
+ const hasEnvironmentProvider = useHasSgEnvironmentProvider();
44
+ const { load: loadPersistedState, save: savePersistedState, clear: clearPersistedState } = useSgPersistence();
45
+ const isInteractive = (clickable || typeof onClick === "function") && !collapsible && !draggable;
13
46
  const trailingNode = trailing ?? trailer;
47
+ const [dragPosition, setDragPosition] = React.useState({
48
+ x: defaultPosition?.x ?? 0,
49
+ y: defaultPosition?.y ?? 0
50
+ });
51
+ const dragPositionRef = React.useRef(dragPosition);
52
+ const [dragHydrated, setDragHydrated] = React.useState(false);
53
+ const [dragging, setDragging] = React.useState(false);
54
+ const dragStateRef = React.useRef(null);
55
+ const suppressNextToggleRef = React.useRef(false);
56
+ const dragStorageKey = React.useMemo(() => {
57
+ if (typeof dragPersistKey === "string" && dragPersistKey.trim().length > 0) {
58
+ return dragPersistKey.trim();
59
+ }
60
+ if (typeof id === "string" && id.trim().length > 0) {
61
+ return `sg-card-pos:${id.trim()}`;
62
+ }
63
+ return null;
64
+ }, [dragPersistKey, id]);
65
+ React.useEffect(() => {
66
+ dragPositionRef.current = dragPosition;
67
+ onPositionChange?.(dragPosition);
68
+ }, [dragPosition, onPositionChange]);
69
+ React.useEffect(() => {
70
+ if (!draggable) {
71
+ setDragHydrated(false);
72
+ return;
73
+ }
74
+ let alive = true;
75
+ (async () => {
76
+ let loaded = null;
77
+ if (dragStorageKey) {
78
+ if (hasEnvironmentProvider) {
79
+ try {
80
+ const state = await loadPersistedState(dragStorageKey);
81
+ loaded = parseStoredCardPosition(state);
82
+ if (!loaded && state !== null && state !== undefined) {
83
+ await clearPersistedState(dragStorageKey);
84
+ }
85
+ }
86
+ catch {
87
+ loaded = null;
88
+ }
89
+ }
90
+ else {
91
+ try {
92
+ const raw = localStorage.getItem(dragStorageKey);
93
+ loaded = parseStoredCardPosition(raw);
94
+ if (!loaded && raw !== null) {
95
+ localStorage.removeItem(dragStorageKey);
96
+ }
97
+ }
98
+ catch {
99
+ loaded = null;
100
+ }
101
+ }
102
+ }
103
+ if (!alive)
104
+ return;
105
+ if (loaded) {
106
+ setDragPosition(loaded);
107
+ }
108
+ else {
109
+ setDragPosition({
110
+ x: defaultPosition?.x ?? 0,
111
+ y: defaultPosition?.y ?? 0
112
+ });
113
+ }
114
+ setDragHydrated(true);
115
+ })();
116
+ return () => {
117
+ alive = false;
118
+ };
119
+ }, [
120
+ clearPersistedState,
121
+ defaultPosition?.x,
122
+ defaultPosition?.y,
123
+ dragStorageKey,
124
+ draggable,
125
+ hasEnvironmentProvider,
126
+ loadPersistedState
127
+ ]);
128
+ React.useEffect(() => {
129
+ if (!draggable || !dragHydrated || !dragStorageKey || dragging)
130
+ return;
131
+ if (hasEnvironmentProvider) {
132
+ void savePersistedState(dragStorageKey, dragPosition);
133
+ return;
134
+ }
135
+ try {
136
+ localStorage.setItem(dragStorageKey, JSON.stringify(dragPosition));
137
+ }
138
+ catch {
139
+ // ignore
140
+ }
141
+ }, [
142
+ dragHydrated,
143
+ dragPosition,
144
+ dragStorageKey,
145
+ draggable,
146
+ dragging,
147
+ hasEnvironmentProvider,
148
+ savePersistedState
149
+ ]);
14
150
  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);
15
151
  const isControlled = typeof controlledOpen === "boolean";
16
152
  const isOpen = collapsible ? (isControlled ? controlledOpen : uncontrolledOpen) : true;
@@ -58,9 +194,13 @@ export function SgCard(props) {
58
194
  ? cn("transition-[box-shadow,transform] duration-150", "hover:shadow-md", "focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-[hsl(var(--primary)/0.35)]", disabled ? "" : "cursor-pointer", disabled ? "" : "active:translate-y-[0.5px]")
59
195
  : "";
60
196
  const disabledClasses = disabled ? "opacity-60 pointer-events-none select-none" : "";
61
- const rootClasses = cn("w-full", sizeClasses.root, variantClasses, interactiveClasses, disabledClasses, className);
197
+ const rootClasses = cn("w-full", sizeClasses.root, variantClasses, interactiveClasses, disabledClasses, draggable ? "touch-none" : "", className);
62
198
  const toggle = React.useCallback(() => setOpen(!isOpen), [isOpen, setOpen]);
63
199
  const onHeaderClick = () => {
200
+ if (suppressNextToggleRef.current) {
201
+ suppressNextToggleRef.current = false;
202
+ return;
203
+ }
64
204
  if (!collapsible)
65
205
  return;
66
206
  if (!toggleOnHeaderClick)
@@ -69,10 +209,74 @@ export function SgCard(props) {
69
209
  return;
70
210
  toggle();
71
211
  };
72
- const ToggleButton = collapsible ? (_jsx("button", { type: "button", onClick: (e) => {
212
+ const onHeaderPointerDown = React.useCallback((event) => {
213
+ if (!draggable || disabled)
214
+ return;
215
+ if (event.button !== 0)
216
+ return;
217
+ const target = event.target;
218
+ const interactiveAncestor = target?.closest("[data-sg-card-toggle=\"true\"],button,a,input,textarea,select,[role=\"button\"],[data-sg-card-no-drag=\"true\"]");
219
+ if (interactiveAncestor && interactiveAncestor !== event.currentTarget) {
220
+ return;
221
+ }
222
+ dragStateRef.current = {
223
+ pointerId: event.pointerId,
224
+ startX: event.clientX,
225
+ startY: event.clientY,
226
+ originX: dragPositionRef.current.x,
227
+ originY: dragPositionRef.current.y,
228
+ moved: false
229
+ };
230
+ event.currentTarget.setPointerCapture?.(event.pointerId);
231
+ setDragging(true);
232
+ event.preventDefault();
233
+ }, [disabled, draggable]);
234
+ const onHeaderPointerMove = React.useCallback((event) => {
235
+ const state = dragStateRef.current;
236
+ if (!state || state.pointerId !== event.pointerId)
237
+ return;
238
+ const dx = event.clientX - state.startX;
239
+ const dy = event.clientY - state.startY;
240
+ if (!state.moved && (Math.abs(dx) > 2 || Math.abs(dy) > 2)) {
241
+ state.moved = true;
242
+ }
243
+ setDragPosition({
244
+ x: state.originX + dx,
245
+ y: state.originY + dy
246
+ });
247
+ if (state.moved) {
248
+ event.preventDefault();
249
+ }
250
+ }, []);
251
+ const endHeaderDrag = React.useCallback((event) => {
252
+ const state = dragStateRef.current;
253
+ if (!state || state.pointerId !== event.pointerId)
254
+ return;
255
+ if (state.moved) {
256
+ suppressNextToggleRef.current = true;
257
+ }
258
+ dragStateRef.current = null;
259
+ setDragging(false);
260
+ try {
261
+ event.currentTarget.releasePointerCapture?.(event.pointerId);
262
+ }
263
+ catch {
264
+ // ignore
265
+ }
266
+ }, []);
267
+ const headerInteractionClasses = disabled
268
+ ? ""
269
+ : draggable
270
+ ? dragging
271
+ ? "cursor-grabbing"
272
+ : "cursor-grab"
273
+ : collapsible && toggleOnHeaderClick
274
+ ? "cursor-pointer"
275
+ : "";
276
+ const ToggleButton = collapsible ? (_jsx("button", { "data-sg-card-toggle": "true", type: "button", onClick: (e) => {
73
277
  e.stopPropagation();
74
278
  toggle();
75
- }, className: cn("inline-flex h-8 w-8 items-center justify-center rounded-md", "text-muted-foreground hover:text-foreground", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[hsl(var(--primary)/0.35)]"), "aria-label": isOpen ? "Recolher" : "Expandir", "aria-expanded": isOpen, disabled: disabled, children: collapseIcon ?? _jsx(DefaultCaret, { open: isOpen }) })) : null;
279
+ }, className: cn("inline-flex h-9 w-9 items-center justify-center rounded-md", "text-muted-foreground hover:text-foreground", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[hsl(var(--primary)/0.35)]"), "aria-label": isOpen ? "Recolher" : "Expandir", "aria-expanded": isOpen, disabled: disabled, children: collapseIcon ?? _jsx(DefaultCaret, { open: isOpen, size: collapseIconSize }) })) : null;
76
280
  const headerHasContent = Boolean(header) ||
77
281
  Boolean(leading) ||
78
282
  Boolean(title) ||
@@ -80,7 +284,7 @@ export function SgCard(props) {
80
284
  Boolean(trailingNode) ||
81
285
  Boolean(actions) ||
82
286
  collapsible;
83
- const renderDefaultHeader = headerHasContent ? (_jsxs("div", { className: cn("flex items-start gap-3", "border-b border-border/60", sizeClasses.header, headerClassName, collapsible && toggleOnHeaderClick ? "cursor-pointer" : ""), onClick: onHeaderClick, role: collapsible && toggleOnHeaderClick ? "button" : undefined, tabIndex: collapsible && toggleOnHeaderClick && !disabled ? 0 : undefined, onKeyDown: (e) => {
287
+ const renderDefaultHeader = headerHasContent ? (_jsxs("div", { className: cn("flex items-start gap-3", "border-b border-border/60", sizeClasses.header, headerClassName, headerInteractionClasses), style: bgColorTitle ? { backgroundColor: bgColorTitle } : undefined, onClick: onHeaderClick, onPointerDown: onHeaderPointerDown, onPointerMove: onHeaderPointerMove, onPointerUp: endHeaderDrag, onPointerCancel: endHeaderDrag, role: collapsible && toggleOnHeaderClick ? "button" : undefined, tabIndex: collapsible && toggleOnHeaderClick && !disabled ? 0 : undefined, onKeyDown: (e) => {
84
288
  if (!collapsible || !toggleOnHeaderClick || disabled)
85
289
  return;
86
290
  if (e.key === "Enter" || e.key === " ") {
@@ -88,7 +292,7 @@ export function SgCard(props) {
88
292
  toggle();
89
293
  }
90
294
  }, "aria-expanded": collapsible ? isOpen : undefined, children: [collapseToggleAlign === "left" ? ToggleButton : null, leading ? _jsx("div", { className: "shrink-0 pt-0.5", children: leading }) : null, _jsxs("div", { className: "min-w-0 flex-1", children: [title ? (_jsx("div", { className: cn("font-medium text-foreground", sizeClasses.title), children: title })) : null, description ? (_jsx("div", { className: cn("mt-0.5 text-muted-foreground", sizeClasses.desc), children: description })) : null] }), _jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [trailingNode ? _jsx("div", { className: "shrink-0", children: trailingNode }) : null, actions ? _jsx("div", { className: "shrink-0", children: actions }) : null, collapseToggleAlign === "right" ? ToggleButton : null] })] })) : null;
91
- const headerNode = header ? (_jsx("div", { className: cn("border-b border-border/60", sizeClasses.header, headerClassName, collapsible && toggleOnHeaderClick ? "cursor-pointer" : ""), onClick: onHeaderClick, role: collapsible && toggleOnHeaderClick ? "button" : undefined, tabIndex: collapsible && toggleOnHeaderClick && !disabled ? 0 : undefined, onKeyDown: (e) => {
295
+ const headerNode = header ? (_jsx("div", { className: cn("border-b border-border/60", sizeClasses.header, headerClassName, headerInteractionClasses), style: bgColorTitle ? { backgroundColor: bgColorTitle } : undefined, onClick: onHeaderClick, onPointerDown: onHeaderPointerDown, onPointerMove: onHeaderPointerMove, onPointerUp: endHeaderDrag, onPointerCancel: endHeaderDrag, role: collapsible && toggleOnHeaderClick ? "button" : undefined, tabIndex: collapsible && toggleOnHeaderClick && !disabled ? 0 : undefined, onKeyDown: (e) => {
92
296
  if (!collapsible || !toggleOnHeaderClick || disabled)
93
297
  return;
94
298
  if (e.key === "Enter" || e.key === " ") {
@@ -96,11 +300,23 @@ export function SgCard(props) {
96
300
  toggle();
97
301
  }
98
302
  }, "aria-expanded": collapsible ? isOpen : undefined, children: header })) : (renderDefaultHeader);
99
- const collapsibleBodyWrapper = collapsible ? (_jsx("div", { className: cn("grid transition-all duration-200 ease-out motion-reduce:transition-none", isOpen ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"), children: _jsxs("div", { className: "overflow-hidden", children: [_jsx("div", { className: cn(sizeClasses.body, bodyClassName), children: children }), footer ? (_jsx("div", { className: cn("border-t border-border/60", sizeClasses.footer, footerClassName), children: footer })) : null] }) })) : null;
303
+ const collapsibleBodyWrapper = collapsible ? (_jsx("div", { className: cn("grid transition-all duration-200 ease-out motion-reduce:transition-none", isOpen ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"), children: _jsxs("div", { className: isOpen ? "overflow-visible" : "overflow-hidden", children: [_jsx("div", { className: cn(sizeClasses.body, bodyClassName), children: children }), footer ? (_jsx("div", { className: cn("border-t border-border/60", sizeClasses.footer, footerClassName), style: bgColorFooter ? { backgroundColor: bgColorFooter } : undefined, children: footer })) : null] }) })) : null;
100
304
  const bodyNode = !collapsible ? _jsx("div", { className: cn(sizeClasses.body, bodyClassName), children: children }) : null;
101
- const footerNode = !collapsible && footer ? (_jsx("div", { className: cn("border-t border-border/60", sizeClasses.footer, footerClassName), children: footer })) : null;
305
+ const footerNode = !collapsible && footer ? (_jsx("div", { className: cn("border-t border-border/60", sizeClasses.footer, footerClassName), style: bgColorFooter ? { backgroundColor: bgColorFooter } : undefined, children: footer })) : null;
102
306
  const Component = isInteractive ? "button" : "section";
103
307
  const buttonLikeProps = isInteractive ? { type: "button", onClick: disabled ? undefined : onClick, disabled } : {};
104
- return (_jsxs(Component, { className: rootClasses, ...buttonLikeProps, ...rest, children: [headerNode, collapsible ? collapsibleBodyWrapper : null, !collapsible ? bodyNode : null, !collapsible ? footerNode : null] }));
308
+ const passiveOnClickProps = !isInteractive && typeof onClick === "function" ? { onClick: disabled ? undefined : onClick } : {};
309
+ const roundedDragX = Math.round(dragPosition.x);
310
+ const roundedDragY = Math.round(dragPosition.y);
311
+ const shouldApplyDragTransform = draggable && (dragging || roundedDragX !== 0 || roundedDragY !== 0);
312
+ const rootStyle = {
313
+ ...style,
314
+ backgroundColor: bgColor ?? style?.backgroundColor,
315
+ transform: shouldApplyDragTransform
316
+ ? mergeTransforms(style?.transform, `translate3d(${roundedDragX}px, ${roundedDragY}px, 0)`)
317
+ : style?.transform,
318
+ willChange: shouldApplyDragTransform ? "transform" : style?.willChange
319
+ };
320
+ return (_jsxs(Component, { id: id, className: rootClasses, style: rootStyle, ...buttonLikeProps, ...passiveOnClickProps, ...rest, children: [headerNode, collapsible ? collapsibleBodyWrapper : null, !collapsible ? bodyNode : null, !collapsible ? footerNode : null] }));
105
321
  }
106
322
  SgCard.displayName = "SgCard";
@@ -0,0 +1,34 @@
1
+ import * as React from "react";
2
+ import { type SgButtonProps } from "../buttons/SgButton";
3
+ import { type SgDialogProps, type SgDialogSeverity } from "./SgDialog";
4
+ export type SgConfirmationDialogButtonConfig = {
5
+ label?: React.ReactNode;
6
+ icon?: React.ReactNode;
7
+ severity?: SgButtonProps["severity"];
8
+ appearance?: SgButtonProps["appearance"];
9
+ shape?: SgButtonProps["shape"];
10
+ disabled?: boolean;
11
+ className?: string;
12
+ onClick?: () => void;
13
+ };
14
+ export type SgConfirmationDialogProps = Omit<SgDialogProps, "title" | "children" | "footer" | "severity" | "shadow" | "showTopAccent"> & {
15
+ title?: React.ReactNode;
16
+ message?: React.ReactNode;
17
+ icon?: React.ReactNode;
18
+ iconPlacement?: "left" | "top";
19
+ severity?: SgDialogSeverity;
20
+ customColor?: SgDialogProps["customColor"];
21
+ elevation?: SgDialogProps["elevation"];
22
+ showSeverityAccent?: boolean;
23
+ cancelButton?: SgConfirmationDialogButtonConfig;
24
+ confirmButton?: SgConfirmationDialogButtonConfig;
25
+ onCancel?: () => void;
26
+ onConfirm?: () => void;
27
+ closeOnCancel?: boolean;
28
+ closeOnConfirm?: boolean;
29
+ };
30
+ export declare function SgConfirmationDialog(props: Readonly<SgConfirmationDialogProps>): import("react/jsx-runtime").JSX.Element;
31
+ export declare namespace SgConfirmationDialog {
32
+ var displayName: string;
33
+ }
34
+ //# sourceMappingURL=SgConfirmationDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SgConfirmationDialog.d.ts","sourceRoot":"","sources":["../../src/overlay/SgConfirmationDialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEnE,OAAO,EAAY,KAAK,aAAa,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEjF,MAAM,MAAM,gCAAgC,GAAG;IAC7C,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,QAAQ,CAAC,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IACrC,UAAU,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAC1C,aAAa,EACb,OAAO,GAAG,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,eAAe,CAC1E,GAAG;IACF,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC/B,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,WAAW,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAC3C,SAAS,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IACvC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,gCAAgC,CAAC;IAChD,aAAa,CAAC,EAAE,gCAAgC,CAAC;IACjD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAoEF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC,yBAAyB,CAAC,2CA2I9E;yBA3Ie,oBAAoB"}
@@ -0,0 +1,81 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { SgButton } from "../buttons/SgButton";
5
+ import { t, useComponentsI18n } from "../i18n";
6
+ import { SgDialog } from "./SgDialog";
7
+ function resolveButtonSeverity(dialogSeverity, fallback) {
8
+ if (!dialogSeverity || dialogSeverity === "plain")
9
+ return fallback;
10
+ return dialogSeverity;
11
+ }
12
+ function DefaultCancelIcon() {
13
+ return (_jsx("svg", { viewBox: "0 0 24 24", className: "size-4", "aria-hidden": "true", children: _jsx("path", { d: "M18 6L6 18M6 6l12 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }));
14
+ }
15
+ function DefaultConfirmIcon() {
16
+ return (_jsx("svg", { viewBox: "0 0 24 24", className: "size-4", "aria-hidden": "true", children: _jsx("path", { d: "M5 12l5 5L20 7", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
17
+ }
18
+ function DefaultInfoSeverityIcon() {
19
+ return (_jsxs("svg", { viewBox: "0 0 24 24", className: "size-5 text-sky-600", "aria-hidden": "true", children: [_jsx("circle", { cx: "12", cy: "12", r: "9", fill: "none", stroke: "currentColor", strokeWidth: "2" }), _jsx("path", { d: "M12 10v6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }), _jsx("circle", { cx: "12", cy: "7", r: "1.25", fill: "currentColor" })] }));
20
+ }
21
+ function DefaultWarningSeverityIcon() {
22
+ return (_jsxs("svg", { viewBox: "0 0 24 24", className: "size-5 text-amber-600", "aria-hidden": "true", children: [_jsx("path", { d: "M12 3L22 20H2L12 3z", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinejoin: "round" }), _jsx("path", { d: "M12 9v5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }), _jsx("circle", { cx: "12", cy: "17.25", r: "1.1", fill: "currentColor" })] }));
23
+ }
24
+ function DefaultDangerSeverityIcon() {
25
+ return (_jsxs("svg", { viewBox: "0 0 24 24", className: "size-5 text-red-600", "aria-hidden": "true", children: [_jsx("circle", { cx: "12", cy: "12", r: "9", fill: "none", stroke: "currentColor", strokeWidth: "2" }), _jsx("path", { d: "M9 9l6 6M15 9l-6 6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })] }));
26
+ }
27
+ function defaultSeverityIcon(severity) {
28
+ switch (severity) {
29
+ case "info":
30
+ return _jsx(DefaultInfoSeverityIcon, {});
31
+ case "warning":
32
+ return _jsx(DefaultWarningSeverityIcon, {});
33
+ case "danger":
34
+ return _jsx(DefaultDangerSeverityIcon, {});
35
+ default:
36
+ return undefined;
37
+ }
38
+ }
39
+ export function SgConfirmationDialog(props) {
40
+ const { open: openProp, defaultOpen = false, onOpenChange, onClose, title, message, icon, iconPlacement = "left", severity = "warning", customColor, elevation, showSeverityAccent = false, cancelButton, confirmButton, onCancel, onConfirm, closeOnCancel = true, closeOnConfirm = true, closeable = false, size = "sm", ...dialogProps } = props;
41
+ const i18n = useComponentsI18n();
42
+ const isControlled = openProp !== undefined;
43
+ const [openUncontrolled, setOpenUncontrolled] = React.useState(defaultOpen);
44
+ const open = isControlled ? Boolean(openProp) : openUncontrolled;
45
+ const setOpen = React.useCallback((next) => {
46
+ if (!isControlled)
47
+ setOpenUncontrolled(next);
48
+ onOpenChange?.(next);
49
+ }, [isControlled, onOpenChange]);
50
+ const fireCancel = React.useCallback(() => {
51
+ cancelButton?.onClick?.();
52
+ onCancel?.();
53
+ }, [cancelButton, onCancel]);
54
+ const handleCancel = React.useCallback(() => {
55
+ fireCancel();
56
+ if (closeOnCancel)
57
+ setOpen(false);
58
+ }, [fireCancel, closeOnCancel, setOpen]);
59
+ const handleConfirm = React.useCallback(() => {
60
+ confirmButton?.onClick?.();
61
+ onConfirm?.();
62
+ if (closeOnConfirm)
63
+ setOpen(false);
64
+ }, [confirmButton, onConfirm, closeOnConfirm, setOpen]);
65
+ const handleDialogOpenChange = React.useCallback((next) => {
66
+ if (!next && open) {
67
+ fireCancel();
68
+ }
69
+ setOpen(next);
70
+ }, [open, fireCancel, setOpen]);
71
+ const cancelLabel = cancelButton?.label ?? t(i18n, "components.actions.cancel");
72
+ const confirmLabel = confirmButton?.label ?? t(i18n, "components.actions.confirm");
73
+ const cancelIcon = cancelButton?.icon ?? _jsx(DefaultCancelIcon, {});
74
+ const confirmIcon = confirmButton?.icon ?? _jsx(DefaultConfirmIcon, {});
75
+ const resolvedIcon = icon !== undefined ? icon : defaultSeverityIcon(severity);
76
+ const cancelSeverity = cancelButton?.severity ?? "secondary";
77
+ const confirmSeverity = confirmButton?.severity ?? resolveButtonSeverity(severity, "primary");
78
+ const messageBlock = (_jsx("div", { className: "text-sm text-muted-foreground", children: message }));
79
+ return (_jsx(SgDialog, { ...dialogProps, open: open, onOpenChange: handleDialogOpenChange, onClose: onClose, closeable: closeable, severity: severity, showTopAccent: showSeverityAccent, customColor: customColor, elevation: elevation, title: title, size: size, footer: (_jsxs(_Fragment, { children: [_jsx(SgButton, { size: "sm", appearance: cancelButton?.appearance ?? "ghost", severity: cancelSeverity, shape: cancelButton?.shape ?? "rounded", disabled: cancelButton?.disabled, className: cancelButton?.className, leftIcon: cancelIcon, onClick: handleCancel, children: cancelLabel }), _jsx(SgButton, { size: "sm", appearance: confirmButton?.appearance ?? "solid", severity: confirmSeverity, shape: confirmButton?.shape ?? "rounded", disabled: confirmButton?.disabled, className: confirmButton?.className, leftIcon: confirmIcon, onClick: handleConfirm, children: confirmLabel })] })), children: resolvedIcon ? (iconPlacement === "top" ? (_jsxs("div", { className: "flex flex-col items-start gap-3", children: [_jsx("div", { className: "shrink-0", children: resolvedIcon }), messageBlock] })) : (_jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "shrink-0", children: resolvedIcon }), _jsx("div", { className: "min-w-0", children: messageBlock })] }))) : (messageBlock) }));
80
+ }
81
+ SgConfirmationDialog.displayName = "SgConfirmationDialog";
@@ -19,6 +19,11 @@ export type SgDialogProps = {
19
19
  autoCloseMs?: number;
20
20
  className?: string;
21
21
  style?: React.CSSProperties;
22
+ customColor?: React.CSSProperties["backgroundColor"];
23
+ elevation?: "none" | "sm" | "md" | "lg" | React.CSSProperties["boxShadow"];
24
+ /** @deprecated use elevation */
25
+ shadow?: React.CSSProperties["boxShadow"];
26
+ showTopAccent?: boolean;
22
27
  overlayClassName?: string;
23
28
  contentClassName?: string;
24
29
  headerClassName?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"SgDialog.d.ts","sourceRoot":"","sources":["../../src/overlay/SgDialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;AAC9D,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,WAAW,GACX,SAAS,GACT,MAAM,GACN,SAAS,GACT,MAAM,GACN,QAAQ,GACR,OAAO,CAAC;AACZ,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAE5D,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAEzB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAC/C,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAuDF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,4BAuPtD;yBAvPe,QAAQ"}
1
+ {"version":3,"file":"SgDialog.d.ts","sourceRoot":"","sources":["../../src/overlay/SgDialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;AAC9D,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,WAAW,GACX,SAAS,GACT,MAAM,GACN,SAAS,GACT,MAAM,GACN,QAAQ,GACR,OAAO,CAAC;AACZ,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAE5D,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAEzB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,WAAW,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC;IACrD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;IAC3E,gCAAgC;IAChC,MAAM,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;IAC1C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAC/C,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA2EF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,4BAmQtD;yBAnQe,QAAQ"}
@@ -51,8 +51,26 @@ function contentStateClass(animation, entered) {
51
51
  return cn(common, entered ? "translate-y-0" : "translate-y-2");
52
52
  }
53
53
  }
54
+ function resolveDialogShadow(elevation, shadow) {
55
+ if (elevation === undefined)
56
+ return shadow;
57
+ if (typeof elevation !== "string")
58
+ return elevation;
59
+ switch (elevation) {
60
+ case "none":
61
+ return "none";
62
+ case "sm":
63
+ return "0 1px 2px rgba(2, 8, 23, 0.12)";
64
+ case "md":
65
+ return "0 10px 24px rgba(2, 8, 23, 0.2), 0 4px 10px rgba(2, 8, 23, 0.12)";
66
+ case "lg":
67
+ return "0 24px 60px rgba(2, 8, 23, 0.32), 0 12px 24px rgba(2, 8, 23, 0.18)";
68
+ default:
69
+ return elevation;
70
+ }
71
+ }
54
72
  export function SgDialog(props) {
55
- const { open: openProp, onOpenChange, defaultOpen = false, title, subtitle, leading, trailing, children, footer, size = "md", severity = "plain", animation = "zoom", transitionMs = 160, autoCloseMs, className, style, overlayClassName, contentClassName, headerClassName, bodyClassName, footerClassName, closeable = true, onClose, closeOnOverlayClick = true, closeOnEsc = true, lockBodyScroll = true, initialFocusRef, restoreFocus = true, ariaLabel } = props;
73
+ const { open: openProp, onOpenChange, defaultOpen = false, title, subtitle, leading, trailing, children, footer, size = "md", severity = "plain", animation = "zoom", transitionMs = 160, autoCloseMs, className, style, customColor, elevation, shadow, showTopAccent = true, overlayClassName, contentClassName, headerClassName, bodyClassName, footerClassName, closeable = true, onClose, closeOnOverlayClick = true, closeOnEsc = true, lockBodyScroll = true, initialFocusRef, restoreFocus = true, ariaLabel } = props;
56
74
  const isControlled = openProp !== undefined;
57
75
  const [openUncontrolled, setOpenUncontrolled] = React.useState(defaultOpen);
58
76
  const open = isControlled ? !!openProp : openUncontrolled;
@@ -167,9 +185,16 @@ export function SgDialog(props) {
167
185
  const overlayBase = "fixed inset-0 bg-black/60 backdrop-blur-[1px] transition-opacity";
168
186
  const overlayState = entered ? "opacity-100" : "opacity-0";
169
187
  const contentBase = "w-full rounded-2xl bg-background text-foreground shadow-2xl border border-border " +
170
- "max-h-[85vh] flex flex-col transition duration-150 ease-out border-t-4";
188
+ "max-h-[85vh] flex flex-col transition duration-150 ease-out" +
189
+ (showTopAccent ? " border-t-4" : "");
190
+ const resolvedShadow = resolveDialogShadow(elevation, shadow);
171
191
  const transitionStyle = { transitionDuration: `${transitionMs}ms` };
172
- return createPortal(_jsxs("div", { className: cn("fixed inset-0 z-[1000]", className), style: style, role: "dialog", "aria-modal": "true", "aria-label": a11yLabel, children: [_jsx("div", { ref: overlayRef, onMouseDown: onOverlayMouseDown, className: cn(overlayBase, overlayState, overlayClassName), style: transitionStyle }), _jsx("div", { className: "fixed inset-0 flex items-center justify-center p-4", children: _jsxs("div", { ref: contentRef, className: cn(contentBase, sizeClass(size), contentStateClass(animation, entered), severityClass(severity), contentClassName), style: transitionStyle, children: [(title || subtitle || closeable || leading || trailing) && (_jsxs("div", { className: cn("px-5 py-4 border-b border-border flex items-start gap-3", headerClassName), children: [leading ? _jsx("div", { className: "shrink-0 pt-0.5", children: leading }) : null, _jsxs("div", { className: "min-w-0 flex-1", children: [title ? _jsx("div", { className: "text-base font-semibold leading-6", children: title }) : null, subtitle ? _jsx("div", { className: "text-sm text-muted-foreground mt-1", children: subtitle }) : null] }), _jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [trailing ? _jsx("div", { className: "shrink-0", children: trailing }) : null, closeable ? (_jsx("button", { type: "button", onClick: close, className: cn("inline-flex items-center justify-center h-9 w-9 rounded-lg", "hover:bg-muted/60 active:bg-muted", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[hsl(var(--ring,var(--primary)))]"), "aria-label": "Close", children: _jsx(CloseIcon, {}) })) : null] })] })), _jsx("div", { className: cn("px-5 py-4 overflow-auto", bodyClassName), children: children }), footer ? (_jsx("div", { className: cn("px-5 py-4 border-t border-border flex items-center justify-end gap-2", footerClassName), children: footer })) : null] }) })] }), document.body);
192
+ const contentStyle = {
193
+ ...transitionStyle,
194
+ ...(customColor !== undefined ? { backgroundColor: customColor } : {}),
195
+ ...(resolvedShadow !== undefined ? { boxShadow: resolvedShadow } : {})
196
+ };
197
+ return createPortal(_jsxs("div", { className: cn("fixed inset-0 z-[1000]", className), style: style, role: "dialog", "aria-modal": "true", "aria-label": a11yLabel, children: [_jsx("div", { ref: overlayRef, onMouseDown: onOverlayMouseDown, className: cn(overlayBase, overlayState, overlayClassName), style: transitionStyle }), _jsx("div", { className: "fixed inset-0 flex items-center justify-center p-4", children: _jsxs("div", { ref: contentRef, className: cn(contentBase, sizeClass(size), contentStateClass(animation, entered), showTopAccent ? severityClass(severity) : false, contentClassName), style: contentStyle, children: [(title || subtitle || closeable || leading || trailing) && (_jsxs("div", { className: cn("px-5 py-4 border-b border-border flex items-start gap-3", headerClassName), children: [leading ? _jsx("div", { className: "shrink-0 pt-0.5", children: leading }) : null, _jsxs("div", { className: "min-w-0 flex-1", children: [title ? _jsx("div", { className: "text-base font-semibold leading-6", children: title }) : null, subtitle ? _jsx("div", { className: "text-sm text-muted-foreground mt-1", children: subtitle }) : null] }), _jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [trailing ? _jsx("div", { className: "shrink-0", children: trailing }) : null, closeable ? (_jsx("button", { type: "button", onClick: close, className: cn("inline-flex items-center justify-center h-9 w-9 rounded-lg", "hover:bg-muted/60 active:bg-muted", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[hsl(var(--ring,var(--primary)))]"), "aria-label": "Close", children: _jsx(CloseIcon, {}) })) : null] })] })), _jsx("div", { className: cn("px-5 py-4 overflow-auto", bodyClassName), children: children }), footer ? (_jsx("div", { className: cn("px-5 py-4 border-t border-border flex items-center justify-end gap-2", footerClassName), children: footer })) : null] }) })] }), document.body);
173
198
  }
174
199
  function CloseIcon() {
175
200
  return (_jsx("svg", { viewBox: "0 0 24 24", className: "size-5", "aria-hidden": "true", children: _jsx("path", { d: "M18 6L6 18M6 6l12 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }));