@arun-dev/headless 4.1.0 → 4.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/dist/dialog/index.cjs +333 -0
- package/dist/dialog/index.d.cts +117 -0
- package/dist/dialog/index.d.ts +117 -0
- package/dist/dialog/index.js +192 -0
- package/dist/radio-group/index.cjs +298 -0
- package/dist/radio-group/index.d.cts +128 -0
- package/dist/radio-group/index.d.ts +128 -0
- package/dist/radio-group/index.js +157 -0
- package/package.json +25 -2
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/dialog/index.ts
|
|
21
|
+
var dialog_exports = {};
|
|
22
|
+
__export(dialog_exports, {
|
|
23
|
+
Dialog: () => index_parts_exports
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(dialog_exports);
|
|
26
|
+
|
|
27
|
+
// src/dialog/index.parts.ts
|
|
28
|
+
var index_parts_exports = {};
|
|
29
|
+
__export(index_parts_exports, {
|
|
30
|
+
Close: () => DialogClose,
|
|
31
|
+
Popup: () => DialogPopup,
|
|
32
|
+
Root: () => DialogRoot,
|
|
33
|
+
Title: () => DialogTitle,
|
|
34
|
+
Trigger: () => DialogTrigger
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// src/dialog/DialogRoot.tsx
|
|
38
|
+
var import_react3 = require("react");
|
|
39
|
+
|
|
40
|
+
// src/core/useControlled.ts
|
|
41
|
+
var import_react = require("react");
|
|
42
|
+
function useControlled({
|
|
43
|
+
controlled,
|
|
44
|
+
default: defaultValue,
|
|
45
|
+
name = "Component",
|
|
46
|
+
state = "value"
|
|
47
|
+
}) {
|
|
48
|
+
const { current: isControlled } = (0, import_react.useRef)(controlled !== void 0);
|
|
49
|
+
const [uncontrolled, setUncontrolled] = (0, import_react.useState)(defaultValue);
|
|
50
|
+
const defaultRef = (0, import_react.useRef)(defaultValue);
|
|
51
|
+
const value = isControlled ? controlled : uncontrolled;
|
|
52
|
+
if (process.env.NODE_ENV !== "production") {
|
|
53
|
+
if (isControlled !== (controlled !== void 0)) {
|
|
54
|
+
console.error(
|
|
55
|
+
`${name}: cannot switch between controlled and uncontrolled \`${state}\`. Decide which one this component is for the whole of its life.`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
if (!isControlled && defaultRef.current !== defaultValue) {
|
|
59
|
+
console.error(
|
|
60
|
+
`${name}: cannot change the default \`${state}\` after mount. It is only read once, so later changes are silently ignored.`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const setValue = (0, import_react.useCallback)(
|
|
65
|
+
(next) => {
|
|
66
|
+
if (!isControlled) setUncontrolled(next);
|
|
67
|
+
},
|
|
68
|
+
[isControlled]
|
|
69
|
+
);
|
|
70
|
+
return [value, setValue];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/dialog/DialogRootContext.ts
|
|
74
|
+
var import_react2 = require("react");
|
|
75
|
+
var DialogRootContext = (0, import_react2.createContext)(null);
|
|
76
|
+
function useDialogRootContext(part) {
|
|
77
|
+
const context = (0, import_react2.useContext)(DialogRootContext);
|
|
78
|
+
if (context === null) {
|
|
79
|
+
throw new Error(`<Dialog.${part}> must be rendered inside <Dialog.Root>.`);
|
|
80
|
+
}
|
|
81
|
+
return context;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/dialog/DialogRoot.tsx
|
|
85
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
86
|
+
function DialogRoot({
|
|
87
|
+
open: openProp,
|
|
88
|
+
defaultOpen,
|
|
89
|
+
onOpenChange,
|
|
90
|
+
closeOnBackdropClick = true,
|
|
91
|
+
children
|
|
92
|
+
}) {
|
|
93
|
+
const [open, setOpenState] = useControlled({
|
|
94
|
+
controlled: openProp,
|
|
95
|
+
default: defaultOpen ?? false,
|
|
96
|
+
name: "Dialog.Root",
|
|
97
|
+
state: "open"
|
|
98
|
+
});
|
|
99
|
+
const setOpen = (0, import_react3.useCallback)(
|
|
100
|
+
(next) => {
|
|
101
|
+
if (next === open) return;
|
|
102
|
+
setOpenState(next);
|
|
103
|
+
onOpenChange?.(next);
|
|
104
|
+
},
|
|
105
|
+
[open, setOpenState, onOpenChange]
|
|
106
|
+
);
|
|
107
|
+
const popupId = (0, import_react3.useId)();
|
|
108
|
+
const [titleId, setTitleId] = (0, import_react3.useState)(void 0);
|
|
109
|
+
const context = (0, import_react3.useMemo)(
|
|
110
|
+
() => ({ open, setOpen, closeOnBackdropClick, popupId, titleId, setTitleId }),
|
|
111
|
+
[open, setOpen, closeOnBackdropClick, popupId, titleId]
|
|
112
|
+
);
|
|
113
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogRootContext.Provider, { value: context, children });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/core/useRender.ts
|
|
117
|
+
var import_react4 = require("react");
|
|
118
|
+
|
|
119
|
+
// src/core/mergeProps.ts
|
|
120
|
+
function isEventHandler(key) {
|
|
121
|
+
return key.length > 2 && key.startsWith("on") && key[2] === key[2]?.toUpperCase();
|
|
122
|
+
}
|
|
123
|
+
function mergeRefs(...refs) {
|
|
124
|
+
return (node) => {
|
|
125
|
+
const cleanups = refs.map((ref) => {
|
|
126
|
+
if (typeof ref === "function") {
|
|
127
|
+
const result = ref(node);
|
|
128
|
+
return typeof result === "function" ? result : () => ref(null);
|
|
129
|
+
}
|
|
130
|
+
if (ref && typeof ref === "object") {
|
|
131
|
+
ref.current = node;
|
|
132
|
+
return () => {
|
|
133
|
+
ref.current = null;
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return void 0;
|
|
137
|
+
});
|
|
138
|
+
return () => {
|
|
139
|
+
cleanups.forEach((cleanup) => {
|
|
140
|
+
if (typeof cleanup === "function") cleanup();
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function isSyntheticEvent(event) {
|
|
146
|
+
return typeof event === "object" && event !== null && "nativeEvent" in event;
|
|
147
|
+
}
|
|
148
|
+
function makeEventPreventable(event) {
|
|
149
|
+
if (Object.prototype.hasOwnProperty.call(event, "preventComponentHandler")) return;
|
|
150
|
+
const target = event;
|
|
151
|
+
target.preventComponentHandler = () => {
|
|
152
|
+
target.componentHandlerPrevented = true;
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function isComponentHandlerPrevented(event) {
|
|
156
|
+
return typeof event === "object" && event !== null && event.componentHandlerPrevented === true;
|
|
157
|
+
}
|
|
158
|
+
function chainHandlers(earlier, value) {
|
|
159
|
+
return (...args) => {
|
|
160
|
+
const event = args[0];
|
|
161
|
+
if (isSyntheticEvent(event)) makeEventPreventable(event);
|
|
162
|
+
value(...args);
|
|
163
|
+
if (isComponentHandlerPrevented(event)) return;
|
|
164
|
+
earlier(...args);
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function mergeProps(...objects) {
|
|
168
|
+
const merged = {};
|
|
169
|
+
for (const props of objects) {
|
|
170
|
+
if (!props) continue;
|
|
171
|
+
for (const key of Object.keys(props)) {
|
|
172
|
+
const value = props[key];
|
|
173
|
+
if (value === void 0) continue;
|
|
174
|
+
if (isEventHandler(key) && typeof value === "function") {
|
|
175
|
+
const earlier = merged[key];
|
|
176
|
+
merged[key] = typeof earlier === "function" ? chainHandlers(earlier, value) : value;
|
|
177
|
+
} else if (key === "className") {
|
|
178
|
+
merged.className = merged.className ? `${String(merged.className)} ${String(value)}` : value;
|
|
179
|
+
} else if (key === "style") {
|
|
180
|
+
merged.style = { ...merged.style, ...value };
|
|
181
|
+
} else if (key === "ref") {
|
|
182
|
+
merged.ref = merged.ref ? mergeRefs(merged.ref, value) : value;
|
|
183
|
+
} else {
|
|
184
|
+
merged[key] = value;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return merged;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// src/core/useRender.ts
|
|
192
|
+
function useRender({
|
|
193
|
+
render,
|
|
194
|
+
defaultTagName,
|
|
195
|
+
props,
|
|
196
|
+
consumerProps
|
|
197
|
+
}) {
|
|
198
|
+
const merged = mergeProps(props, consumerProps);
|
|
199
|
+
if ((0, import_react4.isValidElement)(render)) {
|
|
200
|
+
return (0, import_react4.cloneElement)(render, mergeProps(merged, render.props));
|
|
201
|
+
}
|
|
202
|
+
return (0, import_react4.createElement)(defaultTagName, merged);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/dialog/dialogDataAttributes.ts
|
|
206
|
+
function dialogDataAttributes({ open }) {
|
|
207
|
+
return {
|
|
208
|
+
"data-open": open ? "" : void 0,
|
|
209
|
+
"data-closed": open ? void 0 : ""
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// src/dialog/DialogTrigger.tsx
|
|
214
|
+
function DialogTrigger({ className, children, render, ...rest }) {
|
|
215
|
+
const { open, setOpen, popupId } = useDialogRootContext("Trigger");
|
|
216
|
+
return useRender({
|
|
217
|
+
render,
|
|
218
|
+
defaultTagName: "button",
|
|
219
|
+
props: {
|
|
220
|
+
type: "button",
|
|
221
|
+
"aria-haspopup": "dialog",
|
|
222
|
+
"aria-expanded": open,
|
|
223
|
+
"aria-controls": popupId,
|
|
224
|
+
...dialogDataAttributes({ open }),
|
|
225
|
+
className,
|
|
226
|
+
children,
|
|
227
|
+
onClick() {
|
|
228
|
+
setOpen(true);
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
consumerProps: rest
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/dialog/DialogPopup.tsx
|
|
236
|
+
var import_react5 = require("react");
|
|
237
|
+
function isOnBackdrop(dialog, x, y) {
|
|
238
|
+
const rect = dialog.getBoundingClientRect();
|
|
239
|
+
return x < rect.left || x > rect.right || y < rect.top || y > rect.bottom;
|
|
240
|
+
}
|
|
241
|
+
function DialogPopup({ className, children, render, ...rest }) {
|
|
242
|
+
const { open, setOpen, closeOnBackdropClick, popupId, titleId } = useDialogRootContext("Popup");
|
|
243
|
+
const elementRef = (0, import_react5.useRef)(null);
|
|
244
|
+
const pressedBackdrop = (0, import_react5.useRef)(false);
|
|
245
|
+
const [, resync] = (0, import_react5.useReducer)((n) => n + 1, 0);
|
|
246
|
+
(0, import_react5.useEffect)(() => {
|
|
247
|
+
const dialog = elementRef.current;
|
|
248
|
+
if (!dialog || typeof dialog.showModal !== "function") return;
|
|
249
|
+
if (open && !dialog.open) dialog.showModal();
|
|
250
|
+
else if (!open && dialog.open) dialog.close();
|
|
251
|
+
});
|
|
252
|
+
const element = useRender({
|
|
253
|
+
render,
|
|
254
|
+
defaultTagName: "dialog",
|
|
255
|
+
props: {
|
|
256
|
+
id: popupId,
|
|
257
|
+
"aria-labelledby": titleId,
|
|
258
|
+
...dialogDataAttributes({ open }),
|
|
259
|
+
className,
|
|
260
|
+
children,
|
|
261
|
+
ref: elementRef,
|
|
262
|
+
onCancel(event) {
|
|
263
|
+
if (event.target !== event.currentTarget) return;
|
|
264
|
+
if (event.defaultPrevented) return;
|
|
265
|
+
event.preventDefault();
|
|
266
|
+
setOpen(false);
|
|
267
|
+
},
|
|
268
|
+
onClose(event) {
|
|
269
|
+
if (event.target !== event.currentTarget) return;
|
|
270
|
+
setOpen(false);
|
|
271
|
+
resync();
|
|
272
|
+
},
|
|
273
|
+
onPointerDown(event) {
|
|
274
|
+
const dialog = elementRef.current;
|
|
275
|
+
pressedBackdrop.current = dialog !== null && event.target === dialog && isOnBackdrop(dialog, event.clientX, event.clientY);
|
|
276
|
+
},
|
|
277
|
+
onClick(event) {
|
|
278
|
+
const dialog = elementRef.current;
|
|
279
|
+
const onBackdrop = pressedBackdrop.current && dialog !== null && event.target === dialog && isOnBackdrop(dialog, event.clientX, event.clientY);
|
|
280
|
+
pressedBackdrop.current = false;
|
|
281
|
+
if (onBackdrop && closeOnBackdropClick) setOpen(false);
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
consumerProps: rest
|
|
285
|
+
});
|
|
286
|
+
return element;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// src/dialog/DialogTitle.tsx
|
|
290
|
+
var import_react6 = require("react");
|
|
291
|
+
function DialogTitle({
|
|
292
|
+
id: idProp,
|
|
293
|
+
className,
|
|
294
|
+
children,
|
|
295
|
+
render,
|
|
296
|
+
...rest
|
|
297
|
+
}) {
|
|
298
|
+
const { setTitleId } = useDialogRootContext("Title");
|
|
299
|
+
const generatedId = (0, import_react6.useId)();
|
|
300
|
+
const id = idProp ?? generatedId;
|
|
301
|
+
(0, import_react6.useLayoutEffect)(() => {
|
|
302
|
+
setTitleId(id);
|
|
303
|
+
return () => setTitleId(void 0);
|
|
304
|
+
}, [id, setTitleId]);
|
|
305
|
+
return useRender({
|
|
306
|
+
render,
|
|
307
|
+
defaultTagName: "h2",
|
|
308
|
+
props: { id, className, children },
|
|
309
|
+
consumerProps: rest
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// src/dialog/DialogClose.tsx
|
|
314
|
+
function DialogClose({ className, children, render, ...rest }) {
|
|
315
|
+
const { setOpen } = useDialogRootContext("Close");
|
|
316
|
+
return useRender({
|
|
317
|
+
render,
|
|
318
|
+
defaultTagName: "button",
|
|
319
|
+
props: {
|
|
320
|
+
type: "button",
|
|
321
|
+
className,
|
|
322
|
+
children,
|
|
323
|
+
onClick() {
|
|
324
|
+
setOpen(false);
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
consumerProps: rest
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
331
|
+
0 && (module.exports = {
|
|
332
|
+
Dialog
|
|
333
|
+
});
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, ReactElement, Ref, ComponentPropsWithRef } from 'react';
|
|
3
|
+
|
|
4
|
+
type DialogRootProps = {
|
|
5
|
+
/**
|
|
6
|
+
* Controlled state. Provide `onOpenChange` alongside it.
|
|
7
|
+
*
|
|
8
|
+
* Never `undefined` once mounted: the mode is latched at mount, as for Switch.
|
|
9
|
+
*/
|
|
10
|
+
open?: boolean;
|
|
11
|
+
/** Initial state when uncontrolled. Read once, at mount. */
|
|
12
|
+
defaultOpen?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Called on every request to open or close — Trigger, Close, Esc, a backdrop click or a
|
|
15
|
+
* `<form method="dialog">`. A controlled dialog moves only if the parent accepts it.
|
|
16
|
+
*/
|
|
17
|
+
onOpenChange?: (open: boolean) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Close when the backdrop is clicked. On by default; turn it off where an accidental
|
|
20
|
+
* click would lose work. Esc and `Dialog.Close` still close.
|
|
21
|
+
*/
|
|
22
|
+
closeOnBackdropClick?: boolean;
|
|
23
|
+
children?: ReactNode;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Holds a dialog's state and shares it with its parts. Renders no element of its own.
|
|
27
|
+
*
|
|
28
|
+
* The dialog itself is a native `<dialog>` opened with `showModal()` (decision 7), so
|
|
29
|
+
* the platform supplies the top layer, the backdrop, the inert page behind it, the focus
|
|
30
|
+
* trap and focus return. What this component adds is state: one `open` value, one setter
|
|
31
|
+
* every close path goes through, and the ids that wire up ARIA.
|
|
32
|
+
*/
|
|
33
|
+
declare function DialogRoot({ open: openProp, defaultOpen, onOpenChange, closeOnBackdropClick, children, }: DialogRootProps): react.JSX.Element;
|
|
34
|
+
|
|
35
|
+
type DialogTriggerOwnProps = {
|
|
36
|
+
/** Element to render instead of the default `<button>`. Props and ref are merged onto it. */
|
|
37
|
+
render?: ReactElement;
|
|
38
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
39
|
+
ref?: Ref<HTMLElement>;
|
|
40
|
+
};
|
|
41
|
+
type DialogTriggerProps = DialogTriggerOwnProps & Omit<ComponentPropsWithRef<'button'>, keyof DialogTriggerOwnProps>;
|
|
42
|
+
/**
|
|
43
|
+
* Opens the dialog. A native `<button>`, so focus and activation are the platform's, and
|
|
44
|
+
* where focus returns when the dialog closes.
|
|
45
|
+
*
|
|
46
|
+
* Optional: a dialog opened from state alone — after a save, say — needs no Trigger.
|
|
47
|
+
*/
|
|
48
|
+
declare function DialogTrigger({ className, children, render, ...rest }: DialogTriggerProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
49
|
+
|
|
50
|
+
type DialogPopupOwnProps = {
|
|
51
|
+
/**
|
|
52
|
+
* Element to render instead of the default `<dialog>`. Props and ref are merged onto it.
|
|
53
|
+
*
|
|
54
|
+
* The `<dialog>` is what supplies `showModal()` — the top layer, backdrop, focus trap
|
|
55
|
+
* and inert page; what `render` produces instead is the consumer's choice.
|
|
56
|
+
*/
|
|
57
|
+
render?: ReactElement;
|
|
58
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
59
|
+
ref?: Ref<HTMLElement>;
|
|
60
|
+
};
|
|
61
|
+
type DialogPopupProps = DialogPopupOwnProps & Omit<ComponentPropsWithRef<'dialog'>, keyof DialogPopupOwnProps | 'open'>;
|
|
62
|
+
/**
|
|
63
|
+
* The dialog: a native `<dialog>`, opened with `showModal()` whenever the Root is open
|
|
64
|
+
* and closed with `close()` when it is not. State drives the element, never the reverse.
|
|
65
|
+
*
|
|
66
|
+
* Every way the platform closes a dialog is turned into a request through the Root's
|
|
67
|
+
* setter instead, so a controlled parent can refuse it:
|
|
68
|
+
*
|
|
69
|
+
* - **Esc** fires `cancel`, which is prevented, then reported.
|
|
70
|
+
* - **A backdrop click** is a click on the `<dialog>` itself outside its box. The press
|
|
71
|
+
* has to start there too, so a text selection dragged out of the dialog does not close it.
|
|
72
|
+
* - **`<form method="dialog">`**, or a `cancel` the browser will not let be prevented,
|
|
73
|
+
* closes the element first; the `close` event reports it, and the element reopens if
|
|
74
|
+
* the state stays open.
|
|
75
|
+
*
|
|
76
|
+
* It stays mounted while closed — a closed `<dialog>` is `display: none` — so its content
|
|
77
|
+
* keeps its state between openings. Without a `Dialog.Title`, give it a name with
|
|
78
|
+
* `aria-label`.
|
|
79
|
+
*/
|
|
80
|
+
declare function DialogPopup({ className, children, render, ...rest }: DialogPopupProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
81
|
+
|
|
82
|
+
type DialogTitleOwnProps = {
|
|
83
|
+
/** Element to render instead of the default `<h2>`. Props and ref are merged onto it. */
|
|
84
|
+
render?: ReactElement;
|
|
85
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
86
|
+
ref?: Ref<HTMLElement>;
|
|
87
|
+
};
|
|
88
|
+
type DialogTitleProps = DialogTitleOwnProps & Omit<ComponentPropsWithRef<'h2'>, keyof DialogTitleOwnProps>;
|
|
89
|
+
/**
|
|
90
|
+
* The dialog's heading, and its accessible name: the popup's `aria-labelledby` points at
|
|
91
|
+
* this element's `id` — yours if you pass one, a generated one otherwise.
|
|
92
|
+
*/
|
|
93
|
+
declare function DialogTitle({ id: idProp, className, children, render, ...rest }: DialogTitleProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
94
|
+
|
|
95
|
+
type DialogCloseOwnProps = {
|
|
96
|
+
/** Element to render instead of the default `<button>`. Props and ref are merged onto it. */
|
|
97
|
+
render?: ReactElement;
|
|
98
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
99
|
+
ref?: Ref<HTMLElement>;
|
|
100
|
+
};
|
|
101
|
+
type DialogCloseProps = DialogCloseOwnProps & Omit<ComponentPropsWithRef<'button'>, keyof DialogCloseOwnProps>;
|
|
102
|
+
/**
|
|
103
|
+
* Closes the dialog — a Cancel button, or an icon-only × with an `aria-label`. A native
|
|
104
|
+
* `<button>`; the request goes through the Root, so a controlled parent can refuse it.
|
|
105
|
+
*/
|
|
106
|
+
declare function DialogClose({ className, children, render, ...rest }: DialogCloseProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
107
|
+
|
|
108
|
+
declare namespace index_parts {
|
|
109
|
+
export { DialogClose as Close, DialogPopup as Popup, DialogRoot as Root, DialogTitle as Title, DialogTrigger as Trigger };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** The state Dialog.Root shares with its parts, and projects as `data-*` attributes. */
|
|
113
|
+
type DialogState = {
|
|
114
|
+
open: boolean;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export { index_parts as Dialog, type DialogCloseProps, type DialogPopupProps, type DialogRootProps, type DialogState, type DialogTitleProps, type DialogTriggerProps };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, ReactElement, Ref, ComponentPropsWithRef } from 'react';
|
|
3
|
+
|
|
4
|
+
type DialogRootProps = {
|
|
5
|
+
/**
|
|
6
|
+
* Controlled state. Provide `onOpenChange` alongside it.
|
|
7
|
+
*
|
|
8
|
+
* Never `undefined` once mounted: the mode is latched at mount, as for Switch.
|
|
9
|
+
*/
|
|
10
|
+
open?: boolean;
|
|
11
|
+
/** Initial state when uncontrolled. Read once, at mount. */
|
|
12
|
+
defaultOpen?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Called on every request to open or close — Trigger, Close, Esc, a backdrop click or a
|
|
15
|
+
* `<form method="dialog">`. A controlled dialog moves only if the parent accepts it.
|
|
16
|
+
*/
|
|
17
|
+
onOpenChange?: (open: boolean) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Close when the backdrop is clicked. On by default; turn it off where an accidental
|
|
20
|
+
* click would lose work. Esc and `Dialog.Close` still close.
|
|
21
|
+
*/
|
|
22
|
+
closeOnBackdropClick?: boolean;
|
|
23
|
+
children?: ReactNode;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Holds a dialog's state and shares it with its parts. Renders no element of its own.
|
|
27
|
+
*
|
|
28
|
+
* The dialog itself is a native `<dialog>` opened with `showModal()` (decision 7), so
|
|
29
|
+
* the platform supplies the top layer, the backdrop, the inert page behind it, the focus
|
|
30
|
+
* trap and focus return. What this component adds is state: one `open` value, one setter
|
|
31
|
+
* every close path goes through, and the ids that wire up ARIA.
|
|
32
|
+
*/
|
|
33
|
+
declare function DialogRoot({ open: openProp, defaultOpen, onOpenChange, closeOnBackdropClick, children, }: DialogRootProps): react.JSX.Element;
|
|
34
|
+
|
|
35
|
+
type DialogTriggerOwnProps = {
|
|
36
|
+
/** Element to render instead of the default `<button>`. Props and ref are merged onto it. */
|
|
37
|
+
render?: ReactElement;
|
|
38
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
39
|
+
ref?: Ref<HTMLElement>;
|
|
40
|
+
};
|
|
41
|
+
type DialogTriggerProps = DialogTriggerOwnProps & Omit<ComponentPropsWithRef<'button'>, keyof DialogTriggerOwnProps>;
|
|
42
|
+
/**
|
|
43
|
+
* Opens the dialog. A native `<button>`, so focus and activation are the platform's, and
|
|
44
|
+
* where focus returns when the dialog closes.
|
|
45
|
+
*
|
|
46
|
+
* Optional: a dialog opened from state alone — after a save, say — needs no Trigger.
|
|
47
|
+
*/
|
|
48
|
+
declare function DialogTrigger({ className, children, render, ...rest }: DialogTriggerProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
49
|
+
|
|
50
|
+
type DialogPopupOwnProps = {
|
|
51
|
+
/**
|
|
52
|
+
* Element to render instead of the default `<dialog>`. Props and ref are merged onto it.
|
|
53
|
+
*
|
|
54
|
+
* The `<dialog>` is what supplies `showModal()` — the top layer, backdrop, focus trap
|
|
55
|
+
* and inert page; what `render` produces instead is the consumer's choice.
|
|
56
|
+
*/
|
|
57
|
+
render?: ReactElement;
|
|
58
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
59
|
+
ref?: Ref<HTMLElement>;
|
|
60
|
+
};
|
|
61
|
+
type DialogPopupProps = DialogPopupOwnProps & Omit<ComponentPropsWithRef<'dialog'>, keyof DialogPopupOwnProps | 'open'>;
|
|
62
|
+
/**
|
|
63
|
+
* The dialog: a native `<dialog>`, opened with `showModal()` whenever the Root is open
|
|
64
|
+
* and closed with `close()` when it is not. State drives the element, never the reverse.
|
|
65
|
+
*
|
|
66
|
+
* Every way the platform closes a dialog is turned into a request through the Root's
|
|
67
|
+
* setter instead, so a controlled parent can refuse it:
|
|
68
|
+
*
|
|
69
|
+
* - **Esc** fires `cancel`, which is prevented, then reported.
|
|
70
|
+
* - **A backdrop click** is a click on the `<dialog>` itself outside its box. The press
|
|
71
|
+
* has to start there too, so a text selection dragged out of the dialog does not close it.
|
|
72
|
+
* - **`<form method="dialog">`**, or a `cancel` the browser will not let be prevented,
|
|
73
|
+
* closes the element first; the `close` event reports it, and the element reopens if
|
|
74
|
+
* the state stays open.
|
|
75
|
+
*
|
|
76
|
+
* It stays mounted while closed — a closed `<dialog>` is `display: none` — so its content
|
|
77
|
+
* keeps its state between openings. Without a `Dialog.Title`, give it a name with
|
|
78
|
+
* `aria-label`.
|
|
79
|
+
*/
|
|
80
|
+
declare function DialogPopup({ className, children, render, ...rest }: DialogPopupProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
81
|
+
|
|
82
|
+
type DialogTitleOwnProps = {
|
|
83
|
+
/** Element to render instead of the default `<h2>`. Props and ref are merged onto it. */
|
|
84
|
+
render?: ReactElement;
|
|
85
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
86
|
+
ref?: Ref<HTMLElement>;
|
|
87
|
+
};
|
|
88
|
+
type DialogTitleProps = DialogTitleOwnProps & Omit<ComponentPropsWithRef<'h2'>, keyof DialogTitleOwnProps>;
|
|
89
|
+
/**
|
|
90
|
+
* The dialog's heading, and its accessible name: the popup's `aria-labelledby` points at
|
|
91
|
+
* this element's `id` — yours if you pass one, a generated one otherwise.
|
|
92
|
+
*/
|
|
93
|
+
declare function DialogTitle({ id: idProp, className, children, render, ...rest }: DialogTitleProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
94
|
+
|
|
95
|
+
type DialogCloseOwnProps = {
|
|
96
|
+
/** Element to render instead of the default `<button>`. Props and ref are merged onto it. */
|
|
97
|
+
render?: ReactElement;
|
|
98
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
99
|
+
ref?: Ref<HTMLElement>;
|
|
100
|
+
};
|
|
101
|
+
type DialogCloseProps = DialogCloseOwnProps & Omit<ComponentPropsWithRef<'button'>, keyof DialogCloseOwnProps>;
|
|
102
|
+
/**
|
|
103
|
+
* Closes the dialog — a Cancel button, or an icon-only × with an `aria-label`. A native
|
|
104
|
+
* `<button>`; the request goes through the Root, so a controlled parent can refuse it.
|
|
105
|
+
*/
|
|
106
|
+
declare function DialogClose({ className, children, render, ...rest }: DialogCloseProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
107
|
+
|
|
108
|
+
declare namespace index_parts {
|
|
109
|
+
export { DialogClose as Close, DialogPopup as Popup, DialogRoot as Root, DialogTitle as Title, DialogTrigger as Trigger };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** The state Dialog.Root shares with its parts, and projects as `data-*` attributes. */
|
|
113
|
+
type DialogState = {
|
|
114
|
+
open: boolean;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export { index_parts as Dialog, type DialogCloseProps, type DialogPopupProps, type DialogRootProps, type DialogState, type DialogTitleProps, type DialogTriggerProps };
|