react-confirm-alert 3.2.0 → 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 +42 -7
- package/dist/index.esm.js +137 -38
- package/lib/index.js +132 -32
- package/package.json +2 -1
- package/src/react-confirm-alert.css +64 -20
- package/style.css +64 -20
- package/typings/DialogFrame.d.ts +2 -0
- package/typings/DialogLayout.d.ts +4 -0
- package/typings/api.d.ts +1 -0
- package/typings/classNames.d.ts +7 -1
- package/typings/compat.d.ts +2 -0
- package/typings/index.d.ts +2 -1
- package/typings/types.d.ts +19 -1
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/
|
|
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/
|
|
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/
|
|
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,
|
|
64
|
-
| `import { confirm }` |
|
|
65
|
-
| CSS |
|
|
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,
|
|
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,
|
|
@@ -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;
|
|
@@ -451,12 +457,12 @@ function DialogFrame(props) {
|
|
|
451
457
|
...slot("dialog", props.extraClass),
|
|
452
458
|
role: props.role,
|
|
453
459
|
"aria-label": props.label,
|
|
454
|
-
"aria-labelledby":
|
|
460
|
+
"aria-labelledby": labelledBy,
|
|
455
461
|
"aria-describedby": props.describedBy,
|
|
456
462
|
"data-rca": "",
|
|
457
463
|
"data-state": props.state,
|
|
458
464
|
"data-kind": props.kind,
|
|
459
|
-
"data-skin": props.legacy ? void 0 : "",
|
|
465
|
+
"data-skin": props.legacy && !props.skin ? void 0 : "",
|
|
460
466
|
"data-tone": (_props$tone = props.tone) !== null && _props$tone !== void 0 ? _props$tone : "default",
|
|
461
467
|
"data-depth": String((_props$depth = props.depth) !== null && _props$depth !== void 0 ? _props$depth : 0),
|
|
462
468
|
"data-topmost": ((_props$topmost = props.topmost) !== null && _props$topmost !== void 0 ? _props$topmost : true) ? "" : void 0,
|
|
@@ -470,7 +476,14 @@ function DialogFrame(props) {
|
|
|
470
476
|
tabIndex: -1,
|
|
471
477
|
children: /* @__PURE__ */ jsx(SlotContext.Provider, {
|
|
472
478
|
value: slot,
|
|
473
|
-
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
|
+
})
|
|
474
487
|
})
|
|
475
488
|
});
|
|
476
489
|
return native ? /* @__PURE__ */ jsx("dialog", {
|
|
@@ -498,9 +511,9 @@ var defaultButtons = [{
|
|
|
498
511
|
onClick: () => null
|
|
499
512
|
}];
|
|
500
513
|
function CustomUIHost({ render, args }) {
|
|
501
|
-
return /* @__PURE__ */ jsx(Fragment, { children: render(args) });
|
|
514
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: render(args) });
|
|
502
515
|
}
|
|
503
|
-
function LegacyContent({ props, titleId, close, id }) {
|
|
516
|
+
function LegacyContent({ props, titleId, close, id, skin }) {
|
|
504
517
|
var _props$buttons;
|
|
505
518
|
const latest = useRef({
|
|
506
519
|
props,
|
|
@@ -514,6 +527,7 @@ function LegacyContent({ props, titleId, close, id }) {
|
|
|
514
527
|
const keyboard = (event) => {
|
|
515
528
|
var _topmost, _props$keyCodeForClos, _props$closeOnEscape, _props$onkeyPress;
|
|
516
529
|
if (id && ((_topmost = topmost(store.get())) === null || _topmost === void 0 ? void 0 : _topmost.id) !== id) return;
|
|
530
|
+
if (id && runtime().busy.has(id)) return;
|
|
517
531
|
const { props } = latest.current;
|
|
518
532
|
const close = () => {
|
|
519
533
|
event.preventDefault();
|
|
@@ -532,17 +546,30 @@ function LegacyContent({ props, titleId, close, id }) {
|
|
|
532
546
|
return () => document.removeEventListener("keydown", keyboard, false);
|
|
533
547
|
}, []);
|
|
534
548
|
const slot = useSlot();
|
|
549
|
+
const frame = useContext(FrameContext);
|
|
535
550
|
const { title, message, customUI, childrenElement } = props;
|
|
536
551
|
const buttons = (_props$buttons = props.buttons) !== null && _props$buttons !== void 0 ? _props$buttons : defaultButtons;
|
|
537
|
-
if (customUI)
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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
|
+
}
|
|
546
573
|
return /* @__PURE__ */ jsxs("div", {
|
|
547
574
|
...slot("body"),
|
|
548
575
|
children: [
|
|
@@ -569,19 +596,21 @@ function LegacyContent({ props, titleId, close, id }) {
|
|
|
569
596
|
});
|
|
570
597
|
}
|
|
571
598
|
function confirmAlert(properties) {
|
|
572
|
-
var _ref, _properties$
|
|
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;
|
|
573
601
|
function Content({ id, titleId, dismiss }) {
|
|
574
602
|
return /* @__PURE__ */ jsx(LegacyContent, {
|
|
575
603
|
props: properties,
|
|
576
604
|
titleId,
|
|
577
605
|
close: dismiss,
|
|
578
|
-
id
|
|
606
|
+
id,
|
|
607
|
+
skin
|
|
579
608
|
});
|
|
580
609
|
}
|
|
581
610
|
openEntry({
|
|
582
611
|
kind: "custom",
|
|
583
612
|
title: properties.title,
|
|
584
|
-
modal: (
|
|
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,
|
|
585
614
|
unstyled: properties.unstyled,
|
|
586
615
|
classNames: properties.classNames,
|
|
587
616
|
closeOnClickOutside: (_properties$closeOnCl = properties.closeOnClickOutside) !== null && _properties$closeOnCl !== void 0 ? _properties$closeOnCl : true,
|
|
@@ -589,18 +618,20 @@ function confirmAlert(properties) {
|
|
|
589
618
|
overlayClassName: properties.overlayClassName,
|
|
590
619
|
onClickOutside: properties.onClickOutside,
|
|
591
620
|
legacy: true,
|
|
621
|
+
skin,
|
|
592
622
|
willUnmount: properties.willUnmount,
|
|
593
623
|
afterClose: properties.afterClose,
|
|
594
624
|
render: Content
|
|
595
|
-
}, (_properties$targetId = properties.targetId) !== null && _properties$targetId !== void 0 ? _properties$targetId : null, (
|
|
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");
|
|
596
626
|
}
|
|
597
627
|
function DeclarativeDialog(props) {
|
|
598
|
-
var _props$modal, _props$closeOnClickOu, _props$closeOnEscape2;
|
|
628
|
+
var _ref4, _props$skin, _runtime$defaults$con4, _props$modal, _props$closeOnClickOu, _props$closeOnEscape2;
|
|
599
629
|
const titleId = useId();
|
|
600
630
|
const close = () => {
|
|
601
631
|
var _props$afterClose;
|
|
602
632
|
return (_props$afterClose = props.afterClose) === null || _props$afterClose === void 0 ? void 0 : _props$afterClose.call(props);
|
|
603
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;
|
|
604
635
|
return /* @__PURE__ */ jsx(DialogFrame, {
|
|
605
636
|
modal: (_props$modal = props.modal) !== null && _props$modal !== void 0 ? _props$modal : false,
|
|
606
637
|
state: "open",
|
|
@@ -614,13 +645,15 @@ function DeclarativeDialog(props) {
|
|
|
614
645
|
closeOnClickOutside: (_props$closeOnClickOu = props.closeOnClickOutside) !== null && _props$closeOnClickOu !== void 0 ? _props$closeOnClickOu : true,
|
|
615
646
|
closeOnEscape: (_props$closeOnEscape2 = props.closeOnEscape) !== null && _props$closeOnEscape2 !== void 0 ? _props$closeOnEscape2 : true,
|
|
616
647
|
legacy: true,
|
|
648
|
+
skin,
|
|
617
649
|
role: "dialog",
|
|
618
650
|
onClickOutside: props.onClickOutside,
|
|
619
651
|
onDismiss: close,
|
|
620
652
|
children: /* @__PURE__ */ jsx(LegacyContent, {
|
|
621
653
|
props,
|
|
622
654
|
titleId,
|
|
623
|
-
close
|
|
655
|
+
close,
|
|
656
|
+
skin
|
|
624
657
|
})
|
|
625
658
|
});
|
|
626
659
|
}
|
|
@@ -656,7 +689,7 @@ function open(options, dismissValue, body) {
|
|
|
656
689
|
(_options$signal = options.signal) === null || _options$signal === void 0 || _options$signal.removeEventListener("abort", onAbort);
|
|
657
690
|
resolve(value === void 0 ? dismissValue : value);
|
|
658
691
|
};
|
|
659
|
-
const render = (props) => /* @__PURE__ */ jsx(Fragment, { children: body(props, controller.signal) });
|
|
692
|
+
const render = (props) => /* @__PURE__ */ jsx(Fragment$1, { children: body(props, controller.signal) });
|
|
660
693
|
const onAbort = () => closeDialog$1(id);
|
|
661
694
|
const id = openEntry({
|
|
662
695
|
...options,
|
|
@@ -671,6 +704,7 @@ function open(options, dismissValue, body) {
|
|
|
671
704
|
}
|
|
672
705
|
function Body({ props, children }) {
|
|
673
706
|
const slot = useSlot();
|
|
707
|
+
const frame = useContext(FrameContext);
|
|
674
708
|
return /* @__PURE__ */ jsxs("div", {
|
|
675
709
|
...slot("body"),
|
|
676
710
|
children: [
|
|
@@ -679,22 +713,42 @@ function Body({ props, children }) {
|
|
|
679
713
|
...slot("title"),
|
|
680
714
|
children: props.title
|
|
681
715
|
}),
|
|
682
|
-
props.message != null && /* @__PURE__ */ jsx("
|
|
716
|
+
props.message != null && (typeof props.message === "object" ? /* @__PURE__ */ jsx("div", {
|
|
683
717
|
id: props.descriptionId,
|
|
684
718
|
...slot("message"),
|
|
685
719
|
children: props.message
|
|
720
|
+
}) : /* @__PURE__ */ jsx("p", {
|
|
721
|
+
id: props.descriptionId,
|
|
722
|
+
...slot("message"),
|
|
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
|
|
686
731
|
}),
|
|
687
732
|
children
|
|
688
733
|
]
|
|
689
734
|
});
|
|
690
735
|
}
|
|
691
|
-
function
|
|
692
|
-
const slot = useSlot();
|
|
736
|
+
function useBusy(id) {
|
|
693
737
|
const [busy, setBusy] = useState(null);
|
|
694
|
-
const
|
|
695
|
-
|
|
696
|
-
|
|
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);
|
|
697
746
|
};
|
|
747
|
+
return [busy, mark];
|
|
748
|
+
}
|
|
749
|
+
function Buttons({ props, signal: getSignal, focus }) {
|
|
750
|
+
const slot = useSlot();
|
|
751
|
+
const [busy, setBusy] = useBusy(props.id);
|
|
698
752
|
const press = async (button, index) => {
|
|
699
753
|
const signal = getSignal();
|
|
700
754
|
let waiting = false;
|
|
@@ -711,16 +765,11 @@ function Buttons({ props, signal: getSignal, focus }) {
|
|
|
711
765
|
}
|
|
712
766
|
waiting = true;
|
|
713
767
|
setBusy(index);
|
|
714
|
-
markBusy(true);
|
|
715
|
-
runtime().busy.add(props.id);
|
|
716
768
|
if (await outcome !== false && !signal.aborted) props.close(button.value);
|
|
717
769
|
} catch (error) {
|
|
718
770
|
console.error(error);
|
|
719
771
|
} finally {
|
|
720
|
-
if (
|
|
721
|
-
runtime().busy.delete(props.id);
|
|
722
|
-
markBusy(false);
|
|
723
|
-
if (!signal.aborted) setBusy(null);
|
|
772
|
+
if (waiting) setBusy(null);
|
|
724
773
|
}
|
|
725
774
|
};
|
|
726
775
|
return /* @__PURE__ */ jsx("div", {
|
|
@@ -992,6 +1041,7 @@ function ConfirmDialog(props) {
|
|
|
992
1041
|
descriptionId,
|
|
993
1042
|
title: props.title,
|
|
994
1043
|
message: props.message,
|
|
1044
|
+
content: props.content,
|
|
995
1045
|
dismiss: hide,
|
|
996
1046
|
onClose: hide
|
|
997
1047
|
};
|
|
@@ -1063,4 +1113,53 @@ function ConfirmDialog(props) {
|
|
|
1063
1113
|
children: body
|
|
1064
1114
|
});
|
|
1065
1115
|
}
|
|
1066
|
-
|
|
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 };
|
package/lib/index.js
CHANGED
|
@@ -163,6 +163,7 @@ var slotsFor = (layers) => (slot, extra) => ({
|
|
|
163
163
|
});
|
|
164
164
|
var SlotContext = (0, react.createContext)(slotsFor([]));
|
|
165
165
|
var useSlot = () => (0, react.useContext)(SlotContext);
|
|
166
|
+
var FrameContext = (0, react.createContext)({});
|
|
166
167
|
function configureDialogs(defaults) {
|
|
167
168
|
runtime().defaults = defaults;
|
|
168
169
|
}
|
|
@@ -217,6 +218,7 @@ function EntryView({ entry, depth, topmost }) {
|
|
|
217
218
|
closeOnClickOutside: (_ref2 = (_options$closeOnClick = options.closeOnClickOutside) !== null && _options$closeOnClick !== void 0 ? _options$closeOnClick : defaults.closeOnClickOutside) !== null && _ref2 !== void 0 ? _ref2 : true,
|
|
218
219
|
closeOnEscape: (_ref3 = (_options$closeOnEscap = options.closeOnEscape) !== null && _options$closeOnEscap !== void 0 ? _options$closeOnEscap : defaults.closeOnEscape) !== null && _ref3 !== void 0 ? _ref3 : true,
|
|
219
220
|
legacy: !!options.legacy,
|
|
221
|
+
skin: options.skin,
|
|
220
222
|
onClickOutside: options.onClickOutside,
|
|
221
223
|
onDismiss: dismiss,
|
|
222
224
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Render, {
|
|
@@ -225,6 +227,7 @@ function EntryView({ entry, depth, topmost }) {
|
|
|
225
227
|
descriptionId,
|
|
226
228
|
title: options.title,
|
|
227
229
|
message: options.message,
|
|
230
|
+
content: options.content,
|
|
228
231
|
buttons: (_options$buttons = options.buttons) !== null && _options$buttons !== void 0 ? _options$buttons : [],
|
|
229
232
|
close,
|
|
230
233
|
dismiss,
|
|
@@ -378,12 +381,15 @@ function animationsOf({ frame, panel }) {
|
|
|
378
381
|
// src/DialogFrame.tsx
|
|
379
382
|
var canShowModal = () => typeof HTMLDialogElement !== "undefined" && typeof HTMLDialogElement.prototype.showModal === "function";
|
|
380
383
|
function DialogFrame(props) {
|
|
381
|
-
var _props$tone, _props$depth, _props$topmost;
|
|
384
|
+
var _props$labelledBy, _props$tone, _props$depth, _props$topmost, _props$labelledBy2;
|
|
382
385
|
const frame = (0, react.useRef)(null);
|
|
383
386
|
const panel = (0, react.useRef)(null);
|
|
384
387
|
const pressedOn = (0, react.useRef)(null);
|
|
385
388
|
const stay = (0, react.useRef)(null);
|
|
386
389
|
const native = props.modal && canShowModal();
|
|
390
|
+
const layoutTitleId = (0, react.useId)();
|
|
391
|
+
const [layoutTitles, setLayoutTitles] = (0, react.useState)(0);
|
|
392
|
+
const labelledBy = (_props$labelledBy = props.labelledBy) !== null && _props$labelledBy !== void 0 ? _props$labelledBy : props.label || layoutTitles === 0 ? void 0 : layoutTitleId;
|
|
387
393
|
(0, react.useLayoutEffect)(() => {
|
|
388
394
|
var _panel$current, _panel$current2, _panel$current3;
|
|
389
395
|
const element = frame.current;
|
|
@@ -455,12 +461,12 @@ function DialogFrame(props) {
|
|
|
455
461
|
...slot("dialog", props.extraClass),
|
|
456
462
|
role: props.role,
|
|
457
463
|
"aria-label": props.label,
|
|
458
|
-
"aria-labelledby":
|
|
464
|
+
"aria-labelledby": labelledBy,
|
|
459
465
|
"aria-describedby": props.describedBy,
|
|
460
466
|
"data-rca": "",
|
|
461
467
|
"data-state": props.state,
|
|
462
468
|
"data-kind": props.kind,
|
|
463
|
-
"data-skin": props.legacy ? void 0 : "",
|
|
469
|
+
"data-skin": props.legacy && !props.skin ? void 0 : "",
|
|
464
470
|
"data-tone": (_props$tone = props.tone) !== null && _props$tone !== void 0 ? _props$tone : "default",
|
|
465
471
|
"data-depth": String((_props$depth = props.depth) !== null && _props$depth !== void 0 ? _props$depth : 0),
|
|
466
472
|
"data-topmost": ((_props$topmost = props.topmost) !== null && _props$topmost !== void 0 ? _props$topmost : true) ? "" : void 0,
|
|
@@ -474,7 +480,14 @@ function DialogFrame(props) {
|
|
|
474
480
|
tabIndex: -1,
|
|
475
481
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SlotContext.Provider, {
|
|
476
482
|
value: slot,
|
|
477
|
-
children:
|
|
483
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FrameContext.Provider, {
|
|
484
|
+
value: {
|
|
485
|
+
id: props.id,
|
|
486
|
+
titleId: (_props$labelledBy2 = props.labelledBy) !== null && _props$labelledBy2 !== void 0 ? _props$labelledBy2 : layoutTitleId,
|
|
487
|
+
countTitle: setLayoutTitles
|
|
488
|
+
},
|
|
489
|
+
children: props.children
|
|
490
|
+
})
|
|
478
491
|
})
|
|
479
492
|
});
|
|
480
493
|
return native ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dialog", {
|
|
@@ -504,7 +517,7 @@ var defaultButtons = [{
|
|
|
504
517
|
function CustomUIHost({ render, args }) {
|
|
505
518
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: render(args) });
|
|
506
519
|
}
|
|
507
|
-
function LegacyContent({ props, titleId, close, id }) {
|
|
520
|
+
function LegacyContent({ props, titleId, close, id, skin }) {
|
|
508
521
|
var _props$buttons;
|
|
509
522
|
const latest = (0, react.useRef)({
|
|
510
523
|
props,
|
|
@@ -518,6 +531,7 @@ function LegacyContent({ props, titleId, close, id }) {
|
|
|
518
531
|
const keyboard = (event) => {
|
|
519
532
|
var _topmost, _props$keyCodeForClos, _props$closeOnEscape, _props$onkeyPress;
|
|
520
533
|
if (id && ((_topmost = topmost(store.get())) === null || _topmost === void 0 ? void 0 : _topmost.id) !== id) return;
|
|
534
|
+
if (id && runtime().busy.has(id)) return;
|
|
521
535
|
const { props } = latest.current;
|
|
522
536
|
const close = () => {
|
|
523
537
|
event.preventDefault();
|
|
@@ -536,17 +550,30 @@ function LegacyContent({ props, titleId, close, id }) {
|
|
|
536
550
|
return () => document.removeEventListener("keydown", keyboard, false);
|
|
537
551
|
}, []);
|
|
538
552
|
const slot = useSlot();
|
|
553
|
+
const frame = (0, react.useContext)(FrameContext);
|
|
539
554
|
const { title, message, customUI, childrenElement } = props;
|
|
540
555
|
const buttons = (_props$buttons = props.buttons) !== null && _props$buttons !== void 0 ? _props$buttons : defaultButtons;
|
|
541
|
-
if (customUI)
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
556
|
+
if (customUI) {
|
|
557
|
+
const ui = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CustomUIHost, {
|
|
558
|
+
render: customUI,
|
|
559
|
+
args: {
|
|
560
|
+
title,
|
|
561
|
+
message,
|
|
562
|
+
buttons,
|
|
563
|
+
onClose: close
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
return skin ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
567
|
+
...slot("body"),
|
|
568
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FrameContext.Provider, {
|
|
569
|
+
value: {
|
|
570
|
+
...frame,
|
|
571
|
+
inBody: true
|
|
572
|
+
},
|
|
573
|
+
children: ui
|
|
574
|
+
})
|
|
575
|
+
}) : ui;
|
|
576
|
+
}
|
|
550
577
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
551
578
|
...slot("body"),
|
|
552
579
|
children: [
|
|
@@ -573,19 +600,21 @@ function LegacyContent({ props, titleId, close, id }) {
|
|
|
573
600
|
});
|
|
574
601
|
}
|
|
575
602
|
function confirmAlert(properties) {
|
|
576
|
-
var _ref, _properties$
|
|
603
|
+
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;
|
|
604
|
+
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;
|
|
577
605
|
function Content({ id, titleId, dismiss }) {
|
|
578
606
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LegacyContent, {
|
|
579
607
|
props: properties,
|
|
580
608
|
titleId,
|
|
581
609
|
close: dismiss,
|
|
582
|
-
id
|
|
610
|
+
id,
|
|
611
|
+
skin
|
|
583
612
|
});
|
|
584
613
|
}
|
|
585
614
|
openEntry({
|
|
586
615
|
kind: "custom",
|
|
587
616
|
title: properties.title,
|
|
588
|
-
modal: (
|
|
617
|
+
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,
|
|
589
618
|
unstyled: properties.unstyled,
|
|
590
619
|
classNames: properties.classNames,
|
|
591
620
|
closeOnClickOutside: (_properties$closeOnCl = properties.closeOnClickOutside) !== null && _properties$closeOnCl !== void 0 ? _properties$closeOnCl : true,
|
|
@@ -593,18 +622,20 @@ function confirmAlert(properties) {
|
|
|
593
622
|
overlayClassName: properties.overlayClassName,
|
|
594
623
|
onClickOutside: properties.onClickOutside,
|
|
595
624
|
legacy: true,
|
|
625
|
+
skin,
|
|
596
626
|
willUnmount: properties.willUnmount,
|
|
597
627
|
afterClose: properties.afterClose,
|
|
598
628
|
render: Content
|
|
599
|
-
}, (_properties$targetId = properties.targetId) !== null && _properties$targetId !== void 0 ? _properties$targetId : null, (
|
|
629
|
+
}, (_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");
|
|
600
630
|
}
|
|
601
631
|
function DeclarativeDialog(props) {
|
|
602
|
-
var _props$modal, _props$closeOnClickOu, _props$closeOnEscape2;
|
|
632
|
+
var _ref4, _props$skin, _runtime$defaults$con4, _props$modal, _props$closeOnClickOu, _props$closeOnEscape2;
|
|
603
633
|
const titleId = (0, react.useId)();
|
|
604
634
|
const close = () => {
|
|
605
635
|
var _props$afterClose;
|
|
606
636
|
return (_props$afterClose = props.afterClose) === null || _props$afterClose === void 0 ? void 0 : _props$afterClose.call(props);
|
|
607
637
|
};
|
|
638
|
+
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;
|
|
608
639
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogFrame, {
|
|
609
640
|
modal: (_props$modal = props.modal) !== null && _props$modal !== void 0 ? _props$modal : false,
|
|
610
641
|
state: "open",
|
|
@@ -618,13 +649,15 @@ function DeclarativeDialog(props) {
|
|
|
618
649
|
closeOnClickOutside: (_props$closeOnClickOu = props.closeOnClickOutside) !== null && _props$closeOnClickOu !== void 0 ? _props$closeOnClickOu : true,
|
|
619
650
|
closeOnEscape: (_props$closeOnEscape2 = props.closeOnEscape) !== null && _props$closeOnEscape2 !== void 0 ? _props$closeOnEscape2 : true,
|
|
620
651
|
legacy: true,
|
|
652
|
+
skin,
|
|
621
653
|
role: "dialog",
|
|
622
654
|
onClickOutside: props.onClickOutside,
|
|
623
655
|
onDismiss: close,
|
|
624
656
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LegacyContent, {
|
|
625
657
|
props,
|
|
626
658
|
titleId,
|
|
627
|
-
close
|
|
659
|
+
close,
|
|
660
|
+
skin
|
|
628
661
|
})
|
|
629
662
|
});
|
|
630
663
|
}
|
|
@@ -675,6 +708,7 @@ function open(options, dismissValue, body) {
|
|
|
675
708
|
}
|
|
676
709
|
function Body({ props, children }) {
|
|
677
710
|
const slot = useSlot();
|
|
711
|
+
const frame = (0, react.useContext)(FrameContext);
|
|
678
712
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
679
713
|
...slot("body"),
|
|
680
714
|
children: [
|
|
@@ -683,22 +717,42 @@ function Body({ props, children }) {
|
|
|
683
717
|
...slot("title"),
|
|
684
718
|
children: props.title
|
|
685
719
|
}),
|
|
686
|
-
props.message != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
720
|
+
props.message != null && (typeof props.message === "object" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
687
721
|
id: props.descriptionId,
|
|
688
722
|
...slot("message"),
|
|
689
723
|
children: props.message
|
|
724
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
725
|
+
id: props.descriptionId,
|
|
726
|
+
...slot("message"),
|
|
727
|
+
children: props.message
|
|
728
|
+
})),
|
|
729
|
+
props.content != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FrameContext.Provider, {
|
|
730
|
+
value: {
|
|
731
|
+
...frame,
|
|
732
|
+
inBody: true
|
|
733
|
+
},
|
|
734
|
+
children: props.content
|
|
690
735
|
}),
|
|
691
736
|
children
|
|
692
737
|
]
|
|
693
738
|
});
|
|
694
739
|
}
|
|
695
|
-
function
|
|
696
|
-
const slot = useSlot();
|
|
740
|
+
function useBusy(id) {
|
|
697
741
|
const [busy, setBusy] = (0, react.useState)(null);
|
|
698
|
-
const
|
|
699
|
-
|
|
700
|
-
|
|
742
|
+
const mark = (index) => {
|
|
743
|
+
if (id) {
|
|
744
|
+
var _runtime$frames$get;
|
|
745
|
+
(_runtime$frames$get = runtime().frames.get(id)) === null || _runtime$frames$get === void 0 || _runtime$frames$get.frame.toggleAttribute("data-busy", index !== null);
|
|
746
|
+
if (index === null) runtime().busy.delete(id);
|
|
747
|
+
else runtime().busy.add(id);
|
|
748
|
+
}
|
|
749
|
+
setBusy(index);
|
|
701
750
|
};
|
|
751
|
+
return [busy, mark];
|
|
752
|
+
}
|
|
753
|
+
function Buttons({ props, signal: getSignal, focus }) {
|
|
754
|
+
const slot = useSlot();
|
|
755
|
+
const [busy, setBusy] = useBusy(props.id);
|
|
702
756
|
const press = async (button, index) => {
|
|
703
757
|
const signal = getSignal();
|
|
704
758
|
let waiting = false;
|
|
@@ -715,16 +769,11 @@ function Buttons({ props, signal: getSignal, focus }) {
|
|
|
715
769
|
}
|
|
716
770
|
waiting = true;
|
|
717
771
|
setBusy(index);
|
|
718
|
-
markBusy(true);
|
|
719
|
-
runtime().busy.add(props.id);
|
|
720
772
|
if (await outcome !== false && !signal.aborted) props.close(button.value);
|
|
721
773
|
} catch (error) {
|
|
722
774
|
console.error(error);
|
|
723
775
|
} finally {
|
|
724
|
-
if (
|
|
725
|
-
runtime().busy.delete(props.id);
|
|
726
|
-
markBusy(false);
|
|
727
|
-
if (!signal.aborted) setBusy(null);
|
|
776
|
+
if (waiting) setBusy(null);
|
|
728
777
|
}
|
|
729
778
|
};
|
|
730
779
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -996,6 +1045,7 @@ function ConfirmDialog(props) {
|
|
|
996
1045
|
descriptionId,
|
|
997
1046
|
title: props.title,
|
|
998
1047
|
message: props.message,
|
|
1048
|
+
content: props.content,
|
|
999
1049
|
dismiss: hide,
|
|
1000
1050
|
onClose: hide
|
|
1001
1051
|
};
|
|
@@ -1067,9 +1117,59 @@ function ConfirmDialog(props) {
|
|
|
1067
1117
|
children: body
|
|
1068
1118
|
});
|
|
1069
1119
|
}
|
|
1120
|
+
// src/DialogLayout.tsx
|
|
1121
|
+
/** The default dialog layout for your own content: title, message, your children, then the actions. */
|
|
1122
|
+
function DialogLayout({ title, message, actions, children }) {
|
|
1123
|
+
const slot = useSlot();
|
|
1124
|
+
const frame = (0, react.useContext)(FrameContext);
|
|
1125
|
+
const [busy, setBusy] = useBusy(frame.id);
|
|
1126
|
+
const hasTitle = title != null;
|
|
1127
|
+
const { countTitle } = frame;
|
|
1128
|
+
(0, react.useLayoutEffect)(() => {
|
|
1129
|
+
if (!hasTitle || !countTitle) return;
|
|
1130
|
+
countTitle((n) => n + 1);
|
|
1131
|
+
return () => countTitle((n) => n - 1);
|
|
1132
|
+
}, [hasTitle, countTitle]);
|
|
1133
|
+
const Body = frame.inBody ? react.Fragment : "div";
|
|
1134
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Body, {
|
|
1135
|
+
...frame.inBody ? {} : slot("body"),
|
|
1136
|
+
children: [
|
|
1137
|
+
title != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
|
|
1138
|
+
id: frame.titleId,
|
|
1139
|
+
...slot("title"),
|
|
1140
|
+
children: title
|
|
1141
|
+
}),
|
|
1142
|
+
message != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1143
|
+
...slot("message"),
|
|
1144
|
+
children: message
|
|
1145
|
+
}),
|
|
1146
|
+
children,
|
|
1147
|
+
actions && actions.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1148
|
+
...slot("actions"),
|
|
1149
|
+
children: actions.map(({ label, variant = "secondary", onClick, disabled, className, ...rest }, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1150
|
+
type: "button",
|
|
1151
|
+
...rest,
|
|
1152
|
+
...slot("button", className),
|
|
1153
|
+
"data-variant": variant,
|
|
1154
|
+
"data-busy": busy === i ? "" : void 0,
|
|
1155
|
+
"aria-busy": busy === i || void 0,
|
|
1156
|
+
disabled: disabled || busy !== null,
|
|
1157
|
+
onClick: (event) => {
|
|
1158
|
+
const outcome = onClick === null || onClick === void 0 ? void 0 : onClick(event);
|
|
1159
|
+
if (!(outcome instanceof Promise)) return;
|
|
1160
|
+
setBusy(i);
|
|
1161
|
+
outcome.catch((error) => console.error(error)).finally(() => setBusy(null));
|
|
1162
|
+
},
|
|
1163
|
+
children: label
|
|
1164
|
+
}, i))
|
|
1165
|
+
})
|
|
1166
|
+
]
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1070
1169
|
exports.ConfirmDialog = ConfirmDialog;
|
|
1071
1170
|
exports.Dialog = Dialog;
|
|
1072
1171
|
exports.DialogHost = DialogHost;
|
|
1172
|
+
exports.DialogLayout = DialogLayout;
|
|
1073
1173
|
exports.alert = alert;
|
|
1074
1174
|
exports.closeAll = closeAll;
|
|
1075
1175
|
exports.closeDialog = closeDialog;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-confirm-alert",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "react component confirm dialog.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"react": "^18.1.0",
|
|
62
62
|
"react-dom": "^18.1.0",
|
|
63
63
|
"size-limit": "^14.3.0",
|
|
64
|
+
"tailwindcss": "^4.3.3",
|
|
64
65
|
"typescript": "^7.0.2",
|
|
65
66
|
"vite": "^8.3.5",
|
|
66
67
|
"vitest": "^5.0.5"
|
|
@@ -158,6 +158,7 @@ dialog.react-confirm-alert-overlay::backdrop {
|
|
|
158
158
|
--_primary-fg: #18181b;
|
|
159
159
|
--_ring: #71717a;
|
|
160
160
|
--_error: #f87171;
|
|
161
|
+
color-scheme: dark;
|
|
161
162
|
background: var(--rca-backdrop, rgba(0, 0, 0, 0.7));
|
|
162
163
|
}
|
|
163
164
|
.react-confirm-alert-overlay[data-skin] .react-confirm-alert {
|
|
@@ -184,7 +185,10 @@ dialog.react-confirm-alert-overlay::backdrop {
|
|
|
184
185
|
.react-confirm-alert-overlay[data-skin][data-state=closing] .react-confirm-alert {
|
|
185
186
|
animation: rca-pop-out 150ms ease-in both;
|
|
186
187
|
}
|
|
187
|
-
.
|
|
188
|
+
/* Plain elements inside a skinned dialog look like the slots. Their selectors are type selectors under
|
|
189
|
+
:where(), so they beat a CSS reset such as Tailwind preflight and lose to any class you add. */
|
|
190
|
+
.react-confirm-alert-overlay[data-skin] [data-slot=title],
|
|
191
|
+
:where(.react-confirm-alert-overlay[data-skin]) div :is(h1, h2, h3) {
|
|
188
192
|
margin: 0 0 8px;
|
|
189
193
|
font-size: 18px;
|
|
190
194
|
font-weight: 600;
|
|
@@ -195,14 +199,17 @@ dialog.react-confirm-alert-overlay::backdrop {
|
|
|
195
199
|
.react-confirm-alert-overlay[data-skin] [data-slot=field] {
|
|
196
200
|
margin-top: 16px;
|
|
197
201
|
}
|
|
198
|
-
.react-confirm-alert-overlay[data-skin] [data-slot=label]
|
|
202
|
+
.react-confirm-alert-overlay[data-skin] [data-slot=label],
|
|
203
|
+
:where(.react-confirm-alert-overlay[data-skin]) div label {
|
|
199
204
|
display: block;
|
|
200
205
|
margin-bottom: 6px;
|
|
201
206
|
font-size: 13px;
|
|
202
207
|
font-weight: 500;
|
|
203
208
|
color: var(--rca-title, var(--_title));
|
|
204
209
|
}
|
|
205
|
-
.react-confirm-alert-overlay[data-skin] [data-slot=input]
|
|
210
|
+
.react-confirm-alert-overlay[data-skin] [data-slot=input],
|
|
211
|
+
:where(.react-confirm-alert-overlay[data-skin]) div :is(input:where(:not([type=checkbox], [type=radio], [type=range], [type=file], [type=color], [type=submit], [type=button], [type=reset], [type=image])), textarea, select) {
|
|
212
|
+
display: block;
|
|
206
213
|
box-sizing: border-box;
|
|
207
214
|
width: 100%;
|
|
208
215
|
height: 36px;
|
|
@@ -215,14 +222,35 @@ dialog.react-confirm-alert-overlay::backdrop {
|
|
|
215
222
|
outline: none;
|
|
216
223
|
transition: border-color 150ms, box-shadow 150ms;
|
|
217
224
|
}
|
|
218
|
-
.react-confirm-alert-overlay[data-skin] [data-slot=input]
|
|
225
|
+
.react-confirm-alert-overlay[data-skin] textarea[data-slot=input],
|
|
226
|
+
:where(.react-confirm-alert-overlay[data-skin]) div textarea {
|
|
227
|
+
height: auto;
|
|
228
|
+
min-height: 80px;
|
|
229
|
+
padding: 8px 12px;
|
|
230
|
+
resize: vertical;
|
|
231
|
+
}
|
|
232
|
+
.react-confirm-alert-overlay[data-skin] [data-slot=input]:focus,
|
|
233
|
+
:where(.react-confirm-alert-overlay[data-skin]) div :is(input, textarea, select):where(:focus) {
|
|
219
234
|
border-color: var(--rca-ring, var(--_ring));
|
|
220
235
|
box-shadow: 0 0 0 3px color-mix(in srgb, var(--rca-ring, var(--_ring)) 50%, transparent);
|
|
221
236
|
}
|
|
222
|
-
.react-confirm-alert-overlay[data-skin] [data-slot=input][aria-invalid=true]
|
|
237
|
+
.react-confirm-alert-overlay[data-skin] [data-slot=input][aria-invalid=true],
|
|
238
|
+
:where(.react-confirm-alert-overlay[data-skin]) div :is(input, textarea, select):where([aria-invalid=true]) {
|
|
223
239
|
border-color: var(--rca-danger, var(--_error));
|
|
224
240
|
box-shadow: 0 0 0 3px color-mix(in srgb, var(--rca-danger, var(--_error)) 20%, transparent);
|
|
225
241
|
}
|
|
242
|
+
:where(.react-confirm-alert-overlay[data-skin]) div p {
|
|
243
|
+
margin: 0;
|
|
244
|
+
}
|
|
245
|
+
:where(.react-confirm-alert-overlay[data-skin]) div input:where([type=checkbox], [type=radio]) {
|
|
246
|
+
accent-color: var(--rca-button-bg, var(--_primary));
|
|
247
|
+
}
|
|
248
|
+
:where(.react-confirm-alert-overlay[data-skin]) div * + :is(p, label, input, textarea, select) {
|
|
249
|
+
margin-top: 12px;
|
|
250
|
+
}
|
|
251
|
+
:where(.react-confirm-alert-overlay[data-skin]) div label + :is(input, textarea, select) {
|
|
252
|
+
margin-top: 0;
|
|
253
|
+
}
|
|
226
254
|
.react-confirm-alert-overlay[data-skin] [data-slot=error] {
|
|
227
255
|
margin: 6px 0 0;
|
|
228
256
|
font-size: 13px;
|
|
@@ -233,7 +261,8 @@ dialog.react-confirm-alert-overlay::backdrop {
|
|
|
233
261
|
gap: var(--rca-gap, 8px);
|
|
234
262
|
margin-top: 24px;
|
|
235
263
|
}
|
|
236
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button
|
|
264
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button,
|
|
265
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button {
|
|
237
266
|
display: inline-flex;
|
|
238
267
|
align-items: center;
|
|
239
268
|
justify-content: center;
|
|
@@ -252,38 +281,53 @@ dialog.react-confirm-alert-overlay::backdrop {
|
|
|
252
281
|
white-space: nowrap;
|
|
253
282
|
transition: background-color 150ms, transform 150ms;
|
|
254
283
|
}
|
|
255
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group >
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > [data-variant=primary] {
|
|
284
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > [data-variant=primary],
|
|
285
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where([data-variant=primary]) {
|
|
259
286
|
border-color: transparent;
|
|
260
287
|
background: var(--rca-button-bg, var(--_primary));
|
|
261
288
|
color: var(--rca-button-fg, var(--_primary-fg));
|
|
262
289
|
}
|
|
263
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > [data-variant=danger]
|
|
290
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > [data-variant=danger],
|
|
291
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where([data-variant=danger]) {
|
|
264
292
|
border-color: transparent;
|
|
265
293
|
background: var(--rca-danger, var(--_danger));
|
|
266
294
|
color: #fff;
|
|
267
295
|
}
|
|
268
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > :
|
|
269
|
-
|
|
296
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > :not([data-variant=primary],[data-variant=danger]):enabled:hover,
|
|
297
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where(:not([data-variant=primary],[data-variant=danger]):enabled:hover) {
|
|
298
|
+
background: var(--_hover);
|
|
299
|
+
}
|
|
300
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > [data-variant=primary]:enabled:hover,
|
|
301
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where([data-variant=primary]:enabled:hover) {
|
|
302
|
+
background: var(--rca-button-bg-hover, color-mix(in srgb, var(--rca-button-bg, var(--_primary)) 85%, #000));
|
|
270
303
|
}
|
|
271
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group >
|
|
304
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > [data-variant=danger]:enabled:hover,
|
|
305
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where([data-variant=danger]:enabled:hover) {
|
|
306
|
+
background: var(--rca-danger-hover, color-mix(in srgb, var(--rca-danger, var(--_danger)) 85%, #000));
|
|
307
|
+
}
|
|
308
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button:active,
|
|
309
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where(:active) {
|
|
272
310
|
transform: scale(0.97);
|
|
273
311
|
}
|
|
274
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button:focus-visible
|
|
312
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button:focus-visible,
|
|
313
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where(:focus-visible) {
|
|
275
314
|
outline: var(--rca-focus-ring, 2px solid var(--_ring));
|
|
276
315
|
outline-offset: 2px;
|
|
277
316
|
}
|
|
278
|
-
|
|
317
|
+
/* Not faded: white on a faded red is below 4.5:1. */
|
|
318
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button:disabled:not([aria-busy=true]),
|
|
319
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where(:disabled:not([aria-busy=true])) {
|
|
279
320
|
cursor: default;
|
|
280
|
-
|
|
321
|
+
border-color: var(--rca-border, var(--_border));
|
|
322
|
+
background: var(--rca-bg, var(--_bg));
|
|
323
|
+
color: var(--rca-fg, var(--_fg));
|
|
281
324
|
}
|
|
282
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button[aria-busy=true]
|
|
325
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button[aria-busy=true],
|
|
326
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where([aria-busy=true]) {
|
|
283
327
|
cursor: progress;
|
|
284
|
-
opacity: 1;
|
|
285
328
|
}
|
|
286
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button[aria-busy=true]::before
|
|
329
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button[aria-busy=true]::before,
|
|
330
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where([aria-busy=true])::before {
|
|
287
331
|
content: '';
|
|
288
332
|
width: 12px;
|
|
289
333
|
height: 12px;
|
package/style.css
CHANGED
|
@@ -158,6 +158,7 @@ dialog.react-confirm-alert-overlay::backdrop {
|
|
|
158
158
|
--_primary-fg: #18181b;
|
|
159
159
|
--_ring: #71717a;
|
|
160
160
|
--_error: #f87171;
|
|
161
|
+
color-scheme: dark;
|
|
161
162
|
background: var(--rca-backdrop, rgba(0, 0, 0, 0.7));
|
|
162
163
|
}
|
|
163
164
|
.react-confirm-alert-overlay[data-skin] .react-confirm-alert {
|
|
@@ -184,7 +185,10 @@ dialog.react-confirm-alert-overlay::backdrop {
|
|
|
184
185
|
.react-confirm-alert-overlay[data-skin][data-state=closing] .react-confirm-alert {
|
|
185
186
|
animation: rca-pop-out 150ms ease-in both;
|
|
186
187
|
}
|
|
187
|
-
.
|
|
188
|
+
/* Plain elements inside a skinned dialog look like the slots. Their selectors are type selectors under
|
|
189
|
+
:where(), so they beat a CSS reset such as Tailwind preflight and lose to any class you add. */
|
|
190
|
+
.react-confirm-alert-overlay[data-skin] [data-slot=title],
|
|
191
|
+
:where(.react-confirm-alert-overlay[data-skin]) div :is(h1, h2, h3) {
|
|
188
192
|
margin: 0 0 8px;
|
|
189
193
|
font-size: 18px;
|
|
190
194
|
font-weight: 600;
|
|
@@ -195,14 +199,17 @@ dialog.react-confirm-alert-overlay::backdrop {
|
|
|
195
199
|
.react-confirm-alert-overlay[data-skin] [data-slot=field] {
|
|
196
200
|
margin-top: 16px;
|
|
197
201
|
}
|
|
198
|
-
.react-confirm-alert-overlay[data-skin] [data-slot=label]
|
|
202
|
+
.react-confirm-alert-overlay[data-skin] [data-slot=label],
|
|
203
|
+
:where(.react-confirm-alert-overlay[data-skin]) div label {
|
|
199
204
|
display: block;
|
|
200
205
|
margin-bottom: 6px;
|
|
201
206
|
font-size: 13px;
|
|
202
207
|
font-weight: 500;
|
|
203
208
|
color: var(--rca-title, var(--_title));
|
|
204
209
|
}
|
|
205
|
-
.react-confirm-alert-overlay[data-skin] [data-slot=input]
|
|
210
|
+
.react-confirm-alert-overlay[data-skin] [data-slot=input],
|
|
211
|
+
:where(.react-confirm-alert-overlay[data-skin]) div :is(input:where(:not([type=checkbox], [type=radio], [type=range], [type=file], [type=color], [type=submit], [type=button], [type=reset], [type=image])), textarea, select) {
|
|
212
|
+
display: block;
|
|
206
213
|
box-sizing: border-box;
|
|
207
214
|
width: 100%;
|
|
208
215
|
height: 36px;
|
|
@@ -215,14 +222,35 @@ dialog.react-confirm-alert-overlay::backdrop {
|
|
|
215
222
|
outline: none;
|
|
216
223
|
transition: border-color 150ms, box-shadow 150ms;
|
|
217
224
|
}
|
|
218
|
-
.react-confirm-alert-overlay[data-skin] [data-slot=input]
|
|
225
|
+
.react-confirm-alert-overlay[data-skin] textarea[data-slot=input],
|
|
226
|
+
:where(.react-confirm-alert-overlay[data-skin]) div textarea {
|
|
227
|
+
height: auto;
|
|
228
|
+
min-height: 80px;
|
|
229
|
+
padding: 8px 12px;
|
|
230
|
+
resize: vertical;
|
|
231
|
+
}
|
|
232
|
+
.react-confirm-alert-overlay[data-skin] [data-slot=input]:focus,
|
|
233
|
+
:where(.react-confirm-alert-overlay[data-skin]) div :is(input, textarea, select):where(:focus) {
|
|
219
234
|
border-color: var(--rca-ring, var(--_ring));
|
|
220
235
|
box-shadow: 0 0 0 3px color-mix(in srgb, var(--rca-ring, var(--_ring)) 50%, transparent);
|
|
221
236
|
}
|
|
222
|
-
.react-confirm-alert-overlay[data-skin] [data-slot=input][aria-invalid=true]
|
|
237
|
+
.react-confirm-alert-overlay[data-skin] [data-slot=input][aria-invalid=true],
|
|
238
|
+
:where(.react-confirm-alert-overlay[data-skin]) div :is(input, textarea, select):where([aria-invalid=true]) {
|
|
223
239
|
border-color: var(--rca-danger, var(--_error));
|
|
224
240
|
box-shadow: 0 0 0 3px color-mix(in srgb, var(--rca-danger, var(--_error)) 20%, transparent);
|
|
225
241
|
}
|
|
242
|
+
:where(.react-confirm-alert-overlay[data-skin]) div p {
|
|
243
|
+
margin: 0;
|
|
244
|
+
}
|
|
245
|
+
:where(.react-confirm-alert-overlay[data-skin]) div input:where([type=checkbox], [type=radio]) {
|
|
246
|
+
accent-color: var(--rca-button-bg, var(--_primary));
|
|
247
|
+
}
|
|
248
|
+
:where(.react-confirm-alert-overlay[data-skin]) div * + :is(p, label, input, textarea, select) {
|
|
249
|
+
margin-top: 12px;
|
|
250
|
+
}
|
|
251
|
+
:where(.react-confirm-alert-overlay[data-skin]) div label + :is(input, textarea, select) {
|
|
252
|
+
margin-top: 0;
|
|
253
|
+
}
|
|
226
254
|
.react-confirm-alert-overlay[data-skin] [data-slot=error] {
|
|
227
255
|
margin: 6px 0 0;
|
|
228
256
|
font-size: 13px;
|
|
@@ -233,7 +261,8 @@ dialog.react-confirm-alert-overlay::backdrop {
|
|
|
233
261
|
gap: var(--rca-gap, 8px);
|
|
234
262
|
margin-top: 24px;
|
|
235
263
|
}
|
|
236
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button
|
|
264
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button,
|
|
265
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button {
|
|
237
266
|
display: inline-flex;
|
|
238
267
|
align-items: center;
|
|
239
268
|
justify-content: center;
|
|
@@ -252,38 +281,53 @@ dialog.react-confirm-alert-overlay::backdrop {
|
|
|
252
281
|
white-space: nowrap;
|
|
253
282
|
transition: background-color 150ms, transform 150ms;
|
|
254
283
|
}
|
|
255
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group >
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > [data-variant=primary] {
|
|
284
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > [data-variant=primary],
|
|
285
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where([data-variant=primary]) {
|
|
259
286
|
border-color: transparent;
|
|
260
287
|
background: var(--rca-button-bg, var(--_primary));
|
|
261
288
|
color: var(--rca-button-fg, var(--_primary-fg));
|
|
262
289
|
}
|
|
263
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > [data-variant=danger]
|
|
290
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > [data-variant=danger],
|
|
291
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where([data-variant=danger]) {
|
|
264
292
|
border-color: transparent;
|
|
265
293
|
background: var(--rca-danger, var(--_danger));
|
|
266
294
|
color: #fff;
|
|
267
295
|
}
|
|
268
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > :
|
|
269
|
-
|
|
296
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > :not([data-variant=primary],[data-variant=danger]):enabled:hover,
|
|
297
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where(:not([data-variant=primary],[data-variant=danger]):enabled:hover) {
|
|
298
|
+
background: var(--_hover);
|
|
299
|
+
}
|
|
300
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > [data-variant=primary]:enabled:hover,
|
|
301
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where([data-variant=primary]:enabled:hover) {
|
|
302
|
+
background: var(--rca-button-bg-hover, color-mix(in srgb, var(--rca-button-bg, var(--_primary)) 85%, #000));
|
|
270
303
|
}
|
|
271
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group >
|
|
304
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > [data-variant=danger]:enabled:hover,
|
|
305
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where([data-variant=danger]:enabled:hover) {
|
|
306
|
+
background: var(--rca-danger-hover, color-mix(in srgb, var(--rca-danger, var(--_danger)) 85%, #000));
|
|
307
|
+
}
|
|
308
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button:active,
|
|
309
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where(:active) {
|
|
272
310
|
transform: scale(0.97);
|
|
273
311
|
}
|
|
274
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button:focus-visible
|
|
312
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button:focus-visible,
|
|
313
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where(:focus-visible) {
|
|
275
314
|
outline: var(--rca-focus-ring, 2px solid var(--_ring));
|
|
276
315
|
outline-offset: 2px;
|
|
277
316
|
}
|
|
278
|
-
|
|
317
|
+
/* Not faded: white on a faded red is below 4.5:1. */
|
|
318
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button:disabled:not([aria-busy=true]),
|
|
319
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where(:disabled:not([aria-busy=true])) {
|
|
279
320
|
cursor: default;
|
|
280
|
-
|
|
321
|
+
border-color: var(--rca-border, var(--_border));
|
|
322
|
+
background: var(--rca-bg, var(--_bg));
|
|
323
|
+
color: var(--rca-fg, var(--_fg));
|
|
281
324
|
}
|
|
282
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button[aria-busy=true]
|
|
325
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button[aria-busy=true],
|
|
326
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where([aria-busy=true]) {
|
|
283
327
|
cursor: progress;
|
|
284
|
-
opacity: 1;
|
|
285
328
|
}
|
|
286
|
-
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button[aria-busy=true]::before
|
|
329
|
+
.react-confirm-alert-overlay[data-skin] .react-confirm-alert-button-group > button[aria-busy=true]::before,
|
|
330
|
+
:where(.react-confirm-alert-overlay[data-skin]) div button:where([aria-busy=true])::before {
|
|
287
331
|
content: '';
|
|
288
332
|
width: 12px;
|
|
289
333
|
height: 12px;
|
package/typings/DialogFrame.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ export interface DialogFrameProps {
|
|
|
20
20
|
closeOnEscape: boolean;
|
|
21
21
|
/** the v3 API reads Escape from document keydown and focuses the panel */
|
|
22
22
|
legacy: boolean;
|
|
23
|
+
/** confirmAlert opted into the new default look */
|
|
24
|
+
skin?: boolean | undefined;
|
|
23
25
|
onKeyDown?: ((event: KeyboardEvent<HTMLElement>, close: () => void) => void) | undefined;
|
|
24
26
|
onClickOutside?: (() => void) | undefined;
|
|
25
27
|
onDismiss: () => void;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type ReactElement } from 'react';
|
|
2
|
+
import type { DialogLayoutProps } from './types';
|
|
3
|
+
/** The default dialog layout for your own content: title, message, your children, then the actions. */
|
|
4
|
+
export declare function DialogLayout({ title, message, actions, children }: DialogLayoutProps): ReactElement;
|
package/typings/api.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export declare function Body({ props, children }: {
|
|
|
4
4
|
props: DialogRenderProps<any>;
|
|
5
5
|
children: ReactNode;
|
|
6
6
|
}): ReactElement;
|
|
7
|
+
export declare function useBusy(id: DialogId | undefined): [number | null, (index: number | null) => void];
|
|
7
8
|
export declare function Buttons<R>({ props, signal: getSignal, focus }: {
|
|
8
9
|
props: DialogRenderProps<R>;
|
|
9
10
|
signal: () => AbortSignal;
|
package/typings/classNames.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DialogClassNames, DialogDefaults, DialogSlot, StyleOptions } from './types';
|
|
1
|
+
import type { DialogClassNames, DialogDefaults, DialogId, DialogSlot, StyleOptions } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Style layers from widest to narrowest. confirmAlert keeps the v3 look unless the app sets
|
|
4
4
|
* confirmAlert defaults, so app-wide unstyled or classNames never reach it by surprise.
|
|
@@ -12,5 +12,11 @@ export type SlotProps = (slot: DialogSlot, extra?: string) => {
|
|
|
12
12
|
export declare const slotsFor: (layers: readonly StyleOptions[]) => SlotProps;
|
|
13
13
|
export declare const SlotContext: import("react").Context<SlotProps>;
|
|
14
14
|
export declare const useSlot: () => SlotProps;
|
|
15
|
+
export declare const FrameContext: import("react").Context<{
|
|
16
|
+
id?: DialogId | undefined;
|
|
17
|
+
titleId?: string | undefined;
|
|
18
|
+
inBody?: boolean;
|
|
19
|
+
countTitle?: (change: (count: number) => number) => void;
|
|
20
|
+
}>;
|
|
15
21
|
export declare function configureDialogs(defaults: DialogDefaults): void;
|
|
16
22
|
export type { DialogClassNames };
|
package/typings/compat.d.ts
CHANGED
|
@@ -35,6 +35,8 @@ export interface ReactConfirmAlertProps {
|
|
|
35
35
|
unstyled?: boolean;
|
|
36
36
|
/** classes per slot, added after the app defaults */
|
|
37
37
|
classNames?: DialogClassNames;
|
|
38
|
+
/** use the new default look instead of the 3.0 one; customUI then sits in the dialog body */
|
|
39
|
+
skin?: boolean;
|
|
38
40
|
}
|
|
39
41
|
export type ConfirmAlertOptions = ReactConfirmAlertProps;
|
|
40
42
|
export declare function confirmAlert(properties: ReactConfirmAlertProps): void;
|
package/typings/index.d.ts
CHANGED
|
@@ -5,5 +5,6 @@ export { DialogHost, useDialog } from './DialogHost';
|
|
|
5
5
|
export { configureDialogs } from './classNames';
|
|
6
6
|
export type { DialogHostProps } from './DialogHost';
|
|
7
7
|
export { ConfirmDialog, Dialog } from './Dialog';
|
|
8
|
+
export { DialogLayout } from './DialogLayout';
|
|
8
9
|
export type { ConfirmDialogProps, DialogProps } from './Dialog';
|
|
9
|
-
export type { AlertOptions, BaseOptions, ButtonContext, ButtonDataAttributes, DialogClassNames, DialogDataAttributes, DialogDefaults, DialogSlot, SlotDataAttributes, StyleOptions, ConfirmOptions, CustomOptions, DialogButton, DialogHandle, DialogId, DialogKind, DialogRenderProps, PromptOptions, Strategy } from './types';
|
|
10
|
+
export type { AlertOptions, BaseOptions, ButtonContext, ButtonDataAttributes, DialogAction, DialogClassNames, DialogLayoutProps, DialogDataAttributes, DialogDefaults, DialogSlot, SlotDataAttributes, StyleOptions, ConfirmOptions, CustomOptions, DialogButton, DialogHandle, DialogId, DialogKind, DialogRenderProps, PromptOptions, Strategy } from './types';
|
package/typings/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ButtonHTMLAttributes, ComponentType, InputHTMLAttributes, KeyboardEvent, ReactNode } from 'react';
|
|
1
|
+
import type { ButtonHTMLAttributes, ComponentType, InputHTMLAttributes, KeyboardEvent, MouseEvent, ReactNode } from 'react';
|
|
2
2
|
export type DialogId = string & {
|
|
3
3
|
readonly __brand: 'DialogId';
|
|
4
4
|
};
|
|
@@ -22,6 +22,7 @@ export interface DialogRenderProps<R> {
|
|
|
22
22
|
descriptionId: string;
|
|
23
23
|
title?: ReactNode | undefined;
|
|
24
24
|
message?: ReactNode | undefined;
|
|
25
|
+
content?: ReactNode | undefined;
|
|
25
26
|
buttons: readonly DialogButton<R>[];
|
|
26
27
|
close: (value: R) => void;
|
|
27
28
|
dismiss: () => void;
|
|
@@ -53,6 +54,7 @@ export interface DialogDefaults extends StyleOptions {
|
|
|
53
54
|
confirmAlert?: (StyleOptions & {
|
|
54
55
|
modal?: boolean | undefined;
|
|
55
56
|
strategy?: Strategy | undefined;
|
|
57
|
+
skin?: boolean | undefined;
|
|
56
58
|
}) | undefined;
|
|
57
59
|
}
|
|
58
60
|
/** Attributes on the frame, for CSS and Tailwind data-* variants. A type only, kept in step by tests. */
|
|
@@ -75,6 +77,8 @@ export interface ButtonDataAttributes extends SlotDataAttributes {
|
|
|
75
77
|
export interface BaseOptions extends StyleOptions {
|
|
76
78
|
title?: ReactNode | undefined;
|
|
77
79
|
message?: ReactNode | undefined;
|
|
80
|
+
/** your own content between the message and the buttons */
|
|
81
|
+
content?: ReactNode | undefined;
|
|
78
82
|
closeOnEscape?: boolean | undefined;
|
|
79
83
|
closeOnClickOutside?: boolean | undefined;
|
|
80
84
|
strategy?: Strategy | undefined;
|
|
@@ -124,6 +128,8 @@ export interface EntryOptions extends BaseOptions {
|
|
|
124
128
|
onClickOutside?: (() => void) | undefined;
|
|
125
129
|
/** @internal the v3 API: Escape from document keydown, blur on body.children[0] */
|
|
126
130
|
legacy?: boolean | undefined;
|
|
131
|
+
/** @internal confirmAlert opted into the new default look */
|
|
132
|
+
skin?: boolean | undefined;
|
|
127
133
|
/** @internal confirm's tone, shown as data-tone on the frame */
|
|
128
134
|
tone?: 'default' | 'danger' | undefined;
|
|
129
135
|
/** @internal v3 callback, runs before afterClose when the dialog unmounts */
|
|
@@ -136,3 +142,15 @@ export interface DialogHandle<R> {
|
|
|
136
142
|
dismiss: () => void;
|
|
137
143
|
update: (patch: Partial<Omit<CustomOptions<R>, 'kind'>>) => void;
|
|
138
144
|
}
|
|
145
|
+
export interface DialogAction extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'children'> {
|
|
146
|
+
label: ReactNode;
|
|
147
|
+
variant?: 'primary' | 'secondary' | 'danger' | undefined;
|
|
148
|
+
/** return a Promise to show the button as busy until it settles; the dialog stays open */
|
|
149
|
+
onClick?: ((event: MouseEvent<HTMLButtonElement>) => void | Promise<unknown>) | undefined;
|
|
150
|
+
}
|
|
151
|
+
export interface DialogLayoutProps {
|
|
152
|
+
title?: ReactNode | undefined;
|
|
153
|
+
message?: ReactNode | undefined;
|
|
154
|
+
actions?: readonly DialogAction[] | undefined;
|
|
155
|
+
children?: ReactNode | undefined;
|
|
156
|
+
}
|