@edux-design/modals 0.1.3 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,16 +1,33 @@
1
1
  # @edux-design/modals
2
2
 
3
+ ## 0.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Align the exact Radix Dialog peer to 1.1.19 so dialog and Tooltip 1.2.12 share dismissal-layer ownership. Preserve the existing Modal UI runtime and public composition.
8
+
9
+ ## 0.2.0
10
+
11
+ ### Minor Changes
12
+
13
+ - Add controlled confirmation dialog and anchored popover compositions with pending dismissal policy, error/retry content and safe focus handling. Generate declared public types for both packages.
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies
18
+ - @edux-design/buttons@0.3.0
19
+
3
20
  ## 0.1.3
4
21
 
5
22
  ### Patch Changes
6
23
 
7
- - Rebuilt dist
24
+ - Rebuilt distribution artifacts.
8
25
 
9
26
  ## 0.1.2
10
27
 
11
28
  ### Patch Changes
12
29
 
13
- - Updated design tokens
30
+ - Updated design tokens.
14
31
 
15
32
  ## 0.1.1
16
33
 
package/README.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  Centered modal primitives built on top of Radix Dialog and Edux design tokens.
4
4
 
5
+ This release requires `@radix-ui/react-dialog@1.1.19`. It shares the dismissal
6
+ layer used by `@radix-ui/react-tooltip@1.2.12`, the exact dependency of the new
7
+ Edux Tooltips release. Keep direct Dialog dependencies and any Tooltip overrides
8
+ aligned with this pair so Escape dismisses a nested tooltip before its modal.
9
+
5
10
  Exports:
6
11
 
7
12
  - `Modal`
@@ -63,3 +68,17 @@ export function PublishModal() {
63
68
  Passing `onClose` to `Modal` also enables the default close icon inside `ModalHeader`.
64
69
 
65
70
  `ModalContent` accepts `size`, `backdropProps`, the legacy alias `backDropProps`, `className`, and `contentProps`. Storybook examples live in `src/demos/Modal.stories.jsx`.
71
+
72
+ ### Size contracts
73
+
74
+ The `small`, `medium`, `large`, and `full` values are named component-owned width
75
+ contracts. They intentionally use responsive `min()` and viewport constraints so
76
+ the modal keeps its current geometry across screen sizes. These values are not
77
+ global width tokens: the design-token standards keep calculated, component-specific
78
+ surface dimensions local until multiple unrelated components share the same role.
79
+
80
+ Unknown `size` values fall back to `medium` for compatibility.
81
+
82
+ ## Additional components
83
+
84
+ [Confirmation dialogs](./docs/confirmation.md).
@@ -0,0 +1,78 @@
1
+ import * as _radix_ui_react_dialog from '@radix-ui/react-dialog';
2
+ import React from 'react';
3
+
4
+ /** @type {React.FC<ModalProps>} */
5
+ declare const Modal: React.FC<ModalProps>;
6
+ declare const ModalTrigger: React.ForwardRefExoticComponent<_radix_ui_react_dialog.DialogTriggerProps & React.RefAttributes<HTMLButtonElement>>;
7
+ declare const ModalClose: React.ForwardRefExoticComponent<_radix_ui_react_dialog.DialogCloseProps & React.RefAttributes<HTMLButtonElement>>;
8
+ declare const ModalPortal: React.FC<_radix_ui_react_dialog.DialogPortalProps>;
9
+ /** @type {React.ForwardRefExoticComponent<ModalOverlayProps & React.RefAttributes<HTMLDivElement>>} */
10
+ declare const ModalOverlay: React.ForwardRefExoticComponent<ModalOverlayProps & React.RefAttributes<HTMLDivElement>>;
11
+ /** @type {React.ForwardRefExoticComponent<ModalContentProps & React.RefAttributes<HTMLDivElement>>} */
12
+ declare const ModalContent: React.ForwardRefExoticComponent<ModalContentProps & React.RefAttributes<HTMLDivElement>>;
13
+ /** @type {React.ForwardRefExoticComponent<ModalHeaderProps & React.RefAttributes<HTMLDivElement>>} */
14
+ declare const ModalHeader: React.ForwardRefExoticComponent<ModalHeaderProps & React.RefAttributes<HTMLDivElement>>;
15
+ /** @type {React.ForwardRefExoticComponent<ModalBodyProps & React.RefAttributes<HTMLDivElement>>} */
16
+ declare const ModalBody: React.ForwardRefExoticComponent<ModalBodyProps & React.RefAttributes<HTMLDivElement>>;
17
+ /** @type {React.ForwardRefExoticComponent<ModalFooterProps & React.RefAttributes<HTMLDivElement>>} */
18
+ declare const ModalFooter: React.ForwardRefExoticComponent<ModalFooterProps & React.RefAttributes<HTMLDivElement>>;
19
+ /** @type {React.ForwardRefExoticComponent<ModalTitleProps & React.RefAttributes<HTMLHeadingElement>>} */
20
+ declare const ModalTitle: React.ForwardRefExoticComponent<ModalTitleProps & React.RefAttributes<HTMLHeadingElement>>;
21
+ /** @type {React.ForwardRefExoticComponent<ModalDescriptionProps & React.RefAttributes<HTMLParagraphElement>>} */
22
+ declare const ModalDescription: React.ForwardRefExoticComponent<ModalDescriptionProps & React.RefAttributes<HTMLParagraphElement>>;
23
+ type ModalProps = _radix_ui_react_dialog.DialogProps & {
24
+ onOpen?: () => void;
25
+ onClose?: () => void;
26
+ };
27
+ type ModalContentProps = _radix_ui_react_dialog.DialogContentProps & {
28
+ size?: "small" | "medium" | "large" | "full";
29
+ backdropProps?: _radix_ui_react_dialog.DialogOverlayProps;
30
+ backDropProps?: _radix_ui_react_dialog.DialogOverlayProps;
31
+ contentProps?: _radix_ui_react_dialog.DialogContentProps;
32
+ portalProps?: _radix_ui_react_dialog.DialogPortalProps;
33
+ };
34
+ type ModalOverlayProps = _radix_ui_react_dialog.DialogOverlayProps;
35
+ type ModalHeaderProps = React.HTMLAttributes<HTMLDivElement>;
36
+ type ModalBodyProps = React.HTMLAttributes<HTMLDivElement>;
37
+ type ModalFooterProps = React.HTMLAttributes<HTMLDivElement>;
38
+ type ModalTitleProps = _radix_ui_react_dialog.DialogTitleProps;
39
+ type ModalDescriptionProps = _radix_ui_react_dialog.DialogDescriptionProps;
40
+
41
+ type ConfirmationDialogActionProps = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "children" | "disabled"> & React.RefAttributes<HTMLButtonElement>;
42
+ interface ConfirmationDialogProps {
43
+ open?: boolean;
44
+ defaultOpen?: boolean;
45
+ onOpenChange?: (open: boolean) => void;
46
+ modal?: boolean;
47
+ title: React.ReactNode;
48
+ description?: React.ReactNode;
49
+ children?: React.ReactNode;
50
+ /** A single ref-forwarding button. Omit for an externally opened dialog. */
51
+ trigger?: React.ReactElement;
52
+ onConfirm: React.MouseEventHandler<HTMLButtonElement>;
53
+ onCancel?: React.MouseEventHandler<HTMLButtonElement>;
54
+ confirmLabel?: React.ReactNode;
55
+ cancelLabel?: React.ReactNode;
56
+ pendingLabel?: React.ReactNode;
57
+ pending?: boolean;
58
+ error?: React.ReactNode;
59
+ confirmDisabled?: boolean;
60
+ cancelDisabled?: boolean;
61
+ /** Locks user dismissal. The controlled host may still close explicitly. */
62
+ dismissible?: boolean;
63
+ /** Explicit return target, useful when confirmation removes its own trigger. */
64
+ returnFocusRef?: React.RefObject<HTMLElement | null>;
65
+ isDanger?: boolean;
66
+ /** Pending confirmations block every user dismissal unless explicitly enabled. */
67
+ allowDismissWhilePending?: boolean;
68
+ size?: "small" | "medium" | "large" | "full";
69
+ contentProps?: Omit<_radix_ui_react_dialog.DialogContentProps, "children" | "asChild">;
70
+ backdropProps?: _radix_ui_react_dialog.DialogOverlayProps;
71
+ portalProps?: _radix_ui_react_dialog.DialogPortalProps;
72
+ confirmButtonProps?: ConfirmationDialogActionProps;
73
+ cancelButtonProps?: ConfirmationDialogActionProps;
74
+ }
75
+ /** Controlled confirmation. The host owns mutations, pending/error state and success closure. */
76
+ declare const ConfirmationDialog: React.ForwardRefExoticComponent<ConfirmationDialogProps & React.RefAttributes<HTMLDivElement>>;
77
+
78
+ export { ConfirmationDialog, type ConfirmationDialogActionProps, type ConfirmationDialogProps, Modal, ModalBody, type ModalBodyProps, ModalClose, ModalContent, type ModalContentProps, ModalDescription, type ModalDescriptionProps, ModalFooter, type ModalFooterProps, ModalHeader, type ModalHeaderProps, ModalOverlay, type ModalOverlayProps, ModalPortal, type ModalProps, ModalTitle, type ModalTitleProps, ModalTrigger };
@@ -0,0 +1,78 @@
1
+ import * as _radix_ui_react_dialog from '@radix-ui/react-dialog';
2
+ import React from 'react';
3
+
4
+ /** @type {React.FC<ModalProps>} */
5
+ declare const Modal: React.FC<ModalProps>;
6
+ declare const ModalTrigger: React.ForwardRefExoticComponent<_radix_ui_react_dialog.DialogTriggerProps & React.RefAttributes<HTMLButtonElement>>;
7
+ declare const ModalClose: React.ForwardRefExoticComponent<_radix_ui_react_dialog.DialogCloseProps & React.RefAttributes<HTMLButtonElement>>;
8
+ declare const ModalPortal: React.FC<_radix_ui_react_dialog.DialogPortalProps>;
9
+ /** @type {React.ForwardRefExoticComponent<ModalOverlayProps & React.RefAttributes<HTMLDivElement>>} */
10
+ declare const ModalOverlay: React.ForwardRefExoticComponent<ModalOverlayProps & React.RefAttributes<HTMLDivElement>>;
11
+ /** @type {React.ForwardRefExoticComponent<ModalContentProps & React.RefAttributes<HTMLDivElement>>} */
12
+ declare const ModalContent: React.ForwardRefExoticComponent<ModalContentProps & React.RefAttributes<HTMLDivElement>>;
13
+ /** @type {React.ForwardRefExoticComponent<ModalHeaderProps & React.RefAttributes<HTMLDivElement>>} */
14
+ declare const ModalHeader: React.ForwardRefExoticComponent<ModalHeaderProps & React.RefAttributes<HTMLDivElement>>;
15
+ /** @type {React.ForwardRefExoticComponent<ModalBodyProps & React.RefAttributes<HTMLDivElement>>} */
16
+ declare const ModalBody: React.ForwardRefExoticComponent<ModalBodyProps & React.RefAttributes<HTMLDivElement>>;
17
+ /** @type {React.ForwardRefExoticComponent<ModalFooterProps & React.RefAttributes<HTMLDivElement>>} */
18
+ declare const ModalFooter: React.ForwardRefExoticComponent<ModalFooterProps & React.RefAttributes<HTMLDivElement>>;
19
+ /** @type {React.ForwardRefExoticComponent<ModalTitleProps & React.RefAttributes<HTMLHeadingElement>>} */
20
+ declare const ModalTitle: React.ForwardRefExoticComponent<ModalTitleProps & React.RefAttributes<HTMLHeadingElement>>;
21
+ /** @type {React.ForwardRefExoticComponent<ModalDescriptionProps & React.RefAttributes<HTMLParagraphElement>>} */
22
+ declare const ModalDescription: React.ForwardRefExoticComponent<ModalDescriptionProps & React.RefAttributes<HTMLParagraphElement>>;
23
+ type ModalProps = _radix_ui_react_dialog.DialogProps & {
24
+ onOpen?: () => void;
25
+ onClose?: () => void;
26
+ };
27
+ type ModalContentProps = _radix_ui_react_dialog.DialogContentProps & {
28
+ size?: "small" | "medium" | "large" | "full";
29
+ backdropProps?: _radix_ui_react_dialog.DialogOverlayProps;
30
+ backDropProps?: _radix_ui_react_dialog.DialogOverlayProps;
31
+ contentProps?: _radix_ui_react_dialog.DialogContentProps;
32
+ portalProps?: _radix_ui_react_dialog.DialogPortalProps;
33
+ };
34
+ type ModalOverlayProps = _radix_ui_react_dialog.DialogOverlayProps;
35
+ type ModalHeaderProps = React.HTMLAttributes<HTMLDivElement>;
36
+ type ModalBodyProps = React.HTMLAttributes<HTMLDivElement>;
37
+ type ModalFooterProps = React.HTMLAttributes<HTMLDivElement>;
38
+ type ModalTitleProps = _radix_ui_react_dialog.DialogTitleProps;
39
+ type ModalDescriptionProps = _radix_ui_react_dialog.DialogDescriptionProps;
40
+
41
+ type ConfirmationDialogActionProps = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "children" | "disabled"> & React.RefAttributes<HTMLButtonElement>;
42
+ interface ConfirmationDialogProps {
43
+ open?: boolean;
44
+ defaultOpen?: boolean;
45
+ onOpenChange?: (open: boolean) => void;
46
+ modal?: boolean;
47
+ title: React.ReactNode;
48
+ description?: React.ReactNode;
49
+ children?: React.ReactNode;
50
+ /** A single ref-forwarding button. Omit for an externally opened dialog. */
51
+ trigger?: React.ReactElement;
52
+ onConfirm: React.MouseEventHandler<HTMLButtonElement>;
53
+ onCancel?: React.MouseEventHandler<HTMLButtonElement>;
54
+ confirmLabel?: React.ReactNode;
55
+ cancelLabel?: React.ReactNode;
56
+ pendingLabel?: React.ReactNode;
57
+ pending?: boolean;
58
+ error?: React.ReactNode;
59
+ confirmDisabled?: boolean;
60
+ cancelDisabled?: boolean;
61
+ /** Locks user dismissal. The controlled host may still close explicitly. */
62
+ dismissible?: boolean;
63
+ /** Explicit return target, useful when confirmation removes its own trigger. */
64
+ returnFocusRef?: React.RefObject<HTMLElement | null>;
65
+ isDanger?: boolean;
66
+ /** Pending confirmations block every user dismissal unless explicitly enabled. */
67
+ allowDismissWhilePending?: boolean;
68
+ size?: "small" | "medium" | "large" | "full";
69
+ contentProps?: Omit<_radix_ui_react_dialog.DialogContentProps, "children" | "asChild">;
70
+ backdropProps?: _radix_ui_react_dialog.DialogOverlayProps;
71
+ portalProps?: _radix_ui_react_dialog.DialogPortalProps;
72
+ confirmButtonProps?: ConfirmationDialogActionProps;
73
+ cancelButtonProps?: ConfirmationDialogActionProps;
74
+ }
75
+ /** Controlled confirmation. The host owns mutations, pending/error state and success closure. */
76
+ declare const ConfirmationDialog: React.ForwardRefExoticComponent<ConfirmationDialogProps & React.RefAttributes<HTMLDivElement>>;
77
+
78
+ export { ConfirmationDialog, type ConfirmationDialogActionProps, type ConfirmationDialogProps, Modal, ModalBody, type ModalBodyProps, ModalClose, ModalContent, type ModalContentProps, ModalDescription, type ModalDescriptionProps, ModalFooter, type ModalFooterProps, ModalHeader, type ModalHeaderProps, ModalOverlay, type ModalOverlayProps, ModalPortal, type ModalProps, ModalTitle, type ModalTitleProps, ModalTrigger };
package/dist/index.js CHANGED
@@ -29,6 +29,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
29
  // src/index.js
30
30
  var index_exports = {};
31
31
  __export(index_exports, {
32
+ ConfirmationDialog: () => ConfirmationDialog,
32
33
  Modal: () => Modal,
33
34
  ModalBody: () => ModalBody,
34
35
  ModalClose: () => ModalClose,
@@ -49,6 +50,7 @@ var DialogPrimitive = __toESM(require("@radix-ui/react-dialog"));
49
50
  var import_utils = require("@edux-design/utils");
50
51
  var import_buttons = require("@edux-design/buttons");
51
52
  var import_icons = require("@edux-design/icons");
53
+ var import_jsx_runtime = require("react/jsx-runtime");
52
54
  var MODAL_OVERLAY_Z_INDEX = 2147483645;
53
55
  var MODAL_CONTENT_Z_INDEX = 2147483646;
54
56
  var ModalContext = (0, import_react.createContext)({ hasOnClose: false });
@@ -105,40 +107,40 @@ var Modal = ({
105
107
  );
106
108
  const handleOpenChange = (0, import_react.useCallback)(
107
109
  (nextOpen) => {
108
- onOpenChange == null ? void 0 : onOpenChange(nextOpen);
110
+ onOpenChange?.(nextOpen);
109
111
  if (nextOpen) {
110
- onOpen == null ? void 0 : onOpen();
112
+ onOpen?.();
111
113
  return;
112
114
  }
113
- onClose == null ? void 0 : onClose();
115
+ onClose?.();
114
116
  },
115
117
  [onClose, onOpen, onOpenChange]
116
118
  );
117
- return /* @__PURE__ */ import_react.default.createElement(ModalContext.Provider, { value: contextValue }, /* @__PURE__ */ import_react.default.createElement(
119
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
118
120
  DialogPrimitive.Root,
119
121
  {
120
122
  open,
121
123
  defaultOpen,
122
124
  onOpenChange: handleOpenChange,
123
- ...props
124
- },
125
- children
126
- ));
125
+ ...props,
126
+ children
127
+ }
128
+ ) });
127
129
  };
128
130
  var ModalTrigger = DialogPrimitive.Trigger;
129
131
  var ModalClose = DialogPrimitive.Close;
130
132
  var ModalPortal = DialogPrimitive.Portal;
131
- var ModalHeaderCloseButton = () => /* @__PURE__ */ import_react.default.createElement(ModalClose, { asChild: true }, /* @__PURE__ */ import_react.default.createElement(
133
+ var ModalHeaderCloseButton = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
132
134
  import_buttons.IconButton,
133
135
  {
134
136
  size: "small",
135
137
  isBare: true,
136
- icon: /* @__PURE__ */ import_react.default.createElement(import_icons.Close, { "aria-hidden": "true" }),
138
+ icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.Close, { "aria-hidden": "true" }),
137
139
  "aria-label": MODAL_CLOSE_LABEL
138
140
  }
139
- ));
141
+ ) });
140
142
  var ModalOverlay = (0, import_react.forwardRef)(
141
- ({ className, style, ...props }, ref) => /* @__PURE__ */ import_react.default.createElement(
143
+ ({ className, style, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
142
144
  DialogPrimitive.Overlay,
143
145
  {
144
146
  ref,
@@ -162,50 +164,56 @@ var ModalContent = (0, import_react.forwardRef)(
162
164
  backdropProps,
163
165
  backDropProps,
164
166
  contentProps = {},
167
+ portalProps = {},
165
168
  ...restProps
166
169
  }, ref) => {
167
170
  const resolvedBackdropProps = backdropProps ?? backDropProps ?? {};
168
171
  const { className: backdropClassName, style: backdropStyle, ...overlayProps } = resolvedBackdropProps;
169
- return /* @__PURE__ */ import_react.default.createElement(ModalPortal, null, /* @__PURE__ */ import_react.default.createElement(
170
- ModalOverlay,
171
- {
172
- ...overlayProps,
173
- className: (0, import_utils.cx)(backdropClassName),
174
- style: { ...backdropStyle }
175
- }
176
- ), /* @__PURE__ */ import_react.default.createElement(
177
- DialogPrimitive.Content,
178
- {
179
- ref,
180
- className: (0, import_utils.cx)(
181
- MODAL_SURFACE_CLASSES,
182
- getSizeClasses(size),
183
- className
184
- ),
185
- style: { zIndex: MODAL_CONTENT_Z_INDEX, ...style },
186
- ...contentProps,
187
- ...restProps
188
- },
189
- children
190
- ));
172
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ModalPortal, { ...portalProps, children: [
173
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
174
+ ModalOverlay,
175
+ {
176
+ ...overlayProps,
177
+ className: (0, import_utils.cx)(backdropClassName),
178
+ style: { ...backdropStyle }
179
+ }
180
+ ),
181
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
182
+ DialogPrimitive.Content,
183
+ {
184
+ ref,
185
+ className: (0, import_utils.cx)(
186
+ MODAL_SURFACE_CLASSES,
187
+ getSizeClasses(size),
188
+ className
189
+ ),
190
+ style: { zIndex: MODAL_CONTENT_Z_INDEX, ...style },
191
+ ...contentProps,
192
+ ...restProps,
193
+ children
194
+ }
195
+ )
196
+ ] });
191
197
  }
192
198
  );
193
199
  ModalContent.displayName = "ModalContent";
194
200
  var ModalHeader = (0, import_react.forwardRef)(({ className, children, ...props }, ref) => {
195
201
  const { hasOnClose } = (0, import_react.useContext)(ModalContext);
196
- return /* @__PURE__ */ import_react.default.createElement(
202
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
197
203
  "div",
198
204
  {
199
205
  ref,
200
206
  className: (0, import_utils.cx)(MODAL_HEADER_CLASSES, className),
201
- ...props
202
- },
203
- /* @__PURE__ */ import_react.default.createElement("div", { className: MODAL_HEADER_CONTENT_CLASSES }, children),
204
- hasOnClose ? /* @__PURE__ */ import_react.default.createElement(ModalHeaderCloseButton, null) : null
207
+ ...props,
208
+ children: [
209
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: MODAL_HEADER_CONTENT_CLASSES, children }),
210
+ hasOnClose ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalHeaderCloseButton, {}) : null
211
+ ]
212
+ }
205
213
  );
206
214
  });
207
215
  ModalHeader.displayName = "ModalHeader";
208
- var ModalBody = (0, import_react.forwardRef)(({ className, ...props }, ref) => /* @__PURE__ */ import_react.default.createElement(
216
+ var ModalBody = (0, import_react.forwardRef)(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
209
217
  "div",
210
218
  {
211
219
  ref,
@@ -214,7 +222,7 @@ var ModalBody = (0, import_react.forwardRef)(({ className, ...props }, ref) => /
214
222
  }
215
223
  ));
216
224
  ModalBody.displayName = "ModalBody";
217
- var ModalFooter = (0, import_react.forwardRef)(({ className, ...props }, ref) => /* @__PURE__ */ import_react.default.createElement(
225
+ var ModalFooter = (0, import_react.forwardRef)(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
218
226
  "div",
219
227
  {
220
228
  ref,
@@ -223,7 +231,7 @@ var ModalFooter = (0, import_react.forwardRef)(({ className, ...props }, ref) =>
223
231
  }
224
232
  ));
225
233
  ModalFooter.displayName = "ModalFooter";
226
- var ModalTitle = (0, import_react.forwardRef)(({ className, ...props }, ref) => /* @__PURE__ */ import_react.default.createElement(
234
+ var ModalTitle = (0, import_react.forwardRef)(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
227
235
  DialogPrimitive.Title,
228
236
  {
229
237
  ref,
@@ -232,7 +240,7 @@ var ModalTitle = (0, import_react.forwardRef)(({ className, ...props }, ref) =>
232
240
  }
233
241
  ));
234
242
  ModalTitle.displayName = "ModalTitle";
235
- var ModalDescription = (0, import_react.forwardRef)(({ className, ...props }, ref) => /* @__PURE__ */ import_react.default.createElement(
243
+ var ModalDescription = (0, import_react.forwardRef)(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
236
244
  DialogPrimitive.Description,
237
245
  {
238
246
  ref,
@@ -241,8 +249,153 @@ var ModalDescription = (0, import_react.forwardRef)(({ className, ...props }, re
241
249
  }
242
250
  ));
243
251
  ModalDescription.displayName = "ModalDescription";
252
+
253
+ // src/elements/ConfirmationDialog.tsx
254
+ var import_react2 = require("react");
255
+ var import_buttons2 = require("@edux-design/buttons");
256
+ var import_utils2 = require("@edux-design/utils");
257
+ var import_jsx_runtime2 = require("react/jsx-runtime");
258
+ var ConfirmationDialog = (0, import_react2.forwardRef)(function ConfirmationDialog2({
259
+ open,
260
+ defaultOpen = false,
261
+ onOpenChange,
262
+ modal = true,
263
+ title,
264
+ description,
265
+ children,
266
+ trigger,
267
+ onConfirm,
268
+ onCancel,
269
+ confirmLabel = "Confirm",
270
+ cancelLabel = "Cancel",
271
+ pendingLabel = "Working\u2026",
272
+ pending = false,
273
+ error,
274
+ confirmDisabled = false,
275
+ cancelDisabled = false,
276
+ dismissible = true,
277
+ returnFocusRef,
278
+ isDanger = false,
279
+ allowDismissWhilePending = false,
280
+ size = "small",
281
+ contentProps = {},
282
+ backdropProps,
283
+ portalProps,
284
+ confirmButtonProps = {},
285
+ cancelButtonProps = {}
286
+ }, ref) {
287
+ const [uncontrolledOpen, setUncontrolledOpen] = (0, import_react2.useState)(defaultOpen);
288
+ const resolvedOpen = open ?? uncontrolledOpen;
289
+ const cancelRef = (0, import_react2.useRef)(null);
290
+ const previousFocusRef = (0, import_react2.useRef)(null);
291
+ const statusId = (0, import_react2.useId)();
292
+ const locked = !dismissible || pending && !allowDismissWhilePending;
293
+ const hasBody = children != null || Boolean(error) || pending;
294
+ const {
295
+ onClick: onCancelClick,
296
+ ref: actionRef,
297
+ ...cancelProps
298
+ } = cancelButtonProps;
299
+ const { onClick: onConfirmClick, ...confirmProps } = confirmButtonProps;
300
+ const requestOpen = (next) => {
301
+ if (!next && locked) return;
302
+ if (open === void 0) setUncontrolledOpen(next);
303
+ onOpenChange?.(next);
304
+ };
305
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Modal, { open: resolvedOpen, onOpenChange: requestOpen, modal, children: [
306
+ trigger && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ModalTrigger, { asChild: true, children: trigger }),
307
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
308
+ ModalContent,
309
+ {
310
+ ...contentProps,
311
+ ref,
312
+ size,
313
+ backdropProps,
314
+ portalProps,
315
+ ...!description && !contentProps["aria-describedby"] ? { "aria-describedby": void 0 } : {},
316
+ "aria-busy": pending || void 0,
317
+ className: (0, import_utils2.cx)("motion-reduce:animate-none", contentProps.className),
318
+ onOpenAutoFocus: (event) => {
319
+ previousFocusRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
320
+ contentProps.onOpenAutoFocus?.(event);
321
+ if (!event.defaultPrevented) {
322
+ event.preventDefault();
323
+ if (!locked && !cancelDisabled) cancelRef.current?.focus();
324
+ else if (event.target instanceof HTMLElement) event.target.focus();
325
+ }
326
+ },
327
+ onCloseAutoFocus: (event) => {
328
+ contentProps.onCloseAutoFocus?.(event);
329
+ const target = returnFocusRef?.current ?? (!trigger ? previousFocusRef.current : null);
330
+ if (!event.defaultPrevented && target?.isConnected) {
331
+ event.preventDefault();
332
+ target.focus();
333
+ }
334
+ },
335
+ onEscapeKeyDown: (event) => {
336
+ contentProps.onEscapeKeyDown?.(event);
337
+ if (locked) event.preventDefault();
338
+ },
339
+ onInteractOutside: (event) => {
340
+ contentProps.onInteractOutside?.(event);
341
+ if (locked) event.preventDefault();
342
+ },
343
+ children: [
344
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(ModalHeader, { className: !hasBody ? "border-b-0" : void 0, children: [
345
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ModalTitle, { children: title }),
346
+ description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ModalDescription, { children: description })
347
+ ] }),
348
+ hasBody && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(ModalBody, { className: "min-h-0 text-sm text-fg-base", children: [
349
+ children,
350
+ error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { role: "alert", className: "mt-8 text-fg-danger-emphasis", children: error }),
351
+ pending && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { id: statusId, role: "status", className: "sr-only", children: pendingLabel })
352
+ ] }),
353
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(ModalFooter, { className: "flex-wrap", children: [
354
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
355
+ import_buttons2.Button,
356
+ {
357
+ ...cancelProps,
358
+ ref: (node) => {
359
+ cancelRef.current = node;
360
+ if (typeof actionRef === "function") actionRef(node);
361
+ else if (actionRef) actionRef.current = node;
362
+ },
363
+ type: "button",
364
+ size: "small",
365
+ isSecondary: true,
366
+ disabled: locked || cancelDisabled,
367
+ onClick: (event) => {
368
+ onCancelClick?.(event);
369
+ if (!event.defaultPrevented) onCancel?.(event);
370
+ if (!event.defaultPrevented) requestOpen(false);
371
+ },
372
+ children: cancelLabel
373
+ }
374
+ ),
375
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
376
+ import_buttons2.Button,
377
+ {
378
+ ...confirmProps,
379
+ type: "button",
380
+ size: "small",
381
+ isDanger,
382
+ disabled: pending || confirmDisabled,
383
+ onClick: (event) => {
384
+ onConfirmClick?.(event);
385
+ if (!event.defaultPrevented) onConfirm(event);
386
+ },
387
+ children: pending ? pendingLabel : confirmLabel
388
+ }
389
+ )
390
+ ] })
391
+ ]
392
+ }
393
+ )
394
+ ] });
395
+ });
244
396
  // Annotate the CommonJS export names for ESM import in node:
245
397
  0 && (module.exports = {
398
+ ConfirmationDialog,
246
399
  Modal,
247
400
  ModalBody,
248
401
  ModalClose,