react-confirm-alert 3.1.1 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Readme.md CHANGED
@@ -16,20 +16,55 @@ npm install react-confirm-alert
16
16
 
17
17
  ```tsx
18
18
  import { confirm } from 'react-confirm-alert'
19
- import 'react-confirm-alert/src/react-confirm-alert.css'
19
+ import 'react-confirm-alert/style.css'
20
20
 
21
21
  if (await confirm({ title: 'Delete file?', message: 'This cannot be undone.', tone: 'danger' })) {
22
22
  remove(file)
23
23
  }
24
24
  ```
25
25
 
26
+ `style.css` needs 3.1.1 or later. The older path, `react-confirm-alert/src/react-confirm-alert.css`, still works.
27
+
26
28
  `confirm()` resolves `true` or `false`. `alert()` and `prompt()` work the same way, and `<DialogHost />` placed inside your providers lets dialogs read their context.
27
29
 
30
+ ## Custom UI
31
+
32
+ Put your own form, list or warning in a dialog. Plain HTML gets the default look, even under Tailwind v4 preflight, and any class you add wins:
33
+
34
+ ```jsx
35
+ import { DialogLayout, openDialog } from 'react-confirm-alert'
36
+
37
+ const colour = await openDialog({
38
+ render: ({ close, dismiss }) => (
39
+ <DialogLayout title="Pick a colour" actions={[{ label: 'Cancel', onClick: dismiss }, { label: 'Blue', variant: 'primary', onClick: () => close('blue') }]}>
40
+ <p>Used for the team badge.</p>
41
+ </DialogLayout>
42
+ )
43
+ }).result
44
+ ```
45
+
46
+ 3.0 `customUI` still works. Add `skin: true` for the new look:
47
+
48
+ ```jsx
49
+ confirmAlert({
50
+ skin: true,
51
+ customUI: ({ onClose }) => (
52
+ <>
53
+ <h1>Leave the board?</h1>
54
+ <p>You can join again from the invite link.</p>
55
+ <button onClick={onClose}>Stay</button>
56
+ </>
57
+ )
58
+ })
59
+ ```
60
+
61
+ `confirm`, `alert` and `prompt` take `content` for JSX between the message and the buttons. See [Custom UI](https://ga-mo.github.io/react-confirm-alert/docs/custom-ui/).
62
+
28
63
  ## 3.0 code keeps working
29
64
 
30
65
  ```jsx
31
66
  import { confirmAlert } from 'react-confirm-alert'
32
- import 'react-confirm-alert/src/react-confirm-alert.css'
67
+ import 'react-confirm-alert/style.css'
33
68
 
34
69
  confirmAlert({
35
70
  title: 'Confirm to submit',
@@ -47,7 +82,7 @@ Same options, DOM, class names and CSS path as 3.0.
47
82
 
48
83
  ```css
49
84
  @import 'tailwindcss';
50
- @import 'react-confirm-alert/src/react-confirm-alert.css' layer(components);
85
+ @import 'react-confirm-alert/style.css' layer(components);
51
86
  ```
52
87
 
53
88
  ```tsx
@@ -60,11 +95,11 @@ Importing the CSS into `layer(components)` lets Tailwind utilities win without `
60
95
 
61
96
  | Import | gzip |
62
97
  |---|---|
63
- | `import { confirmAlert }` | 3,571 B |
64
- | `import { confirm }` | 3,851 B |
65
- | CSS | 1,095 B |
98
+ | `import { confirmAlert }` | 3,769 B |
99
+ | `import { confirm }` | 4,042 B |
100
+ | CSS (the shipped file, not minified) | 2,909 B |
66
101
 
67
- Measured on 2026-10-23 with size-limit 14.3.0 (`npx size-limit`, config in `.size-limit.json`): the ESM build bundled and minified with React and React DOM excluded, then gzipped. 3.0.6 measured the same way is 2,396 B, including `prop-types`. The extra bytes are the native modal, focus handling, stacked and queued dialogs, and dialogs that render inside your providers.
102
+ Measured on 2026-10-23 with size-limit 14.3.0 (`npx size-limit`, config in `.size-limit.json`): the ESM build bundled and minified with React and React DOM excluded, then gzipped. 3.0.6 measured the same way is 2,396 B, including `prop-types`. The extra bytes are the native modal, focus handling, stacked and queued dialogs, dialogs that render inside your providers, and the default look for your own content.
68
103
 
69
104
  ## Older versions
70
105
 
package/dist/index.esm.js CHANGED
@@ -1,8 +1,8 @@
1
1
  "use client";
2
- import { Component, createContext, useContext, useEffect, useId, useLayoutEffect, useReducer, useRef, useState, useSyncExternalStore } from "react";
2
+ import { Component, Fragment, createContext, useContext, useEffect, useId, useLayoutEffect, useReducer, useRef, useState, useSyncExternalStore } from "react";
3
3
  import { flushSync } from "react-dom";
4
4
  import { createRoot } from "react-dom/client";
5
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
6
6
  // src/store.ts
7
7
  var emptyState = {
8
8
  entries: [],
@@ -159,6 +159,7 @@ var slotsFor = (layers) => (slot, extra) => ({
159
159
  });
160
160
  var SlotContext = createContext(slotsFor([]));
161
161
  var useSlot = () => useContext(SlotContext);
162
+ var FrameContext = createContext({});
162
163
  function configureDialogs(defaults) {
163
164
  runtime().defaults = defaults;
164
165
  }
@@ -171,7 +172,7 @@ function DialogStack({ target, host }) {
171
172
  if (!(host ? hosts[hosts.length - 1] === host : target !== null || hosts.length === 0)) return null;
172
173
  const openIds = entries.filter((e) => e.phase === "open").map((e) => e.id);
173
174
  const top = openIds[openIds.length - 1];
174
- return /* @__PURE__ */ jsx(Fragment, { children: entries.filter((e) => e.target === target && e.phase !== "queued").map((e) => /* @__PURE__ */ jsx(EntryView, {
175
+ return /* @__PURE__ */ jsx(Fragment$1, { children: entries.filter((e) => e.target === target && e.phase !== "queued").map((e) => /* @__PURE__ */ jsx(EntryView, {
175
176
  entry: e,
176
177
  depth: openIds.indexOf(e.id),
177
178
  topmost: e.id === top
@@ -213,6 +214,7 @@ function EntryView({ entry, depth, topmost }) {
213
214
  closeOnClickOutside: (_ref2 = (_options$closeOnClick = options.closeOnClickOutside) !== null && _options$closeOnClick !== void 0 ? _options$closeOnClick : defaults.closeOnClickOutside) !== null && _ref2 !== void 0 ? _ref2 : true,
214
215
  closeOnEscape: (_ref3 = (_options$closeOnEscap = options.closeOnEscape) !== null && _options$closeOnEscap !== void 0 ? _options$closeOnEscap : defaults.closeOnEscape) !== null && _ref3 !== void 0 ? _ref3 : true,
215
216
  legacy: !!options.legacy,
217
+ skin: options.skin,
216
218
  onClickOutside: options.onClickOutside,
217
219
  onDismiss: dismiss,
218
220
  children: /* @__PURE__ */ jsx(Render, {
@@ -221,6 +223,7 @@ function EntryView({ entry, depth, topmost }) {
221
223
  descriptionId,
222
224
  title: options.title,
223
225
  message: options.message,
226
+ content: options.content,
224
227
  buttons: (_options$buttons = options.buttons) !== null && _options$buttons !== void 0 ? _options$buttons : [],
225
228
  close,
226
229
  dismiss,
@@ -348,7 +351,7 @@ function closeDialog$1(id, { sync = true } = {}) {
348
351
  commit(() => store.set(remove(store.get(), id)), sync);
349
352
  return;
350
353
  }
351
- Promise.all(exits.map((a) => a.finished)).finally(() => store.set(remove(store.get(), id)));
354
+ Promise.all(exits.map((a) => a.finished.catch(() => {}))).then(() => store.set(remove(store.get(), id)));
352
355
  }
353
356
  var reopenQueued = false;
354
357
  function reopenClosedFrames() {
@@ -374,12 +377,15 @@ function animationsOf({ frame, panel }) {
374
377
  // src/DialogFrame.tsx
375
378
  var canShowModal = () => typeof HTMLDialogElement !== "undefined" && typeof HTMLDialogElement.prototype.showModal === "function";
376
379
  function DialogFrame(props) {
377
- var _props$tone, _props$depth, _props$topmost;
380
+ var _props$labelledBy, _props$tone, _props$depth, _props$topmost, _props$labelledBy2;
378
381
  const frame = useRef(null);
379
382
  const panel = useRef(null);
380
383
  const pressedOn = useRef(null);
381
384
  const stay = useRef(null);
382
385
  const native = props.modal && canShowModal();
386
+ const layoutTitleId = useId();
387
+ const [layoutTitles, setLayoutTitles] = useState(0);
388
+ const labelledBy = (_props$labelledBy = props.labelledBy) !== null && _props$labelledBy !== void 0 ? _props$labelledBy : props.label || layoutTitles === 0 ? void 0 : layoutTitleId;
383
389
  useLayoutEffect(() => {
384
390
  var _panel$current, _panel$current2, _panel$current3;
385
391
  const element = frame.current;
@@ -440,7 +446,10 @@ function DialogFrame(props) {
440
446
  const isBusy = () => !!props.id && runtime().busy.has(props.id);
441
447
  const onKeyDown = (e) => {
442
448
  var _props$onKeyDown;
443
- (_props$onKeyDown = props.onKeyDown) === null || _props$onKeyDown === void 0 || _props$onKeyDown.call(props, e, props.onDismiss);
449
+ (_props$onKeyDown = props.onKeyDown) === null || _props$onKeyDown === void 0 || _props$onKeyDown.call(props, e, () => {
450
+ e.preventDefault();
451
+ props.onDismiss();
452
+ });
444
453
  if (!native && !props.legacy && e.key === "Escape" && props.closeOnEscape && !isBusy()) props.onDismiss();
445
454
  };
446
455
  const slot = slotsFor(props.layers);
@@ -448,11 +457,12 @@ function DialogFrame(props) {
448
457
  ...slot("dialog", props.extraClass),
449
458
  role: props.role,
450
459
  "aria-label": props.label,
451
- "aria-labelledby": props.labelledBy,
460
+ "aria-labelledby": labelledBy,
452
461
  "aria-describedby": props.describedBy,
453
462
  "data-rca": "",
454
463
  "data-state": props.state,
455
464
  "data-kind": props.kind,
465
+ "data-skin": props.legacy && !props.skin ? void 0 : "",
456
466
  "data-tone": (_props$tone = props.tone) !== null && _props$tone !== void 0 ? _props$tone : "default",
457
467
  "data-depth": String((_props$depth = props.depth) !== null && _props$depth !== void 0 ? _props$depth : 0),
458
468
  "data-topmost": ((_props$topmost = props.topmost) !== null && _props$topmost !== void 0 ? _props$topmost : true) ? "" : void 0,
@@ -466,7 +476,14 @@ function DialogFrame(props) {
466
476
  tabIndex: -1,
467
477
  children: /* @__PURE__ */ jsx(SlotContext.Provider, {
468
478
  value: slot,
469
- children: props.children
479
+ children: /* @__PURE__ */ jsx(FrameContext.Provider, {
480
+ value: {
481
+ id: props.id,
482
+ titleId: (_props$labelledBy2 = props.labelledBy) !== null && _props$labelledBy2 !== void 0 ? _props$labelledBy2 : layoutTitleId,
483
+ countTitle: setLayoutTitles
484
+ },
485
+ children: props.children
486
+ })
470
487
  })
471
488
  });
472
489
  return native ? /* @__PURE__ */ jsx("dialog", {
@@ -494,9 +511,9 @@ var defaultButtons = [{
494
511
  onClick: () => null
495
512
  }];
496
513
  function CustomUIHost({ render, args }) {
497
- return /* @__PURE__ */ jsx(Fragment, { children: render(args) });
514
+ return /* @__PURE__ */ jsx(Fragment$1, { children: render(args) });
498
515
  }
499
- function LegacyContent({ props, titleId, close, id }) {
516
+ function LegacyContent({ props, titleId, close, id, skin }) {
500
517
  var _props$buttons;
501
518
  const latest = useRef({
502
519
  props,
@@ -510,7 +527,12 @@ function LegacyContent({ props, titleId, close, id }) {
510
527
  const keyboard = (event) => {
511
528
  var _topmost, _props$keyCodeForClos, _props$closeOnEscape, _props$onkeyPress;
512
529
  if (id && ((_topmost = topmost(store.get())) === null || _topmost === void 0 ? void 0 : _topmost.id) !== id) return;
513
- const { props, close } = latest.current;
530
+ if (id && runtime().busy.has(id)) return;
531
+ const { props } = latest.current;
532
+ const close = () => {
533
+ event.preventDefault();
534
+ latest.current.close();
535
+ };
514
536
  const keyCode = event.keyCode;
515
537
  if (((_props$keyCodeForClos = props.keyCodeForClose) !== null && _props$keyCodeForClos !== void 0 ? _props$keyCodeForClos : []).includes(keyCode)) close();
516
538
  if (((_props$closeOnEscape = props.closeOnEscape) !== null && _props$closeOnEscape !== void 0 ? _props$closeOnEscape : true) && keyCode === 27) {
@@ -524,17 +546,30 @@ function LegacyContent({ props, titleId, close, id }) {
524
546
  return () => document.removeEventListener("keydown", keyboard, false);
525
547
  }, []);
526
548
  const slot = useSlot();
549
+ const frame = useContext(FrameContext);
527
550
  const { title, message, customUI, childrenElement } = props;
528
551
  const buttons = (_props$buttons = props.buttons) !== null && _props$buttons !== void 0 ? _props$buttons : defaultButtons;
529
- if (customUI) return /* @__PURE__ */ jsx(CustomUIHost, {
530
- render: customUI,
531
- args: {
532
- title,
533
- message,
534
- buttons,
535
- onClose: close
536
- }
537
- });
552
+ if (customUI) {
553
+ const ui = /* @__PURE__ */ jsx(CustomUIHost, {
554
+ render: customUI,
555
+ args: {
556
+ title,
557
+ message,
558
+ buttons,
559
+ onClose: close
560
+ }
561
+ });
562
+ return skin ? /* @__PURE__ */ jsx("div", {
563
+ ...slot("body"),
564
+ children: /* @__PURE__ */ jsx(FrameContext.Provider, {
565
+ value: {
566
+ ...frame,
567
+ inBody: true
568
+ },
569
+ children: ui
570
+ })
571
+ }) : ui;
572
+ }
538
573
  return /* @__PURE__ */ jsxs("div", {
539
574
  ...slot("body"),
540
575
  children: [
@@ -561,19 +596,21 @@ function LegacyContent({ props, titleId, close, id }) {
561
596
  });
562
597
  }
563
598
  function confirmAlert(properties) {
564
- var _ref, _properties$modal, _runtime$defaults$con, _properties$closeOnCl, _properties$closeOnEs, _properties$targetId, _ref2, _properties$strategy, _runtime$defaults$con2;
599
+ var _ref, _properties$skin, _runtime$defaults$con, _ref2, _properties$modal, _runtime$defaults$con2, _properties$closeOnCl, _properties$closeOnEs, _properties$targetId, _ref3, _properties$strategy, _runtime$defaults$con3;
600
+ const skin = (_ref = (_properties$skin = properties.skin) !== null && _properties$skin !== void 0 ? _properties$skin : (_runtime$defaults$con = runtime().defaults.confirmAlert) === null || _runtime$defaults$con === void 0 ? void 0 : _runtime$defaults$con.skin) !== null && _ref !== void 0 ? _ref : false;
565
601
  function Content({ id, titleId, dismiss }) {
566
602
  return /* @__PURE__ */ jsx(LegacyContent, {
567
603
  props: properties,
568
604
  titleId,
569
605
  close: dismiss,
570
- id
606
+ id,
607
+ skin
571
608
  });
572
609
  }
573
610
  openEntry({
574
611
  kind: "custom",
575
612
  title: properties.title,
576
- modal: (_ref = (_properties$modal = properties.modal) !== null && _properties$modal !== void 0 ? _properties$modal : (_runtime$defaults$con = runtime().defaults.confirmAlert) === null || _runtime$defaults$con === void 0 ? void 0 : _runtime$defaults$con.modal) !== null && _ref !== void 0 ? _ref : false,
613
+ modal: (_ref2 = (_properties$modal = properties.modal) !== null && _properties$modal !== void 0 ? _properties$modal : (_runtime$defaults$con2 = runtime().defaults.confirmAlert) === null || _runtime$defaults$con2 === void 0 ? void 0 : _runtime$defaults$con2.modal) !== null && _ref2 !== void 0 ? _ref2 : false,
577
614
  unstyled: properties.unstyled,
578
615
  classNames: properties.classNames,
579
616
  closeOnClickOutside: (_properties$closeOnCl = properties.closeOnClickOutside) !== null && _properties$closeOnCl !== void 0 ? _properties$closeOnCl : true,
@@ -581,18 +618,20 @@ function confirmAlert(properties) {
581
618
  overlayClassName: properties.overlayClassName,
582
619
  onClickOutside: properties.onClickOutside,
583
620
  legacy: true,
621
+ skin,
584
622
  willUnmount: properties.willUnmount,
585
623
  afterClose: properties.afterClose,
586
624
  render: Content
587
- }, (_properties$targetId = properties.targetId) !== null && _properties$targetId !== void 0 ? _properties$targetId : null, (_ref2 = (_properties$strategy = properties.strategy) !== null && _properties$strategy !== void 0 ? _properties$strategy : (_runtime$defaults$con2 = runtime().defaults.confirmAlert) === null || _runtime$defaults$con2 === void 0 ? void 0 : _runtime$defaults$con2.strategy) !== null && _ref2 !== void 0 ? _ref2 : "replace");
625
+ }, (_properties$targetId = properties.targetId) !== null && _properties$targetId !== void 0 ? _properties$targetId : null, (_ref3 = (_properties$strategy = properties.strategy) !== null && _properties$strategy !== void 0 ? _properties$strategy : (_runtime$defaults$con3 = runtime().defaults.confirmAlert) === null || _runtime$defaults$con3 === void 0 ? void 0 : _runtime$defaults$con3.strategy) !== null && _ref3 !== void 0 ? _ref3 : "replace");
588
626
  }
589
627
  function DeclarativeDialog(props) {
590
- var _props$modal, _props$closeOnClickOu, _props$closeOnEscape2;
628
+ var _ref4, _props$skin, _runtime$defaults$con4, _props$modal, _props$closeOnClickOu, _props$closeOnEscape2;
591
629
  const titleId = useId();
592
630
  const close = () => {
593
631
  var _props$afterClose;
594
632
  return (_props$afterClose = props.afterClose) === null || _props$afterClose === void 0 ? void 0 : _props$afterClose.call(props);
595
633
  };
634
+ const skin = (_ref4 = (_props$skin = props.skin) !== null && _props$skin !== void 0 ? _props$skin : (_runtime$defaults$con4 = runtime().defaults.confirmAlert) === null || _runtime$defaults$con4 === void 0 ? void 0 : _runtime$defaults$con4.skin) !== null && _ref4 !== void 0 ? _ref4 : false;
596
635
  return /* @__PURE__ */ jsx(DialogFrame, {
597
636
  modal: (_props$modal = props.modal) !== null && _props$modal !== void 0 ? _props$modal : false,
598
637
  state: "open",
@@ -606,13 +645,15 @@ function DeclarativeDialog(props) {
606
645
  closeOnClickOutside: (_props$closeOnClickOu = props.closeOnClickOutside) !== null && _props$closeOnClickOu !== void 0 ? _props$closeOnClickOu : true,
607
646
  closeOnEscape: (_props$closeOnEscape2 = props.closeOnEscape) !== null && _props$closeOnEscape2 !== void 0 ? _props$closeOnEscape2 : true,
608
647
  legacy: true,
648
+ skin,
609
649
  role: "dialog",
610
650
  onClickOutside: props.onClickOutside,
611
651
  onDismiss: close,
612
652
  children: /* @__PURE__ */ jsx(LegacyContent, {
613
653
  props,
614
654
  titleId,
615
- close
655
+ close,
656
+ skin
616
657
  })
617
658
  });
618
659
  }
@@ -648,7 +689,7 @@ function open(options, dismissValue, body) {
648
689
  (_options$signal = options.signal) === null || _options$signal === void 0 || _options$signal.removeEventListener("abort", onAbort);
649
690
  resolve(value === void 0 ? dismissValue : value);
650
691
  };
651
- const render = (props) => /* @__PURE__ */ jsx(Fragment, { children: body(props, controller.signal) });
692
+ const render = (props) => /* @__PURE__ */ jsx(Fragment$1, { children: body(props, controller.signal) });
652
693
  const onAbort = () => closeDialog$1(id);
653
694
  const id = openEntry({
654
695
  ...options,
@@ -663,6 +704,7 @@ function open(options, dismissValue, body) {
663
704
  }
664
705
  function Body({ props, children }) {
665
706
  const slot = useSlot();
707
+ const frame = useContext(FrameContext);
666
708
  return /* @__PURE__ */ jsxs("div", {
667
709
  ...slot("body"),
668
710
  children: [
@@ -671,22 +713,42 @@ function Body({ props, children }) {
671
713
  ...slot("title"),
672
714
  children: props.title
673
715
  }),
674
- props.message != null && /* @__PURE__ */ jsx("p", {
716
+ props.message != null && (typeof props.message === "object" ? /* @__PURE__ */ jsx("div", {
717
+ id: props.descriptionId,
718
+ ...slot("message"),
719
+ children: props.message
720
+ }) : /* @__PURE__ */ jsx("p", {
675
721
  id: props.descriptionId,
676
722
  ...slot("message"),
677
723
  children: props.message
724
+ })),
725
+ props.content != null && /* @__PURE__ */ jsx(FrameContext.Provider, {
726
+ value: {
727
+ ...frame,
728
+ inBody: true
729
+ },
730
+ children: props.content
678
731
  }),
679
732
  children
680
733
  ]
681
734
  });
682
735
  }
683
- function Buttons({ props, signal: getSignal, focus }) {
684
- const slot = useSlot();
736
+ function useBusy(id) {
685
737
  const [busy, setBusy] = useState(null);
686
- const markBusy = (on) => {
687
- var _runtime$frames$get;
688
- return (_runtime$frames$get = runtime().frames.get(props.id)) === null || _runtime$frames$get === void 0 ? void 0 : _runtime$frames$get.frame.toggleAttribute("data-busy", on);
738
+ const mark = (index) => {
739
+ if (id) {
740
+ var _runtime$frames$get;
741
+ (_runtime$frames$get = runtime().frames.get(id)) === null || _runtime$frames$get === void 0 || _runtime$frames$get.frame.toggleAttribute("data-busy", index !== null);
742
+ if (index === null) runtime().busy.delete(id);
743
+ else runtime().busy.add(id);
744
+ }
745
+ setBusy(index);
689
746
  };
747
+ return [busy, mark];
748
+ }
749
+ function Buttons({ props, signal: getSignal, focus }) {
750
+ const slot = useSlot();
751
+ const [busy, setBusy] = useBusy(props.id);
690
752
  const press = async (button, index) => {
691
753
  const signal = getSignal();
692
754
  let waiting = false;
@@ -703,16 +765,11 @@ function Buttons({ props, signal: getSignal, focus }) {
703
765
  }
704
766
  waiting = true;
705
767
  setBusy(index);
706
- markBusy(true);
707
- runtime().busy.add(props.id);
708
768
  if (await outcome !== false && !signal.aborted) props.close(button.value);
709
769
  } catch (error) {
710
770
  console.error(error);
711
771
  } finally {
712
- if (!waiting) return;
713
- runtime().busy.delete(props.id);
714
- markBusy(false);
715
- if (!signal.aborted) setBusy(null);
772
+ if (waiting) setBusy(null);
716
773
  }
717
774
  };
718
775
  return /* @__PURE__ */ jsx("div", {
@@ -984,6 +1041,7 @@ function ConfirmDialog(props) {
984
1041
  descriptionId,
985
1042
  title: props.title,
986
1043
  message: props.message,
1044
+ content: props.content,
987
1045
  dismiss: hide,
988
1046
  onClose: hide
989
1047
  };
@@ -1055,4 +1113,53 @@ function ConfirmDialog(props) {
1055
1113
  children: body
1056
1114
  });
1057
1115
  }
1058
- export { ConfirmDialog, Dialog, DialogHost, alert, closeAll, closeDialog, configureDialogs, confirm, confirmAlert, ReactConfirmAlert as default, openDialog, prompt, useDialog };
1116
+ // src/DialogLayout.tsx
1117
+ /** The default dialog layout for your own content: title, message, your children, then the actions. */
1118
+ function DialogLayout({ title, message, actions, children }) {
1119
+ const slot = useSlot();
1120
+ const frame = useContext(FrameContext);
1121
+ const [busy, setBusy] = useBusy(frame.id);
1122
+ const hasTitle = title != null;
1123
+ const { countTitle } = frame;
1124
+ useLayoutEffect(() => {
1125
+ if (!hasTitle || !countTitle) return;
1126
+ countTitle((n) => n + 1);
1127
+ return () => countTitle((n) => n - 1);
1128
+ }, [hasTitle, countTitle]);
1129
+ const Body = frame.inBody ? Fragment : "div";
1130
+ return /* @__PURE__ */ jsxs(Body, {
1131
+ ...frame.inBody ? {} : slot("body"),
1132
+ children: [
1133
+ title != null && /* @__PURE__ */ jsx("h1", {
1134
+ id: frame.titleId,
1135
+ ...slot("title"),
1136
+ children: title
1137
+ }),
1138
+ message != null && /* @__PURE__ */ jsx("div", {
1139
+ ...slot("message"),
1140
+ children: message
1141
+ }),
1142
+ children,
1143
+ actions && actions.length > 0 && /* @__PURE__ */ jsx("div", {
1144
+ ...slot("actions"),
1145
+ children: actions.map(({ label, variant = "secondary", onClick, disabled, className, ...rest }, i) => /* @__PURE__ */ jsx("button", {
1146
+ type: "button",
1147
+ ...rest,
1148
+ ...slot("button", className),
1149
+ "data-variant": variant,
1150
+ "data-busy": busy === i ? "" : void 0,
1151
+ "aria-busy": busy === i || void 0,
1152
+ disabled: disabled || busy !== null,
1153
+ onClick: (event) => {
1154
+ const outcome = onClick === null || onClick === void 0 ? void 0 : onClick(event);
1155
+ if (!(outcome instanceof Promise)) return;
1156
+ setBusy(i);
1157
+ outcome.catch((error) => console.error(error)).finally(() => setBusy(null));
1158
+ },
1159
+ children: label
1160
+ }, i))
1161
+ })
1162
+ ]
1163
+ });
1164
+ }
1165
+ export { ConfirmDialog, Dialog, DialogHost, DialogLayout, alert, closeAll, closeDialog, configureDialogs, confirm, confirmAlert, ReactConfirmAlert as default, openDialog, prompt, useDialog };