@funstack/ui 0.1.0-alpha.1 → 0.1.0-alpha.3
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/accordion/accordion-context.mjs.map +1 -1
- package/dist/accordion/accordion-item.mjs +18 -4
- package/dist/accordion/accordion-item.mjs.map +1 -1
- package/dist/accordion/accordion-panel.mjs +2 -3
- package/dist/accordion/accordion-panel.mjs.map +1 -1
- package/dist/accordion/accordion-root.d.mts.map +1 -1
- package/dist/accordion/accordion-root.mjs +32 -14
- package/dist/accordion/accordion-root.mjs.map +1 -1
- package/dist/accordion/accordion-trigger.mjs +4 -5
- package/dist/accordion/accordion-trigger.mjs.map +1 -1
- package/dist/checkbox/checkbox.d.mts +5 -3
- package/dist/checkbox/checkbox.d.mts.map +1 -1
- package/dist/checkbox/checkbox.mjs +25 -9
- package/dist/checkbox/checkbox.mjs.map +1 -1
- package/dist/checkbox-group/checkbox-group-context.mjs.map +1 -1
- package/dist/checkbox-group/checkbox-group-item.d.mts +2 -1
- package/dist/checkbox-group/checkbox-group-item.d.mts.map +1 -1
- package/dist/checkbox-group/checkbox-group-item.mjs +3 -1
- package/dist/checkbox-group/checkbox-group-item.mjs.map +1 -1
- package/dist/checkbox-group/checkbox-group-root.d.mts +5 -2
- package/dist/checkbox-group/checkbox-group-root.d.mts.map +1 -1
- package/dist/checkbox-group/checkbox-group-root.mjs +24 -5
- package/dist/checkbox-group/checkbox-group-root.mjs.map +1 -1
- package/dist/combobox/combobox-clear.d.mts +34 -0
- package/dist/combobox/combobox-clear.d.mts.map +1 -0
- package/dist/combobox/combobox-clear.mjs +58 -0
- package/dist/combobox/combobox-clear.mjs.map +1 -0
- package/dist/combobox/combobox-context.mjs +23 -0
- package/dist/combobox/combobox-context.mjs.map +1 -0
- package/dist/combobox/combobox-group.d.mts +72 -0
- package/dist/combobox/combobox-group.d.mts.map +1 -0
- package/dist/combobox/combobox-group.mjs +104 -0
- package/dist/combobox/combobox-group.mjs.map +1 -0
- package/dist/combobox/combobox-input.d.mts +42 -0
- package/dist/combobox/combobox-input.d.mts.map +1 -0
- package/dist/combobox/combobox-input.mjs +102 -0
- package/dist/combobox/combobox-input.mjs.map +1 -0
- package/dist/combobox/combobox-label.d.mts +22 -0
- package/dist/combobox/combobox-label.d.mts.map +1 -0
- package/dist/combobox/combobox-label.mjs +32 -0
- package/dist/combobox/combobox-label.mjs.map +1 -0
- package/dist/combobox/combobox-list.d.mts +37 -0
- package/dist/combobox/combobox-list.d.mts.map +1 -0
- package/dist/combobox/combobox-list.mjs +70 -0
- package/dist/combobox/combobox-list.mjs.map +1 -0
- package/dist/combobox/combobox-option.d.mts +45 -0
- package/dist/combobox/combobox-option.d.mts.map +1 -0
- package/dist/combobox/combobox-option.mjs +88 -0
- package/dist/combobox/combobox-option.mjs.map +1 -0
- package/dist/combobox/combobox-popup.d.mts +49 -0
- package/dist/combobox/combobox-popup.d.mts.map +1 -0
- package/dist/combobox/combobox-popup.mjs +86 -0
- package/dist/combobox/combobox-popup.mjs.map +1 -0
- package/dist/combobox/combobox-root.d.mts +135 -0
- package/dist/combobox/combobox-root.d.mts.map +1 -0
- package/dist/combobox/combobox-root.mjs +523 -0
- package/dist/combobox/combobox-root.mjs.map +1 -0
- package/dist/combobox/combobox-trigger.d.mts +36 -0
- package/dist/combobox/combobox-trigger.d.mts.map +1 -0
- package/dist/combobox/combobox-trigger.mjs +92 -0
- package/dist/combobox/combobox-trigger.mjs.map +1 -0
- package/dist/combobox/index.d.mts +36 -0
- package/dist/combobox/index.d.mts.map +1 -0
- package/dist/combobox/index.mjs +36 -0
- package/dist/combobox/index.mjs.map +1 -0
- package/dist/dialog/dialog-close.d.mts +17 -10
- package/dist/dialog/dialog-close.d.mts.map +1 -1
- package/dist/dialog/dialog-close.mjs +31 -20
- package/dist/dialog/dialog-close.mjs.map +1 -1
- package/dist/dialog/dialog-context.mjs.map +1 -1
- package/dist/dialog/dialog-form.d.mts +12 -5
- package/dist/dialog/dialog-form.d.mts.map +1 -1
- package/dist/dialog/dialog-form.mjs +16 -28
- package/dist/dialog/dialog-form.mjs.map +1 -1
- package/dist/dialog/dialog-popup.d.mts +10 -4
- package/dist/dialog/dialog-popup.d.mts.map +1 -1
- package/dist/dialog/dialog-popup.mjs +106 -41
- package/dist/dialog/dialog-popup.mjs.map +1 -1
- package/dist/dialog/dialog-root.d.mts +14 -8
- package/dist/dialog/dialog-root.d.mts.map +1 -1
- package/dist/dialog/dialog-root.mjs +66 -11
- package/dist/dialog/dialog-root.mjs.map +1 -1
- package/dist/dialog/dialog-trigger.d.mts +3 -2
- package/dist/dialog/dialog-trigger.d.mts.map +1 -1
- package/dist/dialog/dialog-trigger.mjs.map +1 -1
- package/dist/dialog/index.d.mts +4 -2
- package/dist/dialog/index.d.mts.map +1 -1
- package/dist/dialog/index.mjs +4 -2
- package/dist/dialog/index.mjs.map +1 -1
- package/dist/field/field-control.d.mts +33 -0
- package/dist/field/field-control.d.mts.map +1 -0
- package/dist/field/field-control.mjs +68 -0
- package/dist/field/field-control.mjs.map +1 -0
- package/dist/field/field-description.d.mts +24 -0
- package/dist/field/field-description.d.mts.map +1 -0
- package/dist/field/field-description.mjs +34 -0
- package/dist/field/field-description.mjs.map +1 -0
- package/dist/field/field-error.d.mts +30 -0
- package/dist/field/field-error.d.mts.map +1 -0
- package/dist/field/field-error.mjs +42 -0
- package/dist/field/field-error.mjs.map +1 -0
- package/dist/field/field-label.d.mts +25 -0
- package/dist/field/field-label.d.mts.map +1 -0
- package/dist/field/field-label.mjs +46 -0
- package/dist/field/field-label.mjs.map +1 -0
- package/dist/field/field-root.d.mts +53 -0
- package/dist/field/field-root.d.mts.map +1 -0
- package/dist/field/field-root.mjs +317 -0
- package/dist/field/field-root.mjs.map +1 -0
- package/dist/field/index.d.mts +18 -0
- package/dist/field/index.d.mts.map +1 -0
- package/dist/field/index.mjs +17 -0
- package/dist/field/index.mjs.map +1 -0
- package/dist/form/form-root.d.mts +58 -0
- package/dist/form/form-root.d.mts.map +1 -0
- package/dist/form/form-root.mjs +146 -0
- package/dist/form/form-root.mjs.map +1 -0
- package/dist/form/form-submit.d.mts +31 -0
- package/dist/form/form-submit.d.mts.map +1 -0
- package/dist/form/form-submit.mjs +52 -0
- package/dist/form/form-submit.mjs.map +1 -0
- package/dist/form/index.d.mts +11 -0
- package/dist/form/index.d.mts.map +1 -0
- package/dist/form/index.mjs +11 -0
- package/dist/form/index.mjs.map +1 -0
- package/dist/index.d.mts +46 -2
- package/dist/index.mjs +8 -1
- package/dist/internal/content-focus-probe.mjs +42 -0
- package/dist/internal/content-focus-probe.mjs.map +1 -0
- package/dist/internal/field-context.d.mts +19 -0
- package/dist/internal/field-context.d.mts.map +1 -0
- package/dist/internal/field-context.mjs +14 -0
- package/dist/internal/field-context.mjs.map +1 -0
- package/dist/internal/focus-first-invalid.mjs +67 -0
- package/dist/internal/focus-first-invalid.mjs.map +1 -0
- package/dist/internal/form-status-watcher.mjs +31 -0
- package/dist/internal/form-status-watcher.mjs.map +1 -0
- package/dist/internal/modal-registry.mjs +39 -0
- package/dist/internal/modal-registry.mjs.map +1 -0
- package/dist/internal/platform-attributes.d.mts +12 -1
- package/dist/internal/platform-attributes.d.mts.map +1 -1
- package/dist/internal/types.d.mts +11 -1
- package/dist/internal/types.d.mts.map +1 -1
- package/dist/internal/use-controllable-action.mjs +35 -13
- package/dist/internal/use-controllable-action.mjs.map +1 -1
- package/dist/internal/use-field-control.mjs +107 -0
- package/dist/internal/use-field-control.mjs.map +1 -0
- package/dist/internal/use-form-owner-event.mjs +44 -0
- package/dist/internal/use-form-owner-event.mjs.map +1 -0
- package/dist/internal/use-form-reset.mjs +12 -31
- package/dist/internal/use-form-reset.mjs.map +1 -1
- package/dist/internal/use-hydrated-mount.mjs +21 -0
- package/dist/internal/use-hydrated-mount.mjs.map +1 -0
- package/dist/internal/use-latest.mjs +32 -0
- package/dist/internal/use-latest.mjs.map +1 -0
- package/dist/internal/use-modal-registration.mjs +47 -0
- package/dist/internal/use-modal-registration.mjs.map +1 -0
- package/dist/internal/use-native-group-reconciliation.mjs +6 -5
- package/dist/internal/use-native-group-reconciliation.mjs.map +1 -1
- package/dist/internal/use-native-select-value.mjs +184 -0
- package/dist/internal/use-native-select-value.mjs.map +1 -0
- package/dist/internal/use-popover-popup.mjs +8 -22
- package/dist/internal/use-popover-popup.mjs.map +1 -1
- package/dist/internal/use-popover-root.mjs +58 -23
- package/dist/internal/use-popover-root.mjs.map +1 -1
- package/dist/internal/validity.d.mts +24 -0
- package/dist/internal/validity.d.mts.map +1 -0
- package/dist/internal/validity.mjs +108 -0
- package/dist/internal/validity.mjs.map +1 -0
- package/dist/menu/menu-group.d.mts.map +1 -1
- package/dist/menu/menu-group.mjs +3 -2
- package/dist/menu/menu-group.mjs.map +1 -1
- package/dist/menu/menu-radio-group.mjs +9 -4
- package/dist/menu/menu-radio-group.mjs.map +1 -1
- package/dist/menu/menu-root.mjs.map +1 -1
- package/dist/radio-group/radio-group-root.d.mts +4 -2
- package/dist/radio-group/radio-group-root.d.mts.map +1 -1
- package/dist/radio-group/radio-group-root.mjs +34 -8
- package/dist/radio-group/radio-group-root.mjs.map +1 -1
- package/dist/select/index.d.mts +30 -0
- package/dist/select/index.d.mts.map +1 -0
- package/dist/select/index.mjs +30 -0
- package/dist/select/index.mjs.map +1 -0
- package/dist/select/select-context.mjs +13 -0
- package/dist/select/select-context.mjs.map +1 -0
- package/dist/select/select-dom.mjs +44 -0
- package/dist/select/select-dom.mjs.map +1 -0
- package/dist/select/select-group-label.d.mts +22 -0
- package/dist/select/select-group-label.d.mts.map +1 -0
- package/dist/select/select-group-label.mjs +39 -0
- package/dist/select/select-group-label.mjs.map +1 -0
- package/dist/select/select-group.d.mts +28 -0
- package/dist/select/select-group.d.mts.map +1 -0
- package/dist/select/select-group.mjs +47 -0
- package/dist/select/select-group.mjs.map +1 -0
- package/dist/select/select-option.d.mts +42 -0
- package/dist/select/select-option.d.mts.map +1 -0
- package/dist/select/select-option.mjs +65 -0
- package/dist/select/select-option.mjs.map +1 -0
- package/dist/select/select-root.d.mts +68 -0
- package/dist/select/select-root.d.mts.map +1 -0
- package/dist/select/select-root.mjs +114 -0
- package/dist/select/select-root.mjs.map +1 -0
- package/dist/select/select-separator.d.mts +20 -0
- package/dist/select/select-separator.d.mts.map +1 -0
- package/dist/select/select-separator.mjs +30 -0
- package/dist/select/select-separator.mjs.map +1 -0
- package/dist/select/select-trigger.d.mts +29 -0
- package/dist/select/select-trigger.d.mts.map +1 -0
- package/dist/select/select-trigger.mjs +48 -0
- package/dist/select/select-trigger.mjs.map +1 -0
- package/dist/select/select-value.d.mts +31 -0
- package/dist/select/select-value.d.mts.map +1 -0
- package/dist/select/select-value.mjs +101 -0
- package/dist/select/select-value.mjs.map +1 -0
- package/dist/slider/index.d.mts +32 -0
- package/dist/slider/index.d.mts.map +1 -0
- package/dist/slider/index.mjs +27 -0
- package/dist/slider/index.mjs.map +1 -0
- package/dist/slider/slider-arithmetic.mjs +120 -0
- package/dist/slider/slider-arithmetic.mjs.map +1 -0
- package/dist/slider/slider-context.mjs +13 -0
- package/dist/slider/slider-context.mjs.map +1 -0
- package/dist/slider/slider-indicator.d.mts +22 -0
- package/dist/slider/slider-indicator.d.mts.map +1 -0
- package/dist/slider/slider-indicator.mjs +36 -0
- package/dist/slider/slider-indicator.mjs.map +1 -0
- package/dist/slider/slider-label.d.mts +23 -0
- package/dist/slider/slider-label.d.mts.map +1 -0
- package/dist/slider/slider-label.mjs +44 -0
- package/dist/slider/slider-label.mjs.map +1 -0
- package/dist/slider/slider-root.d.mts +111 -0
- package/dist/slider/slider-root.d.mts.map +1 -0
- package/dist/slider/slider-root.mjs +582 -0
- package/dist/slider/slider-root.mjs.map +1 -0
- package/dist/slider/slider-thumb.d.mts +58 -0
- package/dist/slider/slider-thumb.d.mts.map +1 -0
- package/dist/slider/slider-thumb.mjs +145 -0
- package/dist/slider/slider-thumb.mjs.map +1 -0
- package/dist/slider/slider-track.d.mts +29 -0
- package/dist/slider/slider-track.d.mts.map +1 -0
- package/dist/slider/slider-track.mjs +54 -0
- package/dist/slider/slider-track.mjs.map +1 -0
- package/dist/slider/slider-value.d.mts +34 -0
- package/dist/slider/slider-value.d.mts.map +1 -0
- package/dist/slider/slider-value.mjs +38 -0
- package/dist/slider/slider-value.mjs.map +1 -0
- package/dist/switch/switch.d.mts +5 -3
- package/dist/switch/switch.d.mts.map +1 -1
- package/dist/switch/switch.mjs +24 -8
- package/dist/switch/switch.mjs.map +1 -1
- package/dist/tabs/tabs-root.mjs +13 -3
- package/dist/tabs/tabs-root.mjs.map +1 -1
- package/dist/toast/index.d.mts +25 -0
- package/dist/toast/index.d.mts.map +1 -0
- package/dist/toast/index.mjs +26 -0
- package/dist/toast/index.mjs.map +1 -0
- package/dist/toast/toast-action.d.mts +39 -0
- package/dist/toast/toast-action.d.mts.map +1 -0
- package/dist/toast/toast-action.mjs +64 -0
- package/dist/toast/toast-action.mjs.map +1 -0
- package/dist/toast/toast-close.d.mts +25 -0
- package/dist/toast/toast-close.d.mts.map +1 -0
- package/dist/toast/toast-close.mjs +46 -0
- package/dist/toast/toast-close.mjs.map +1 -0
- package/dist/toast/toast-context.mjs +19 -0
- package/dist/toast/toast-context.mjs.map +1 -0
- package/dist/toast/toast-description.d.mts +24 -0
- package/dist/toast/toast-description.d.mts.map +1 -0
- package/dist/toast/toast-description.mjs +35 -0
- package/dist/toast/toast-description.mjs.map +1 -0
- package/dist/toast/toast-root.d.mts +43 -0
- package/dist/toast/toast-root.d.mts.map +1 -0
- package/dist/toast/toast-root.mjs +204 -0
- package/dist/toast/toast-root.mjs.map +1 -0
- package/dist/toast/toast-title.d.mts +24 -0
- package/dist/toast/toast-title.d.mts.map +1 -0
- package/dist/toast/toast-title.mjs +33 -0
- package/dist/toast/toast-title.mjs.map +1 -0
- package/dist/toast/toast-viewport.d.mts +52 -0
- package/dist/toast/toast-viewport.d.mts.map +1 -0
- package/dist/toast/toast-viewport.mjs +621 -0
- package/dist/toast/toast-viewport.mjs.map +1 -0
- package/dist/toast/toast.d.mts +119 -0
- package/dist/toast/toast.d.mts.map +1 -0
- package/dist/toast/toast.mjs +153 -0
- package/dist/toast/toast.mjs.map +1 -0
- package/package.json +7 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { RenderProp, StateValue } from "../internal/types.mjs";
|
|
2
|
+
import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
|
|
3
|
+
//#region src/form/form-submit.d.ts
|
|
4
|
+
export interface FormSubmitState {
|
|
5
|
+
/** The enclosing form's submission is in flight (`useFormStatus().pending`). */
|
|
6
|
+
readonly pending: boolean;
|
|
7
|
+
readonly disabled: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface FormSubmitProps extends Omit<ComponentPropsWithRef<"button">, "type" | "className" | "style" | "children"> {
|
|
10
|
+
/** Native `disabled`; owned. Never set by the library (conventions 3.6). */
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
className?: StateValue<string | undefined, FormSubmitState>;
|
|
13
|
+
style?: StateValue<CSSProperties | undefined, FormSubmitState>;
|
|
14
|
+
children?: StateValue<ReactNode, FormSubmitState>;
|
|
15
|
+
render?: RenderProp<ComponentPropsWithRef<"button">, FormSubmitState>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The form's submit button: `<button type="submit">` with `data-pending` while the enclosing
|
|
19
|
+
* form's submission is in flight (`useFormStatus`). Works in any React-rendered form
|
|
20
|
+
* (`Form.Root`, `Dialog.Form`, a plain `<form action>`). Never disabled while pending: block
|
|
21
|
+
* repeat clicks with CSS on `[data-pending]` if you need to.
|
|
22
|
+
*
|
|
23
|
+
* Owned keys (conventions 1.2): `type` (on a `<button>`), `disabled`, `data-pending`,
|
|
24
|
+
* `data-disabled`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function FormSubmit({ disabled, className, style, children, render, ...rest }: FormSubmitProps): ReactNode;
|
|
27
|
+
export declare namespace FormSubmit {
|
|
28
|
+
var displayName: string;
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
//# sourceMappingURL=form-submit.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-submit.d.mts","names":[],"sources":["../../src/form/form-submit.tsx"],"mappings":";;;iBAUiB;;WAEN;WACA;;iBAGM,wBAAwB,KACvC;;EAIA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,iCAAiC;;;;;;;;;;;wBAYvC,aACd,UACA,WACA,OACA,UACA,WACG,QACF,kBAAe;yBAPF"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { dataAttr } from "../internal/data-attr.mjs";
|
|
3
|
+
import { rendersButton, useRender } from "../internal/use-render.mjs";
|
|
4
|
+
import { useCallback, useRef } from "react";
|
|
5
|
+
import { useFormStatus } from "react-dom";
|
|
6
|
+
//#region src/form/form-submit.tsx
|
|
7
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
8
|
+
/**
|
|
9
|
+
* The form's submit button: `<button type="submit">` with `data-pending` while the enclosing
|
|
10
|
+
* form's submission is in flight (`useFormStatus`). Works in any React-rendered form
|
|
11
|
+
* (`Form.Root`, `Dialog.Form`, a plain `<form action>`). Never disabled while pending: block
|
|
12
|
+
* repeat clicks with CSS on `[data-pending]` if you need to.
|
|
13
|
+
*
|
|
14
|
+
* Owned keys (conventions 1.2): `type` (on a `<button>`), `disabled`, `data-pending`,
|
|
15
|
+
* `data-disabled`.
|
|
16
|
+
*/
|
|
17
|
+
function FormSubmit({ disabled = false, className, style, children, render, ...rest }) {
|
|
18
|
+
const { pending } = useFormStatus();
|
|
19
|
+
const warned = useRef(false);
|
|
20
|
+
const ref = useCallback((el) => {
|
|
21
|
+
const submitter = el.tagName === "BUTTON" || el.tagName === "INPUT" && ["submit", "image"].includes(el.type);
|
|
22
|
+
if (isDev && !warned.current && !submitter) {
|
|
23
|
+
warned.current = true;
|
|
24
|
+
console.error(`Form.Submit: expected to render a <button> (or <input type="submit">) but got <${el.tagName.toLowerCase()}>. Only a submit button submits the form natively.`);
|
|
25
|
+
}
|
|
26
|
+
return () => {};
|
|
27
|
+
}, []);
|
|
28
|
+
return useRender({
|
|
29
|
+
defaultElement: "button",
|
|
30
|
+
render,
|
|
31
|
+
state: {
|
|
32
|
+
pending,
|
|
33
|
+
disabled
|
|
34
|
+
},
|
|
35
|
+
className,
|
|
36
|
+
style,
|
|
37
|
+
children,
|
|
38
|
+
props: rest,
|
|
39
|
+
libraryProps: {
|
|
40
|
+
...rendersButton(render) && { type: "submit" },
|
|
41
|
+
disabled,
|
|
42
|
+
"data-pending": dataAttr(pending),
|
|
43
|
+
"data-disabled": dataAttr(disabled)
|
|
44
|
+
},
|
|
45
|
+
ref
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
FormSubmit.displayName = "FormSubmit";
|
|
49
|
+
//#endregion
|
|
50
|
+
export { FormSubmit };
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=form-submit.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-submit.mjs","names":[],"sources":["../../src/form/form-submit.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback, useRef } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { useFormStatus } from \"react-dom\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { rendersButton, useRender } from \"../internal/use-render.js\";\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\nexport interface FormSubmitState {\n /** The enclosing form's submission is in flight (`useFormStatus().pending`). */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface FormSubmitProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"type\" | \"className\" | \"style\" | \"children\"\n> {\n /** Native `disabled`; owned. Never set by the library (conventions 3.6). */\n disabled?: boolean;\n className?: StateValue<string | undefined, FormSubmitState>;\n style?: StateValue<CSSProperties | undefined, FormSubmitState>;\n children?: StateValue<ReactNode, FormSubmitState>;\n render?: RenderProp<ComponentPropsWithRef<\"button\">, FormSubmitState>;\n}\n\n/**\n * The form's submit button: `<button type=\"submit\">` with `data-pending` while the enclosing\n * form's submission is in flight (`useFormStatus`). Works in any React-rendered form\n * (`Form.Root`, `Dialog.Form`, a plain `<form action>`). Never disabled while pending: block\n * repeat clicks with CSS on `[data-pending]` if you need to.\n *\n * Owned keys (conventions 1.2): `type` (on a `<button>`), `disabled`, `data-pending`,\n * `data-disabled`.\n */\nexport function FormSubmit({\n disabled = false,\n className,\n style,\n children,\n render,\n ...rest\n}: FormSubmitProps) {\n const { pending } = useFormStatus();\n const warned = useRef(false);\n const ref = useCallback((el: HTMLElement) => {\n const submitter =\n el.tagName === \"BUTTON\" ||\n (el.tagName === \"INPUT\" && [\"submit\", \"image\"].includes((el as HTMLInputElement).type));\n if (isDev && !warned.current && !submitter) {\n warned.current = true;\n console.error(\n `Form.Submit: expected to render a <button> (or <input type=\"submit\">) but got ` +\n `<${el.tagName.toLowerCase()}>. Only a submit button submits the form natively.`,\n );\n }\n // A cleanup, so the merged ref never calls this with `null`.\n return () => {};\n }, []);\n const state: FormSubmitState = { pending, disabled };\n return useRender<ComponentPropsWithRef<\"button\">, FormSubmitState>({\n defaultElement: \"button\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps: {\n ...(rendersButton(render) && { type: \"submit\" as const }),\n disabled,\n \"data-pending\": dataAttr(pending),\n \"data-disabled\": dataAttr(disabled),\n },\n ref,\n });\n}\n\nFormSubmit.displayName = \"FormSubmit\";\n"],"mappings":";;;;;;AAQA,MAAM,QAAQ,QAAQ,IAAI,aAAa;;;;;;;;;;AA6BvC,SAAgB,WAAW,EACzB,WAAW,OACX,WACA,OACA,UACA,QACA,GAAG,QACe;CAClB,MAAM,EAAE,YAAY,cAAc;CAClC,MAAM,SAAS,OAAO,KAAK;CAC3B,MAAM,MAAM,aAAa,OAAoB;EAC3C,MAAM,YACJ,GAAG,YAAY,YACd,GAAG,YAAY,WAAW,CAAC,UAAU,OAAO,CAAC,CAAC,SAAU,GAAwB,IAAI;EACvF,IAAI,SAAS,CAAC,OAAO,WAAW,CAAC,WAAW;GAC1C,OAAO,UAAU;GACjB,QAAQ,MACN,kFACM,GAAG,QAAQ,YAAY,EAAE,mDACjC;EACF;EAEA,aAAa,CAAC;CAChB,GAAG,CAAC,CAAC;CAEL,OAAO,UAA4D;EACjE,gBAAgB;EAChB;EACA,OAAA;GAJ+B;GAAS;EAIpC;EACJ;EACA;EACA;EACA,OAAO;EACP,cAAc;GACZ,GAAI,cAAc,MAAM,KAAK,EAAE,MAAM,SAAkB;GACvD;GACA,gBAAgB,SAAS,OAAO;GAChC,iBAAiB,SAAS,QAAQ;EACpC;EACA;CACF,CAAC;AACH;AAEA,WAAW,cAAc"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FieldErrorValue, FormErrors } from "../internal/validity.mjs";
|
|
2
|
+
import { FormRoot, FormRootProps, FormRootState } from "./form-root.mjs";
|
|
3
|
+
import { FormSubmit, FormSubmitProps, FormSubmitState } from "./form-submit.mjs";
|
|
4
|
+
//#region src/form/index.d.ts
|
|
5
|
+
export declare const Form: {
|
|
6
|
+
readonly Root: typeof FormRoot;
|
|
7
|
+
readonly Submit: typeof FormSubmit;
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export type { FieldErrorValue, FormErrors, FormRootProps, FormRootState, FormSubmitProps, FormSubmitState };
|
|
11
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/form/index.ts"],"mappings":";;;;qBAGa;WAAS,aAAI;WAAY,eAAM"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FormRoot } from "./form-root.mjs";
|
|
2
|
+
import { FormSubmit } from "./form-submit.mjs";
|
|
3
|
+
//#region src/form/index.ts
|
|
4
|
+
const Form = {
|
|
5
|
+
Root: FormRoot,
|
|
6
|
+
Submit: FormSubmit
|
|
7
|
+
};
|
|
8
|
+
//#endregion
|
|
9
|
+
export { Form };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/form/index.ts"],"sourcesContent":["import { FormRoot } from \"./form-root.js\";\nimport { FormSubmit } from \"./form-submit.js\";\n\nexport const Form = { Root: FormRoot, Submit: FormSubmit } as const;\n\nexport type { FormRootProps, FormRootState, FormErrors, FieldErrorValue } from \"./form-root.js\";\nexport type { FormSubmitProps, FormSubmitState } from \"./form-submit.js\";\n"],"mappings":";;;AAGA,MAAa,OAAO;CAAE,MAAM;CAAU,QAAQ;AAAW"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "./internal/platform-attributes.mjs";
|
|
2
|
-
import { Action, ActionDetails, ChangeAction, ChangeReason, RenderProp, StateValue } from "./internal/types.mjs";
|
|
2
|
+
import { Action, ActionDetails, ChangeAction, ChangeReason, RenderProp, StateValue, UrgentChangeAction } from "./internal/types.mjs";
|
|
3
3
|
import { AccordionItemProps, AccordionItemState } from "./accordion/accordion-item.mjs";
|
|
4
4
|
import { AccordionPanelProps, AccordionPanelState } from "./accordion/accordion-panel.mjs";
|
|
5
5
|
import { AccordionRootExclusiveProps, AccordionRootMultipleProps, AccordionRootProps, AccordionRootState, AccordionValueChangeDetails } from "./accordion/accordion-root.mjs";
|
|
@@ -12,6 +12,16 @@ import { CheckboxGroupParentProps, CheckboxGroupParentState } from "./checkbox-g
|
|
|
12
12
|
import { CheckboxGroup } from "./checkbox-group/index.mjs";
|
|
13
13
|
import { Checkbox, CheckboxCheckedChangeDetails, CheckboxProps, CheckboxState } from "./checkbox/checkbox.mjs";
|
|
14
14
|
import "./checkbox/index.mjs";
|
|
15
|
+
import { ComboboxClearProps, ComboboxClearState } from "./combobox/combobox-clear.mjs";
|
|
16
|
+
import { ComboboxListProps, ComboboxListState } from "./combobox/combobox-list.mjs";
|
|
17
|
+
import { ComboboxEmptyProps, ComboboxEmptyState, ComboboxGroupLabelProps, ComboboxGroupLabelState, ComboboxGroupProps, ComboboxGroupState, ComboboxStatusProps, ComboboxStatusState } from "./combobox/combobox-group.mjs";
|
|
18
|
+
import { ComboboxInputProps, ComboboxInputState } from "./combobox/combobox-input.mjs";
|
|
19
|
+
import { ComboboxLabelProps, ComboboxLabelState } from "./combobox/combobox-label.mjs";
|
|
20
|
+
import { ComboboxOptionProps, ComboboxOptionState } from "./combobox/combobox-option.mjs";
|
|
21
|
+
import { ComboboxPopupProps, ComboboxPopupState } from "./combobox/combobox-popup.mjs";
|
|
22
|
+
import { ComboboxInputValueChangeDetails, ComboboxOpenChangeDetails, ComboboxRootProps, ComboboxRootState, ComboboxValueChangeDetails } from "./combobox/combobox-root.mjs";
|
|
23
|
+
import { ComboboxTriggerProps, ComboboxTriggerState } from "./combobox/combobox-trigger.mjs";
|
|
24
|
+
import { Combobox } from "./combobox/index.mjs";
|
|
15
25
|
import { DialogCloseDetails, DialogCloseProps, DialogCloseState } from "./dialog/dialog-close.mjs";
|
|
16
26
|
import { DialogPopupProps, DialogPopupState } from "./dialog/dialog-popup.mjs";
|
|
17
27
|
import { DialogDescriptionProps, DialogDescriptionState } from "./dialog/dialog-description.mjs";
|
|
@@ -20,6 +30,17 @@ import { DialogOpenChangeDetails, DialogRootProps, DialogRootState } from "./dia
|
|
|
20
30
|
import { DialogTitleProps, DialogTitleState } from "./dialog/dialog-title.mjs";
|
|
21
31
|
import { DialogTriggerProps, DialogTriggerState } from "./dialog/dialog-trigger.mjs";
|
|
22
32
|
import { Dialog } from "./dialog/index.mjs";
|
|
33
|
+
import { FieldErrorValue, FieldValidity, FormErrors } from "./internal/validity.mjs";
|
|
34
|
+
import { FieldState } from "./internal/field-context.mjs";
|
|
35
|
+
import { FieldControlProps } from "./field/field-control.mjs";
|
|
36
|
+
import { FieldDescriptionProps } from "./field/field-description.mjs";
|
|
37
|
+
import { FieldErrorProps } from "./field/field-error.mjs";
|
|
38
|
+
import { FieldLabelProps } from "./field/field-label.mjs";
|
|
39
|
+
import { FieldRootProps, FieldValidate } from "./field/field-root.mjs";
|
|
40
|
+
import { Field } from "./field/index.mjs";
|
|
41
|
+
import { FormRootProps, FormRootState } from "./form/form-root.mjs";
|
|
42
|
+
import { FormSubmitProps, FormSubmitState } from "./form/form-submit.mjs";
|
|
43
|
+
import { Form } from "./form/index.mjs";
|
|
23
44
|
import { Toggle, TogglePressedChangeDetails, ToggleProps, ToggleState } from "./toggle/toggle.mjs";
|
|
24
45
|
import "./toggle/index.mjs";
|
|
25
46
|
import { TabsActivationMode, TabsOrientation } from "./tabs/tabs-context.mjs";
|
|
@@ -57,4 +78,27 @@ import { RadioGroupItemProps, RadioGroupItemState } from "./radio-group/radio-gr
|
|
|
57
78
|
import { RadioGroupRootProps, RadioGroupRootState, RadioGroupValueChangeDetails } from "./radio-group/radio-group-root.mjs";
|
|
58
79
|
import { RadioGroupLabelProps } from "./radio-group/radio-group-label.mjs";
|
|
59
80
|
import { RadioGroup } from "./radio-group/index.mjs";
|
|
60
|
-
|
|
81
|
+
import { SelectRootProps, SelectRootState, SelectValueChangeDetails } from "./select/select-root.mjs";
|
|
82
|
+
import { SelectGroupProps } from "./select/select-group.mjs";
|
|
83
|
+
import { SelectGroupLabelProps } from "./select/select-group-label.mjs";
|
|
84
|
+
import { SelectOptionProps, SelectOptionState } from "./select/select-option.mjs";
|
|
85
|
+
import { SelectSeparatorProps } from "./select/select-separator.mjs";
|
|
86
|
+
import { SelectTriggerProps } from "./select/select-trigger.mjs";
|
|
87
|
+
import { SelectValueProps } from "./select/select-value.mjs";
|
|
88
|
+
import { Select } from "./select/index.mjs";
|
|
89
|
+
import { ToastActionDetails, ToastActionOptions, ToastCloseDetails, ToastItem, ToastManager, ToastOptions, ToastPriority, ToastPromiseOptions, createToastManager, toast } from "./toast/toast.mjs";
|
|
90
|
+
import { ToastActionProps, ToastActionState } from "./toast/toast-action.mjs";
|
|
91
|
+
import { ToastCloseProps, ToastCloseState } from "./toast/toast-close.mjs";
|
|
92
|
+
import { ToastRootProps, ToastRootState } from "./toast/toast-root.mjs";
|
|
93
|
+
import { ToastDescriptionProps } from "./toast/toast-description.mjs";
|
|
94
|
+
import { ToastTitleProps } from "./toast/toast-title.mjs";
|
|
95
|
+
import { ToastViewportProps, ToastViewportState } from "./toast/toast-viewport.mjs";
|
|
96
|
+
import { Toast } from "./toast/index.mjs";
|
|
97
|
+
import { SliderOrientation, SliderRootProps, SliderRootState, SliderValue, SliderValueChangeDetails, SliderValueCommitDetails } from "./slider/slider-root.mjs";
|
|
98
|
+
import { SliderTrackProps, SliderTrackState } from "./slider/slider-track.mjs";
|
|
99
|
+
import { SliderIndicatorProps, SliderIndicatorState } from "./slider/slider-indicator.mjs";
|
|
100
|
+
import { SliderLabelProps } from "./slider/slider-label.mjs";
|
|
101
|
+
import { SliderThumbProps, SliderThumbState } from "./slider/slider-thumb.mjs";
|
|
102
|
+
import { SliderValueProps, SliderValueState } from "./slider/slider-value.mjs";
|
|
103
|
+
import { Slider } from "./slider/index.mjs";
|
|
104
|
+
export { Accordion, type AccordionItemProps, type AccordionItemState, type AccordionPanelProps, type AccordionPanelState, type AccordionRootExclusiveProps, type AccordionRootMultipleProps, type AccordionRootProps, type AccordionRootState, type AccordionTriggerProps, type AccordionTriggerState, type AccordionValueChangeDetails, type Action, type ActionDetails, type ChangeAction, type ChangeReason, Checkbox, type CheckboxCheckedChangeDetails, CheckboxGroup, type CheckboxGroupItemProps, type CheckboxGroupItemState, type CheckboxGroupLabelProps, type CheckboxGroupParentProps, type CheckboxGroupParentState, type CheckboxGroupRootProps, type CheckboxGroupRootState, type CheckboxGroupValueChangeDetails, type CheckboxProps, type CheckboxState, Combobox, type ComboboxClearProps, type ComboboxClearState, type ComboboxEmptyProps, type ComboboxEmptyState, type ComboboxGroupLabelProps, type ComboboxGroupLabelState, type ComboboxGroupProps, type ComboboxGroupState, type ComboboxInputProps, type ComboboxInputState, type ComboboxInputValueChangeDetails, type ComboboxLabelProps, type ComboboxLabelState, type ComboboxListProps, type ComboboxListState, type ComboboxOpenChangeDetails, type ComboboxOptionProps, type ComboboxOptionState, type ComboboxPopupProps, type ComboboxPopupState, type ComboboxRootProps, type ComboboxRootState, type ComboboxStatusProps, type ComboboxStatusState, type ComboboxTriggerProps, type ComboboxTriggerState, type ComboboxValueChangeDetails, Dialog, type DialogCloseDetails, type DialogCloseProps, type DialogCloseState, type DialogDescriptionProps, type DialogDescriptionState, type DialogFormProps, type DialogFormState, type DialogOpenChangeDetails, type DialogPopupProps, type DialogPopupState, type DialogRootProps, type DialogRootState, type DialogTitleProps, type DialogTitleState, type DialogTriggerProps, type DialogTriggerState, Field, type FieldControlProps, type FieldDescriptionProps, type FieldErrorProps, type FieldErrorValue, type FieldLabelProps, type FieldRootProps, type FieldState, type FieldValidate, type FieldValidity, Form, type FormErrors, type FormRootProps, type FormRootState, type FormSubmitProps, type FormSubmitState, Menu, type MenuAlign, type MenuCheckboxItemProps, type MenuCheckboxItemState, type MenuGroupLabelProps, type MenuGroupLabelState, type MenuGroupProps, type MenuGroupState, type MenuItemDetails, type MenuItemProps, type MenuItemState, type MenuOpenChangeDetails, type MenuPopupProps, type MenuPopupState, type MenuRadioGroupProps, type MenuRadioGroupState, type MenuRadioItemProps, type MenuRadioItemState, type MenuRootProps, type MenuRootState, type MenuSeparatorProps, type MenuSeparatorState, type MenuSide, type MenuSubmenuTriggerProps, type MenuSubmenuTriggerState, type MenuTriggerProps, type MenuTriggerState, Popover, type PopoverAlign, type PopoverCloseProps, type PopoverCloseState, type PopoverDescriptionProps, type PopoverDescriptionState, type PopoverOpenChangeDetails, type PopoverPopupProps, type PopoverPopupState, type PopoverRootProps, type PopoverRootState, type PopoverSide, type PopoverTitleProps, type PopoverTitleState, type PopoverTriggerProps, type PopoverTriggerState, RadioGroup, type RadioGroupItemProps, type RadioGroupItemState, type RadioGroupLabelProps, type RadioGroupRootProps, type RadioGroupRootState, type RadioGroupValueChangeDetails, type RenderProp, Select, type SelectGroupLabelProps, type SelectGroupProps, type SelectOptionProps, type SelectOptionState, type SelectRootProps, type SelectRootState, type SelectSeparatorProps, type SelectTriggerProps, type SelectValueChangeDetails, type SelectValueProps, Slider, type SliderIndicatorProps, type SliderIndicatorState, type SliderLabelProps, type SliderOrientation, type SliderRootProps, type SliderRootState, type SliderThumbProps, type SliderThumbState, type SliderTrackProps, type SliderTrackState, type SliderValue, type SliderValueChangeDetails, type SliderValueCommitDetails, type SliderValueProps, type SliderValueState, type StateValue, Switch, type SwitchCheckedChangeDetails, type SwitchProps, type SwitchState, Tabs, type TabsActivationMode, type TabsIndicatorProps, type TabsIndicatorState, type TabsListProps, type TabsListState, type TabsOrientation, type TabsPanelProps, type TabsPanelState, type TabsRootProps, type TabsRootState, type TabsTabProps, type TabsTabState, type TabsValueChangeDetails, Toast, type ToastActionDetails, type ToastActionOptions, type ToastActionProps, type ToastActionState, type ToastCloseDetails, type ToastCloseProps, type ToastCloseState, type ToastDescriptionProps, type ToastItem, type ToastManager, type ToastOptions, type ToastPriority, type ToastPromiseOptions, type ToastRootProps, type ToastRootState, type ToastTitleProps, type ToastViewportProps, type ToastViewportState, Toggle, type TogglePressedChangeDetails, type ToggleProps, type ToggleState, Tooltip, type TooltipAlign, type TooltipPopupProps, type TooltipPopupState, type TooltipProvides, type TooltipRootProps, type TooltipRootState, type TooltipSide, type TooltipTriggerProps, type TooltipTriggerState, type UrgentChangeAction, createToastManager, toast };
|
package/dist/index.mjs
CHANGED
|
@@ -9,4 +9,11 @@ import { Popover } from "./popover/index.mjs";
|
|
|
9
9
|
import { Menu } from "./menu/index.mjs";
|
|
10
10
|
import { RadioGroup } from "./radio-group/index.mjs";
|
|
11
11
|
import { CheckboxGroup } from "./checkbox-group/index.mjs";
|
|
12
|
-
|
|
12
|
+
import { Select } from "./select/index.mjs";
|
|
13
|
+
import { Combobox } from "./combobox/index.mjs";
|
|
14
|
+
import { createToastManager, toast } from "./toast/toast.mjs";
|
|
15
|
+
import { Toast } from "./toast/index.mjs";
|
|
16
|
+
import { Slider } from "./slider/index.mjs";
|
|
17
|
+
import { Field } from "./field/index.mjs";
|
|
18
|
+
import { Form } from "./form/index.mjs";
|
|
19
|
+
export { Accordion, Checkbox, CheckboxGroup, Combobox, Dialog, Field, Form, Menu, Popover, RadioGroup, Select, Slider, Switch, Tabs, Toast, Toggle, Tooltip, createToastManager, toast };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useLayoutEffect, useRef } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/internal/content-focus-probe.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Records, from its layout-effect cleanup, whether focus is inside the popup when the content
|
|
7
|
+
* goes away: React runs a deleted component's layout cleanups before it detaches the host nodes,
|
|
8
|
+
* so `document.activeElement` still names the focused element there.
|
|
9
|
+
*/
|
|
10
|
+
function ContentFocusProbe({ onDetach, children }) {
|
|
11
|
+
useLayoutEffect(() => onDetach, [onDetach]);
|
|
12
|
+
return children;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The content probe shared by the popover-like Popups (Popover, Menu) and Dialog.Popup: a
|
|
16
|
+
* library-driven close with `transition` unmounts the content, the focused Close part included,
|
|
17
|
+
* in the close commit, before the layout effect that hides the popup runs, so focus falls to
|
|
18
|
+
* `<body>` and the platform may restore nothing. The probe tells the owner that this happened.
|
|
19
|
+
*/
|
|
20
|
+
function useContentFocusProbe(popupRef) {
|
|
21
|
+
const lostFocus = useRef(false);
|
|
22
|
+
const record = useCallback(() => {
|
|
23
|
+
const element = popupRef.current;
|
|
24
|
+
if (element === null) return;
|
|
25
|
+
const active = element.ownerDocument.activeElement;
|
|
26
|
+
if (active !== null && active !== element && element.contains(active)) lostFocus.current = true;
|
|
27
|
+
}, [popupRef]);
|
|
28
|
+
return {
|
|
29
|
+
wrapContent: useCallback((children) => /* @__PURE__ */ jsx(ContentFocusProbe, {
|
|
30
|
+
onDetach: record,
|
|
31
|
+
children
|
|
32
|
+
}), [record]),
|
|
33
|
+
hadFocus: useCallback(() => lostFocus.current, []),
|
|
34
|
+
forget: useCallback(() => {
|
|
35
|
+
lostFocus.current = false;
|
|
36
|
+
}, [])
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { useContentFocusProbe };
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=content-focus-probe.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-focus-probe.mjs","names":[],"sources":["../../src/internal/content-focus-probe.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback, useLayoutEffect, useRef } from \"react\";\nimport type { ReactNode, RefObject } from \"react\";\n\n/**\n * Records, from its layout-effect cleanup, whether focus is inside the popup when the content\n * goes away: React runs a deleted component's layout cleanups before it detaches the host nodes,\n * so `document.activeElement` still names the focused element there.\n */\nfunction ContentFocusProbe({\n onDetach,\n children,\n}: {\n onDetach: () => void;\n children?: ReactNode;\n}): ReactNode {\n useLayoutEffect(() => onDetach, [onDetach]);\n return children;\n}\n\nexport interface ContentFocusProbeHook {\n /**\n * Wraps the library-mode content of a top-layer popup: call it only while the content is\n * shown (`visible ? wrapContent(x) : null`), or inside the `<Activity>` for `keepMounted`.\n */\n readonly wrapContent: (children: ReactNode) => ReactNode;\n /**\n * Whether the wrapped content unmounted (or was hidden by `<Activity>`) while it held focus\n * since the last `forget()`. The owner reads it after its close\n * (design/components/popover.md 6.2 rule 2, design/components/dialog.md 6.2 rule 2).\n * Handlers and effects only.\n */\n readonly hadFocus: () => boolean;\n /**\n * Clear the record: on every open, after the owner used it, and whenever the content went\n * away while the popup stays open (not a close).\n */\n readonly forget: () => void;\n}\n\n/**\n * The content probe shared by the popover-like Popups (Popover, Menu) and Dialog.Popup: a\n * library-driven close with `transition` unmounts the content, the focused Close part included,\n * in the close commit, before the layout effect that hides the popup runs, so focus falls to\n * `<body>` and the platform may restore nothing. The probe tells the owner that this happened.\n */\nexport function useContentFocusProbe(\n popupRef: RefObject<HTMLElement | null>,\n): ContentFocusProbeHook {\n const lostFocus = useRef(false);\n\n const record = useCallback(() => {\n const element = popupRef.current;\n if (element === null) return;\n const active = element.ownerDocument.activeElement;\n if (active !== null && active !== element && element.contains(active)) {\n lostFocus.current = true;\n }\n }, [popupRef]);\n\n const wrapContent = useCallback(\n (children: ReactNode): ReactNode => (\n <ContentFocusProbe onDetach={record}>{children}</ContentFocusProbe>\n ),\n [record],\n );\n const hadFocus = useCallback(() => lostFocus.current, []);\n const forget = useCallback(() => {\n lostFocus.current = false;\n }, []);\n\n return { wrapContent, hadFocus, forget };\n}\n"],"mappings":";;;;;;;;;AASA,SAAS,kBAAkB,EACzB,UACA,YAIY;CACZ,sBAAsB,UAAU,CAAC,QAAQ,CAAC;CAC1C,OAAO;AACT;;;;;;;AA4BA,SAAgB,qBACd,UACuB;CACvB,MAAM,YAAY,OAAO,KAAK;CAE9B,MAAM,SAAS,kBAAkB;EAC/B,MAAM,UAAU,SAAS;EACzB,IAAI,YAAY,MAAM;EACtB,MAAM,SAAS,QAAQ,cAAc;EACrC,IAAI,WAAW,QAAQ,WAAW,WAAW,QAAQ,SAAS,MAAM,GAClE,UAAU,UAAU;CAExB,GAAG,CAAC,QAAQ,CAAC;CAab,OAAO;EAAE,aAXW,aACjB,aACC,oBAAC,mBAAD;GAAmB,UAAU;GAAS;EAA4B,CAAA,GAEpE,CAAC,MAAM,CAOU;EAAG,UALL,kBAAkB,UAAU,SAAS,CAAC,CAK1B;EAAG,QAJjB,kBAAkB;GAC/B,UAAU,UAAU;EACtB,GAAG,CAAC,CAEiC;CAAE;AACzC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { FieldValidity } from "./validity.mjs";
|
|
2
|
+
//#region src/internal/field-context.d.ts
|
|
3
|
+
/** The state every Field part receives (design/components/field-form.md 4.1). */
|
|
4
|
+
export interface FieldState {
|
|
5
|
+
/** An error is shown: the control matches `:user-invalid`, or a server error for this field is shown. */
|
|
6
|
+
readonly invalid: boolean;
|
|
7
|
+
/** The messages shown: `[validationMessage]` for a native constraint, the strings `validate` returned for a custom one, the server's strings for a server error; `[]` while valid. */
|
|
8
|
+
readonly messages: readonly string[];
|
|
9
|
+
/** A snapshot of the control's `ValidityState` (all valid on the server and before the first read). */
|
|
10
|
+
readonly validity: FieldValidity;
|
|
11
|
+
/** `Field.Root disabled` or the control's own `disabled`. */
|
|
12
|
+
readonly disabled: boolean;
|
|
13
|
+
/** `Field.Root required` or the control's own `required`. */
|
|
14
|
+
readonly required: boolean;
|
|
15
|
+
/** The enclosing form's submission is in flight (`useFormStatus().pending`); `false` outside a form. */
|
|
16
|
+
readonly pending: boolean;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
//# sourceMappingURL=field-context.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-context.d.mts","names":[],"sources":["../../src/internal/field-context.ts"],"mappings":";;;iBAKiB;;WAEN;;WAEA;;WAEA,UAAU;;WAEV;;WAEA;;WAEA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createContext, use } from "react";
|
|
3
|
+
//#region src/internal/field-context.ts
|
|
4
|
+
const FieldContext = createContext(null);
|
|
5
|
+
function useFieldContext(part) {
|
|
6
|
+
const ctx = use(FieldContext);
|
|
7
|
+
if (ctx === null) throw new Error(`${part} must be rendered inside a Field.Root.`);
|
|
8
|
+
return ctx;
|
|
9
|
+
}
|
|
10
|
+
const FormContext = createContext(null);
|
|
11
|
+
//#endregion
|
|
12
|
+
export { FieldContext, FormContext, useFieldContext };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=field-context.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-context.mjs","names":[],"sources":["../../src/internal/field-context.ts"],"sourcesContent":["\"use client\";\nimport { createContext, use } from \"react\";\nimport type { FieldValidity, FormErrors } from \"./validity.js\";\n\n/** The state every Field part receives (design/components/field-form.md 4.1). */\nexport interface FieldState {\n /** An error is shown: the control matches `:user-invalid`, or a server error for this field is shown. */\n readonly invalid: boolean;\n /** The messages shown: `[validationMessage]` for a native constraint, the strings `validate` returned for a custom one, the server's strings for a server error; `[]` while valid. */\n readonly messages: readonly string[];\n /** A snapshot of the control's `ValidityState` (all valid on the server and before the first read). */\n readonly validity: FieldValidity;\n /** `Field.Root disabled` or the control's own `disabled`. */\n readonly disabled: boolean;\n /** `Field.Root required` or the control's own `required`. */\n readonly required: boolean;\n /** The enclosing form's submission is in flight (`useFormStatus().pending`); `false` outside a form. */\n readonly pending: boolean;\n}\n\nexport interface FieldIds {\n readonly control: string;\n readonly label: string;\n readonly description: string;\n readonly error: string;\n}\n\n/** What a control reports to its Field (design 7.1). */\nexport interface FieldControlRegistration {\n /** The element the Field labels, describes and listens to (the input, or a group's fieldset). */\n readonly element: HTMLElement;\n /** `<label for>` can target it. Groups report `false`. */\n readonly labelable: boolean;\n /** The control's own `required` / `disabled` props (for the Field's `data-*` state). */\n readonly required: boolean;\n readonly disabled: boolean;\n}\n\nexport interface FieldContextValue {\n readonly ids: FieldIds;\n /** `Field.Root name`. */\n readonly name: string | undefined;\n /** `Field.Root disabled` / `required` (the props, not the merged state). */\n readonly disabled: boolean;\n readonly required: boolean;\n readonly state: FieldState;\n /** The registered control is labelable (default `true`, also before any registration). */\n readonly labelable: boolean;\n /** Registers the Field's control; returns the unregister function. The first one wins. */\n readonly registerControl: (registration: FieldControlRegistration) => () => void;\n /** A `Field.Error` is mounted (the Field may cancel the platform bubble). */\n readonly registerError: () => () => void;\n /**\n * The control's value changed in the DOM without an `input` / `change` event the Field could\n * hear (a Combobox selection, a CheckboxGroup's derived `required`, a rolled-back optimistic\n * value): re-reads the slot and the mirror after the commit that wrote it, and refreshes the\n * other Fields of the `Form.Root` (cross-field rules). `edit`: the user made the change, so a\n * shown server error is dismissed as by an `input` event. Call from a layout effect.\n */\n readonly notifyChange: (edit: boolean) => void;\n}\n\nexport const FieldContext = createContext<FieldContextValue | null>(null);\n\nexport function useFieldContext(part: string): FieldContextValue {\n const ctx = use(FieldContext);\n if (ctx === null) throw new Error(`${part} must be rendered inside a Field.Root.`);\n return ctx;\n}\n\n/** What a Field registers with its Form (design 3.5). */\nexport interface FormFieldRegistration {\n /** The control, or a group's first enabled (or checked) member. */\n readonly getFocusTarget: () => HTMLElement | null;\n /** The Field shows a server error at the latest commit. */\n readonly showsServerError: () => boolean;\n /** The server messages shown, for an announcement when focus does not move. */\n readonly serverMessages: () => readonly string[];\n /** Re-runs `validate` and re-reads validity (another Field's control changed). */\n readonly refresh: () => void;\n}\n\nexport interface FormContextValue {\n readonly errors: FormErrors | undefined;\n readonly registerField: (registration: FormFieldRegistration) => () => void;\n /**\n * The `FormData` of the submission in flight when the Form ran it itself (the intercepted path),\n * else `null`: the stale-value suppression of design 3.2 item 2 is computed against it.\n */\n readonly submittedData: () => FormData | null;\n /** Refreshes every registered Field except `source` (a change no DOM event carried). */\n readonly refreshFields: (source: FormFieldRegistration | null) => void;\n}\n\nexport const FormContext = createContext<FormContextValue | null>(null);\n"],"mappings":";;;AA8DA,MAAa,eAAe,cAAwC,IAAI;AAExE,SAAgB,gBAAgB,MAAiC;CAC/D,MAAM,MAAM,IAAI,YAAY;CAC5B,IAAI,QAAQ,MAAM,MAAM,IAAI,MAAM,GAAG,KAAK,uCAAuC;CACjF,OAAO;AACT;AA0BA,MAAa,cAAc,cAAuC,IAAI"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { sortByDocumentOrder } from "./validity.mjs";
|
|
2
|
+
//#region src/internal/focus-first-invalid.ts
|
|
3
|
+
/**
|
|
4
|
+
* Speaks `message` through `element.ariaNotify()` where the browser has it (design 5.3: the
|
|
5
|
+
* error is read when focus moves to the control; when focus is already there nothing would be
|
|
6
|
+
* read). Does nothing without `ariaNotify` or with an empty message.
|
|
7
|
+
*/
|
|
8
|
+
function announce(element, message) {
|
|
9
|
+
const notifying = element;
|
|
10
|
+
if (message === "" || typeof notifying.ariaNotify !== "function") return;
|
|
11
|
+
notifying.ariaNotify(message);
|
|
12
|
+
}
|
|
13
|
+
/** The submission attempt in progress: every target added during the current task. */
|
|
14
|
+
let batch = null;
|
|
15
|
+
/**
|
|
16
|
+
* Adds a control whose `invalid` event a Field cancelled during a submission attempt, and
|
|
17
|
+
* focuses the first collected control in document order in a microtask, so the result does not
|
|
18
|
+
* depend on the browser's dispatch order or on how many forms are on the page.
|
|
19
|
+
*
|
|
20
|
+
* The batch lives until the end of the task (a `setTimeout`), not just one microtask: when the
|
|
21
|
+
* browser dispatches the `invalid` events itself (a click on a submit button), it runs a
|
|
22
|
+
* microtask checkpoint after each listener, so the microtask of the first event runs before the
|
|
23
|
+
* second event fires. Every add therefore queues a focus of the batch's first target; later ones
|
|
24
|
+
* re-focus the same element, which is a no-op.
|
|
25
|
+
*
|
|
26
|
+
* Skips the focus when focus moved during the attempt to an element that is not one of the
|
|
27
|
+
* collected targets: a plain `<input required>` with no Field kept the platform's behaviour
|
|
28
|
+
* (bubble and focus), and the library does not fight it.
|
|
29
|
+
*/
|
|
30
|
+
function collectInvalid(target, message = "") {
|
|
31
|
+
if (target === null) return;
|
|
32
|
+
if (batch === null) {
|
|
33
|
+
batch = {
|
|
34
|
+
targets: [],
|
|
35
|
+
messages: /* @__PURE__ */ new Map(),
|
|
36
|
+
activeBefore: target.ownerDocument.activeElement,
|
|
37
|
+
moved: false,
|
|
38
|
+
announced: null
|
|
39
|
+
};
|
|
40
|
+
setTimeout(() => {
|
|
41
|
+
batch = null;
|
|
42
|
+
}, 0);
|
|
43
|
+
}
|
|
44
|
+
const current = batch;
|
|
45
|
+
if (!current.targets.includes(target)) current.targets.push(target);
|
|
46
|
+
current.messages.set(target, message);
|
|
47
|
+
queueMicrotask(() => focusFirst(current));
|
|
48
|
+
}
|
|
49
|
+
function focusFirst(current) {
|
|
50
|
+
const { targets, activeBefore } = current;
|
|
51
|
+
const connected = targets.filter((t) => t.isConnected);
|
|
52
|
+
if (connected.length === 0) return;
|
|
53
|
+
const active = connected[0].ownerDocument.activeElement;
|
|
54
|
+
if (active !== activeBefore && active !== null && !connected.includes(active)) return;
|
|
55
|
+
const first = sortByDocumentOrder(connected, (t) => t)[0];
|
|
56
|
+
if (active !== first) {
|
|
57
|
+
current.moved = true;
|
|
58
|
+
first.focus();
|
|
59
|
+
} else if (!current.moved && current.announced !== first) {
|
|
60
|
+
current.announced = first;
|
|
61
|
+
announce(first, current.messages.get(first) ?? "");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
export { announce, collectInvalid };
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=focus-first-invalid.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focus-first-invalid.mjs","names":[],"sources":["../../src/internal/focus-first-invalid.ts"],"sourcesContent":["// Directive-free: the module-level first-invalid collector (design/components/field-form.md 2.5).\nimport { sortByDocumentOrder } from \"./validity.js\";\n\ninterface Batch {\n readonly targets: HTMLElement[];\n /** The message each target's Field shows, for the announcement when focus does not move. */\n readonly messages: Map<HTMLElement, string>;\n /** `document.activeElement` when the first target arrived. */\n readonly activeBefore: Element | null;\n /** The batch moved focus (later microtasks then re-focus, never announce). */\n moved: boolean;\n /** The target already announced. */\n announced: HTMLElement | null;\n}\n\ntype NotifyingElement = HTMLElement & { ariaNotify?: (announcement: string) => void };\n\n/**\n * Speaks `message` through `element.ariaNotify()` where the browser has it (design 5.3: the\n * error is read when focus moves to the control; when focus is already there nothing would be\n * read). Does nothing without `ariaNotify` or with an empty message.\n */\nexport function announce(element: HTMLElement, message: string): void {\n const notifying = element as NotifyingElement;\n if (message === \"\" || typeof notifying.ariaNotify !== \"function\") return;\n notifying.ariaNotify(message);\n}\n\n/** The submission attempt in progress: every target added during the current task. */\nlet batch: Batch | null = null;\n\n/**\n * Adds a control whose `invalid` event a Field cancelled during a submission attempt, and\n * focuses the first collected control in document order in a microtask, so the result does not\n * depend on the browser's dispatch order or on how many forms are on the page.\n *\n * The batch lives until the end of the task (a `setTimeout`), not just one microtask: when the\n * browser dispatches the `invalid` events itself (a click on a submit button), it runs a\n * microtask checkpoint after each listener, so the microtask of the first event runs before the\n * second event fires. Every add therefore queues a focus of the batch's first target; later ones\n * re-focus the same element, which is a no-op.\n *\n * Skips the focus when focus moved during the attempt to an element that is not one of the\n * collected targets: a plain `<input required>` with no Field kept the platform's behaviour\n * (bubble and focus), and the library does not fight it.\n */\nexport function collectInvalid(target: HTMLElement | null, message = \"\"): void {\n if (target === null) return;\n if (batch === null) {\n batch = {\n targets: [],\n messages: new Map(),\n activeBefore: target.ownerDocument.activeElement,\n moved: false,\n announced: null,\n };\n setTimeout(() => {\n batch = null;\n }, 0);\n }\n const current = batch;\n if (!current.targets.includes(target)) current.targets.push(target);\n current.messages.set(target, message);\n queueMicrotask(() => focusFirst(current));\n}\n\nfunction focusFirst(current: Batch): void {\n const { targets, activeBefore } = current;\n const connected = targets.filter((t) => t.isConnected);\n if (connected.length === 0) return;\n const active = connected[0]!.ownerDocument.activeElement;\n if (active !== activeBefore && active !== null && !connected.includes(active as HTMLElement)) {\n return;\n }\n const first = sortByDocumentOrder(connected, (t) => t)[0]!;\n if (active !== first) {\n current.moved = true;\n first.focus();\n } else if (!current.moved && current.announced !== first) {\n // Focus was already on the first invalid control (an implicit submission with Enter): the\n // cancelled bubble was the only announcement, so speak the message instead.\n current.announced = first;\n announce(first, current.messages.get(first) ?? \"\");\n }\n}\n"],"mappings":";;;;;;;AAsBA,SAAgB,SAAS,SAAsB,SAAuB;CACpE,MAAM,YAAY;CAClB,IAAI,YAAY,MAAM,OAAO,UAAU,eAAe,YAAY;CAClE,UAAU,WAAW,OAAO;AAC9B;;AAGA,IAAI,QAAsB;;;;;;;;;;;;;;;;AAiB1B,SAAgB,eAAe,QAA4B,UAAU,IAAU;CAC7E,IAAI,WAAW,MAAM;CACrB,IAAI,UAAU,MAAM;EAClB,QAAQ;GACN,SAAS,CAAC;GACV,0BAAU,IAAI,IAAI;GAClB,cAAc,OAAO,cAAc;GACnC,OAAO;GACP,WAAW;EACb;EACA,iBAAiB;GACf,QAAQ;EACV,GAAG,CAAC;CACN;CACA,MAAM,UAAU;CAChB,IAAI,CAAC,QAAQ,QAAQ,SAAS,MAAM,GAAG,QAAQ,QAAQ,KAAK,MAAM;CAClE,QAAQ,SAAS,IAAI,QAAQ,OAAO;CACpC,qBAAqB,WAAW,OAAO,CAAC;AAC1C;AAEA,SAAS,WAAW,SAAsB;CACxC,MAAM,EAAE,SAAS,iBAAiB;CAClC,MAAM,YAAY,QAAQ,QAAQ,MAAM,EAAE,WAAW;CACrD,IAAI,UAAU,WAAW,GAAG;CAC5B,MAAM,SAAS,UAAU,EAAE,CAAE,cAAc;CAC3C,IAAI,WAAW,gBAAgB,WAAW,QAAQ,CAAC,UAAU,SAAS,MAAqB,GACzF;CAEF,MAAM,QAAQ,oBAAoB,YAAY,MAAM,CAAC,CAAC,CAAC;CACvD,IAAI,WAAW,OAAO;EACpB,QAAQ,QAAQ;EAChB,MAAM,MAAM;CACd,OAAO,IAAI,CAAC,QAAQ,SAAS,QAAQ,cAAc,OAAO;EAGxD,QAAQ,YAAY;EACpB,SAAS,OAAO,QAAQ,SAAS,IAAI,KAAK,KAAK,EAAE;CACnD;AACF"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { useFormStatus } from "react-dom";
|
|
4
|
+
//#region src/internal/form-status-watcher.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Observes React's form channel (Dialog design 8.1, Field/Form design 3.2): rendered inside a
|
|
7
|
+
* `<form>`, `useFormStatus().pending` covers the action's whole transition, including the
|
|
8
|
+
* Suspense wait of what it re-renders. On `true → false` the submission settled without throwing
|
|
9
|
+
* (a rejection unmounts the form through the nearest Error Boundary, so the edge is never seen).
|
|
10
|
+
* Used by `Dialog.Form` and `Form.Root`.
|
|
11
|
+
*/
|
|
12
|
+
function FormStatusWatcher({ onPendingChange, onSettled }) {
|
|
13
|
+
const { pending } = useFormStatus();
|
|
14
|
+
const previous = useRef(false);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const was = previous.current;
|
|
17
|
+
previous.current = pending;
|
|
18
|
+
if (was === pending) return;
|
|
19
|
+
onPendingChange(pending);
|
|
20
|
+
if (was && !pending) onSettled();
|
|
21
|
+
}, [
|
|
22
|
+
pending,
|
|
23
|
+
onPendingChange,
|
|
24
|
+
onSettled
|
|
25
|
+
]);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { FormStatusWatcher };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=form-status-watcher.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-status-watcher.mjs","names":[],"sources":["../../src/internal/form-status-watcher.tsx"],"sourcesContent":["\"use client\";\nimport { useEffect, useRef } from \"react\";\nimport { useFormStatus } from \"react-dom\";\n\nexport interface FormStatusWatcherProps {\n readonly onPendingChange: (pending: boolean) => void;\n readonly onSettled: () => void;\n}\n\n/**\n * Observes React's form channel (Dialog design 8.1, Field/Form design 3.2): rendered inside a\n * `<form>`, `useFormStatus().pending` covers the action's whole transition, including the\n * Suspense wait of what it re-renders. On `true → false` the submission settled without throwing\n * (a rejection unmounts the form through the nearest Error Boundary, so the edge is never seen).\n * Used by `Dialog.Form` and `Form.Root`.\n */\nexport function FormStatusWatcher({ onPendingChange, onSettled }: FormStatusWatcherProps) {\n const { pending } = useFormStatus();\n const previous = useRef(false);\n useEffect(() => {\n const was = previous.current;\n previous.current = pending;\n if (was === pending) return;\n onPendingChange(pending);\n if (was && !pending) onSettled();\n }, [pending, onPendingChange, onSettled]);\n return null;\n}\n"],"mappings":";;;;;;;;;;;AAgBA,SAAgB,kBAAkB,EAAE,iBAAiB,aAAqC;CACxF,MAAM,EAAE,YAAY,cAAc;CAClC,MAAM,WAAW,OAAO,KAAK;CAC7B,gBAAgB;EACd,MAAM,MAAM,SAAS;EACrB,SAAS,UAAU;EACnB,IAAI,QAAQ,SAAS;EACrB,gBAAgB,OAAO;EACvB,IAAI,OAAO,CAAC,SAAS,UAAU;CACjC,GAAG;EAAC;EAAS;EAAiB;CAAS,CAAC;CACxC,OAAO;AACT"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//#region src/internal/modal-registry.ts
|
|
2
|
+
const stack = [];
|
|
3
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
4
|
+
function notify() {
|
|
5
|
+
for (const listener of [...listeners]) listener();
|
|
6
|
+
}
|
|
7
|
+
/** Records `dialog` as the topmost open modal dialog (moves it to the top if already present). */
|
|
8
|
+
function pushModal(dialog) {
|
|
9
|
+
const index = stack.indexOf(dialog);
|
|
10
|
+
if (index !== -1 && index === stack.length - 1) return;
|
|
11
|
+
if (index !== -1) stack.splice(index, 1);
|
|
12
|
+
stack.push(dialog);
|
|
13
|
+
notify();
|
|
14
|
+
}
|
|
15
|
+
/** Forgets `dialog` (closed or unmounted). */
|
|
16
|
+
function removeModal(dialog) {
|
|
17
|
+
const index = stack.indexOf(dialog);
|
|
18
|
+
if (index === -1) return;
|
|
19
|
+
stack.splice(index, 1);
|
|
20
|
+
notify();
|
|
21
|
+
}
|
|
22
|
+
function subscribeModals(listener) {
|
|
23
|
+
listeners.add(listener);
|
|
24
|
+
return () => {
|
|
25
|
+
listeners.delete(listener);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** The topmost open modal dialog the library opened, or `null`. */
|
|
29
|
+
function getTopmostModal() {
|
|
30
|
+
return stack.at(-1) ?? null;
|
|
31
|
+
}
|
|
32
|
+
/** The server snapshot: nothing is open on the server. */
|
|
33
|
+
function getServerTopmostModal() {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { getServerTopmostModal, getTopmostModal, pushModal, removeModal, subscribeModals };
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=modal-registry.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modal-registry.mjs","names":[],"sources":["../../src/internal/modal-registry.ts"],"sourcesContent":["// No \"use client\" directive: a plain module-level store, no React and no DOM calls\n// (design/components/toast.md 4.3, conventions 3.5 \"module-level registry\").\n//\n// The stack of open modal `<dialog>` elements the library's Dialog opened. A top-layer element\n// outside the topmost modal dialog is inert and absent from the accessibility tree, so Toast's\n// Viewport portals its region into the topmost one. Writers (`use-modal-registration.ts`) run\n// in layout effects and platform event listeners, never during render; readers subscribe through\n// `useSyncExternalStore` with a server snapshot of `null`. Never the source of truth for a\n// component's own state.\n\nconst stack: Element[] = [];\nconst listeners = new Set<() => void>();\n\nfunction notify(): void {\n for (const listener of [...listeners]) listener();\n}\n\n/** Records `dialog` as the topmost open modal dialog (moves it to the top if already present). */\nexport function pushModal(dialog: Element): void {\n const index = stack.indexOf(dialog);\n if (index !== -1 && index === stack.length - 1) return;\n if (index !== -1) stack.splice(index, 1);\n stack.push(dialog);\n notify();\n}\n\n/** Forgets `dialog` (closed or unmounted). */\nexport function removeModal(dialog: Element): void {\n const index = stack.indexOf(dialog);\n if (index === -1) return;\n stack.splice(index, 1);\n notify();\n}\n\nexport function subscribeModals(listener: () => void): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\n/** The topmost open modal dialog the library opened, or `null`. */\nexport function getTopmostModal(): Element | null {\n return stack.at(-1) ?? null;\n}\n\n/** The server snapshot: nothing is open on the server. */\nexport function getServerTopmostModal(): Element | null {\n return null;\n}\n"],"mappings":";AAUA,MAAM,QAAmB,CAAC;AAC1B,MAAM,4BAAY,IAAI,IAAgB;AAEtC,SAAS,SAAe;CACtB,KAAK,MAAM,YAAY,CAAC,GAAG,SAAS,GAAG,SAAS;AAClD;;AAGA,SAAgB,UAAU,QAAuB;CAC/C,MAAM,QAAQ,MAAM,QAAQ,MAAM;CAClC,IAAI,UAAU,MAAM,UAAU,MAAM,SAAS,GAAG;CAChD,IAAI,UAAU,IAAI,MAAM,OAAO,OAAO,CAAC;CACvC,MAAM,KAAK,MAAM;CACjB,OAAO;AACT;;AAGA,SAAgB,YAAY,QAAuB;CACjD,MAAM,QAAQ,MAAM,QAAQ,MAAM;CAClC,IAAI,UAAU,IAAI;CAClB,MAAM,OAAO,OAAO,CAAC;CACrB,OAAO;AACT;AAEA,SAAgB,gBAAgB,UAAkC;CAChE,UAAU,IAAI,QAAQ;CACtB,aAAa;EACX,UAAU,OAAO,QAAQ;CAC3B;AACF;;AAGA,SAAgB,kBAAkC;CAChD,OAAO,MAAM,GAAG,EAAE,KAAK;AACzB;;AAGA,SAAgB,wBAAwC;CACtD,OAAO;AACT"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as React from "react";
|
|
1
2
|
//#region src/internal/platform-attributes.d.ts
|
|
2
3
|
declare module "react" {
|
|
3
4
|
interface HTMLAttributes<T> {
|
|
@@ -41,6 +42,17 @@ declare module "react" {
|
|
|
41
42
|
/** `interestfor` on `<area href>` (Chromium 142+). Not in `@types/react` 19.3.0 (DefinitelyTyped #74664). */
|
|
42
43
|
interestfor?: string | undefined;
|
|
43
44
|
}
|
|
45
|
+
namespace JSX {
|
|
46
|
+
interface IntrinsicElements {
|
|
47
|
+
/**
|
|
48
|
+
* `<selectedcontent>` (customizable select: Chromium 135+, Safari 27): inside a select's
|
|
49
|
+
* `<button>`, the browser clones the selected option's content into it. React renders the
|
|
50
|
+
* unknown lowercase tag verbatim on client and server. `Select.Value` renders it.
|
|
51
|
+
* Not in `@types/react` 19.3.0 (no DefinitelyTyped issue filed as of 2026-10-17).
|
|
52
|
+
*/
|
|
53
|
+
selectedcontent: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
44
56
|
interface CSSProperties {
|
|
45
57
|
/** `interest-delay` shorthand (Chromium 142+). Not in `csstype` 3.2.3. */
|
|
46
58
|
interestDelay?: string | undefined;
|
|
@@ -51,5 +63,4 @@ declare module "react" {
|
|
|
51
63
|
}
|
|
52
64
|
}
|
|
53
65
|
//#endregion
|
|
54
|
-
export {};
|
|
55
66
|
//# sourceMappingURL=platform-attributes.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform-attributes.d.mts","names":[],"sources":["../../src/internal/platform-attributes.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"platform-attributes.d.mts","names":[],"sources":["../../src/internal/platform-attributes.ts"],"mappings":";;;YAkBY,eAAe;;;;;;IAMvB;;;;;;IAMA;;YAGQ,qBAAqB;;;;;;IAM7B;;;;;IAKA;;;;;;IAMA;;YAWQ,qBAAqB;;IAE7B;;YAGQ,mBAAmB;;IAE3B;;YAGQ;cACE;;;;;;;MAOR,iBAAiB,MAAM,kBAAkB,MAAM,eAAe,cAAc;;;YAItE;;IAER;;IAEA;;IAEA"}
|
|
@@ -13,6 +13,14 @@ export interface ActionDetails {
|
|
|
13
13
|
}
|
|
14
14
|
/** An action called with the next value of a controllable state. */
|
|
15
15
|
export type ChangeAction<T, Details extends ActionDetails = ActionDetails> = Action<[next: T, details: Details]>;
|
|
16
|
+
/**
|
|
17
|
+
* A change action the library calls **synchronously, outside any Transition, and does not
|
|
18
|
+
* await**: the urgent lane (conventions 2.6; first used by Combobox's `inputValueChangeAction`).
|
|
19
|
+
* The body runs during the DOM event, so `(next) => setText(next)` is an urgent update, which is
|
|
20
|
+
* what a controlled text input needs. Nothing is pending or optimistic for it; a returned promise
|
|
21
|
+
* is ignored (development warns once).
|
|
22
|
+
*/
|
|
23
|
+
export type UrgentChangeAction<T, Details extends ActionDetails = ActionDetails> = (next: T, details: Details) => void;
|
|
16
24
|
/**
|
|
17
25
|
* Why a change action was dispatched (conventions 2.2). One library-wide vocabulary: every
|
|
18
26
|
* `<Component><State>ChangeDetails` that exposes `reason` declares it with this type.
|
|
@@ -24,8 +32,10 @@ export type ChangeAction<T, Details extends ActionDetails = ActionDetails> = Act
|
|
|
24
32
|
* `details.event` is the platform's `close` event. Dialog is the first component to use it.
|
|
25
33
|
* - `"platform"`: the browser changed the state itself: find-in-page, a form reset (`event` is
|
|
26
34
|
* the `reset` event), or interaction before hydration found at mount (`event` is `undefined`).
|
|
35
|
+
* - `"timeout"`: the library's own timer closed the component; no user gesture and no platform
|
|
36
|
+
* event (`event` is `undefined`). Toast's auto-dismiss is the only user.
|
|
27
37
|
*/
|
|
28
|
-
export type ChangeReason = "activate" | "dismiss" | "platform";
|
|
38
|
+
export type ChangeReason = "activate" | "dismiss" | "platform" | "timeout";
|
|
29
39
|
/** A prop value that is either a plain value or a function of the part's state. */
|
|
30
40
|
export type StateValue<T, State> = T | ((state: State) => T);
|
|
31
41
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../../src/internal/types.ts"],"mappings":";;;;;;;;YAQY,OAAO,4CAA4C,MAAM;iBAEpD;;WAEN,OAAO;;;YAIN,aAAa,GAAG,gBAAgB,gBAAgB,iBAAiB,QAC1E,MAAM,GAAG,SAAS
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../../src/internal/types.ts"],"mappings":";;;;;;;;YAQY,OAAO,4CAA4C,MAAM;iBAEpD;;WAEN,OAAO;;;YAIN,aAAa,GAAG,gBAAgB,gBAAgB,iBAAiB,QAC1E,MAAM,GAAG,SAAS;;;;;;;;YAUT,mBAAmB,GAAG,gBAAgB,gBAAgB,kBAChE,MAAM,GACN,SAAS;;;;;;;;;;;;;;;YAiBC;;YAGA,WAAW,GAAG,SAAS,MAAM,OAAO,UAAU;;;;;;YAO9C,WAAW,OAAO,SAC1B,aAAa,QAAQ,YACnB,OAAO,OAAO,OAAO,UAAU"}
|