@funstack/ui 0.1.0-alpha.6 → 0.1.0-alpha.7
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/button/button.mjs +1 -1
- package/dist/button/button.mjs.map +1 -1
- package/dist/combobox/combobox-clear.d.mts +6 -3
- package/dist/combobox/combobox-clear.d.mts.map +1 -1
- package/dist/combobox/combobox-clear.mjs +10 -5
- package/dist/combobox/combobox-clear.mjs.map +1 -1
- package/dist/combobox/combobox-trigger.d.mts +6 -2
- package/dist/combobox/combobox-trigger.d.mts.map +1 -1
- package/dist/combobox/combobox-trigger.mjs +10 -4
- package/dist/combobox/combobox-trigger.mjs.map +1 -1
- package/dist/dialog/dialog-close.d.mts +8 -3
- package/dist/dialog/dialog-close.d.mts.map +1 -1
- package/dist/dialog/dialog-close.mjs +11 -7
- package/dist/dialog/dialog-close.mjs.map +1 -1
- package/dist/dialog/dialog-trigger.d.mts +8 -3
- package/dist/dialog/dialog-trigger.d.mts.map +1 -1
- package/dist/dialog/dialog-trigger.mjs +15 -8
- package/dist/dialog/dialog-trigger.mjs.map +1 -1
- package/dist/form/form-submit.d.mts +4 -1
- package/dist/form/form-submit.d.mts.map +1 -1
- package/dist/form/form-submit.mjs.map +1 -1
- package/dist/internal/use-action-trigger.mjs +9 -7
- package/dist/internal/use-action-trigger.mjs.map +1 -1
- package/dist/internal/use-popover-trigger.mjs +6 -5
- package/dist/internal/use-popover-trigger.mjs.map +1 -1
- package/dist/menu/menu-dom.mjs +7 -1
- package/dist/menu/menu-dom.mjs.map +1 -1
- package/dist/menu/menu-item.d.mts +6 -2
- package/dist/menu/menu-item.d.mts.map +1 -1
- package/dist/menu/menu-item.mjs +5 -3
- package/dist/menu/menu-item.mjs.map +1 -1
- package/dist/menu/menu-trigger.d.mts +6 -3
- package/dist/menu/menu-trigger.d.mts.map +1 -1
- package/dist/menu/menu-trigger.mjs +2 -2
- package/dist/menu/menu-trigger.mjs.map +1 -1
- package/dist/number-field/number-field-decrement.d.mts +4 -3
- package/dist/number-field/number-field-decrement.d.mts.map +1 -1
- package/dist/number-field/number-field-decrement.mjs +4 -3
- package/dist/number-field/number-field-decrement.mjs.map +1 -1
- package/dist/number-field/number-field-increment.d.mts +8 -3
- package/dist/number-field/number-field-increment.d.mts.map +1 -1
- package/dist/number-field/number-field-increment.mjs +8 -5
- package/dist/number-field/number-field-increment.mjs.map +1 -1
- package/dist/number-field/number-field-root.mjs +1 -1
- package/dist/number-field/number-field-root.mjs.map +1 -1
- package/dist/number-field/use-step-button.mjs +6 -1
- package/dist/number-field/use-step-button.mjs.map +1 -1
- package/dist/popover/popover-close.d.mts +9 -3
- package/dist/popover/popover-close.d.mts.map +1 -1
- package/dist/popover/popover-close.mjs +12 -7
- package/dist/popover/popover-close.mjs.map +1 -1
- package/dist/popover/popover-trigger.d.mts +8 -4
- package/dist/popover/popover-trigger.d.mts.map +1 -1
- package/dist/popover/popover-trigger.mjs +3 -3
- package/dist/popover/popover-trigger.mjs.map +1 -1
- package/dist/slider/slider-root.mjs +1 -1
- package/dist/slider/slider-root.mjs.map +1 -1
- package/dist/toast/toast-action.d.mts +4 -3
- package/dist/toast/toast-action.d.mts.map +1 -1
- package/dist/toast/toast-action.mjs +7 -5
- package/dist/toast/toast-action.mjs.map +1 -1
- package/dist/toggle/toggle.d.mts +4 -1
- package/dist/toggle/toggle.d.mts.map +1 -1
- package/dist/toggle/toggle.mjs +14 -8
- package/dist/toggle/toggle.mjs.map +1 -1
- package/dist/toggle-group/toggle-group-item.d.mts +6 -2
- package/dist/toggle-group/toggle-group-item.d.mts.map +1 -1
- package/dist/toggle-group/toggle-group-item.mjs +9 -4
- package/dist/toggle-group/toggle-group-item.mjs.map +1 -1
- package/package.json +1 -1
package/dist/button/button.mjs
CHANGED
|
@@ -23,7 +23,7 @@ import { useFormStatus } from "react-dom";
|
|
|
23
23
|
function Button({ action, formAction, type, disabled = false, focusableWhenDisabled = false, transition, className, style, children, render, ...rest }) {
|
|
24
24
|
const toolbar = useToolbarContext();
|
|
25
25
|
const focusable = focusableWhenDisabled || toolbar !== null || !isHostButton(render);
|
|
26
|
-
const { pending: ownPending, run } = useActionTrigger(
|
|
26
|
+
const { pending: ownPending, run } = useActionTrigger();
|
|
27
27
|
const status = useFormStatus();
|
|
28
28
|
const submittedWith = useRef(void 0);
|
|
29
29
|
const formPending = status.pending && typeof formAction === "function" && (status.action === formAction || status.action === submittedWith.current);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.mjs","names":[],"sources":["../../src/button/button.tsx"],"sourcesContent":["\"use client\";\nimport { useRef } from \"react\";\nimport type {\n ComponentPropsWithRef,\n CSSProperties,\n MouseEvent,\n ReactNode,\n ViewTransitionClass,\n} from \"react\";\nimport { useFormStatus } from \"react-dom\";\nimport { createActionDetails } from \"../internal/action-details.js\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport { useToolbarContext } from \"../internal/toolbar-context.js\";\nimport { useTransitionBoundary } from \"../internal/transition-boundary.js\";\nimport type { Action, ActionDetails, RenderProp, StateValue } from \"../internal/types.js\";\nimport { useActionTrigger } from \"../internal/use-action-trigger.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { isHostButton, rendersButton, useRender } from \"../internal/use-render.js\";\n\n/** Details passed to `Button`'s `action`. Serialisable; `event` is virtual (conventions 2.2). */\nexport interface ButtonActionDetails extends ActionDetails {}\n\nexport interface ButtonState {\n /**\n * This Button's own `action` is in flight, or a submission it started through its own\n * function `formAction` is pending.\n */\n readonly pending: boolean;\n /** The `disabled` prop. Not set by pending. */\n readonly disabled: boolean;\n}\n\nexport interface ButtonProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"onClick\" | \"aria-disabled\" | \"className\" | \"style\" | \"children\" | \"type\" | \"formAction\"\n> {\n /**\n * Runs inside this Button's own transition on click, Enter or Space (`data-pending`\n * meanwhile). Sync or async; may be a Server Function. A rejection reaches the nearest Error\n * Boundary above the Button. While it is pending the Button ignores further activation and\n * renders `aria-disabled=\"true\"`. Not combinable with `formAction` (development error).\n */\n action?: Action<[details: ButtonActionDetails]>;\n /**\n * React's `formAction`: a function (a Server Function, a `useActionState` dispatch, a client\n * function) or a URL, run by React instead of the form's `action` when this Button submits the\n * form. With a function, the Button is `pending` while its own submission runs (not while\n * another submitter's does), ignores further activation meanwhile, and defaults to\n * `type=\"submit\"`.\n */\n formAction?: ComponentPropsWithRef<\"button\">[\"formAction\"];\n /**\n * Default `\"button\"`, or `\"submit\"` when `formAction` is given. Owned on a `<button>` and on a\n * component `render` element (set it here, not on the element; a component should spread it);\n * omitted for a host element of another tag (`<a>`). `action` together with\n * `\"submit\"` / `\"reset\"` warns in development: one click would do two things.\n */\n type?: \"button\" | \"submit\" | \"reset\";\n /**\n * Native `disabled` (owned; a `render` element cannot re-enable it) and `data-disabled`.\n * Never dispatches. With `focusableWhenDisabled`, inside a `Toolbar.Root`, or when the\n * rendered element is not a host `<button>` (`render={<a href />}`, a component), renders\n * `aria-disabled=\"true\"` instead.\n */\n disabled?: boolean;\n /**\n * Keep a disabled Button focusable: `aria-disabled=\"true\"` instead of native `disabled`, so\n * keyboard and screen-reader users still find it and a Tooltip on it still opens. Default\n * `false`; implied `true` inside a `Toolbar.Root` (APG Toolbar) and for a `render` element that\n * is not a host `<button>`.\n */\n focusableWhenDisabled?: boolean;\n /**\n * Forwarded as `default` to the Button's `<ViewTransition>`: animates the commit that ends the\n * action (content keyed on `pending`), not a click. Static; any value makes a mount inside a\n * transition start a document view transition (issue #35).\n */\n transition?: ViewTransitionClass;\n className?: StateValue<string | undefined, ButtonState>;\n style?: StateValue<CSSProperties | undefined, ButtonState>;\n children?: StateValue<ReactNode, ButtonState>;\n render?: RenderProp<ComponentPropsWithRef<\"button\">, ButtonState>;\n}\n\n/**\n * A button whose click is a transition (design/components/button.md): `<button type=\"button\">`\n * that runs `action` in its own transition, with `data-pending` and `aria-disabled=\"true\"` while\n * it runs, and ignores further activation meanwhile (conventions 2.5). With a function\n * `formAction` it is a form submitter with its own per-submitter pending. Inside a\n * `Toolbar.Root` a disabled Button stays focusable (`aria-disabled`).\n *\n * Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `aria-disabled`,\n * `data-pending`, `data-disabled`, `onClick`.\n */\nexport function Button({\n action,\n formAction,\n type,\n disabled = false,\n focusableWhenDisabled = false,\n transition,\n className,\n style,\n children,\n render,\n ...rest\n}: ButtonProps) {\n const toolbar = useToolbarContext();\n // Not a host <button> (a link, a component): native `disabled` would mean nothing to the\n // browser and hide the state from AT, so the focusable-disabled branch (review fold F1).\n const focusable = focusableWhenDisabled || toolbar !== null || !isHostButton(render);\n const { pending: ownPending, run } = useActionTrigger(
|
|
1
|
+
{"version":3,"file":"button.mjs","names":[],"sources":["../../src/button/button.tsx"],"sourcesContent":["\"use client\";\nimport { useRef } from \"react\";\nimport type {\n ComponentPropsWithRef,\n CSSProperties,\n MouseEvent,\n ReactNode,\n ViewTransitionClass,\n} from \"react\";\nimport { useFormStatus } from \"react-dom\";\nimport { createActionDetails } from \"../internal/action-details.js\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport { useToolbarContext } from \"../internal/toolbar-context.js\";\nimport { useTransitionBoundary } from \"../internal/transition-boundary.js\";\nimport type { Action, ActionDetails, RenderProp, StateValue } from \"../internal/types.js\";\nimport { useActionTrigger } from \"../internal/use-action-trigger.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { isHostButton, rendersButton, useRender } from \"../internal/use-render.js\";\n\n/** Details passed to `Button`'s `action`. Serialisable; `event` is virtual (conventions 2.2). */\nexport interface ButtonActionDetails extends ActionDetails {}\n\nexport interface ButtonState {\n /**\n * This Button's own `action` is in flight, or a submission it started through its own\n * function `formAction` is pending.\n */\n readonly pending: boolean;\n /** The `disabled` prop. Not set by pending. */\n readonly disabled: boolean;\n}\n\nexport interface ButtonProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"onClick\" | \"aria-disabled\" | \"className\" | \"style\" | \"children\" | \"type\" | \"formAction\"\n> {\n /**\n * Runs inside this Button's own transition on click, Enter or Space (`data-pending`\n * meanwhile). Sync or async; may be a Server Function. A rejection reaches the nearest Error\n * Boundary above the Button. While it is pending the Button ignores further activation and\n * renders `aria-disabled=\"true\"`. Not combinable with `formAction` (development error).\n */\n action?: Action<[details: ButtonActionDetails]>;\n /**\n * React's `formAction`: a function (a Server Function, a `useActionState` dispatch, a client\n * function) or a URL, run by React instead of the form's `action` when this Button submits the\n * form. With a function, the Button is `pending` while its own submission runs (not while\n * another submitter's does), ignores further activation meanwhile, and defaults to\n * `type=\"submit\"`.\n */\n formAction?: ComponentPropsWithRef<\"button\">[\"formAction\"];\n /**\n * Default `\"button\"`, or `\"submit\"` when `formAction` is given. Owned on a `<button>` and on a\n * component `render` element (set it here, not on the element; a component should spread it);\n * omitted for a host element of another tag (`<a>`). `action` together with\n * `\"submit\"` / `\"reset\"` warns in development: one click would do two things.\n */\n type?: \"button\" | \"submit\" | \"reset\";\n /**\n * Native `disabled` (owned; a `render` element cannot re-enable it) and `data-disabled`.\n * Never dispatches. With `focusableWhenDisabled`, inside a `Toolbar.Root`, or when the\n * rendered element is not a host `<button>` (`render={<a href />}`, a component), renders\n * `aria-disabled=\"true\"` instead.\n */\n disabled?: boolean;\n /**\n * Keep a disabled Button focusable: `aria-disabled=\"true\"` instead of native `disabled`, so\n * keyboard and screen-reader users still find it and a Tooltip on it still opens. Default\n * `false`; implied `true` inside a `Toolbar.Root` (APG Toolbar) and for a `render` element that\n * is not a host `<button>`.\n */\n focusableWhenDisabled?: boolean;\n /**\n * Forwarded as `default` to the Button's `<ViewTransition>`: animates the commit that ends the\n * action (content keyed on `pending`), not a click. Static; any value makes a mount inside a\n * transition start a document view transition (issue #35).\n */\n transition?: ViewTransitionClass;\n className?: StateValue<string | undefined, ButtonState>;\n style?: StateValue<CSSProperties | undefined, ButtonState>;\n children?: StateValue<ReactNode, ButtonState>;\n render?: RenderProp<ComponentPropsWithRef<\"button\">, ButtonState>;\n}\n\n/**\n * A button whose click is a transition (design/components/button.md): `<button type=\"button\">`\n * that runs `action` in its own transition, with `data-pending` and `aria-disabled=\"true\"` while\n * it runs, and ignores further activation meanwhile (conventions 2.5). With a function\n * `formAction` it is a form submitter with its own per-submitter pending. Inside a\n * `Toolbar.Root` a disabled Button stays focusable (`aria-disabled`).\n *\n * Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `aria-disabled`,\n * `data-pending`, `data-disabled`, `onClick`.\n */\nexport function Button({\n action,\n formAction,\n type,\n disabled = false,\n focusableWhenDisabled = false,\n transition,\n className,\n style,\n children,\n render,\n ...rest\n}: ButtonProps) {\n const toolbar = useToolbarContext();\n // Not a host <button> (a link, a component): native `disabled` would mean nothing to the\n // browser and hide the state from AT, so the focusable-disabled branch (review fold F1).\n const focusable = focusableWhenDisabled || toolbar !== null || !isHostButton(render);\n const { pending: ownPending, run } = useActionTrigger();\n\n // Per-submitter pending (design 6.2): React reports the submitter's function as\n // `status.action`. `submittedWith` keeps the function this Button last submitted with, so an\n // inline `formAction` whose identity changes during the submission still matches.\n const status = useFormStatus();\n const submittedWith = useRef<unknown>(undefined);\n const formPending =\n status.pending &&\n typeof formAction === \"function\" &&\n // Written only by the click handler, before the submission whose status this render shows,\n // so the render that reads it is never stale for that submission.\n // oxlint-disable-next-line react/refs\n (status.action === formAction || status.action === submittedWith.current);\n const pending = ownPending || formPending;\n\n useDevWarning(\n action !== undefined && formAction !== undefined,\n \"Button: `action` and `formAction` were both given. One click would run two actions; the \" +\n \"Button runs `action` and leaves `formAction` to the form submission. Pass one of them.\",\n );\n useDevWarning(\n action !== undefined && (type === \"submit\" || type === \"reset\"),\n `Button: \\`action\\` together with type=\"${type}\". One click would run \\`action\\` in a ` +\n `transition and ${type === \"reset\" ? \"reset\" : \"submit\"} the form. Use \\`formAction\\` ` +\n `for a submitter, or drop \\`type\\`.`,\n );\n\n const state: ButtonState = { pending, disabled };\n\n const element = useRender<ComponentPropsWithRef<\"button\">, ButtonState>({\n defaultElement: \"button\",\n render,\n state,\n className,\n style,\n children,\n props: formAction === undefined ? rest : { ...rest, formAction },\n libraryProps: {\n ...(rendersButton(render) && {\n type: type ?? (formAction !== undefined ? \"submit\" : \"button\"),\n }),\n // `undefined` (not `false`) in the focusable case, so the owned key removes a render\n // element's `disabled` too.\n disabled: disabled && !focusable ? true : undefined,\n \"aria-disabled\": pending || (disabled && focusable) ? true : undefined,\n \"data-pending\": dataAttr(pending),\n \"data-disabled\": dataAttr(disabled),\n onClick: (event: MouseEvent<HTMLButtonElement>) => {\n // `event.defaultPrevented` is never read: an outer part (`Dialog.Trigger`) cancels the\n // click as part of its own mechanism and the Button must still run (design 5.1).\n if (disabled) {\n // A disabled link must not navigate either.\n event.preventDefault();\n return;\n }\n if (action === undefined) {\n // A native button (a `formAction` submitter, a command, a link): only the guard.\n if (formPending) {\n event.preventDefault();\n return;\n }\n submittedWith.current = formAction;\n return;\n }\n const details: ButtonActionDetails = createActionDetails(event.nativeEvent, {});\n // Ignored while pending: do not navigate, submit or run a command either.\n if (!run(() => action(details))) event.preventDefault();\n },\n },\n });\n\n return useTransitionBoundary(\"Button\", transition, element);\n}\n\nButton.displayName = \"Button\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA8FA,SAAgB,OAAO,EACrB,QACA,YACA,MACA,WAAW,OACX,wBAAwB,OACxB,YACA,WACA,OACA,UACA,QACA,GAAG,QACW;CACd,MAAM,UAAU,kBAAkB;CAGlC,MAAM,YAAY,yBAAyB,YAAY,QAAQ,CAAC,aAAa,MAAM;CACnF,MAAM,EAAE,SAAS,YAAY,QAAQ,iBAAiB;CAKtD,MAAM,SAAS,cAAc;CAC7B,MAAM,gBAAgB,OAAgB,KAAA,CAAS;CAC/C,MAAM,cACJ,OAAO,WACP,OAAO,eAAe,eAIrB,OAAO,WAAW,cAAc,OAAO,WAAW,cAAc;CACnE,MAAM,UAAU,cAAc;CAE9B,cACE,WAAW,KAAA,KAAa,eAAe,KAAA,GACvC,gLAEF;CACA,cACE,WAAW,KAAA,MAAc,SAAS,YAAY,SAAS,UACvD,0CAA0C,KAAK,wDAC3B,SAAS,UAAU,UAAU,SAAS,iEAE5D;CAIA,MAAM,UAAU,UAAwD;EACtE,gBAAgB;EAChB;EACA,OAAA;GAL2B;GAAS;EAKhC;EACJ;EACA;EACA;EACA,OAAO,eAAe,KAAA,IAAY,OAAO;GAAE,GAAG;GAAM;EAAW;EAC/D,cAAc;GACZ,GAAI,cAAc,MAAM,KAAK,EAC3B,MAAM,SAAS,eAAe,KAAA,IAAY,WAAW,UACvD;GAGA,UAAU,YAAY,CAAC,YAAY,OAAO,KAAA;GAC1C,iBAAiB,WAAY,YAAY,YAAa,OAAO,KAAA;GAC7D,gBAAgB,SAAS,OAAO;GAChC,iBAAiB,SAAS,QAAQ;GAClC,UAAU,UAAyC;IAGjD,IAAI,UAAU;KAEZ,MAAM,eAAe;KACrB;IACF;IACA,IAAI,WAAW,KAAA,GAAW;KAExB,IAAI,aAAa;MACf,MAAM,eAAe;MACrB;KACF;KACA,cAAc,UAAU;KACxB;IACF;IACA,MAAM,UAA+B,oBAAoB,MAAM,aAAa,CAAC,CAAC;IAE9E,IAAI,CAAC,UAAU,OAAO,OAAO,CAAC,GAAG,MAAM,eAAe;GACxD;EACF;CACF,CAAC;CAED,OAAO,sBAAsB,UAAU,YAAY,OAAO;AAC5D;AAEA,OAAO,cAAc"}
|
|
@@ -8,7 +8,10 @@ export interface ComboboxClearState {
|
|
|
8
8
|
readonly disabled: boolean;
|
|
9
9
|
}
|
|
10
10
|
export interface ComboboxClearProps extends Omit<ComponentPropsWithRef<"button">, "onClick" | "onPointerDown" | "onMouseDown" | "tabIndex" | "disabled" | "className" | "style" | "children"> {
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Disables this button; the Root's `disabled` does too. Native `disabled` on a `<button>`; with
|
|
13
|
+
* a link or component `render`, `aria-disabled="true"` instead.
|
|
14
|
+
*/
|
|
12
15
|
disabled?: boolean;
|
|
13
16
|
className?: StateValue<string | undefined, ComboboxClearState>;
|
|
14
17
|
style?: StateValue<CSSProperties | undefined, ComboboxClearState>;
|
|
@@ -23,8 +26,8 @@ export interface ComboboxClearProps extends Omit<ComponentPropsWithRef<"button">
|
|
|
23
26
|
* A click while the listbox is open closes it (the platform's light dismiss: the button is
|
|
24
27
|
* outside the popup).
|
|
25
28
|
*
|
|
26
|
-
* Owned keys: `type` (on a `<button>`), `tabIndex`, `disabled`, `
|
|
27
|
-
* `onMouseDown`, `data-disabled`.
|
|
29
|
+
* Owned keys: `type` (on a `<button>`), `tabIndex`, `disabled`, `aria-disabled` (link or component
|
|
30
|
+
* `render`), `onClick`, `onPointerDown`, `onMouseDown`, `data-disabled`.
|
|
28
31
|
*/
|
|
29
32
|
export declare function ComboboxClear({ disabled: ownDisabled, className, style, children, render, ...rest }: ComboboxClearProps): ReactNode;
|
|
30
33
|
export declare namespace ComboboxClear {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox-clear.d.mts","names":[],"sources":["../../src/combobox/combobox-clear.tsx"],"mappings":";;;iBAeiB;;WAEN;WACA;WACA;;iBAGM,2BAA2B,KAC1C
|
|
1
|
+
{"version":3,"file":"combobox-clear.d.mts","names":[],"sources":["../../src/combobox/combobox-clear.tsx"],"mappings":";;;iBAeiB;;WAEN;WACA;WACA;;iBAGM,2BAA2B,KAC1C;;;;;EAcA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;;EAEjC,SAAS,WAAW,iCAAiC;;;;;;;;;;;;wBAavC,gBACd,UAAU,aACV,WACA,OACA,UACA,WACG,QACF,qBAAkB;yBAPL"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { dataAttr } from "../internal/data-attr.mjs";
|
|
3
|
-
import { rendersButton } from "../internal/render-element.mjs";
|
|
3
|
+
import { isHostButton, rendersButton } from "../internal/render-element.mjs";
|
|
4
4
|
import { useRender } from "../internal/use-render.mjs";
|
|
5
5
|
import { useComboboxContext } from "./combobox-context.mjs";
|
|
6
6
|
import { useNameWarning } from "./combobox-trigger.mjs";
|
|
@@ -12,13 +12,14 @@ import { useNameWarning } from "./combobox-trigger.mjs";
|
|
|
12
12
|
* A click while the listbox is open closes it (the platform's light dismiss: the button is
|
|
13
13
|
* outside the popup).
|
|
14
14
|
*
|
|
15
|
-
* Owned keys: `type` (on a `<button>`), `tabIndex`, `disabled`, `
|
|
16
|
-
* `onMouseDown`, `data-disabled`.
|
|
15
|
+
* Owned keys: `type` (on a `<button>`), `tabIndex`, `disabled`, `aria-disabled` (link or component
|
|
16
|
+
* `render`), `onClick`, `onPointerDown`, `onMouseDown`, `data-disabled`.
|
|
17
17
|
*/
|
|
18
18
|
function ComboboxClear({ disabled: ownDisabled = false, className, style, children, render, ...rest }) {
|
|
19
19
|
const ctx = useComboboxContext("Combobox.Clear");
|
|
20
20
|
const disabled = ownDisabled || ctx.disabled;
|
|
21
21
|
const ref = useNameWarning("Combobox.Clear");
|
|
22
|
+
const focusable = !isHostButton(render);
|
|
22
23
|
const state = {
|
|
23
24
|
value: ctx.value,
|
|
24
25
|
inputValue: ctx.inputValue,
|
|
@@ -27,7 +28,8 @@ function ComboboxClear({ disabled: ownDisabled = false, className, style, childr
|
|
|
27
28
|
const libraryProps = {
|
|
28
29
|
...rendersButton(render) && { type: "button" },
|
|
29
30
|
tabIndex: -1,
|
|
30
|
-
disabled,
|
|
31
|
+
disabled: focusable ? void 0 : disabled,
|
|
32
|
+
...focusable && { "aria-disabled": disabled ? true : void 0 },
|
|
31
33
|
onPointerDown: (event) => {
|
|
32
34
|
event.preventDefault();
|
|
33
35
|
},
|
|
@@ -35,7 +37,10 @@ function ComboboxClear({ disabled: ownDisabled = false, className, style, childr
|
|
|
35
37
|
event.preventDefault();
|
|
36
38
|
},
|
|
37
39
|
onClick: (event) => {
|
|
38
|
-
if (disabled)
|
|
40
|
+
if (disabled) {
|
|
41
|
+
event.preventDefault();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
39
44
|
ctx.clear(event.nativeEvent);
|
|
40
45
|
},
|
|
41
46
|
"data-disabled": dataAttr(disabled)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox-clear.mjs","names":[],"sources":["../../src/combobox/combobox-clear.tsx"],"sourcesContent":["\"use client\";\nimport type {\n ComponentPropsWithRef,\n CSSProperties,\n MouseEvent,\n PointerEvent,\n ReactNode,\n} from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { rendersButton, useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { useComboboxContext } from \"./combobox-context.js\";\nimport { useNameWarning } from \"./combobox-trigger.js\";\n\nexport interface ComboboxClearState {\n /** Optimistic selection. */\n readonly value: string | null;\n readonly inputValue: string;\n readonly disabled: boolean;\n}\n\nexport interface ComboboxClearProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n | \"onClick\"\n | \"onPointerDown\"\n | \"onMouseDown\"\n | \"tabIndex\"\n | \"disabled\"\n | \"className\"\n | \"style\"\n | \"children\"\n> {\n
|
|
1
|
+
{"version":3,"file":"combobox-clear.mjs","names":[],"sources":["../../src/combobox/combobox-clear.tsx"],"sourcesContent":["\"use client\";\nimport type {\n ComponentPropsWithRef,\n CSSProperties,\n MouseEvent,\n PointerEvent,\n ReactNode,\n} from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { isHostButton, rendersButton, useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { useComboboxContext } from \"./combobox-context.js\";\nimport { useNameWarning } from \"./combobox-trigger.js\";\n\nexport interface ComboboxClearState {\n /** Optimistic selection. */\n readonly value: string | null;\n readonly inputValue: string;\n readonly disabled: boolean;\n}\n\nexport interface ComboboxClearProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n | \"onClick\"\n | \"onPointerDown\"\n | \"onMouseDown\"\n | \"tabIndex\"\n | \"disabled\"\n | \"className\"\n | \"style\"\n | \"children\"\n> {\n /**\n * Disables this button; the Root's `disabled` does too. Native `disabled` on a `<button>`; with\n * a link or component `render`, `aria-disabled=\"true\"` instead.\n */\n disabled?: boolean;\n className?: StateValue<string | undefined, ComboboxClearState>;\n style?: StateValue<CSSProperties | undefined, ComboboxClearState>;\n children?: StateValue<ReactNode, ComboboxClearState>;\n /** Should render a `<button>`. */\n render?: RenderProp<ComponentPropsWithRef<\"button\">, ComboboxClearState>;\n}\n\n/**\n * The optional button that clears the combobox: `<button type=\"button\" tabindex=\"-1\">`. A click\n * empties the text (urgent) and dispatches `valueChangeAction(null)` unless nothing is selected;\n * the input keeps focus. Always rendered: hide it with CSS (`:placeholder-shown`) or `state`.\n * A click while the listbox is open closes it (the platform's light dismiss: the button is\n * outside the popup).\n *\n * Owned keys: `type` (on a `<button>`), `tabIndex`, `disabled`, `aria-disabled` (link or component\n * `render`), `onClick`, `onPointerDown`, `onMouseDown`, `data-disabled`.\n */\nexport function ComboboxClear({\n disabled: ownDisabled = false,\n className,\n style,\n children,\n render,\n ...rest\n}: ComboboxClearProps) {\n const ctx = useComboboxContext(\"Combobox.Clear\");\n const disabled = ownDisabled || ctx.disabled;\n const ref = useNameWarning(\"Combobox.Clear\");\n // A link or component render: native `disabled` would mean nothing on a link; the click\n // handler refuses, so `aria-disabled` (#83).\n const focusable = !isHostButton(render);\n const state: ComboboxClearState = { value: ctx.value, inputValue: ctx.inputValue, disabled };\n\n const libraryProps: ComponentPropsWithRef<\"button\"> & DataAttributes = {\n ...(rendersButton(render) && { type: \"button\" as const }),\n tabIndex: -1,\n disabled: focusable ? undefined : disabled,\n ...(focusable && { \"aria-disabled\": disabled ? true : undefined }),\n onPointerDown: (event: PointerEvent<HTMLButtonElement>) => {\n event.preventDefault();\n },\n onMouseDown: (event: MouseEvent<HTMLButtonElement>) => {\n event.preventDefault();\n },\n onClick: (event: MouseEvent<HTMLButtonElement>) => {\n if (disabled) {\n // A disabled link must not navigate either.\n event.preventDefault();\n return;\n }\n ctx.clear(event.nativeEvent);\n },\n \"data-disabled\": dataAttr(disabled),\n };\n\n return useRender<ComponentPropsWithRef<\"button\">, ComboboxClearState>({\n defaultElement: \"button\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps,\n ref,\n });\n}\n\nComboboxClear.displayName = \"ComboboxClear\";\n"],"mappings":";;;;;;;;;;;;;;;;;AAuDA,SAAgB,cAAc,EAC5B,UAAU,cAAc,OACxB,WACA,OACA,UACA,QACA,GAAG,QACkB;CACrB,MAAM,MAAM,mBAAmB,gBAAgB;CAC/C,MAAM,WAAW,eAAe,IAAI;CACpC,MAAM,MAAM,eAAe,gBAAgB;CAG3C,MAAM,YAAY,CAAC,aAAa,MAAM;CACtC,MAAM,QAA4B;EAAE,OAAO,IAAI;EAAO,YAAY,IAAI;EAAY;CAAS;CAE3F,MAAM,eAAiE;EACrE,GAAI,cAAc,MAAM,KAAK,EAAE,MAAM,SAAkB;EACvD,UAAU;EACV,UAAU,YAAY,KAAA,IAAY;EAClC,GAAI,aAAa,EAAE,iBAAiB,WAAW,OAAO,KAAA,EAAU;EAChE,gBAAgB,UAA2C;GACzD,MAAM,eAAe;EACvB;EACA,cAAc,UAAyC;GACrD,MAAM,eAAe;EACvB;EACA,UAAU,UAAyC;GACjD,IAAI,UAAU;IAEZ,MAAM,eAAe;IACrB;GACF;GACA,IAAI,MAAM,MAAM,WAAW;EAC7B;EACA,iBAAiB,SAAS,QAAQ;CACpC;CAEA,OAAO,UAA+D;EACpE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;EACA;CACF,CAAC;AACH;AAEA,cAAc,cAAc"}
|
|
@@ -9,7 +9,10 @@ export interface ComboboxTriggerState {
|
|
|
9
9
|
readonly disabled: boolean;
|
|
10
10
|
}
|
|
11
11
|
export interface ComboboxTriggerProps extends Omit<ComponentPropsWithRef<"button">, "onClick" | "onPointerDown" | "onMouseDown" | "onPointerCancel" | "tabIndex" | "aria-expanded" | "aria-controls" | "disabled" | "className" | "style" | "children"> {
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Disables this button; the Root's `disabled` does too. Native `disabled` on a `<button>`; with
|
|
14
|
+
* a link or component `render`, `aria-disabled="true"` instead.
|
|
15
|
+
*/
|
|
13
16
|
disabled?: boolean;
|
|
14
17
|
className?: StateValue<string | undefined, ComboboxTriggerState>;
|
|
15
18
|
style?: StateValue<CSSProperties | undefined, ComboboxTriggerState>;
|
|
@@ -24,7 +27,8 @@ export interface ComboboxTriggerProps extends Omit<ComponentPropsWithRef<"button
|
|
|
24
27
|
* stop). A press does not take focus from the input, and a click focuses the input. No
|
|
25
28
|
* `commandfor`: the listbox is anchored to the input, never to this button (design 1).
|
|
26
29
|
*
|
|
27
|
-
* Owned keys: `type` (on a `<button>`), `tabIndex`, `disabled`, `aria-
|
|
30
|
+
* Owned keys: `type` (on a `<button>`), `tabIndex`, `disabled`, `aria-disabled` (link or
|
|
31
|
+
* component `render`), `aria-expanded`,
|
|
28
32
|
* `aria-controls` (token-merged), `onClick`, `onPointerDown`, `onPointerCancel`, `onMouseDown`,
|
|
29
33
|
* `data-open`, `data-pending`, `data-disabled`.
|
|
30
34
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox-trigger.d.mts","names":[],"sources":["../../src/combobox/combobox-trigger.tsx"],"mappings":";;;iBAeiB;;WAEN;;WAEA;WACA;;iBAGM,6BAA6B,KAC5C
|
|
1
|
+
{"version":3,"file":"combobox-trigger.d.mts","names":[],"sources":["../../src/combobox/combobox-trigger.tsx"],"mappings":";;;iBAeiB;;WAEN;;WAEA;WACA;;iBAGM,6BAA6B,KAC5C;;;;;EAiBA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;EAE9C,WAAW,WAAW,WAAW;;EAEjC,SAAS,WAAW,iCAAiC;;;;;;;;;;;;;wBAsCvC,kBACd,UAAU,aACV,WACA,OACA,UACA,WACG,QACF,uBAAoB;yBAPP"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { dataAttr } from "../internal/data-attr.mjs";
|
|
3
|
-
import { rendersButton } from "../internal/render-element.mjs";
|
|
3
|
+
import { isHostButton, rendersButton } from "../internal/render-element.mjs";
|
|
4
4
|
import { useRender } from "../internal/use-render.mjs";
|
|
5
5
|
import { useComboboxContext } from "./combobox-context.mjs";
|
|
6
6
|
import { useCallback, useRef } from "react";
|
|
@@ -30,7 +30,8 @@ function useNameWarning(part) {
|
|
|
30
30
|
* stop). A press does not take focus from the input, and a click focuses the input. No
|
|
31
31
|
* `commandfor`: the listbox is anchored to the input, never to this button (design 1).
|
|
32
32
|
*
|
|
33
|
-
* Owned keys: `type` (on a `<button>`), `tabIndex`, `disabled`, `aria-
|
|
33
|
+
* Owned keys: `type` (on a `<button>`), `tabIndex`, `disabled`, `aria-disabled` (link or
|
|
34
|
+
* component `render`), `aria-expanded`,
|
|
34
35
|
* `aria-controls` (token-merged), `onClick`, `onPointerDown`, `onPointerCancel`, `onMouseDown`,
|
|
35
36
|
* `data-open`, `data-pending`, `data-disabled`.
|
|
36
37
|
*/
|
|
@@ -40,6 +41,7 @@ function ComboboxTrigger({ disabled: ownDisabled = false, className, style, chil
|
|
|
40
41
|
const disabled = ownDisabled || ctx.disabled;
|
|
41
42
|
const openAtPress = useRef(null);
|
|
42
43
|
const ref = useNameWarning("Combobox.Trigger");
|
|
44
|
+
const focusable = !isHostButton(render);
|
|
43
45
|
const state = {
|
|
44
46
|
open: popover.optimistic,
|
|
45
47
|
pending: popover.pending,
|
|
@@ -48,7 +50,8 @@ function ComboboxTrigger({ disabled: ownDisabled = false, className, style, chil
|
|
|
48
50
|
const libraryProps = {
|
|
49
51
|
...rendersButton(render) && { type: "button" },
|
|
50
52
|
tabIndex: -1,
|
|
51
|
-
disabled,
|
|
53
|
+
disabled: focusable ? void 0 : disabled,
|
|
54
|
+
...focusable && { "aria-disabled": disabled ? true : void 0 },
|
|
52
55
|
"aria-expanded": popover.optimistic,
|
|
53
56
|
"aria-controls": ctx.ids.list,
|
|
54
57
|
onPointerDown: (event) => {
|
|
@@ -64,7 +67,10 @@ function ComboboxTrigger({ disabled: ownDisabled = false, className, style, chil
|
|
|
64
67
|
onClick: (event) => {
|
|
65
68
|
const wasOpen = (event.detail === 0 ? null : openAtPress.current) ?? popover.intended();
|
|
66
69
|
openAtPress.current = null;
|
|
67
|
-
if (disabled)
|
|
70
|
+
if (disabled) {
|
|
71
|
+
event.preventDefault();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
68
74
|
const native = event.nativeEvent;
|
|
69
75
|
if (wasOpen) popover.requestClose(native, "activate");
|
|
70
76
|
else popover.requestOpen(native, "activate");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox-trigger.mjs","names":[],"sources":["../../src/combobox/combobox-trigger.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback, useRef } from \"react\";\nimport type {\n ComponentPropsWithRef,\n CSSProperties,\n MouseEvent,\n PointerEvent,\n ReactNode,\n} from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { rendersButton, useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { useComboboxContext } from \"./combobox-context.js\";\n\nexport interface ComboboxTriggerState {\n /** Optimistic open state. */\n readonly open: boolean;\n /** The open (or close) transition is in flight. */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface ComboboxTriggerProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n | \"onClick\"\n | \"onPointerDown\"\n | \"onMouseDown\"\n | \"onPointerCancel\"\n | \"tabIndex\"\n | \"aria-expanded\"\n | \"aria-controls\"\n | \"disabled\"\n | \"className\"\n | \"style\"\n | \"children\"\n> {\n
|
|
1
|
+
{"version":3,"file":"combobox-trigger.mjs","names":[],"sources":["../../src/combobox/combobox-trigger.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback, useRef } from \"react\";\nimport type {\n ComponentPropsWithRef,\n CSSProperties,\n MouseEvent,\n PointerEvent,\n ReactNode,\n} from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { isHostButton, rendersButton, useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { useComboboxContext } from \"./combobox-context.js\";\n\nexport interface ComboboxTriggerState {\n /** Optimistic open state. */\n readonly open: boolean;\n /** The open (or close) transition is in flight. */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface ComboboxTriggerProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n | \"onClick\"\n | \"onPointerDown\"\n | \"onMouseDown\"\n | \"onPointerCancel\"\n | \"tabIndex\"\n | \"aria-expanded\"\n | \"aria-controls\"\n | \"disabled\"\n | \"className\"\n | \"style\"\n | \"children\"\n> {\n /**\n * Disables this button; the Root's `disabled` does too. Native `disabled` on a `<button>`; with\n * a link or component `render`, `aria-disabled=\"true\"` instead.\n */\n disabled?: boolean;\n className?: StateValue<string | undefined, ComboboxTriggerState>;\n style?: StateValue<CSSProperties | undefined, ComboboxTriggerState>;\n /** Rendered from optimistic state; must not suspend. */\n children?: StateValue<ReactNode, ComboboxTriggerState>;\n /** Should render a `<button>`. */\n render?: RenderProp<ComponentPropsWithRef<\"button\">, ComboboxTriggerState>;\n}\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\n/**\n * Development check (design 9): an icon-only Trigger needs a name from the consumer. Checked a\n * microtask after the element attaches, once per instance.\n */\nexport function useNameWarning(part: string): (element: HTMLElement) => () => void {\n const warned = useRef(false);\n return useCallback(\n (element: HTMLElement) => {\n if (!isDev || warned.current) return () => {};\n queueMicrotask(() => {\n if (warned.current || !element.isConnected) return;\n if (element.hasAttribute(\"aria-label\") || element.hasAttribute(\"aria-labelledby\")) return;\n if ((element.textContent ?? \"\").trim() !== \"\" || element.hasAttribute(\"title\")) return;\n warned.current = true;\n console.error(`${part}: the button has no accessible name. Give it an \\`aria-label\\`.`);\n });\n return () => {};\n },\n [part],\n );\n}\n\n/**\n * The optional button that toggles the listbox: `<button type=\"button\" tabindex=\"-1\"\n * aria-expanded aria-controls>` (the APG keeps it out of the Tab sequence; the input is the one\n * stop). A press does not take focus from the input, and a click focuses the input. No\n * `commandfor`: the listbox is anchored to the input, never to this button (design 1).\n *\n * Owned keys: `type` (on a `<button>`), `tabIndex`, `disabled`, `aria-disabled` (link or\n * component `render`), `aria-expanded`,\n * `aria-controls` (token-merged), `onClick`, `onPointerDown`, `onPointerCancel`, `onMouseDown`,\n * `data-open`, `data-pending`, `data-disabled`.\n */\nexport function ComboboxTrigger({\n disabled: ownDisabled = false,\n className,\n style,\n children,\n render,\n ...rest\n}: ComboboxTriggerProps) {\n const ctx = useComboboxContext(\"Combobox.Trigger\");\n const { popover } = ctx;\n const disabled = ownDisabled || ctx.disabled;\n // What the user saw at the press: a press outside the popup light-dismisses it before the\n // click arrives, so the toggle is decided against the state at `pointerdown`.\n const openAtPress = useRef<boolean | null>(null);\n const ref = useNameWarning(\"Combobox.Trigger\");\n\n // A link or component render: native `disabled` would mean nothing on a link; the click\n // handler refuses, so `aria-disabled` (#83).\n const focusable = !isHostButton(render);\n\n const state: ComboboxTriggerState = {\n open: popover.optimistic,\n pending: popover.pending,\n disabled,\n };\n\n const libraryProps: ComponentPropsWithRef<\"button\"> & DataAttributes = {\n ...(rendersButton(render) && { type: \"button\" as const }),\n tabIndex: -1,\n disabled: focusable ? undefined : disabled,\n ...(focusable && { \"aria-disabled\": disabled ? true : undefined }),\n \"aria-expanded\": popover.optimistic,\n \"aria-controls\": ctx.ids.list,\n onPointerDown: (event: PointerEvent<HTMLButtonElement>) => {\n openAtPress.current = popover.intended();\n event.preventDefault();\n },\n onPointerCancel: () => {\n // The press never becomes a click.\n openAtPress.current = null;\n },\n onMouseDown: (event: MouseEvent<HTMLButtonElement>) => {\n event.preventDefault();\n },\n onClick: (event: MouseEvent<HTMLButtonElement>) => {\n // `detail === 0`: a click without a press (assistive technology, `element.click()`); a\n // snapshot left by a press that never clicked (dragged off) does not belong to it.\n const pressed = event.detail === 0 ? null : openAtPress.current;\n const wasOpen = pressed ?? popover.intended();\n openAtPress.current = null;\n if (disabled) {\n // A disabled link must not navigate either.\n event.preventDefault();\n return;\n }\n const native = event.nativeEvent;\n if (wasOpen) popover.requestClose(native, \"activate\");\n else popover.requestOpen(native, \"activate\");\n ctx.inputRef.current?.focus();\n },\n \"data-open\": dataAttr(popover.optimistic),\n \"data-pending\": dataAttr(popover.pending),\n \"data-disabled\": dataAttr(disabled),\n };\n\n return useRender<ComponentPropsWithRef<\"button\">, ComboboxTriggerState>({\n defaultElement: \"button\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps,\n ref,\n });\n}\n\nComboboxTrigger.displayName = \"ComboboxTrigger\";\n"],"mappings":";;;;;;;AAkDA,MAAM,QAAQ,QAAQ,IAAI,aAAa;;;;;AAMvC,SAAgB,eAAe,MAAoD;CACjF,MAAM,SAAS,OAAO,KAAK;CAC3B,OAAO,aACJ,YAAyB;EACxB,IAAI,CAAC,SAAS,OAAO,SAAS,aAAa,CAAC;EAC5C,qBAAqB;GACnB,IAAI,OAAO,WAAW,CAAC,QAAQ,aAAa;GAC5C,IAAI,QAAQ,aAAa,YAAY,KAAK,QAAQ,aAAa,iBAAiB,GAAG;GACnF,KAAK,QAAQ,eAAe,GAAA,CAAI,KAAK,MAAM,MAAM,QAAQ,aAAa,OAAO,GAAG;GAChF,OAAO,UAAU;GACjB,QAAQ,MAAM,GAAG,KAAK,gEAAgE;EACxF,CAAC;EACD,aAAa,CAAC;CAChB,GACA,CAAC,IAAI,CACP;AACF;;;;;;;;;;;;AAaA,SAAgB,gBAAgB,EAC9B,UAAU,cAAc,OACxB,WACA,OACA,UACA,QACA,GAAG,QACoB;CACvB,MAAM,MAAM,mBAAmB,kBAAkB;CACjD,MAAM,EAAE,YAAY;CACpB,MAAM,WAAW,eAAe,IAAI;CAGpC,MAAM,cAAc,OAAuB,IAAI;CAC/C,MAAM,MAAM,eAAe,kBAAkB;CAI7C,MAAM,YAAY,CAAC,aAAa,MAAM;CAEtC,MAAM,QAA8B;EAClC,MAAM,QAAQ;EACd,SAAS,QAAQ;EACjB;CACF;CAEA,MAAM,eAAiE;EACrE,GAAI,cAAc,MAAM,KAAK,EAAE,MAAM,SAAkB;EACvD,UAAU;EACV,UAAU,YAAY,KAAA,IAAY;EAClC,GAAI,aAAa,EAAE,iBAAiB,WAAW,OAAO,KAAA,EAAU;EAChE,iBAAiB,QAAQ;EACzB,iBAAiB,IAAI,IAAI;EACzB,gBAAgB,UAA2C;GACzD,YAAY,UAAU,QAAQ,SAAS;GACvC,MAAM,eAAe;EACvB;EACA,uBAAuB;GAErB,YAAY,UAAU;EACxB;EACA,cAAc,UAAyC;GACrD,MAAM,eAAe;EACvB;EACA,UAAU,UAAyC;GAIjD,MAAM,WADU,MAAM,WAAW,IAAI,OAAO,YAAY,YAC7B,QAAQ,SAAS;GAC5C,YAAY,UAAU;GACtB,IAAI,UAAU;IAEZ,MAAM,eAAe;IACrB;GACF;GACA,MAAM,SAAS,MAAM;GACrB,IAAI,SAAS,QAAQ,aAAa,QAAQ,UAAU;QAC/C,QAAQ,YAAY,QAAQ,UAAU;GAC3C,IAAI,SAAS,SAAS,MAAM;EAC9B;EACA,aAAa,SAAS,QAAQ,UAAU;EACxC,gBAAgB,SAAS,QAAQ,OAAO;EACxC,iBAAiB,SAAS,QAAQ;CACpC;CAEA,OAAO,UAAiE;EACtE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;EACA;CACF,CAAC;AACH;AAEA,gBAAgB,cAAc"}
|
|
@@ -24,7 +24,11 @@ export interface DialogCloseProps extends Omit<ComponentPropsWithRef<"button">,
|
|
|
24
24
|
action?: Action<[details: DialogCloseDetails]>;
|
|
25
25
|
/** Becomes `dialog.returnValue` (and `details.returnValue` of `openChangeAction(false)`). */
|
|
26
26
|
value?: string;
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Never closes. Native `disabled` on a `<button>`; owned, so a `render` element cannot
|
|
29
|
+
* re-enable it. With a link or component `render` whose click this part handles (library mode,
|
|
30
|
+
* or with `action`), `aria-disabled="true"` instead: the element stays focusable.
|
|
31
|
+
*/
|
|
28
32
|
disabled?: boolean;
|
|
29
33
|
className?: StateValue<string | undefined, DialogCloseState>;
|
|
30
34
|
style?: StateValue<CSSProperties | undefined, DialogCloseState>;
|
|
@@ -37,10 +41,11 @@ export interface DialogCloseProps extends Omit<ComponentPropsWithRef<"button">,
|
|
|
37
41
|
* `value` as the `returnValue`. In library mode the click is always cancelled and the close goes
|
|
38
42
|
* through the Root (`openChangeAction(false)` awaited, then `close(value)` in the commit, inside
|
|
39
43
|
* the view transition if the Popup's `transition` is set). With `action` the action runs first in
|
|
40
|
-
* this part's own transition (`data-pending` on the button
|
|
44
|
+
* this part's own transition (`data-pending` and `aria-disabled` on the button; a second click
|
|
45
|
+
* meanwhile is ignored); a rejection leaves the dialog open.
|
|
41
46
|
*
|
|
42
47
|
* Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `commandfor`,
|
|
43
|
-
* `command`, `value`, `onClick` (library mode, or with `action`), `data-pending`,
|
|
48
|
+
* `command`, `value`, `onClick` (library mode, or with `action`), `aria-disabled`, `data-pending`,
|
|
44
49
|
* `data-disabled`.
|
|
45
50
|
*/
|
|
46
51
|
export declare function DialogClose({ action, value, disabled, className, style, children, render, ...rest }: DialogCloseProps): ReactNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog-close.d.mts","names":[],"sources":["../../src/dialog/dialog-close.tsx"],"mappings":";;;iBAWiB;;WAEN;WACA;;;iBAIM,2BAA2B;;WAEjC;;iBAGM,yBAAyB,KACxC;;;;;;;;;;EAYA,SAAS,QAAQ,SAAS;;EAE1B
|
|
1
|
+
{"version":3,"file":"dialog-close.d.mts","names":[],"sources":["../../src/dialog/dialog-close.tsx"],"mappings":";;;iBAWiB;;WAEN;WACA;;;iBAIM,2BAA2B;;WAEjC;;iBAGM,yBAAyB,KACxC;;;;;;;;;;EAYA,SAAS,QAAQ,SAAS;;EAE1B;;;;;;EAMA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,iCAAiC;;;;;;;;;;;;;;;wBAkBvC,cACd,QACA,OACA,UACA,WACA,OACA,UACA,WACG,QACF,mBAAgB;yBATH"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createActionDetails } from "../internal/action-details.mjs";
|
|
3
3
|
import { dataAttr } from "../internal/data-attr.mjs";
|
|
4
4
|
import { useActionTrigger } from "../internal/use-action-trigger.mjs";
|
|
5
|
-
import { rendersButton } from "../internal/render-element.mjs";
|
|
5
|
+
import { isHostButton, rendersButton } from "../internal/render-element.mjs";
|
|
6
6
|
import { useRender } from "../internal/use-render.mjs";
|
|
7
7
|
import { useDialogContext } from "./dialog-context.mjs";
|
|
8
8
|
import { useCallback } from "react";
|
|
@@ -14,18 +14,21 @@ const isDev = process.env.NODE_ENV !== "production";
|
|
|
14
14
|
* `value` as the `returnValue`. In library mode the click is always cancelled and the close goes
|
|
15
15
|
* through the Root (`openChangeAction(false)` awaited, then `close(value)` in the commit, inside
|
|
16
16
|
* the view transition if the Popup's `transition` is set). With `action` the action runs first in
|
|
17
|
-
* this part's own transition (`data-pending` on the button
|
|
17
|
+
* this part's own transition (`data-pending` and `aria-disabled` on the button; a second click
|
|
18
|
+
* meanwhile is ignored); a rejection leaves the dialog open.
|
|
18
19
|
*
|
|
19
20
|
* Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `commandfor`,
|
|
20
|
-
* `command`, `value`, `onClick` (library mode, or with `action`), `data-pending`,
|
|
21
|
+
* `command`, `value`, `onClick` (library mode, or with `action`), `aria-disabled`, `data-pending`,
|
|
21
22
|
* `data-disabled`.
|
|
22
23
|
*/
|
|
23
24
|
function DialogClose({ action, value, disabled = false, className, style, children, render, ...rest }) {
|
|
24
25
|
const ctx = useDialogContext("Dialog.Close");
|
|
25
|
-
const { pending, run } = useActionTrigger(ctx.openGeneration);
|
|
26
|
+
const { pending, run } = useActionTrigger({ openGeneration: ctx.openGeneration });
|
|
26
27
|
const { ids, dialogRef, recordCloseIntent } = ctx;
|
|
27
28
|
const isLibrary = ctx.mode === "library";
|
|
28
29
|
const installClick = isLibrary || action !== void 0;
|
|
30
|
+
const focusable = installClick && !isHostButton(render);
|
|
31
|
+
const noInvoker = focusable && disabled;
|
|
29
32
|
const state = {
|
|
30
33
|
pending,
|
|
31
34
|
disabled
|
|
@@ -61,11 +64,12 @@ function DialogClose({ action, value, disabled = false, className, style, childr
|
|
|
61
64
|
};
|
|
62
65
|
const libraryProps = {
|
|
63
66
|
...rendersButton(render) && { type: "button" },
|
|
64
|
-
disabled,
|
|
65
|
-
commandfor: ids.dialog,
|
|
66
|
-
command: "request-close",
|
|
67
|
+
disabled: focusable ? void 0 : disabled,
|
|
68
|
+
commandfor: noInvoker ? void 0 : ids.dialog,
|
|
69
|
+
command: noInvoker ? void 0 : "request-close",
|
|
67
70
|
value,
|
|
68
71
|
...installClick && { onClick },
|
|
72
|
+
"aria-disabled": pending || focusable && disabled ? true : void 0,
|
|
69
73
|
"data-pending": dataAttr(pending),
|
|
70
74
|
"data-disabled": dataAttr(disabled)
|
|
71
75
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog-close.mjs","names":[],"sources":["../../src/dialog/dialog-close.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, MouseEvent, ReactNode } from \"react\";\nimport { createActionDetails } from \"../internal/action-details.js\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { Action, ActionDetails, RenderProp, StateValue } from \"../internal/types.js\";\nimport { useActionTrigger } from \"../internal/use-action-trigger.js\";\nimport { rendersButton, useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { useDialogContext } from \"./dialog-context.js\";\n\nexport interface DialogCloseState {\n /** The `action` is in flight. */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\n/** Details passed to `Dialog.Close`'s `action`. */\nexport interface DialogCloseDetails extends ActionDetails {\n /** The `value` the dialog closes with (`dialog.returnValue`); `\"\"` without `value`. */\n readonly returnValue: string;\n}\n\nexport interface DialogCloseProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"onClick\" | \"value\" | \"className\" | \"style\" | \"children\"\n> {\n /**\n * Runs inside this part's own transition (`data-pending` on the button), then closes the\n * dialog with `value` as `returnValue`: in library mode through the Root\n * (`openChangeAction(false)` awaited, view transition if set), in JS-free mode with\n * `close(value)`. A rejection reaches the nearest Error Boundary above the Close part and\n * leaves the dialog open. If the dialog was dismissed and opened again while the action ran,\n * the close is dropped: it belonged to the earlier open. Without `action` in JS-free mode the\n * button is a native `command=\"request-close\"` invoker.\n */\n action?: Action<[details: DialogCloseDetails]>;\n /** Becomes `dialog.returnValue` (and `details.returnValue` of `openChangeAction(false)`). */\n value?: string;\n
|
|
1
|
+
{"version":3,"file":"dialog-close.mjs","names":[],"sources":["../../src/dialog/dialog-close.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, MouseEvent, ReactNode } from \"react\";\nimport { createActionDetails } from \"../internal/action-details.js\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { Action, ActionDetails, RenderProp, StateValue } from \"../internal/types.js\";\nimport { useActionTrigger } from \"../internal/use-action-trigger.js\";\nimport { isHostButton, rendersButton, useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { useDialogContext } from \"./dialog-context.js\";\n\nexport interface DialogCloseState {\n /** The `action` is in flight. */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\n/** Details passed to `Dialog.Close`'s `action`. */\nexport interface DialogCloseDetails extends ActionDetails {\n /** The `value` the dialog closes with (`dialog.returnValue`); `\"\"` without `value`. */\n readonly returnValue: string;\n}\n\nexport interface DialogCloseProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"onClick\" | \"value\" | \"className\" | \"style\" | \"children\"\n> {\n /**\n * Runs inside this part's own transition (`data-pending` on the button), then closes the\n * dialog with `value` as `returnValue`: in library mode through the Root\n * (`openChangeAction(false)` awaited, view transition if set), in JS-free mode with\n * `close(value)`. A rejection reaches the nearest Error Boundary above the Close part and\n * leaves the dialog open. If the dialog was dismissed and opened again while the action ran,\n * the close is dropped: it belonged to the earlier open. Without `action` in JS-free mode the\n * button is a native `command=\"request-close\"` invoker.\n */\n action?: Action<[details: DialogCloseDetails]>;\n /** Becomes `dialog.returnValue` (and `details.returnValue` of `openChangeAction(false)`). */\n value?: string;\n /**\n * Never closes. Native `disabled` on a `<button>`; owned, so a `render` element cannot\n * re-enable it. With a link or component `render` whose click this part handles (library mode,\n * or with `action`), `aria-disabled=\"true\"` instead: the element stays focusable.\n */\n disabled?: boolean;\n className?: StateValue<string | undefined, DialogCloseState>;\n style?: StateValue<CSSProperties | undefined, DialogCloseState>;\n children?: StateValue<ReactNode, DialogCloseState>;\n render?: RenderProp<ComponentPropsWithRef<\"button\">, DialogCloseState>;\n}\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\n/**\n * A close button: `<button type=\"button\" commandfor command=\"request-close\">`. In JS-free mode\n * without `action` the browser closes the dialog (a close request: `cancel`, then `close`), with\n * `value` as the `returnValue`. In library mode the click is always cancelled and the close goes\n * through the Root (`openChangeAction(false)` awaited, then `close(value)` in the commit, inside\n * the view transition if the Popup's `transition` is set). With `action` the action runs first in\n * this part's own transition (`data-pending` and `aria-disabled` on the button; a second click\n * meanwhile is ignored); a rejection leaves the dialog open.\n *\n * Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `commandfor`,\n * `command`, `value`, `onClick` (library mode, or with `action`), `aria-disabled`, `data-pending`,\n * `data-disabled`.\n */\nexport function DialogClose({\n action,\n value,\n disabled = false,\n className,\n style,\n children,\n render,\n ...rest\n}: DialogCloseProps) {\n const ctx = useDialogContext(\"Dialog.Close\");\n // The open-generation guard: a close from a previous opening must not close a new one.\n const { pending, run } = useActionTrigger({ openGeneration: ctx.openGeneration });\n const { ids, dialogRef, recordCloseIntent } = ctx;\n const isLibrary = ctx.mode === \"library\";\n const hasAction = action !== undefined;\n const installClick = isLibrary || hasAction;\n // A link or component render whose click this part handles: `aria-disabled`, enforced by the\n // handler (#83). JS-free mode without `action` is the browser's `request-close` command,\n // which only native `disabled` stops.\n const focusable = installClick && !isHostButton(render);\n // Disabled in the `aria-disabled` branch: no `commandfor` / `command` either. Without native\n // `disabled` the element would still be a live invoker before hydration (a component render\n // spreading onto a real <button>), and the command would run (wave 9 review M1).\n const noInvoker = focusable && disabled;\n\n const state: DialogCloseState = { pending, disabled };\n\n const devRef = useCallback(\n (element: HTMLElement) => {\n if (isDev && !installClick && element.tagName !== \"BUTTON\") {\n console.error(\n `Dialog.Close: rendered a <${element.tagName.toLowerCase()}> in JS-free mode without ` +\n '`action`. Only a <button> runs `command=\"request-close\"`; this element will not ' +\n \"close the dialog.\",\n );\n }\n // A cleanup, so the merged ref never calls back with `null`.\n return () => {};\n },\n [installClick],\n );\n\n const close = (event: Event, returnValue: string): void => {\n if (isLibrary) {\n // A library-owned close (design 2.3): awaited, then `close(returnValue)` in the commit. A\n // no-op when `false` is already intended (a repeated click, or a dismissal meanwhile).\n ctx.requestClose(event, \"activate\", returnValue);\n return;\n }\n // JS-free mode with `action`: a browser-owned close; there is no Root state to go through.\n const dialog = dialogRef.current;\n if (dialog === null || !dialog.open) return;\n recordCloseIntent({ reason: \"activate\", event });\n dialog.close(returnValue);\n };\n\n const onClick = (event: MouseEvent<HTMLElement>) => {\n // Cancelling the click cancels the command.\n event.preventDefault();\n if (disabled) return;\n const native = event.nativeEvent;\n const returnValue = value ?? \"\";\n if (action === undefined) {\n close(native, returnValue);\n return;\n }\n const details = createActionDetails(native, { returnValue });\n // A second activation while this Close's own run is in flight starts nothing (the hook's\n // guard; the click is already cancelled above, #81). A close whose open was superseded by a\n // newer one (dismissed and re-opened meanwhile) is dropped by the hook.\n run(\n () => action(details),\n () => close(native, returnValue),\n );\n };\n\n const libraryProps: ComponentPropsWithRef<\"button\"> & DataAttributes = {\n ...(rendersButton(render) && { type: \"button\" as const }),\n // Owned: a render element must not re-enable a disabled Close. Absent in the focusable branch.\n disabled: focusable ? undefined : disabled,\n commandfor: noInvoker ? undefined : ids.dialog,\n command: noInvoker ? undefined : \"request-close\",\n value,\n ...(installClick && { onClick }),\n // Exactly while `data-pending` (#81): a Close that will not act is unavailable to AT. Owned,\n // so an idle Close removes a render element's own `aria-disabled`.\n \"aria-disabled\": pending || (focusable && disabled) ? true : undefined,\n \"data-pending\": dataAttr(pending),\n \"data-disabled\": dataAttr(disabled),\n };\n\n return useRender<ComponentPropsWithRef<\"button\">, DialogCloseState>({\n defaultElement: \"button\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps,\n ref: devRef,\n });\n}\n\nDialogClose.displayName = \"DialogClose\";\n"],"mappings":";;;;;;;;;AAmDA,MAAM,QAAQ,QAAQ,IAAI,aAAa;;;;;;;;;;;;;;AAevC,SAAgB,YAAY,EAC1B,QACA,OACA,WAAW,OACX,WACA,OACA,UACA,QACA,GAAG,QACgB;CACnB,MAAM,MAAM,iBAAiB,cAAc;CAE3C,MAAM,EAAE,SAAS,QAAQ,iBAAiB,EAAE,gBAAgB,IAAI,eAAe,CAAC;CAChF,MAAM,EAAE,KAAK,WAAW,sBAAsB;CAC9C,MAAM,YAAY,IAAI,SAAS;CAE/B,MAAM,eAAe,aADH,WAAW,KAAA;CAK7B,MAAM,YAAY,gBAAgB,CAAC,aAAa,MAAM;CAItD,MAAM,YAAY,aAAa;CAE/B,MAAM,QAA0B;EAAE;EAAS;CAAS;CAEpD,MAAM,SAAS,aACZ,YAAyB;EACxB,IAAI,SAAS,CAAC,gBAAgB,QAAQ,YAAY,UAChD,QAAQ,MACN,6BAA6B,QAAQ,QAAQ,YAAY,EAAE,gIAG7D;EAGF,aAAa,CAAC;CAChB,GACA,CAAC,YAAY,CACf;CAEA,MAAM,SAAS,OAAc,gBAA8B;EACzD,IAAI,WAAW;GAGb,IAAI,aAAa,OAAO,YAAY,WAAW;GAC/C;EACF;EAEA,MAAM,SAAS,UAAU;EACzB,IAAI,WAAW,QAAQ,CAAC,OAAO,MAAM;EACrC,kBAAkB;GAAE,QAAQ;GAAY;EAAM,CAAC;EAC/C,OAAO,MAAM,WAAW;CAC1B;CAEA,MAAM,WAAW,UAAmC;EAElD,MAAM,eAAe;EACrB,IAAI,UAAU;EACd,MAAM,SAAS,MAAM;EACrB,MAAM,cAAc,SAAS;EAC7B,IAAI,WAAW,KAAA,GAAW;GACxB,MAAM,QAAQ,WAAW;GACzB;EACF;EACA,MAAM,UAAU,oBAAoB,QAAQ,EAAE,YAAY,CAAC;EAI3D,UACQ,OAAO,OAAO,SACd,MAAM,QAAQ,WAAW,CACjC;CACF;CAEA,MAAM,eAAiE;EACrE,GAAI,cAAc,MAAM,KAAK,EAAE,MAAM,SAAkB;EAEvD,UAAU,YAAY,KAAA,IAAY;EAClC,YAAY,YAAY,KAAA,IAAY,IAAI;EACxC,SAAS,YAAY,KAAA,IAAY;EACjC;EACA,GAAI,gBAAgB,EAAE,QAAQ;EAG9B,iBAAiB,WAAY,aAAa,WAAY,OAAO,KAAA;EAC7D,gBAAgB,SAAS,OAAO;EAChC,iBAAiB,SAAS,QAAQ;CACpC;CAEA,OAAO,UAA6D;EAClE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;EACA,KAAK;CACP,CAAC;AACH;AAEA,YAAY,cAAc"}
|
|
@@ -13,7 +13,11 @@ export interface DialogTriggerState {
|
|
|
13
13
|
readonly disabled: boolean;
|
|
14
14
|
}
|
|
15
15
|
export interface DialogTriggerProps extends Omit<ComponentPropsWithRef<"button">, "onClick" | "className" | "style" | "children"> {
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Never dispatches. Native `disabled` on a `<button>` (no longer an invoker); with a link or
|
|
18
|
+
* component `render` where the Trigger handles the click (library mode, or a non-modal
|
|
19
|
+
* dialog), `aria-disabled="true"` instead: the element stays focusable.
|
|
20
|
+
*/
|
|
17
21
|
disabled?: boolean;
|
|
18
22
|
className?: StateValue<string | undefined, DialogTriggerState>;
|
|
19
23
|
style?: StateValue<CSSProperties | undefined, DialogTriggerState>;
|
|
@@ -29,8 +33,9 @@ export interface DialogTriggerProps extends Omit<ComponentPropsWithRef<"button">
|
|
|
29
33
|
* `data-pending` until the dialog has opened. Non-modal dialogs have no command and always use a
|
|
30
34
|
* click handler (`show()`).
|
|
31
35
|
*
|
|
32
|
-
* Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `
|
|
33
|
-
*
|
|
36
|
+
* Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `aria-disabled` (a
|
|
37
|
+
* link or component `render` with the click handler installed), `commandfor`, `command`,
|
|
38
|
+
* `onClick`, `data-open`, `data-pending`, `data-disabled`.
|
|
34
39
|
*/
|
|
35
40
|
export declare function DialogTrigger({ disabled, className, style, children, render, ...rest }: DialogTriggerProps): ReactNode;
|
|
36
41
|
export declare namespace DialogTrigger {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog-trigger.d.mts","names":[],"sources":["../../src/dialog/dialog-trigger.tsx"],"mappings":";;;iBASiB;;WAEN;;;;;;WAMA;WACA;;iBAGM,2BAA2B,KAC1C
|
|
1
|
+
{"version":3,"file":"dialog-trigger.d.mts","names":[],"sources":["../../src/dialog/dialog-trigger.tsx"],"mappings":";;;iBASiB;;WAEN;;;;;;WAMA;WACA;;iBAGM,2BAA2B,KAC1C;;;;;;EAQA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;EAE9C,WAAW,WAAW,WAAW;;EAEjC,SAAS,WAAW,iCAAiC;;;;;;;;;;;;;wBAgBvC,gBACd,UACA,WACA,OACA,UACA,WACG,QACF,qBAAkB;yBAPL"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { dataAttr } from "../internal/data-attr.mjs";
|
|
3
|
-
import { rendersButton } from "../internal/render-element.mjs";
|
|
3
|
+
import { isHostButton, rendersButton } from "../internal/render-element.mjs";
|
|
4
4
|
import { useRender } from "../internal/use-render.mjs";
|
|
5
5
|
import { useDialogContext } from "./dialog-context.mjs";
|
|
6
6
|
import { useCallback, useEffect } from "react";
|
|
@@ -13,8 +13,9 @@ const isDev = process.env.NODE_ENV !== "production";
|
|
|
13
13
|
* `data-pending` until the dialog has opened. Non-modal dialogs have no command and always use a
|
|
14
14
|
* click handler (`show()`).
|
|
15
15
|
*
|
|
16
|
-
* Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `
|
|
17
|
-
*
|
|
16
|
+
* Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `aria-disabled` (a
|
|
17
|
+
* link or component `render` with the click handler installed), `commandfor`, `command`,
|
|
18
|
+
* `onClick`, `data-open`, `data-pending`, `data-disabled`.
|
|
18
19
|
*/
|
|
19
20
|
function DialogTrigger({ disabled = false, className, style, children, render, ...rest }) {
|
|
20
21
|
const ctx = useDialogContext("Dialog.Trigger");
|
|
@@ -41,6 +42,8 @@ function DialogTrigger({ disabled = false, className, style, children, render, .
|
|
|
41
42
|
if (document.getElementById(ids.dialog) === null) console.error("Dialog.Trigger: no Dialog.Popup is rendered for this Dialog.Root; the trigger opens nothing.");
|
|
42
43
|
}, [ids.dialog]);
|
|
43
44
|
const installClick = isLibrary || !modal;
|
|
45
|
+
const focusable = installClick && !isHostButton(render);
|
|
46
|
+
const invoker = modal && !(focusable && disabled);
|
|
44
47
|
const onClick = (event) => {
|
|
45
48
|
if (isLibrary) {
|
|
46
49
|
event.preventDefault();
|
|
@@ -48,19 +51,23 @@ function DialogTrigger({ disabled = false, className, style, children, render, .
|
|
|
48
51
|
ctx.requestOpen(event.nativeEvent, "activate");
|
|
49
52
|
return;
|
|
50
53
|
}
|
|
51
|
-
if (disabled)
|
|
54
|
+
if (disabled) {
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
52
58
|
const dialog = dialogRef.current;
|
|
53
59
|
if (dialog !== null && !dialog.open && dialog.isConnected) dialog.show();
|
|
54
60
|
};
|
|
55
61
|
const libraryProps = {
|
|
56
62
|
...rendersButton(render) && { type: "button" },
|
|
57
|
-
disabled,
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
disabled: focusable ? void 0 : disabled,
|
|
64
|
+
...focusable && { "aria-disabled": disabled ? true : void 0 },
|
|
65
|
+
commandfor: invoker ? ids.dialog : void 0,
|
|
66
|
+
command: invoker ? "show-modal" : void 0,
|
|
60
67
|
...installClick && { onClick },
|
|
61
68
|
"data-open": dataAttr(state.open),
|
|
62
69
|
"data-pending": dataAttr(state.pending),
|
|
63
|
-
"data-disabled": dataAttr(
|
|
70
|
+
"data-disabled": dataAttr(disabled)
|
|
64
71
|
};
|
|
65
72
|
return useRender({
|
|
66
73
|
defaultElement: "button",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog-trigger.mjs","names":[],"sources":["../../src/dialog/dialog-trigger.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback, useEffect } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, MouseEvent, ReactNode } from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { rendersButton, useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { useDialogContext } from \"./dialog-context.js\";\n\nexport interface DialogTriggerState {\n /** Optimistic open state (library mode); `false` in JS-free mode. */\n readonly open: boolean;\n /**\n * The Root's transition is pending: an open in flight, a close a part asked for in flight\n * (`openChangeAction(false)` awaited while the dialog is still open), a close notification in\n * flight, or a re-open requested while a close notification is still settling.\n */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface DialogTriggerProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"onClick\" | \"className\" | \"style\" | \"children\"\n> {\n
|
|
1
|
+
{"version":3,"file":"dialog-trigger.mjs","names":[],"sources":["../../src/dialog/dialog-trigger.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback, useEffect } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, MouseEvent, ReactNode } from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { isHostButton, rendersButton, useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { useDialogContext } from \"./dialog-context.js\";\n\nexport interface DialogTriggerState {\n /** Optimistic open state (library mode); `false` in JS-free mode. */\n readonly open: boolean;\n /**\n * The Root's transition is pending: an open in flight, a close a part asked for in flight\n * (`openChangeAction(false)` awaited while the dialog is still open), a close notification in\n * flight, or a re-open requested while a close notification is still settling.\n */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface DialogTriggerProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"onClick\" | \"className\" | \"style\" | \"children\"\n> {\n /**\n * Never dispatches. Native `disabled` on a `<button>` (no longer an invoker); with a link or\n * component `render` where the Trigger handles the click (library mode, or a non-modal\n * dialog), `aria-disabled=\"true\"` instead: the element stays focusable.\n */\n disabled?: boolean;\n className?: StateValue<string | undefined, DialogTriggerState>;\n style?: StateValue<CSSProperties | undefined, DialogTriggerState>;\n /** Rendered from optimistic state; must not suspend. */\n children?: StateValue<ReactNode, DialogTriggerState>;\n /** Should render a `<button>`; another element works only in library mode (click handler). */\n render?: RenderProp<ComponentPropsWithRef<\"button\">, DialogTriggerState>;\n}\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\n/**\n * The opener: `<button type=\"button\" commandfor command=\"show-modal\">`. In JS-free mode the\n * browser runs the command. In library mode the click is cancelled (so the command does not run)\n * and dispatched through the Root's transition; the button shows optimistic `data-open` and\n * `data-pending` until the dialog has opened. Non-modal dialogs have no command and always use a\n * click handler (`show()`).\n *\n * Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `aria-disabled` (a\n * link or component `render` with the click handler installed), `commandfor`, `command`,\n * `onClick`, `data-open`, `data-pending`, `data-disabled`.\n */\nexport function DialogTrigger({\n disabled = false,\n className,\n style,\n children,\n render,\n ...rest\n}: DialogTriggerProps) {\n const ctx = useDialogContext(\"Dialog.Trigger\");\n const isLibrary = ctx.mode === \"library\";\n const { modal, ids, triggerRef, dialogRef } = ctx;\n\n const state: DialogTriggerState = {\n open: ctx.optimistic,\n // Every Root dispatch is an open, a part-initiated close or a dismissal notification, and\n // all of them belong to the Trigger (design 4.2). Not `optimistic !== committed`: a re-open\n // while a close notification is still settling has both `true` (the close never committed)\n // and is pending all the same.\n pending: ctx.pending,\n disabled,\n };\n\n const libraryRef = useCallback(\n (element: HTMLElement) => {\n if (isDev && !isLibrary && modal && element.tagName !== \"BUTTON\") {\n console.error(\n `Dialog.Trigger: rendered a <${element.tagName.toLowerCase()}> in JS-free mode. Only a ` +\n '<button> runs `command=\"show-modal\"`; this trigger will not open the dialog. Render ' +\n \"a <button>, or put the Dialog in library mode (`defaultOpen={false}`).\",\n );\n }\n triggerRef.current = element;\n return () => {\n if (triggerRef.current === element) triggerRef.current = null;\n };\n },\n [isLibrary, modal, triggerRef],\n );\n\n // A Trigger without a Popup opens nothing (design 14, dev warnings).\n useEffect(() => {\n if (!isDev) return;\n if (document.getElementById(ids.dialog) === null) {\n console.error(\n \"Dialog.Trigger: no Dialog.Popup is rendered for this Dialog.Root; the trigger opens nothing.\",\n );\n }\n }, [ids.dialog]);\n\n // Library mode (both modal and non-modal) and non-modal JS-free mode need a click handler;\n // modal JS-free mode is the browser's command.\n const installClick = isLibrary || !modal;\n // A link or component render whose click this part handles: native `disabled` would mean\n // nothing on a link; the handler refuses and cancels, so `aria-disabled` is safe (#83). The\n // modal JS-free command needs native `disabled` to stop it.\n const focusable = installClick && !isHostButton(render);\n // Disabled in the `aria-disabled` branch: no `commandfor` / `command` either. Without native\n // `disabled` the element would still be a live invoker before hydration (a component render\n // spreading onto a real <button>), and the command would run (wave 9 review M1).\n const invoker = modal && !(focusable && disabled);\n const onClick = (event: MouseEvent<HTMLElement>) => {\n if (isLibrary) {\n // Cancelling the click cancels the command (it runs as the button's activation\n // behaviour, only if the click was not cancelled).\n event.preventDefault();\n if (disabled) return;\n ctx.requestOpen(event.nativeEvent, \"activate\");\n return;\n }\n if (disabled) {\n // A disabled link must not navigate either.\n event.preventDefault();\n return;\n }\n const dialog = dialogRef.current;\n if (dialog !== null && !dialog.open && dialog.isConnected) dialog.show();\n };\n\n const libraryProps: ComponentPropsWithRef<\"button\"> & DataAttributes = {\n // `type` only applies to a <button>; on any other element the key is not owned.\n ...(rendersButton(render) && { type: \"button\" as const }),\n // Owned: a render element must not re-enable a disabled Trigger (in JS-free mode the\n // browser would run its command). Owned as absent in the focusable branch.\n disabled: focusable ? undefined : disabled,\n ...(focusable && { \"aria-disabled\": disabled ? true : undefined }),\n commandfor: invoker ? ids.dialog : undefined,\n command: invoker ? \"show-modal\" : undefined,\n // Only owned when installed, so a render element keeps its own `onClick` in modal JS-free\n // mode (Accordion's rule).\n ...(installClick && { onClick }),\n \"data-open\": dataAttr(state.open),\n \"data-pending\": dataAttr(state.pending),\n // Every mode: the styling hook agrees with the rendered `disabled` / `aria-disabled`\n // (conventions 1.2, wave 9 fold W8).\n \"data-disabled\": dataAttr(disabled),\n };\n\n return useRender<ComponentPropsWithRef<\"button\">, DialogTriggerState>({\n defaultElement: \"button\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps,\n ref: libraryRef,\n });\n}\n\nDialogTrigger.displayName = \"DialogTrigger\";\n"],"mappings":";;;;;;;AAuCA,MAAM,QAAQ,QAAQ,IAAI,aAAa;;;;;;;;;;;;AAavC,SAAgB,cAAc,EAC5B,WAAW,OACX,WACA,OACA,UACA,QACA,GAAG,QACkB;CACrB,MAAM,MAAM,iBAAiB,gBAAgB;CAC7C,MAAM,YAAY,IAAI,SAAS;CAC/B,MAAM,EAAE,OAAO,KAAK,YAAY,cAAc;CAE9C,MAAM,QAA4B;EAChC,MAAM,IAAI;EAKV,SAAS,IAAI;EACb;CACF;CAEA,MAAM,aAAa,aAChB,YAAyB;EACxB,IAAI,SAAS,CAAC,aAAa,SAAS,QAAQ,YAAY,UACtD,QAAQ,MACN,+BAA+B,QAAQ,QAAQ,YAAY,EAAE,yLAG/D;EAEF,WAAW,UAAU;EACrB,aAAa;GACX,IAAI,WAAW,YAAY,SAAS,WAAW,UAAU;EAC3D;CACF,GACA;EAAC;EAAW;EAAO;CAAU,CAC/B;CAGA,gBAAgB;EACd,IAAI,CAAC,OAAO;EACZ,IAAI,SAAS,eAAe,IAAI,MAAM,MAAM,MAC1C,QAAQ,MACN,8FACF;CAEJ,GAAG,CAAC,IAAI,MAAM,CAAC;CAIf,MAAM,eAAe,aAAa,CAAC;CAInC,MAAM,YAAY,gBAAgB,CAAC,aAAa,MAAM;CAItD,MAAM,UAAU,SAAS,EAAE,aAAa;CACxC,MAAM,WAAW,UAAmC;EAClD,IAAI,WAAW;GAGb,MAAM,eAAe;GACrB,IAAI,UAAU;GACd,IAAI,YAAY,MAAM,aAAa,UAAU;GAC7C;EACF;EACA,IAAI,UAAU;GAEZ,MAAM,eAAe;GACrB;EACF;EACA,MAAM,SAAS,UAAU;EACzB,IAAI,WAAW,QAAQ,CAAC,OAAO,QAAQ,OAAO,aAAa,OAAO,KAAK;CACzE;CAEA,MAAM,eAAiE;EAErE,GAAI,cAAc,MAAM,KAAK,EAAE,MAAM,SAAkB;EAGvD,UAAU,YAAY,KAAA,IAAY;EAClC,GAAI,aAAa,EAAE,iBAAiB,WAAW,OAAO,KAAA,EAAU;EAChE,YAAY,UAAU,IAAI,SAAS,KAAA;EACnC,SAAS,UAAU,eAAe,KAAA;EAGlC,GAAI,gBAAgB,EAAE,QAAQ;EAC9B,aAAa,SAAS,MAAM,IAAI;EAChC,gBAAgB,SAAS,MAAM,OAAO;EAGtC,iBAAiB,SAAS,QAAQ;CACpC;CAEA,OAAO,UAA+D;EACpE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;EACA,KAAK;CACP,CAAC;AACH;AAEA,cAAc,cAAc"}
|
|
@@ -7,7 +7,10 @@ export interface FormSubmitState {
|
|
|
7
7
|
readonly disabled: boolean;
|
|
8
8
|
}
|
|
9
9
|
export interface FormSubmitProps extends Omit<ComponentPropsWithRef<"button">, "type" | "className" | "style" | "children"> {
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Native `disabled`; owned. Never set by the library (conventions 3.6). Always native, also
|
|
12
|
+
* with a component `render`: a submitter has no handler to honour `aria-disabled` (#83).
|
|
13
|
+
*/
|
|
11
14
|
disabled?: boolean;
|
|
12
15
|
className?: StateValue<string | undefined, FormSubmitState>;
|
|
13
16
|
style?: StateValue<CSSProperties | undefined, FormSubmitState>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-submit.d.mts","names":[],"sources":["../../src/form/form-submit.tsx"],"mappings":";;;iBAUiB;;WAEN;WACA;;iBAGM,wBAAwB,KACvC
|
|
1
|
+
{"version":3,"file":"form-submit.d.mts","names":[],"sources":["../../src/form/form-submit.tsx"],"mappings":";;;iBAUiB;;WAEN;WACA;;iBAGM,wBAAwB,KACvC;;;;;EAOA;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"}
|
|
@@ -1 +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
|
|
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 /**\n * Native `disabled`; owned. Never set by the library (conventions 3.6). Always native, also\n * with a component `render`: a submitter has no handler to honour `aria-disabled` (#83).\n */\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;;;;;;;;;;AAgCvC,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"}
|