@funstack/ui 0.1.0-alpha.5 → 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/accordion/accordion-item.mjs +2 -2
- package/dist/accordion/accordion-panel.mjs +2 -2
- package/dist/accordion/accordion-root.mjs +3 -3
- package/dist/accordion/accordion-trigger.mjs +2 -2
- package/dist/button/button.d.mts +78 -0
- package/dist/button/button.d.mts.map +1 -0
- package/dist/button/button.mjs +77 -0
- package/dist/button/button.mjs.map +1 -0
- package/dist/button/index.d.mts +3 -0
- package/dist/button/index.mjs +1 -0
- package/dist/checkbox/checkbox.mjs +2 -2
- package/dist/checkbox-group/checkbox-group-item.mjs +1 -1
- package/dist/checkbox-group/checkbox-group-parent.mjs +1 -1
- package/dist/checkbox-group/checkbox-group-root.mjs +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-group.mjs +1 -1
- package/dist/combobox/combobox-list.mjs +1 -1
- package/dist/combobox/combobox-option.mjs +1 -1
- package/dist/combobox/combobox-popup.mjs +2 -2
- package/dist/combobox/combobox-root.mjs +3 -3
- 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 +12 -8
- package/dist/dialog/dialog-close.mjs.map +1 -1
- package/dist/dialog/dialog-form.mjs +1 -1
- package/dist/dialog/dialog-popup.mjs +3 -3
- package/dist/dialog/dialog-root.mjs +2 -2
- 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/field/field-root.mjs +3 -3
- package/dist/form/form-root.mjs +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/index.d.mts +3 -2
- package/dist/index.mjs +2 -1
- package/dist/internal/render-element.mjs +18 -5
- package/dist/internal/render-element.mjs.map +1 -1
- package/dist/internal/use-action-trigger.mjs +17 -6
- package/dist/internal/use-action-trigger.mjs.map +1 -1
- package/dist/internal/use-field-control.mjs +1 -1
- package/dist/internal/use-popover-root.mjs +1 -1
- package/dist/internal/use-popover-trigger.mjs +6 -5
- package/dist/internal/use-popover-trigger.mjs.map +1 -1
- package/dist/internal/use-render.mjs +2 -2
- package/dist/internal/use-render.mjs.map +1 -1
- package/dist/menu/menu-checkbox-item.mjs +2 -2
- package/dist/menu/menu-dom.mjs +7 -1
- package/dist/menu/menu-dom.mjs.map +1 -1
- package/dist/menu/menu-group.mjs +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 +6 -4
- package/dist/menu/menu-item.mjs.map +1 -1
- package/dist/menu/menu-popup.mjs +1 -1
- package/dist/menu/menu-radio-group.mjs +1 -1
- package/dist/menu/menu-root.mjs +1 -1
- package/dist/menu/menu-submenu-trigger.mjs +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 +3 -3
- 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 +9 -6
- package/dist/number-field/number-field-increment.mjs.map +1 -1
- package/dist/number-field/number-field-input.mjs +1 -1
- package/dist/number-field/number-field-root.mjs +5 -5
- 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 +13 -8
- package/dist/popover/popover-close.mjs.map +1 -1
- package/dist/popover/popover-popup.mjs +2 -2
- 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/radio-group/radio-group-item.mjs +1 -1
- package/dist/radio-group/radio-group-root.mjs +1 -1
- package/dist/select/select-root.mjs +1 -1
- package/dist/slider/slider-root.mjs +6 -6
- package/dist/slider/slider-root.mjs.map +1 -1
- package/dist/slider/slider-thumb.mjs +1 -1
- package/dist/slider/slider-value.mjs +1 -1
- package/dist/switch/switch.mjs +2 -2
- package/dist/tabs/tabs-list.mjs +1 -1
- package/dist/tabs/tabs-panel.mjs +1 -1
- package/dist/tabs/tabs-root.mjs +2 -2
- package/dist/tabs/tabs-tab.mjs +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 +8 -6
- package/dist/toast/toast-action.mjs.map +1 -1
- package/dist/toast/toast-root.mjs +1 -1
- package/dist/toast/toast-viewport.mjs +3 -3
- package/dist/toggle/toggle.d.mts +4 -1
- package/dist/toggle/toggle.d.mts.map +1 -1
- package/dist/toggle/toggle.mjs +16 -10
- 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 +10 -5
- package/dist/toggle-group/toggle-group-item.mjs.map +1 -1
- package/dist/toggle-group/toggle-group-root.mjs +3 -3
- package/dist/toolbar/index.d.mts +4 -5
- package/dist/toolbar/index.d.mts.map +1 -1
- package/dist/toolbar/index.mjs +3 -4
- package/dist/toolbar/index.mjs.map +1 -1
- package/dist/toolbar/toolbar-root.mjs +1 -1
- package/dist/toolbar/toolbar-separator.mjs +1 -1
- package/package.json +2 -1
- package/dist/toolbar/toolbar-button.d.mts +0 -42
- package/dist/toolbar/toolbar-button.d.mts.map +0 -1
- package/dist/toolbar/toolbar-button.mjs +0 -50
- package/dist/toolbar/toolbar-button.mjs.map +0 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { createActionDetails } from "../internal/action-details.mjs";
|
|
3
3
|
import { dataAttr } from "../internal/data-attr.mjs";
|
|
4
|
+
import { useActionTrigger } from "../internal/use-action-trigger.mjs";
|
|
5
|
+
import { useDevWarning } from "../internal/use-dev-warning.mjs";
|
|
6
|
+
import { mergeTokenLists } from "../internal/merge-props.mjs";
|
|
7
|
+
import { useRender } from "../internal/use-render.mjs";
|
|
4
8
|
import { transitionTypes } from "../internal/transition-types.mjs";
|
|
5
9
|
import { useLatest } from "../internal/use-latest.mjs";
|
|
6
10
|
import { useControllableAction } from "../internal/use-controllable-action.mjs";
|
|
7
|
-
import { mergeTokenLists } from "../internal/merge-props.mjs";
|
|
8
|
-
import { useRender } from "../internal/use-render.mjs";
|
|
9
|
-
import { useDevWarning } from "../internal/use-dev-warning.mjs";
|
|
10
|
-
import { useActionTrigger } from "../internal/use-action-trigger.mjs";
|
|
11
11
|
import { FieldContext } from "../internal/field-context.mjs";
|
|
12
12
|
import { useFieldValueChange } from "../internal/use-field-control.mjs";
|
|
13
13
|
import { useNativeGroupReconciliation } from "../internal/use-native-group-reconciliation.mjs";
|
|
@@ -112,7 +112,7 @@ function NumberFieldRoot(props) {
|
|
|
112
112
|
}
|
|
113
113
|
mirror();
|
|
114
114
|
}, [mirror]);
|
|
115
|
-
const { pending: stepPending, run: runStep } = useActionTrigger();
|
|
115
|
+
const { pending: stepPending, run: runStep } = useActionTrigger({ ignoreWhilePending: false });
|
|
116
116
|
const runCommit = async (next, details) => {
|
|
117
117
|
const seq = ++commitSeq.current;
|
|
118
118
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"number-field-root.mjs","names":[],"sources":["../../src/number-field/number-field-root.tsx"],"sourcesContent":["\"use client\";\nimport {\n addTransitionType,\n use,\n useCallback,\n useEffect,\n useId,\n useInsertionEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode, RefObject } from \"react\";\nimport { createActionDetails } from \"../internal/action-details.js\";\nimport { announce as speak } from \"../internal/announce.js\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport { FieldContext } from \"../internal/field-context.js\";\nimport { mergeTokenLists } from \"../internal/merge-props.js\";\nimport { transitionTypes } from \"../internal/transition-types.js\";\nimport type {\n ActionDetails,\n ChangeAction,\n ChangeReason,\n RenderProp,\n StateValue,\n} from \"../internal/types.js\";\nimport { useActionTrigger } from \"../internal/use-action-trigger.js\";\nimport { useControllableAction } from \"../internal/use-controllable-action.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { useFieldValueChange } from \"../internal/use-field-control.js\";\nimport type { FormAssociatedElement } from \"../internal/use-form-reset.js\";\nimport { useLatest } from \"../internal/use-latest.js\";\nimport { useNativeGroupReconciliation } from \"../internal/use-native-group-reconciliation.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { boundValue, stepBy, stepValue } from \"./number-field-arithmetic.js\";\nimport type { StepBounds } from \"./number-field-arithmetic.js\";\nimport { NumberFieldContext } from \"./number-field-context.js\";\nimport type {\n NumberFieldContextValue,\n NumberFieldSource,\n StepDirection,\n} from \"./number-field-context.js\";\nimport { createNumberFormatter, formatKey } from \"./number-field-format.js\";\n\n/** Details passed as the second argument of both NumberField actions. */\nexport interface NumberFieldValueChangeDetails extends ActionDetails {\n /**\n * `\"activate\"`: the user typed, pressed a key, a button or turned the wheel (`details.event` is\n * the `focusout` / Enter `keydown` of a typed value, the `keydown` of a key, the `pointerdown` /\n * `pointerup` of a button (`undefined` for the repeats of a held button and for a hold that\n * ended at its bound), the `wheel` event). `\"platform\"`: a form reset (the `reset`\n * event) or text typed before hydration (`undefined`). `\"dismiss\"`: the commit action rejected\n * and the field reverted to the committed value (uncontrolled; `valueChangeAction` only).\n */\n readonly reason: ChangeReason;\n /** What produced the value: typed text, a button, a key, the wheel; `null` for \"platform\" and \"dismiss\". */\n readonly source: \"input\" | \"increment\" | \"decrement\" | \"keyboard\" | \"wheel\" | null;\n}\n\n/** The state every NumberField part's function props receive (one object, Field's precedent). */\nexport interface NumberFieldState {\n /** The shown value: the gesture's value while one is open or in flight, else the optimistic value; `null` when empty. */\n readonly value: number | null;\n /** The shown text: the draft while the user is editing, else `value` formatted. */\n readonly text: string;\n /** A step or commit dispatched through this NumberField is in flight. */\n readonly pending: boolean;\n readonly disabled: boolean;\n readonly readOnly: boolean;\n readonly required: boolean;\n}\n\nexport interface NumberFieldRootProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n \"defaultValue\" | \"role\" | \"className\" | \"style\" | \"children\"\n> {\n /** Controlled value; `null` is empty, `undefined` uncontrolled. */\n value?: number | null;\n /** Initial value in uncontrolled mode, and its form-reset target (latest). Default `null` (empty). */\n defaultValue?: number | null;\n /**\n * Runs inside the Root's step transition on every step: the parse of typed text at commit,\n * each key press, each tick of a hold, each wheel event, each platform change, and the revert\n * after a rejected commit (`reason: \"dismiss\"`, uncontrolled). Keep it cheap: adopt the value\n * (`setValue(next)` in the sync prefix is a transition update), start a deferred render.\n */\n valueChangeAction?: ChangeAction<number | null, NumberFieldValueChangeDetails>;\n /**\n * Runs inside the Root's commit transition once per gesture: a typed text's blur or Enter, a\n * hold's release, each key press, each wheel event, a form reset, text typed before hydration.\n * Where the save goes; uncontrolled state is committed when it resolves and reverts when it\n * rejects. May be a Server Function.\n */\n valueCommitAction?: ChangeAction<number | null, NumberFieldValueChangeDetails>;\n /** Native `min` on the hidden input (a constraint) and the lower bound of stepping. */\n min?: number;\n /** Native `max` on the hidden input (a constraint) and the upper bound of stepping. */\n max?: number;\n /** Native `step` and the stepping grid from `min` (else `0`). Default `1`, or `0.01` when `format.style` is `\"percent\"`. */\n step?: number;\n /** PageUp / PageDown and Shift+Arrow. Default `10 × step`. */\n largeStep?: number;\n /**\n * `Intl.NumberFormatOptions` for the text: `style` `\"decimal\"` (default), `\"percent\"`,\n * `\"currency\"` (with `currency`), `\"unit\"` (with `unit`); fraction digits, grouping, sign\n * display. `notation: \"compact\"` is a dev warning (unparsable).\n */\n format?: Intl.NumberFormatOptions;\n /**\n * BCP 47 tag(s). Default: the runtime's locale (`Intl.NumberFormat().resolvedOptions().locale`).\n * Pass the page's `lang`; a dev warning fires when `document.documentElement.lang` disagrees\n * and this prop is absent.\n */\n locale?: string | readonly string[];\n /** Step with the mouse wheel while the input is focused. Default `false`. */\n stepOnWheel?: boolean;\n /** Native `name` on the hidden input; defaults to the Field's `name` inside a `Field.Root`. */\n name?: string;\n /** Native `form` on the hidden input. */\n form?: string;\n /** Disables both inputs and the buttons; inherited by every part. */\n disabled?: boolean;\n /** Native `readonly` on both inputs; no stepping (buttons disabled, keys and wheel inert). */\n readOnly?: boolean;\n /** Native `required` on the hidden input (empty is `valueMissing`); or the Field's. */\n required?: boolean;\n /**\n * What is announced (`ariaNotify()`, else a live region) after a key press, a wheel event, a\n * click activation and a hold's release. Default: `(_, state) => state.text`. Return `null` to\n * announce nothing.\n */\n announce?: (value: number | null, state: NumberFieldState) => string | null;\n className?: StateValue<string | undefined, NumberFieldState>;\n style?: StateValue<CSSProperties | undefined, NumberFieldState>;\n children?: StateValue<ReactNode, NumberFieldState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, NumberFieldState>;\n}\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\nconst STEP_TYPES = transitionTypes(\"number-field\", \"change\");\nconst COMMIT_TYPES = transitionTypes(\"number-field\", \"commit\");\n\n/** A finite number, or `null` (`NaN` / `Infinity` are treated as empty). */\nfunction sanitize(value: number | null | undefined): number | null | undefined {\n if (value === undefined || value === null) return value;\n return Number.isFinite(value) ? value : null;\n}\n\n/** The hidden input's value for `value`. */\nexport function hiddenText(value: number | null): string {\n return value === null ? \"\" : String(value);\n}\n\nfunction languageOf(tag: string): string {\n return tag.split(\"-\")[0]?.toLowerCase() ?? \"\";\n}\n\n/**\n * Owns the number field's state (design/components/number-field.md 2): the committed value\n * through `useControllableAction` with `valueCommitAction` (once per gesture), each step through\n * its own transition (`valueChangeAction`), the urgent draft text, the urgent value of the open\n * gesture (`live`), the press-and-hold, the formatter and parser, the announcements, mount\n * reconciliation and form reset (through the hidden input the Input renders).\n *\n * Owned keys (conventions 1.2): `role=\"group\"`, `aria-labelledby` (token-merged: the consumer's,\n * then `NumberField.Label`'s id while one is mounted, then a Field's label id), `data-pending`,\n * `data-disabled`, `data-invalid` (inside a Field).\n */\nexport function NumberFieldRoot(props: NumberFieldRootProps) {\n const {\n value: valueProp,\n defaultValue: defaultValueProp,\n valueChangeAction,\n valueCommitAction,\n min,\n max,\n step: stepProp,\n largeStep: largeStepProp,\n format,\n locale,\n stepOnWheel = false,\n name: nameProp,\n form,\n disabled: disabledProp = false,\n readOnly = false,\n required: requiredProp = false,\n announce,\n className,\n style,\n children,\n render,\n ...rest\n } = props;\n\n const value = sanitize(valueProp);\n const defaultValue = sanitize(defaultValueProp);\n\n // --- Format, bounds -----------------------------------------------------------------------\n\n const key = formatKey(format);\n const localeKey = locale === undefined ? \"\" : JSON.stringify(locale);\n // Memoised on the content of `format` and `locale`, not their identity (an inline object).\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const formatter = useMemo(() => createNumberFormatter(locale, format), [localeKey, key]);\n const defaultStep = formatter.percent ? 0.01 : 1;\n const stepValid = stepProp === undefined || (Number.isFinite(stepProp) && stepProp > 0);\n const stepSize = stepProp !== undefined && stepValid ? stepProp : defaultStep;\n const largeStepValid =\n largeStepProp === undefined || (Number.isFinite(largeStepProp) && largeStepProp > 0);\n const largeStep = largeStepProp !== undefined && largeStepValid ? largeStepProp : 10 * stepSize;\n const bounds = useMemo<StepBounds>(\n () => ({\n min: Number.isFinite(min) ? min : undefined,\n max: Number.isFinite(max) ? max : undefined,\n step: stepSize,\n }),\n [min, max, stepSize],\n );\n\n // --- Field ----------------------------------------------------------------------------------\n\n const fieldContext = use(FieldContext);\n const disabled = disabledProp || (fieldContext?.disabled ?? false);\n const required = requiredProp || (fieldContext?.required ?? false);\n const name = nameProp ?? fieldContext?.name;\n const fieldInvalid = fieldContext === null ? undefined : fieldContext.state.invalid;\n\n const id = useId();\n const inputId = fieldContext?.ids.control ?? `${id}-input`;\n const labelId = `${id}-label`;\n\n // --- State (design 2) -----------------------------------------------------------------------\n\n const [draft, setDraft] = useState<string | null>(null);\n const draftRef = useRef<string | null>(null);\n /** The value the open gesture has reached (`null` inside: an empty value). */\n const [live, setLive] = useState<{ readonly value: number | null } | null>(null);\n const [hold, setHold] = useState<StepDirection | null>(null);\n const [stepHeld, setStepHeld] = useState(false);\n const [pendingSource, setPendingSource] = useState<NumberFieldSource>(null);\n\n const liveRef = useRef<number | null>(null);\n /** The shown value when the open gesture began; `null` when no gesture is open. */\n const gestureStart = useRef<{ readonly value: number | null } | null>(null);\n /** The value the next tick of a hold steps from. */\n const holdBase = useRef<number | null>(null);\n const holdRef = useRef<StepDirection | null>(null);\n const commitSeq = useRef(0);\n const lastReason = useRef<ChangeReason | null>(null);\n const shownRef = useRef<number | null>(null);\n const visibleRef = useRef<HTMLInputElement | null>(null);\n const hiddenRef = useRef<HTMLInputElement | null>(null);\n\n const clearDraft = useCallback(() => {\n if (draftRef.current === null) return;\n draftRef.current = null;\n setDraft(null);\n }, []);\n\n /** The visible input is invalid exactly when the hidden one is (6.2). */\n const mirror = useCallback(() => {\n const hidden = hiddenRef.current;\n const visible = visibleRef.current;\n if (hidden === null || visible === null) return;\n const message = hidden.validationMessage;\n if (visible.validationMessage !== message) visible.setCustomValidity(message);\n }, []);\n\n const writeHidden = useCallback(\n (next: number | null) => {\n const hidden = hiddenRef.current;\n if (hidden !== null) {\n const text = hiddenText(next);\n if (hidden.value !== text) hidden.value = text;\n }\n mirror();\n },\n [mirror],\n );\n\n const { pending: stepPending, run: runStep } = useActionTrigger();\n\n const runCommit = async (\n next: number | null,\n details: NumberFieldValueChangeDetails,\n ): Promise<void> => {\n const seq = ++commitSeq.current;\n try {\n await valueCommitAction?.(next, details);\n } catch (error) {\n // Design 2.4: in uncontrolled mode, tell the preview channel about the rollback, for the\n // latest commit only. Yield once to leave the commit's transition (conventions 2.6).\n if (valueProp === undefined && seq === commitSeq.current) {\n await undefined;\n // `step` and `committedRef` are declared below; this runs long after the render.\n step(committedRef.current, null, \"dismiss\", undefined);\n }\n throw error;\n }\n };\n\n const commitState = useControllableAction<number | null, NumberFieldValueChangeDetails>({\n controlled: value,\n defaultValue,\n initialValue: null,\n action: runCommit,\n transitionTypes: () => COMMIT_TYPES,\n warn: { component: \"NumberField.Root\", prop: \"value\" },\n });\n const { optimistic, committed, dispatch } = commitState;\n const committedRef = useLatest(committed);\n\n const held = hold !== null || stepHeld || stepPending || commitState.pending;\n const shown = held && live !== null ? live.value : optimistic;\n const pending = stepPending || commitState.pending;\n const text = draft ?? formatter.format(shown);\n // The value a button press steps from (4.3 step 3): the parsed draft while one is open.\n const parsedDraft = draft === null ? undefined : formatter.parse(draft);\n const stepBase = parsedDraft === undefined ? shown : parsedDraft;\n useInsertionEffect(() => {\n shownRef.current = shown;\n });\n\n const state = useMemo<NumberFieldState>(\n () => ({ value: shown, text, pending, disabled, readOnly, required }),\n [shown, text, pending, disabled, readOnly, required],\n );\n\n // A step, a key and a typed commit fire no `input` / `change` on the element the Field hears\n // for the value (the hidden input is written by script): tell it at the commit (design 7.2).\n useFieldValueChange(optimistic, () => {\n const edit = lastReason.current === \"activate\";\n lastReason.current = null;\n return edit;\n });\n\n const latest = useLatest({\n valueChangeAction,\n runStep,\n formatter,\n bounds,\n largeStep,\n disabled,\n readOnly,\n announce,\n state,\n });\n\n // --- Step and commit (design 2.4) -----------------------------------------------------------\n\n const step = useCallback(\n (\n next: number | null,\n source: NumberFieldSource,\n reason: ChangeReason,\n event: Event | undefined,\n ): boolean => {\n // What the text can show is what the form posts (5.4): a step finer than the format's\n // digits is rounded like a typed value.\n const { formatter: f } = latest.current;\n if (next !== null) next = f.round(next);\n // No-op against what the user sees (conventions 2.3 item 8).\n if (Object.is(next, shownRef.current)) return false;\n liveRef.current = next;\n shownRef.current = next;\n lastReason.current = reason;\n setLive({ value: next });\n clearDraft();\n setPendingSource(source);\n writeHidden(next);\n // A revert has no commit after it: the optimistic value already shows the committed one.\n if (reason !== \"dismiss\") setStepHeld(true);\n const { valueChangeAction: action, runStep: run } = latest.current;\n // Without a step action nothing runs and no transition starts (Slider M2).\n if (action === undefined) return true;\n run(() => {\n for (const type of STEP_TYPES) addTransitionType(type);\n return action(next, createActionDetails(event, { reason, source }));\n });\n return true;\n },\n [clearDraft, latest, writeHidden],\n );\n\n const announceValue = useCallback(\n (next: number | null) => {\n const visible = visibleRef.current;\n if (visible === null) return;\n const { announce: compose, formatter: f, state: s } = latest.current;\n const nextState: NumberFieldState = { ...s, value: next, text: f.format(next) };\n const message = compose === undefined ? nextState.text : compose(next, nextState);\n if (message !== null) speak(visible, message);\n },\n [latest],\n );\n\n const commit = useCallback(\n (source: NumberFieldSource, reason: ChangeReason, event: Event | undefined): boolean => {\n const start = gestureStart.current;\n gestureStart.current = null;\n // The commit's own pending state holds `live` from here (set in this same batch).\n setStepHeld(false);\n const next = liveRef.current;\n // Nothing changed since the gesture began (a hold that returned to its start).\n if (start === null || Object.is(next, start.value)) return false;\n lastReason.current = reason;\n setPendingSource(source);\n dispatch(next, createActionDetails(event, { reason, source }));\n if (reason === \"activate\" && source !== \"input\" && source !== null) announceValue(next);\n return true;\n },\n [announceValue, dispatch],\n );\n\n /** Opens a gesture at the shown value. */\n const openGesture = useCallback(() => {\n gestureStart.current = { value: shownRef.current };\n liveRef.current = shownRef.current;\n }, []);\n\n /** The value a key or a button steps from: the parsed draft if one is open, else the shown value. */\n const resolveBase = useCallback((): number | null => {\n const open = draftRef.current;\n if (open !== null) {\n const parsed = latest.current.formatter.parse(open);\n clearDraft();\n if (parsed !== undefined) return parsed;\n }\n return shownRef.current;\n }, [clearDraft, latest]);\n\n /** A one-step gesture: a key press, a wheel event, a click activation. */\n const oneStep = useCallback(\n (compute: (base: number | null) => number | null, source: NumberFieldSource, event: Event) => {\n const base = resolveBase();\n openGesture();\n step(compute(base), source, \"activate\", event);\n commit(source, \"activate\", event);\n },\n [commit, openGesture, resolveBase, step],\n );\n\n // --- Typing (design 4.1) ----------------------------------------------------------------------\n\n const typed = useCallback((next: string) => {\n draftRef.current = next;\n setDraft(next);\n }, []);\n\n const commitDraft = useCallback(\n (event: Event) => {\n const open = draftRef.current;\n if (open === null) return;\n const { formatter: f, disabled: off, readOnly: ro } = latest.current;\n if (off || ro) {\n clearDraft();\n return;\n }\n const parsed = f.parse(open);\n // Unparsable: revert to the committed value's text, no dispatch, no error.\n if (parsed === undefined || Object.is(parsed, shownRef.current)) {\n clearDraft();\n return;\n }\n openGesture();\n step(parsed, \"input\", \"activate\", event);\n commit(\"input\", \"activate\", event);\n },\n [clearDraft, commit, latest, openGesture, step],\n );\n\n // --- Keyboard and wheel (design 4.2, 4.4) ----------------------------------------------------\n\n const keyDown = useCallback(\n (event: KeyboardEvent) => {\n const { disabled: off, readOnly: ro, bounds: b, largeStep: large } = latest.current;\n // WebKit fires `compositionend` before the confirming keydown, which then has\n // `isComposing === false` and `keyCode === 229` (Combobox's guard).\n if (off || ro || event.isComposing || event.keyCode === 229) return;\n if (event.key === \"Enter\") {\n commitDraft(event);\n return;\n }\n if (event.altKey || event.ctrlKey || event.metaKey) return;\n let compute: ((base: number | null) => number | null) | null = null;\n switch (event.key) {\n case \"ArrowUp\":\n compute = event.shiftKey ? (v) => stepBy(v, large, b) : (v) => stepValue(v, 1, b);\n break;\n case \"ArrowDown\":\n compute = event.shiftKey ? (v) => stepBy(v, -large, b) : (v) => stepValue(v, -1, b);\n break;\n case \"PageUp\":\n compute = (v) => stepBy(v, large, b);\n break;\n case \"PageDown\":\n compute = (v) => stepBy(v, -large, b);\n break;\n case \"Home\": {\n // Shift+Home / Shift+End are text selection.\n if (event.shiftKey) break;\n const target = boundValue(\"min\", b);\n if (target !== undefined) compute = () => target;\n break;\n }\n case \"End\": {\n if (event.shiftKey) break;\n const target = boundValue(\"max\", b);\n if (target !== undefined) compute = () => target;\n break;\n }\n default:\n break;\n }\n if (compute === null) return;\n event.preventDefault();\n oneStep(compute, \"keyboard\", event);\n },\n [commitDraft, latest, oneStep],\n );\n\n const wheel = useCallback(\n (event: WheelEvent) => {\n const { disabled: off, readOnly: ro, bounds: b } = latest.current;\n const visible = visibleRef.current;\n if (off || ro || visible === null || event.deltaY === 0) return;\n // Only while the input is focused; unfocused, the page scrolls.\n if (visible.ownerDocument.activeElement !== visible) return;\n event.preventDefault();\n const n = event.deltaY < 0 ? 1 : -1;\n oneStep((v) => stepValue(v, n, b), \"wheel\", event);\n },\n [latest, oneStep],\n );\n\n // --- Buttons (design 4.3) ---------------------------------------------------------------------\n\n const endHold = useCallback(\n (direction: StepDirection, event: Event | undefined) => {\n if (holdRef.current !== direction) return;\n holdRef.current = null;\n setHold(null);\n commit(direction, \"activate\", event);\n },\n [commit],\n );\n\n const tickHold = useCallback(\n (direction: StepDirection, event: Event | undefined): boolean => {\n if (holdRef.current !== direction) return false;\n const { bounds: b } = latest.current;\n const n = direction === \"increment\" ? 1 : -1;\n const next = stepValue(holdBase.current, n, b);\n holdBase.current = next;\n step(next, direction, \"activate\", event);\n // At the bound: end the hold now, before the button re-renders disabled (a disabled\n // element may not deliver the release).\n if (stepValue(next, n, b) === next) {\n endHold(direction, event);\n return false;\n }\n return true;\n },\n [endHold, latest, step],\n );\n\n const beginHold = useCallback(\n (direction: StepDirection, event: Event): boolean => {\n const { disabled: off, readOnly: ro } = latest.current;\n if (off || ro || holdRef.current !== null) return false;\n const base = resolveBase();\n openGesture();\n // A capture alone holds `live` on screen: start it from what is shown.\n setLive({ value: shownRef.current });\n holdBase.current = base;\n holdRef.current = direction;\n setHold(direction);\n return tickHold(direction, event);\n },\n [latest, openGesture, resolveBase, tickHold],\n );\n\n const activate = useCallback(\n (direction: StepDirection, event: Event) => {\n const { disabled: off, readOnly: ro, bounds: b } = latest.current;\n if (off || ro || holdRef.current !== null) return;\n const n = direction === \"increment\" ? 1 : -1;\n oneStep((v) => stepValue(v, n, b), direction, event);\n },\n [latest, oneStep],\n );\n\n // --- Labels ---------------------------------------------------------------------------------\n\n const [labelCount, setLabelCount] = useState(0);\n const registerLabel = useCallback(() => {\n setLabelCount((count) => count + 1);\n return () => setLabelCount((count) => count - 1);\n }, []);\n\n // --- Residual browser paths: mount reconciliation and form reset (design 4.5, 6.4) ----------\n\n const [mountValue] = useState(value);\n const resetTarget = (): number | null =>\n valueProp !== undefined ? (mountValue ?? null) : (defaultValue ?? null);\n const resetTargetText = hiddenText(resetTarget());\n\n const { onItemMounted } = useNativeGroupReconciliation<number | null>({\n element: hiddenRef as RefObject<FormAssociatedElement | null>,\n readCurrent: () => {\n const visible = visibleRef.current;\n const current = shownRef.current;\n if (visible === null) return current;\n const domText = visible.value;\n if (domText === formatter.format(current)) return current;\n if (visible.ownerDocument.activeElement === visible) {\n // Still typing (even a partial `12,` or `-`): adopted as the draft, never overwritten\n // under the caret. The blur commits it, or reverts it by the ordinary rule.\n draftRef.current = domText;\n setDraft(domText);\n return current;\n }\n const parsed = formatter.parse(domText);\n if (parsed === undefined || Object.is(parsed, current)) {\n // Another locale's formatting of the same value, or text that is no number: show the\n // client's text (react-dom kept the hydrated DOM value).\n visible.value = formatter.format(current);\n return current;\n }\n return parsed;\n },\n readDefault: () => {\n // A reset clears an open draft whatever the target.\n clearDraft();\n return resetTarget();\n },\n base: () => shownRef.current,\n onPlatformChange: (next, event) => {\n openGesture();\n step(next, null, \"platform\", event);\n commit(null, \"platform\", event);\n },\n onResetEvent: () => {\n // react-dom rewrites a controlled text input's `defaultValue` after every update; the\n // visible text's reset target is written right before the reset algorithm reads it.\n const visible = visibleRef.current;\n if (visible !== null) visible.defaultValue = formatter.format(resetTarget());\n },\n });\n\n // --- Development warnings -------------------------------------------------------------------\n\n useDevWarning(\n (valueProp !== undefined && valueProp !== null && !Number.isFinite(valueProp)) ||\n (defaultValueProp !== undefined &&\n defaultValueProp !== null &&\n !Number.isFinite(defaultValueProp)),\n \"NumberField.Root: `value` / `defaultValue` must be a finite number or `null`; \" +\n `got ${String(valueProp ?? defaultValueProp)}, treated as empty.`,\n );\n useDevWarning(\n bounds.min !== undefined && bounds.max !== undefined && bounds.min > bounds.max,\n `NumberField.Root: \\`min\\` (${String(min)}) is greater than \\`max\\` (${String(max)}).`,\n );\n useDevWarning(\n !stepValid || !largeStepValid,\n \"NumberField.Root: `step` and `largeStep` must be positive numbers; using the defaults.\",\n );\n useDevWarning(formatter.warning !== null, `NumberField.Root: ${formatter.warning ?? \"\"}`);\n const warnedLang = useRef(false);\n useEffect(() => {\n if (!isDev || warnedLang.current || locale !== undefined) return;\n const lang = document.documentElement.lang;\n if (lang === \"\" || languageOf(lang) === languageOf(formatter.locale)) return;\n warnedLang.current = true;\n console.error(\n `NumberField.Root: the page is \\`lang=\"${lang}\"\\` but NumberField formats as ` +\n `\\`${formatter.locale}\\` (the runtime's locale); pass \\`locale\\`.`,\n );\n }, [locale, formatter]);\n\n // An unmount mid-hold leaves nothing behind (the buttons detach their own listeners).\n useEffect(\n () => () => {\n holdRef.current = null;\n },\n [],\n );\n\n // --- Render ---------------------------------------------------------------------------------\n\n const context = useMemo<NumberFieldContextValue>(\n () => ({\n state,\n bounds,\n stepBase,\n pendingSource: pending ? pendingSource : null,\n inputId,\n labelId,\n name,\n form,\n allowsFractions: formatter.allowsFractions || !Number.isInteger(stepSize),\n stepOnWheel,\n resetTargetText,\n fieldInvalid,\n visibleRef,\n hiddenRef,\n typed,\n commitDraft,\n keyDown,\n wheel,\n beginHold,\n tickHold,\n endHold,\n activate,\n mirror,\n registerLabel,\n onInputMounted: onItemMounted,\n }),\n [\n state,\n bounds,\n stepBase,\n pending,\n pendingSource,\n inputId,\n labelId,\n name,\n form,\n formatter,\n stepSize,\n stepOnWheel,\n resetTargetText,\n fieldInvalid,\n typed,\n commitDraft,\n keyDown,\n wheel,\n beginHold,\n tickHold,\n endHold,\n activate,\n mirror,\n registerLabel,\n onItemMounted,\n ],\n );\n\n const labelledBy = mergeTokenLists(labelCount > 0 ? labelId : undefined, fieldContext?.ids.label);\n const libraryProps: Partial<ComponentPropsWithRef<\"div\">> & DataAttributes = {\n role: \"group\",\n ...(labelledBy !== \"\" ? { \"aria-labelledby\": labelledBy } : {}),\n ...(fieldContext !== null ? { \"data-invalid\": dataAttr(fieldInvalid === true) } : {}),\n \"data-pending\": dataAttr(pending),\n \"data-disabled\": dataAttr(disabled),\n };\n\n const element = useRender<ComponentPropsWithRef<\"div\">, NumberFieldState>({\n defaultElement: \"div\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps,\n });\n\n return <NumberFieldContext value={context}>{element}</NumberFieldContext>;\n}\n\nNumberFieldRoot.displayName = \"NumberFieldRoot\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA2IA,MAAM,QAAQ,QAAQ,IAAI,aAAa;AAEvC,MAAM,aAAa,gBAAgB,gBAAgB,QAAQ;AAC3D,MAAM,eAAe,gBAAgB,gBAAgB,QAAQ;;AAG7D,SAAS,SAAS,OAA6D;CAC7E,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM,OAAO;CAClD,OAAO,OAAO,SAAS,KAAK,IAAI,QAAQ;AAC1C;;AAGA,SAAgB,WAAW,OAA8B;CACvD,OAAO,UAAU,OAAO,KAAK,OAAO,KAAK;AAC3C;AAEA,SAAS,WAAW,KAAqB;CACvC,OAAO,IAAI,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,YAAY,KAAK;AAC7C;;;;;;;;;;;;AAaA,SAAgB,gBAAgB,OAA6B;CAC3D,MAAM,EACJ,OAAO,WACP,cAAc,kBACd,mBACA,mBACA,KACA,KACA,MAAM,UACN,WAAW,eACX,QACA,QACA,cAAc,OACd,MAAM,UACN,MACA,UAAU,eAAe,OACzB,WAAW,OACX,UAAU,eAAe,OACzB,UAAA,YACA,WACA,OACA,UACA,QACA,GAAG,SACD;CAEJ,MAAM,QAAQ,SAAS,SAAS;CAChC,MAAM,eAAe,SAAS,gBAAgB;CAI9C,MAAM,MAAM,UAAU,MAAM;CAC5B,MAAM,YAAY,WAAW,KAAA,IAAY,KAAK,KAAK,UAAU,MAAM;CAGnE,MAAM,YAAY,cAAc,sBAAsB,QAAQ,MAAM,GAAG,CAAC,WAAW,GAAG,CAAC;CACvF,MAAM,cAAc,UAAU,UAAU,MAAO;CAC/C,MAAM,YAAY,aAAa,KAAA,KAAc,OAAO,SAAS,QAAQ,KAAK,WAAW;CACrF,MAAM,WAAW,aAAa,KAAA,KAAa,YAAY,WAAW;CAClE,MAAM,iBACJ,kBAAkB,KAAA,KAAc,OAAO,SAAS,aAAa,KAAK,gBAAgB;CACpF,MAAM,YAAY,kBAAkB,KAAA,KAAa,iBAAiB,gBAAgB,KAAK;CACvF,MAAM,SAAS,eACN;EACL,KAAK,OAAO,SAAS,GAAG,IAAI,MAAM,KAAA;EAClC,KAAK,OAAO,SAAS,GAAG,IAAI,MAAM,KAAA;EAClC,MAAM;CACR,IACA;EAAC;EAAK;EAAK;CAAQ,CACrB;CAIA,MAAM,eAAe,IAAI,YAAY;CACrC,MAAM,WAAW,iBAAiB,cAAc,YAAY;CAC5D,MAAM,WAAW,iBAAiB,cAAc,YAAY;CAC5D,MAAM,OAAO,YAAY,cAAc;CACvC,MAAM,eAAe,iBAAiB,OAAO,KAAA,IAAY,aAAa,MAAM;CAE5E,MAAM,KAAK,MAAM;CACjB,MAAM,UAAU,cAAc,IAAI,WAAW,GAAG,GAAG;CACnD,MAAM,UAAU,GAAG,GAAG;CAItB,MAAM,CAAC,OAAO,YAAY,SAAwB,IAAI;CACtD,MAAM,WAAW,OAAsB,IAAI;;CAE3C,MAAM,CAAC,MAAM,WAAW,SAAmD,IAAI;CAC/E,MAAM,CAAC,MAAM,WAAW,SAA+B,IAAI;CAC3D,MAAM,CAAC,UAAU,eAAe,SAAS,KAAK;CAC9C,MAAM,CAAC,eAAe,oBAAoB,SAA4B,IAAI;CAE1E,MAAM,UAAU,OAAsB,IAAI;;CAE1C,MAAM,eAAe,OAAiD,IAAI;;CAE1E,MAAM,WAAW,OAAsB,IAAI;CAC3C,MAAM,UAAU,OAA6B,IAAI;CACjD,MAAM,YAAY,OAAO,CAAC;CAC1B,MAAM,aAAa,OAA4B,IAAI;CACnD,MAAM,WAAW,OAAsB,IAAI;CAC3C,MAAM,aAAa,OAAgC,IAAI;CACvD,MAAM,YAAY,OAAgC,IAAI;CAEtD,MAAM,aAAa,kBAAkB;EACnC,IAAI,SAAS,YAAY,MAAM;EAC/B,SAAS,UAAU;EACnB,SAAS,IAAI;CACf,GAAG,CAAC,CAAC;;CAGL,MAAM,SAAS,kBAAkB;EAC/B,MAAM,SAAS,UAAU;EACzB,MAAM,UAAU,WAAW;EAC3B,IAAI,WAAW,QAAQ,YAAY,MAAM;EACzC,MAAM,UAAU,OAAO;EACvB,IAAI,QAAQ,sBAAsB,SAAS,QAAQ,kBAAkB,OAAO;CAC9E,GAAG,CAAC,CAAC;CAEL,MAAM,cAAc,aACjB,SAAwB;EACvB,MAAM,SAAS,UAAU;EACzB,IAAI,WAAW,MAAM;GACnB,MAAM,OAAO,WAAW,IAAI;GAC5B,IAAI,OAAO,UAAU,MAAM,OAAO,QAAQ;EAC5C;EACA,OAAO;CACT,GACA,CAAC,MAAM,CACT;CAEA,MAAM,EAAE,SAAS,aAAa,KAAK,YAAY,iBAAiB;CAEhE,MAAM,YAAY,OAChB,MACA,YACkB;EAClB,MAAM,MAAM,EAAE,UAAU;EACxB,IAAI;GACF,MAAM,oBAAoB,MAAM,OAAO;EACzC,SAAS,OAAO;GAGd,IAAI,cAAc,KAAA,KAAa,QAAQ,UAAU,SAAS;IACxD,MAAM,KAAA;IAEN,KAAK,aAAa,SAAS,MAAM,WAAW,KAAA,CAAS;GACvD;GACA,MAAM;EACR;CACF;CAEA,MAAM,cAAc,sBAAoE;EACtF,YAAY;EACZ;EACA,cAAc;EACd,QAAQ;EACR,uBAAuB;EACvB,MAAM;GAAE,WAAW;GAAoB,MAAM;EAAQ;CACvD,CAAC;CACD,MAAM,EAAE,YAAY,WAAW,aAAa;CAC5C,MAAM,eAAe,UAAU,SAAS;CAGxC,MAAM,SADO,SAAS,QAAQ,YAAY,eAAe,YAAY,YAC/C,SAAS,OAAO,KAAK,QAAQ;CACnD,MAAM,UAAU,eAAe,YAAY;CAC3C,MAAM,OAAO,SAAS,UAAU,OAAO,KAAK;CAE5C,MAAM,cAAc,UAAU,OAAO,KAAA,IAAY,UAAU,MAAM,KAAK;CACtE,MAAM,WAAW,gBAAgB,KAAA,IAAY,QAAQ;CACrD,yBAAyB;EACvB,SAAS,UAAU;CACrB,CAAC;CAED,MAAM,QAAQ,eACL;EAAE,OAAO;EAAO;EAAM;EAAS;EAAU;EAAU;CAAS,IACnE;EAAC;EAAO;EAAM;EAAS;EAAU;EAAU;CAAQ,CACrD;CAIA,oBAAoB,kBAAkB;EACpC,MAAM,OAAO,WAAW,YAAY;EACpC,WAAW,UAAU;EACrB,OAAO;CACT,CAAC;CAED,MAAM,SAAS,UAAU;EACvB;EACA;EACA;EACA;EACA;EACA;EACA;EACA,UAAA;EACA;CACF,CAAC;CAID,MAAM,OAAO,aAET,MACA,QACA,QACA,UACY;EAGZ,MAAM,EAAE,WAAW,MAAM,OAAO;EAChC,IAAI,SAAS,MAAM,OAAO,EAAE,MAAM,IAAI;EAEtC,IAAI,OAAO,GAAG,MAAM,SAAS,OAAO,GAAG,OAAO;EAC9C,QAAQ,UAAU;EAClB,SAAS,UAAU;EACnB,WAAW,UAAU;EACrB,QAAQ,EAAE,OAAO,KAAK,CAAC;EACvB,WAAW;EACX,iBAAiB,MAAM;EACvB,YAAY,IAAI;EAEhB,IAAI,WAAW,WAAW,YAAY,IAAI;EAC1C,MAAM,EAAE,mBAAmB,QAAQ,SAAS,QAAQ,OAAO;EAE3D,IAAI,WAAW,KAAA,GAAW,OAAO;EACjC,UAAU;GACR,KAAK,MAAM,QAAQ,YAAY,kBAAkB,IAAI;GACrD,OAAO,OAAO,MAAM,oBAAoB,OAAO;IAAE;IAAQ;GAAO,CAAC,CAAC;EACpE,CAAC;EACD,OAAO;CACT,GACA;EAAC;EAAY;EAAQ;CAAW,CAClC;CAEA,MAAM,gBAAgB,aACnB,SAAwB;EACvB,MAAM,UAAU,WAAW;EAC3B,IAAI,YAAY,MAAM;EACtB,MAAM,EAAE,UAAU,SAAS,WAAW,GAAG,OAAO,MAAM,OAAO;EAC7D,MAAM,YAA8B;GAAE,GAAG;GAAG,OAAO;GAAM,MAAM,EAAE,OAAO,IAAI;EAAE;EAC9E,MAAM,UAAU,YAAY,KAAA,IAAY,UAAU,OAAO,QAAQ,MAAM,SAAS;EAChF,IAAI,YAAY,MAAM,SAAM,SAAS,OAAO;CAC9C,GACA,CAAC,MAAM,CACT;CAEA,MAAM,SAAS,aACZ,QAA2B,QAAsB,UAAsC;EACtF,MAAM,QAAQ,aAAa;EAC3B,aAAa,UAAU;EAEvB,YAAY,KAAK;EACjB,MAAM,OAAO,QAAQ;EAErB,IAAI,UAAU,QAAQ,OAAO,GAAG,MAAM,MAAM,KAAK,GAAG,OAAO;EAC3D,WAAW,UAAU;EACrB,iBAAiB,MAAM;EACvB,SAAS,MAAM,oBAAoB,OAAO;GAAE;GAAQ;EAAO,CAAC,CAAC;EAC7D,IAAI,WAAW,cAAc,WAAW,WAAW,WAAW,MAAM,cAAc,IAAI;EACtF,OAAO;CACT,GACA,CAAC,eAAe,QAAQ,CAC1B;;CAGA,MAAM,cAAc,kBAAkB;EACpC,aAAa,UAAU,EAAE,OAAO,SAAS,QAAQ;EACjD,QAAQ,UAAU,SAAS;CAC7B,GAAG,CAAC,CAAC;;CAGL,MAAM,cAAc,kBAAiC;EACnD,MAAM,OAAO,SAAS;EACtB,IAAI,SAAS,MAAM;GACjB,MAAM,SAAS,OAAO,QAAQ,UAAU,MAAM,IAAI;GAClD,WAAW;GACX,IAAI,WAAW,KAAA,GAAW,OAAO;EACnC;EACA,OAAO,SAAS;CAClB,GAAG,CAAC,YAAY,MAAM,CAAC;;CAGvB,MAAM,UAAU,aACb,SAAiD,QAA2B,UAAiB;EAC5F,MAAM,OAAO,YAAY;EACzB,YAAY;EACZ,KAAK,QAAQ,IAAI,GAAG,QAAQ,YAAY,KAAK;EAC7C,OAAO,QAAQ,YAAY,KAAK;CAClC,GACA;EAAC;EAAQ;EAAa;EAAa;CAAI,CACzC;CAIA,MAAM,QAAQ,aAAa,SAAiB;EAC1C,SAAS,UAAU;EACnB,SAAS,IAAI;CACf,GAAG,CAAC,CAAC;CAEL,MAAM,cAAc,aACjB,UAAiB;EAChB,MAAM,OAAO,SAAS;EACtB,IAAI,SAAS,MAAM;EACnB,MAAM,EAAE,WAAW,GAAG,UAAU,KAAK,UAAU,OAAO,OAAO;EAC7D,IAAI,OAAO,IAAI;GACb,WAAW;GACX;EACF;EACA,MAAM,SAAS,EAAE,MAAM,IAAI;EAE3B,IAAI,WAAW,KAAA,KAAa,OAAO,GAAG,QAAQ,SAAS,OAAO,GAAG;GAC/D,WAAW;GACX;EACF;EACA,YAAY;EACZ,KAAK,QAAQ,SAAS,YAAY,KAAK;EACvC,OAAO,SAAS,YAAY,KAAK;CACnC,GACA;EAAC;EAAY;EAAQ;EAAQ;EAAa;CAAI,CAChD;CAIA,MAAM,UAAU,aACb,UAAyB;EACxB,MAAM,EAAE,UAAU,KAAK,UAAU,IAAI,QAAQ,GAAG,WAAW,UAAU,OAAO;EAG5E,IAAI,OAAO,MAAM,MAAM,eAAe,MAAM,YAAY,KAAK;EAC7D,IAAI,MAAM,QAAQ,SAAS;GACzB,YAAY,KAAK;GACjB;EACF;EACA,IAAI,MAAM,UAAU,MAAM,WAAW,MAAM,SAAS;EACpD,IAAI,UAA2D;EAC/D,QAAQ,MAAM,KAAd;GACE,KAAK;IACH,UAAU,MAAM,YAAY,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,MAAM,UAAU,GAAG,GAAG,CAAC;IAChF;GACF,KAAK;IACH,UAAU,MAAM,YAAY,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,MAAM,UAAU,GAAG,IAAI,CAAC;IAClF;GACF,KAAK;IACH,WAAW,MAAM,OAAO,GAAG,OAAO,CAAC;IACnC;GACF,KAAK;IACH,WAAW,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC;IACpC;GACF,KAAK,QAAQ;IAEX,IAAI,MAAM,UAAU;IACpB,MAAM,SAAS,WAAW,OAAO,CAAC;IAClC,IAAI,WAAW,KAAA,GAAW,gBAAgB;IAC1C;GACF;GACA,KAAK,OAAO;IACV,IAAI,MAAM,UAAU;IACpB,MAAM,SAAS,WAAW,OAAO,CAAC;IAClC,IAAI,WAAW,KAAA,GAAW,gBAAgB;IAC1C;GACF;EAGF;EACA,IAAI,YAAY,MAAM;EACtB,MAAM,eAAe;EACrB,QAAQ,SAAS,YAAY,KAAK;CACpC,GACA;EAAC;EAAa;EAAQ;CAAO,CAC/B;CAEA,MAAM,QAAQ,aACX,UAAsB;EACrB,MAAM,EAAE,UAAU,KAAK,UAAU,IAAI,QAAQ,MAAM,OAAO;EAC1D,MAAM,UAAU,WAAW;EAC3B,IAAI,OAAO,MAAM,YAAY,QAAQ,MAAM,WAAW,GAAG;EAEzD,IAAI,QAAQ,cAAc,kBAAkB,SAAS;EACrD,MAAM,eAAe;EACrB,MAAM,IAAI,MAAM,SAAS,IAAI,IAAI;EACjC,SAAS,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,SAAS,KAAK;CACnD,GACA,CAAC,QAAQ,OAAO,CAClB;CAIA,MAAM,UAAU,aACb,WAA0B,UAA6B;EACtD,IAAI,QAAQ,YAAY,WAAW;EACnC,QAAQ,UAAU;EAClB,QAAQ,IAAI;EACZ,OAAO,WAAW,YAAY,KAAK;CACrC,GACA,CAAC,MAAM,CACT;CAEA,MAAM,WAAW,aACd,WAA0B,UAAsC;EAC/D,IAAI,QAAQ,YAAY,WAAW,OAAO;EAC1C,MAAM,EAAE,QAAQ,MAAM,OAAO;EAC7B,MAAM,IAAI,cAAc,cAAc,IAAI;EAC1C,MAAM,OAAO,UAAU,SAAS,SAAS,GAAG,CAAC;EAC7C,SAAS,UAAU;EACnB,KAAK,MAAM,WAAW,YAAY,KAAK;EAGvC,IAAI,UAAU,MAAM,GAAG,CAAC,MAAM,MAAM;GAClC,QAAQ,WAAW,KAAK;GACxB,OAAO;EACT;EACA,OAAO;CACT,GACA;EAAC;EAAS;EAAQ;CAAI,CACxB;CAEA,MAAM,YAAY,aACf,WAA0B,UAA0B;EACnD,MAAM,EAAE,UAAU,KAAK,UAAU,OAAO,OAAO;EAC/C,IAAI,OAAO,MAAM,QAAQ,YAAY,MAAM,OAAO;EAClD,MAAM,OAAO,YAAY;EACzB,YAAY;EAEZ,QAAQ,EAAE,OAAO,SAAS,QAAQ,CAAC;EACnC,SAAS,UAAU;EACnB,QAAQ,UAAU;EAClB,QAAQ,SAAS;EACjB,OAAO,SAAS,WAAW,KAAK;CAClC,GACA;EAAC;EAAQ;EAAa;EAAa;CAAQ,CAC7C;CAEA,MAAM,WAAW,aACd,WAA0B,UAAiB;EAC1C,MAAM,EAAE,UAAU,KAAK,UAAU,IAAI,QAAQ,MAAM,OAAO;EAC1D,IAAI,OAAO,MAAM,QAAQ,YAAY,MAAM;EAC3C,MAAM,IAAI,cAAc,cAAc,IAAI;EAC1C,SAAS,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,WAAW,KAAK;CACrD,GACA,CAAC,QAAQ,OAAO,CAClB;CAIA,MAAM,CAAC,YAAY,iBAAiB,SAAS,CAAC;CAC9C,MAAM,gBAAgB,kBAAkB;EACtC,eAAe,UAAU,QAAQ,CAAC;EAClC,aAAa,eAAe,UAAU,QAAQ,CAAC;CACjD,GAAG,CAAC,CAAC;CAIL,MAAM,CAAC,cAAc,SAAS,KAAK;CACnC,MAAM,oBACJ,cAAc,KAAA,IAAa,cAAc,OAAS,gBAAgB;CACpE,MAAM,kBAAkB,WAAW,YAAY,CAAC;CAEhD,MAAM,EAAE,kBAAkB,6BAA4C;EACpE,SAAS;EACT,mBAAmB;GACjB,MAAM,UAAU,WAAW;GAC3B,MAAM,UAAU,SAAS;GACzB,IAAI,YAAY,MAAM,OAAO;GAC7B,MAAM,UAAU,QAAQ;GACxB,IAAI,YAAY,UAAU,OAAO,OAAO,GAAG,OAAO;GAClD,IAAI,QAAQ,cAAc,kBAAkB,SAAS;IAGnD,SAAS,UAAU;IACnB,SAAS,OAAO;IAChB,OAAO;GACT;GACA,MAAM,SAAS,UAAU,MAAM,OAAO;GACtC,IAAI,WAAW,KAAA,KAAa,OAAO,GAAG,QAAQ,OAAO,GAAG;IAGtD,QAAQ,QAAQ,UAAU,OAAO,OAAO;IACxC,OAAO;GACT;GACA,OAAO;EACT;EACA,mBAAmB;GAEjB,WAAW;GACX,OAAO,YAAY;EACrB;EACA,YAAY,SAAS;EACrB,mBAAmB,MAAM,UAAU;GACjC,YAAY;GACZ,KAAK,MAAM,MAAM,YAAY,KAAK;GAClC,OAAO,MAAM,YAAY,KAAK;EAChC;EACA,oBAAoB;GAGlB,MAAM,UAAU,WAAW;GAC3B,IAAI,YAAY,MAAM,QAAQ,eAAe,UAAU,OAAO,YAAY,CAAC;EAC7E;CACF,CAAC;CAID,cACG,cAAc,KAAA,KAAa,cAAc,QAAQ,CAAC,OAAO,SAAS,SAAS,KACzE,qBAAqB,KAAA,KACpB,qBAAqB,QACrB,CAAC,OAAO,SAAS,gBAAgB,GACrC,2FACS,OAAO,aAAa,gBAAgB,EAAE,oBACjD;CACA,cACE,OAAO,QAAQ,KAAA,KAAa,OAAO,QAAQ,KAAA,KAAa,OAAO,MAAM,OAAO,KAC5E,8BAA8B,OAAO,GAAG,EAAE,6BAA6B,OAAO,GAAG,EAAE,GACrF;CACA,cACE,CAAC,aAAa,CAAC,gBACf,wFACF;CACA,cAAc,UAAU,YAAY,MAAM,qBAAqB,UAAU,WAAW,IAAI;CACxF,MAAM,aAAa,OAAO,KAAK;CAC/B,gBAAgB;EACd,IAAI,CAAC,SAAS,WAAW,WAAW,WAAW,KAAA,GAAW;EAC1D,MAAM,OAAO,SAAS,gBAAgB;EACtC,IAAI,SAAS,MAAM,WAAW,IAAI,MAAM,WAAW,UAAU,MAAM,GAAG;EACtE,WAAW,UAAU;EACrB,QAAQ,MACN,yCAAyC,KAAK,mCACvC,UAAU,OAAO,4CAC1B;CACF,GAAG,CAAC,QAAQ,SAAS,CAAC;CAGtB,sBACc;EACV,QAAQ,UAAU;CACpB,GACA,CAAC,CACH;CAIA,MAAM,UAAU,eACP;EACL;EACA;EACA;EACA,eAAe,UAAU,gBAAgB;EACzC;EACA;EACA;EACA;EACA,iBAAiB,UAAU,mBAAmB,CAAC,OAAO,UAAU,QAAQ;EACxE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,gBAAgB;CAClB,IACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CACF;CAEA,MAAM,aAAa,gBAAgB,aAAa,IAAI,UAAU,KAAA,GAAW,cAAc,IAAI,KAAK;CAChG,MAAM,eAAuE;EAC3E,MAAM;EACN,GAAI,eAAe,KAAK,EAAE,mBAAmB,WAAW,IAAI,CAAC;EAC7D,GAAI,iBAAiB,OAAO,EAAE,gBAAgB,SAAS,iBAAiB,IAAI,EAAE,IAAI,CAAC;EACnF,gBAAgB,SAAS,OAAO;EAChC,iBAAiB,SAAS,QAAQ;CACpC;CAEA,MAAM,UAAU,UAA0D;EACxE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;CACF,CAAC;CAED,OAAO,oBAAC,oBAAD;EAAoB,OAAO;EAAU,UAAA;CAA4B,CAAA;AAC1E;AAEA,gBAAgB,cAAc"}
|
|
1
|
+
{"version":3,"file":"number-field-root.mjs","names":[],"sources":["../../src/number-field/number-field-root.tsx"],"sourcesContent":["\"use client\";\nimport {\n addTransitionType,\n use,\n useCallback,\n useEffect,\n useId,\n useInsertionEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode, RefObject } from \"react\";\nimport { createActionDetails } from \"../internal/action-details.js\";\nimport { announce as speak } from \"../internal/announce.js\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport { FieldContext } from \"../internal/field-context.js\";\nimport { mergeTokenLists } from \"../internal/merge-props.js\";\nimport { transitionTypes } from \"../internal/transition-types.js\";\nimport type {\n ActionDetails,\n ChangeAction,\n ChangeReason,\n RenderProp,\n StateValue,\n} from \"../internal/types.js\";\nimport { useActionTrigger } from \"../internal/use-action-trigger.js\";\nimport { useControllableAction } from \"../internal/use-controllable-action.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { useFieldValueChange } from \"../internal/use-field-control.js\";\nimport type { FormAssociatedElement } from \"../internal/use-form-reset.js\";\nimport { useLatest } from \"../internal/use-latest.js\";\nimport { useNativeGroupReconciliation } from \"../internal/use-native-group-reconciliation.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { boundValue, stepBy, stepValue } from \"./number-field-arithmetic.js\";\nimport type { StepBounds } from \"./number-field-arithmetic.js\";\nimport { NumberFieldContext } from \"./number-field-context.js\";\nimport type {\n NumberFieldContextValue,\n NumberFieldSource,\n StepDirection,\n} from \"./number-field-context.js\";\nimport { createNumberFormatter, formatKey } from \"./number-field-format.js\";\n\n/** Details passed as the second argument of both NumberField actions. */\nexport interface NumberFieldValueChangeDetails extends ActionDetails {\n /**\n * `\"activate\"`: the user typed, pressed a key, a button or turned the wheel (`details.event` is\n * the `focusout` / Enter `keydown` of a typed value, the `keydown` of a key, the `pointerdown` /\n * `pointerup` of a button (`undefined` for the repeats of a held button and for a hold that\n * ended at its bound), the `wheel` event). `\"platform\"`: a form reset (the `reset`\n * event) or text typed before hydration (`undefined`). `\"dismiss\"`: the commit action rejected\n * and the field reverted to the committed value (uncontrolled; `valueChangeAction` only).\n */\n readonly reason: ChangeReason;\n /** What produced the value: typed text, a button, a key, the wheel; `null` for \"platform\" and \"dismiss\". */\n readonly source: \"input\" | \"increment\" | \"decrement\" | \"keyboard\" | \"wheel\" | null;\n}\n\n/** The state every NumberField part's function props receive (one object, Field's precedent). */\nexport interface NumberFieldState {\n /** The shown value: the gesture's value while one is open or in flight, else the optimistic value; `null` when empty. */\n readonly value: number | null;\n /** The shown text: the draft while the user is editing, else `value` formatted. */\n readonly text: string;\n /** A step or commit dispatched through this NumberField is in flight. */\n readonly pending: boolean;\n readonly disabled: boolean;\n readonly readOnly: boolean;\n readonly required: boolean;\n}\n\nexport interface NumberFieldRootProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n \"defaultValue\" | \"role\" | \"className\" | \"style\" | \"children\"\n> {\n /** Controlled value; `null` is empty, `undefined` uncontrolled. */\n value?: number | null;\n /** Initial value in uncontrolled mode, and its form-reset target (latest). Default `null` (empty). */\n defaultValue?: number | null;\n /**\n * Runs inside the Root's step transition on every step: the parse of typed text at commit,\n * each key press, each tick of a hold, each wheel event, each platform change, and the revert\n * after a rejected commit (`reason: \"dismiss\"`, uncontrolled). Keep it cheap: adopt the value\n * (`setValue(next)` in the sync prefix is a transition update), start a deferred render.\n */\n valueChangeAction?: ChangeAction<number | null, NumberFieldValueChangeDetails>;\n /**\n * Runs inside the Root's commit transition once per gesture: a typed text's blur or Enter, a\n * hold's release, each key press, each wheel event, a form reset, text typed before hydration.\n * Where the save goes; uncontrolled state is committed when it resolves and reverts when it\n * rejects. May be a Server Function.\n */\n valueCommitAction?: ChangeAction<number | null, NumberFieldValueChangeDetails>;\n /** Native `min` on the hidden input (a constraint) and the lower bound of stepping. */\n min?: number;\n /** Native `max` on the hidden input (a constraint) and the upper bound of stepping. */\n max?: number;\n /** Native `step` and the stepping grid from `min` (else `0`). Default `1`, or `0.01` when `format.style` is `\"percent\"`. */\n step?: number;\n /** PageUp / PageDown and Shift+Arrow. Default `10 × step`. */\n largeStep?: number;\n /**\n * `Intl.NumberFormatOptions` for the text: `style` `\"decimal\"` (default), `\"percent\"`,\n * `\"currency\"` (with `currency`), `\"unit\"` (with `unit`); fraction digits, grouping, sign\n * display. `notation: \"compact\"` is a dev warning (unparsable).\n */\n format?: Intl.NumberFormatOptions;\n /**\n * BCP 47 tag(s). Default: the runtime's locale (`Intl.NumberFormat().resolvedOptions().locale`).\n * Pass the page's `lang`; a dev warning fires when `document.documentElement.lang` disagrees\n * and this prop is absent.\n */\n locale?: string | readonly string[];\n /** Step with the mouse wheel while the input is focused. Default `false`. */\n stepOnWheel?: boolean;\n /** Native `name` on the hidden input; defaults to the Field's `name` inside a `Field.Root`. */\n name?: string;\n /** Native `form` on the hidden input. */\n form?: string;\n /** Disables both inputs and the buttons; inherited by every part. */\n disabled?: boolean;\n /** Native `readonly` on both inputs; no stepping (buttons disabled, keys and wheel inert). */\n readOnly?: boolean;\n /** Native `required` on the hidden input (empty is `valueMissing`); or the Field's. */\n required?: boolean;\n /**\n * What is announced (`ariaNotify()`, else a live region) after a key press, a wheel event, a\n * click activation and a hold's release. Default: `(_, state) => state.text`. Return `null` to\n * announce nothing.\n */\n announce?: (value: number | null, state: NumberFieldState) => string | null;\n className?: StateValue<string | undefined, NumberFieldState>;\n style?: StateValue<CSSProperties | undefined, NumberFieldState>;\n children?: StateValue<ReactNode, NumberFieldState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, NumberFieldState>;\n}\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\nconst STEP_TYPES = transitionTypes(\"number-field\", \"change\");\nconst COMMIT_TYPES = transitionTypes(\"number-field\", \"commit\");\n\n/** A finite number, or `null` (`NaN` / `Infinity` are treated as empty). */\nfunction sanitize(value: number | null | undefined): number | null | undefined {\n if (value === undefined || value === null) return value;\n return Number.isFinite(value) ? value : null;\n}\n\n/** The hidden input's value for `value`. */\nexport function hiddenText(value: number | null): string {\n return value === null ? \"\" : String(value);\n}\n\nfunction languageOf(tag: string): string {\n return tag.split(\"-\")[0]?.toLowerCase() ?? \"\";\n}\n\n/**\n * Owns the number field's state (design/components/number-field.md 2): the committed value\n * through `useControllableAction` with `valueCommitAction` (once per gesture), each step through\n * its own transition (`valueChangeAction`), the urgent draft text, the urgent value of the open\n * gesture (`live`), the press-and-hold, the formatter and parser, the announcements, mount\n * reconciliation and form reset (through the hidden input the Input renders).\n *\n * Owned keys (conventions 1.2): `role=\"group\"`, `aria-labelledby` (token-merged: the consumer's,\n * then `NumberField.Label`'s id while one is mounted, then a Field's label id), `data-pending`,\n * `data-disabled`, `data-invalid` (inside a Field).\n */\nexport function NumberFieldRoot(props: NumberFieldRootProps) {\n const {\n value: valueProp,\n defaultValue: defaultValueProp,\n valueChangeAction,\n valueCommitAction,\n min,\n max,\n step: stepProp,\n largeStep: largeStepProp,\n format,\n locale,\n stepOnWheel = false,\n name: nameProp,\n form,\n disabled: disabledProp = false,\n readOnly = false,\n required: requiredProp = false,\n announce,\n className,\n style,\n children,\n render,\n ...rest\n } = props;\n\n const value = sanitize(valueProp);\n const defaultValue = sanitize(defaultValueProp);\n\n // --- Format, bounds -----------------------------------------------------------------------\n\n const key = formatKey(format);\n const localeKey = locale === undefined ? \"\" : JSON.stringify(locale);\n // Memoised on the content of `format` and `locale`, not their identity (an inline object).\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const formatter = useMemo(() => createNumberFormatter(locale, format), [localeKey, key]);\n const defaultStep = formatter.percent ? 0.01 : 1;\n const stepValid = stepProp === undefined || (Number.isFinite(stepProp) && stepProp > 0);\n const stepSize = stepProp !== undefined && stepValid ? stepProp : defaultStep;\n const largeStepValid =\n largeStepProp === undefined || (Number.isFinite(largeStepProp) && largeStepProp > 0);\n const largeStep = largeStepProp !== undefined && largeStepValid ? largeStepProp : 10 * stepSize;\n const bounds = useMemo<StepBounds>(\n () => ({\n min: Number.isFinite(min) ? min : undefined,\n max: Number.isFinite(max) ? max : undefined,\n step: stepSize,\n }),\n [min, max, stepSize],\n );\n\n // --- Field ----------------------------------------------------------------------------------\n\n const fieldContext = use(FieldContext);\n const disabled = disabledProp || (fieldContext?.disabled ?? false);\n const required = requiredProp || (fieldContext?.required ?? false);\n const name = nameProp ?? fieldContext?.name;\n const fieldInvalid = fieldContext === null ? undefined : fieldContext.state.invalid;\n\n const id = useId();\n const inputId = fieldContext?.ids.control ?? `${id}-input`;\n const labelId = `${id}-label`;\n\n // --- State (design 2) -----------------------------------------------------------------------\n\n const [draft, setDraft] = useState<string | null>(null);\n const draftRef = useRef<string | null>(null);\n /** The value the open gesture has reached (`null` inside: an empty value). */\n const [live, setLive] = useState<{ readonly value: number | null } | null>(null);\n const [hold, setHold] = useState<StepDirection | null>(null);\n const [stepHeld, setStepHeld] = useState(false);\n const [pendingSource, setPendingSource] = useState<NumberFieldSource>(null);\n\n const liveRef = useRef<number | null>(null);\n /** The shown value when the open gesture began; `null` when no gesture is open. */\n const gestureStart = useRef<{ readonly value: number | null } | null>(null);\n /** The value the next tick of a hold steps from. */\n const holdBase = useRef<number | null>(null);\n const holdRef = useRef<StepDirection | null>(null);\n const commitSeq = useRef(0);\n const lastReason = useRef<ChangeReason | null>(null);\n const shownRef = useRef<number | null>(null);\n const visibleRef = useRef<HTMLInputElement | null>(null);\n const hiddenRef = useRef<HTMLInputElement | null>(null);\n\n const clearDraft = useCallback(() => {\n if (draftRef.current === null) return;\n draftRef.current = null;\n setDraft(null);\n }, []);\n\n /** The visible input is invalid exactly when the hidden one is (6.2). */\n const mirror = useCallback(() => {\n const hidden = hiddenRef.current;\n const visible = visibleRef.current;\n if (hidden === null || visible === null) return;\n const message = hidden.validationMessage;\n if (visible.validationMessage !== message) visible.setCustomValidity(message);\n }, []);\n\n const writeHidden = useCallback(\n (next: number | null) => {\n const hidden = hiddenRef.current;\n if (hidden !== null) {\n const text = hiddenText(next);\n if (hidden.value !== text) hidden.value = text;\n }\n mirror();\n },\n [mirror],\n );\n\n // A step is not a re-entry: a continuous control dispatches one step per key press or pointer\n // move and every step must run (design/components/button.md 16.1), so no in-flight guard.\n const { pending: stepPending, run: runStep } = useActionTrigger({ ignoreWhilePending: false });\n\n const runCommit = async (\n next: number | null,\n details: NumberFieldValueChangeDetails,\n ): Promise<void> => {\n const seq = ++commitSeq.current;\n try {\n await valueCommitAction?.(next, details);\n } catch (error) {\n // Design 2.4: in uncontrolled mode, tell the preview channel about the rollback, for the\n // latest commit only. Yield once to leave the commit's transition (conventions 2.6).\n if (valueProp === undefined && seq === commitSeq.current) {\n await undefined;\n // `step` and `committedRef` are declared below; this runs long after the render.\n step(committedRef.current, null, \"dismiss\", undefined);\n }\n throw error;\n }\n };\n\n const commitState = useControllableAction<number | null, NumberFieldValueChangeDetails>({\n controlled: value,\n defaultValue,\n initialValue: null,\n action: runCommit,\n transitionTypes: () => COMMIT_TYPES,\n warn: { component: \"NumberField.Root\", prop: \"value\" },\n });\n const { optimistic, committed, dispatch } = commitState;\n const committedRef = useLatest(committed);\n\n const held = hold !== null || stepHeld || stepPending || commitState.pending;\n const shown = held && live !== null ? live.value : optimistic;\n const pending = stepPending || commitState.pending;\n const text = draft ?? formatter.format(shown);\n // The value a button press steps from (4.3 step 3): the parsed draft while one is open.\n const parsedDraft = draft === null ? undefined : formatter.parse(draft);\n const stepBase = parsedDraft === undefined ? shown : parsedDraft;\n useInsertionEffect(() => {\n shownRef.current = shown;\n });\n\n const state = useMemo<NumberFieldState>(\n () => ({ value: shown, text, pending, disabled, readOnly, required }),\n [shown, text, pending, disabled, readOnly, required],\n );\n\n // A step, a key and a typed commit fire no `input` / `change` on the element the Field hears\n // for the value (the hidden input is written by script): tell it at the commit (design 7.2).\n useFieldValueChange(optimistic, () => {\n const edit = lastReason.current === \"activate\";\n lastReason.current = null;\n return edit;\n });\n\n const latest = useLatest({\n valueChangeAction,\n runStep,\n formatter,\n bounds,\n largeStep,\n disabled,\n readOnly,\n announce,\n state,\n });\n\n // --- Step and commit (design 2.4) -----------------------------------------------------------\n\n const step = useCallback(\n (\n next: number | null,\n source: NumberFieldSource,\n reason: ChangeReason,\n event: Event | undefined,\n ): boolean => {\n // What the text can show is what the form posts (5.4): a step finer than the format's\n // digits is rounded like a typed value.\n const { formatter: f } = latest.current;\n if (next !== null) next = f.round(next);\n // No-op against what the user sees (conventions 2.3 item 8).\n if (Object.is(next, shownRef.current)) return false;\n liveRef.current = next;\n shownRef.current = next;\n lastReason.current = reason;\n setLive({ value: next });\n clearDraft();\n setPendingSource(source);\n writeHidden(next);\n // A revert has no commit after it: the optimistic value already shows the committed one.\n if (reason !== \"dismiss\") setStepHeld(true);\n const { valueChangeAction: action, runStep: run } = latest.current;\n // Without a step action nothing runs and no transition starts (Slider M2).\n if (action === undefined) return true;\n run(() => {\n for (const type of STEP_TYPES) addTransitionType(type);\n return action(next, createActionDetails(event, { reason, source }));\n });\n return true;\n },\n [clearDraft, latest, writeHidden],\n );\n\n const announceValue = useCallback(\n (next: number | null) => {\n const visible = visibleRef.current;\n if (visible === null) return;\n const { announce: compose, formatter: f, state: s } = latest.current;\n const nextState: NumberFieldState = { ...s, value: next, text: f.format(next) };\n const message = compose === undefined ? nextState.text : compose(next, nextState);\n if (message !== null) speak(visible, message);\n },\n [latest],\n );\n\n const commit = useCallback(\n (source: NumberFieldSource, reason: ChangeReason, event: Event | undefined): boolean => {\n const start = gestureStart.current;\n gestureStart.current = null;\n // The commit's own pending state holds `live` from here (set in this same batch).\n setStepHeld(false);\n const next = liveRef.current;\n // Nothing changed since the gesture began (a hold that returned to its start).\n if (start === null || Object.is(next, start.value)) return false;\n lastReason.current = reason;\n setPendingSource(source);\n dispatch(next, createActionDetails(event, { reason, source }));\n if (reason === \"activate\" && source !== \"input\" && source !== null) announceValue(next);\n return true;\n },\n [announceValue, dispatch],\n );\n\n /** Opens a gesture at the shown value. */\n const openGesture = useCallback(() => {\n gestureStart.current = { value: shownRef.current };\n liveRef.current = shownRef.current;\n }, []);\n\n /** The value a key or a button steps from: the parsed draft if one is open, else the shown value. */\n const resolveBase = useCallback((): number | null => {\n const open = draftRef.current;\n if (open !== null) {\n const parsed = latest.current.formatter.parse(open);\n clearDraft();\n if (parsed !== undefined) return parsed;\n }\n return shownRef.current;\n }, [clearDraft, latest]);\n\n /** A one-step gesture: a key press, a wheel event, a click activation. */\n const oneStep = useCallback(\n (compute: (base: number | null) => number | null, source: NumberFieldSource, event: Event) => {\n const base = resolveBase();\n openGesture();\n step(compute(base), source, \"activate\", event);\n commit(source, \"activate\", event);\n },\n [commit, openGesture, resolveBase, step],\n );\n\n // --- Typing (design 4.1) ----------------------------------------------------------------------\n\n const typed = useCallback((next: string) => {\n draftRef.current = next;\n setDraft(next);\n }, []);\n\n const commitDraft = useCallback(\n (event: Event) => {\n const open = draftRef.current;\n if (open === null) return;\n const { formatter: f, disabled: off, readOnly: ro } = latest.current;\n if (off || ro) {\n clearDraft();\n return;\n }\n const parsed = f.parse(open);\n // Unparsable: revert to the committed value's text, no dispatch, no error.\n if (parsed === undefined || Object.is(parsed, shownRef.current)) {\n clearDraft();\n return;\n }\n openGesture();\n step(parsed, \"input\", \"activate\", event);\n commit(\"input\", \"activate\", event);\n },\n [clearDraft, commit, latest, openGesture, step],\n );\n\n // --- Keyboard and wheel (design 4.2, 4.4) ----------------------------------------------------\n\n const keyDown = useCallback(\n (event: KeyboardEvent) => {\n const { disabled: off, readOnly: ro, bounds: b, largeStep: large } = latest.current;\n // WebKit fires `compositionend` before the confirming keydown, which then has\n // `isComposing === false` and `keyCode === 229` (Combobox's guard).\n if (off || ro || event.isComposing || event.keyCode === 229) return;\n if (event.key === \"Enter\") {\n commitDraft(event);\n return;\n }\n if (event.altKey || event.ctrlKey || event.metaKey) return;\n let compute: ((base: number | null) => number | null) | null = null;\n switch (event.key) {\n case \"ArrowUp\":\n compute = event.shiftKey ? (v) => stepBy(v, large, b) : (v) => stepValue(v, 1, b);\n break;\n case \"ArrowDown\":\n compute = event.shiftKey ? (v) => stepBy(v, -large, b) : (v) => stepValue(v, -1, b);\n break;\n case \"PageUp\":\n compute = (v) => stepBy(v, large, b);\n break;\n case \"PageDown\":\n compute = (v) => stepBy(v, -large, b);\n break;\n case \"Home\": {\n // Shift+Home / Shift+End are text selection.\n if (event.shiftKey) break;\n const target = boundValue(\"min\", b);\n if (target !== undefined) compute = () => target;\n break;\n }\n case \"End\": {\n if (event.shiftKey) break;\n const target = boundValue(\"max\", b);\n if (target !== undefined) compute = () => target;\n break;\n }\n default:\n break;\n }\n if (compute === null) return;\n event.preventDefault();\n oneStep(compute, \"keyboard\", event);\n },\n [commitDraft, latest, oneStep],\n );\n\n const wheel = useCallback(\n (event: WheelEvent) => {\n const { disabled: off, readOnly: ro, bounds: b } = latest.current;\n const visible = visibleRef.current;\n if (off || ro || visible === null || event.deltaY === 0) return;\n // Only while the input is focused; unfocused, the page scrolls.\n if (visible.ownerDocument.activeElement !== visible) return;\n event.preventDefault();\n const n = event.deltaY < 0 ? 1 : -1;\n oneStep((v) => stepValue(v, n, b), \"wheel\", event);\n },\n [latest, oneStep],\n );\n\n // --- Buttons (design 4.3) ---------------------------------------------------------------------\n\n const endHold = useCallback(\n (direction: StepDirection, event: Event | undefined) => {\n if (holdRef.current !== direction) return;\n holdRef.current = null;\n setHold(null);\n commit(direction, \"activate\", event);\n },\n [commit],\n );\n\n const tickHold = useCallback(\n (direction: StepDirection, event: Event | undefined): boolean => {\n if (holdRef.current !== direction) return false;\n const { bounds: b } = latest.current;\n const n = direction === \"increment\" ? 1 : -1;\n const next = stepValue(holdBase.current, n, b);\n holdBase.current = next;\n step(next, direction, \"activate\", event);\n // At the bound: end the hold now, before the button re-renders disabled (a disabled\n // element may not deliver the release).\n if (stepValue(next, n, b) === next) {\n endHold(direction, event);\n return false;\n }\n return true;\n },\n [endHold, latest, step],\n );\n\n const beginHold = useCallback(\n (direction: StepDirection, event: Event): boolean => {\n const { disabled: off, readOnly: ro } = latest.current;\n if (off || ro || holdRef.current !== null) return false;\n const base = resolveBase();\n openGesture();\n // A capture alone holds `live` on screen: start it from what is shown.\n setLive({ value: shownRef.current });\n holdBase.current = base;\n holdRef.current = direction;\n setHold(direction);\n return tickHold(direction, event);\n },\n [latest, openGesture, resolveBase, tickHold],\n );\n\n const activate = useCallback(\n (direction: StepDirection, event: Event) => {\n const { disabled: off, readOnly: ro, bounds: b } = latest.current;\n if (off || ro || holdRef.current !== null) return;\n const n = direction === \"increment\" ? 1 : -1;\n oneStep((v) => stepValue(v, n, b), direction, event);\n },\n [latest, oneStep],\n );\n\n // --- Labels ---------------------------------------------------------------------------------\n\n const [labelCount, setLabelCount] = useState(0);\n const registerLabel = useCallback(() => {\n setLabelCount((count) => count + 1);\n return () => setLabelCount((count) => count - 1);\n }, []);\n\n // --- Residual browser paths: mount reconciliation and form reset (design 4.5, 6.4) ----------\n\n const [mountValue] = useState(value);\n const resetTarget = (): number | null =>\n valueProp !== undefined ? (mountValue ?? null) : (defaultValue ?? null);\n const resetTargetText = hiddenText(resetTarget());\n\n const { onItemMounted } = useNativeGroupReconciliation<number | null>({\n element: hiddenRef as RefObject<FormAssociatedElement | null>,\n readCurrent: () => {\n const visible = visibleRef.current;\n const current = shownRef.current;\n if (visible === null) return current;\n const domText = visible.value;\n if (domText === formatter.format(current)) return current;\n if (visible.ownerDocument.activeElement === visible) {\n // Still typing (even a partial `12,` or `-`): adopted as the draft, never overwritten\n // under the caret. The blur commits it, or reverts it by the ordinary rule.\n draftRef.current = domText;\n setDraft(domText);\n return current;\n }\n const parsed = formatter.parse(domText);\n if (parsed === undefined || Object.is(parsed, current)) {\n // Another locale's formatting of the same value, or text that is no number: show the\n // client's text (react-dom kept the hydrated DOM value).\n visible.value = formatter.format(current);\n return current;\n }\n return parsed;\n },\n readDefault: () => {\n // A reset clears an open draft whatever the target.\n clearDraft();\n return resetTarget();\n },\n base: () => shownRef.current,\n onPlatformChange: (next, event) => {\n openGesture();\n step(next, null, \"platform\", event);\n commit(null, \"platform\", event);\n },\n onResetEvent: () => {\n // react-dom rewrites a controlled text input's `defaultValue` after every update; the\n // visible text's reset target is written right before the reset algorithm reads it.\n const visible = visibleRef.current;\n if (visible !== null) visible.defaultValue = formatter.format(resetTarget());\n },\n });\n\n // --- Development warnings -------------------------------------------------------------------\n\n useDevWarning(\n (valueProp !== undefined && valueProp !== null && !Number.isFinite(valueProp)) ||\n (defaultValueProp !== undefined &&\n defaultValueProp !== null &&\n !Number.isFinite(defaultValueProp)),\n \"NumberField.Root: `value` / `defaultValue` must be a finite number or `null`; \" +\n `got ${String(valueProp ?? defaultValueProp)}, treated as empty.`,\n );\n useDevWarning(\n bounds.min !== undefined && bounds.max !== undefined && bounds.min > bounds.max,\n `NumberField.Root: \\`min\\` (${String(min)}) is greater than \\`max\\` (${String(max)}).`,\n );\n useDevWarning(\n !stepValid || !largeStepValid,\n \"NumberField.Root: `step` and `largeStep` must be positive numbers; using the defaults.\",\n );\n useDevWarning(formatter.warning !== null, `NumberField.Root: ${formatter.warning ?? \"\"}`);\n const warnedLang = useRef(false);\n useEffect(() => {\n if (!isDev || warnedLang.current || locale !== undefined) return;\n const lang = document.documentElement.lang;\n if (lang === \"\" || languageOf(lang) === languageOf(formatter.locale)) return;\n warnedLang.current = true;\n console.error(\n `NumberField.Root: the page is \\`lang=\"${lang}\"\\` but NumberField formats as ` +\n `\\`${formatter.locale}\\` (the runtime's locale); pass \\`locale\\`.`,\n );\n }, [locale, formatter]);\n\n // An unmount mid-hold leaves nothing behind (the buttons detach their own listeners).\n useEffect(\n () => () => {\n holdRef.current = null;\n },\n [],\n );\n\n // --- Render ---------------------------------------------------------------------------------\n\n const context = useMemo<NumberFieldContextValue>(\n () => ({\n state,\n bounds,\n stepBase,\n pendingSource: pending ? pendingSource : null,\n inputId,\n labelId,\n name,\n form,\n allowsFractions: formatter.allowsFractions || !Number.isInteger(stepSize),\n stepOnWheel,\n resetTargetText,\n fieldInvalid,\n visibleRef,\n hiddenRef,\n typed,\n commitDraft,\n keyDown,\n wheel,\n beginHold,\n tickHold,\n endHold,\n activate,\n mirror,\n registerLabel,\n onInputMounted: onItemMounted,\n }),\n [\n state,\n bounds,\n stepBase,\n pending,\n pendingSource,\n inputId,\n labelId,\n name,\n form,\n formatter,\n stepSize,\n stepOnWheel,\n resetTargetText,\n fieldInvalid,\n typed,\n commitDraft,\n keyDown,\n wheel,\n beginHold,\n tickHold,\n endHold,\n activate,\n mirror,\n registerLabel,\n onItemMounted,\n ],\n );\n\n const labelledBy = mergeTokenLists(labelCount > 0 ? labelId : undefined, fieldContext?.ids.label);\n const libraryProps: Partial<ComponentPropsWithRef<\"div\">> & DataAttributes = {\n role: \"group\",\n ...(labelledBy !== \"\" ? { \"aria-labelledby\": labelledBy } : {}),\n ...(fieldContext !== null ? { \"data-invalid\": dataAttr(fieldInvalid === true) } : {}),\n \"data-pending\": dataAttr(pending),\n \"data-disabled\": dataAttr(disabled),\n };\n\n const element = useRender<ComponentPropsWithRef<\"div\">, NumberFieldState>({\n defaultElement: \"div\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps,\n });\n\n return <NumberFieldContext value={context}>{element}</NumberFieldContext>;\n}\n\nNumberFieldRoot.displayName = \"NumberFieldRoot\";\n"],"mappings":"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"}
|
|
@@ -27,7 +27,11 @@ function useStepButton(ctx, direction, disabled) {
|
|
|
27
27
|
}, []);
|
|
28
28
|
const onPointerDown = (event) => {
|
|
29
29
|
if (event.button !== 0 || !event.isPrimary) return;
|
|
30
|
-
if (disabled
|
|
30
|
+
if (disabled) {
|
|
31
|
+
event.preventDefault();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (active.current !== null) return;
|
|
31
35
|
const element = event.currentTarget;
|
|
32
36
|
const native = event.nativeEvent;
|
|
33
37
|
event.preventDefault();
|
|
@@ -85,6 +89,7 @@ function useStepButton(ctx, direction, disabled) {
|
|
|
85
89
|
}, 400);
|
|
86
90
|
};
|
|
87
91
|
const onClick = (event) => {
|
|
92
|
+
if (disabled) event.preventDefault();
|
|
88
93
|
if (pointerSeen.current || active.current !== null || event.detail !== 0) {
|
|
89
94
|
pointerSeen.current = false;
|
|
90
95
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-step-button.mjs","names":[],"sources":["../../src/number-field/use-step-button.ts"],"sourcesContent":["\"use client\";\nimport { useEffect, useRef } from \"react\";\nimport type { MouseEvent as ReactMouseEvent, PointerEvent as ReactPointerEvent } from \"react\";\nimport type { NumberFieldContextValue, StepDirection } from \"./number-field-context.js\";\n\n/** First repeat after the press, then the interval between repeats (Base UI, React Aria). */\nexport const HOLD_DELAY = 400;\nexport const HOLD_INTERVAL = 60;\n\ninterface ActiveHold {\n readonly pointerId: number;\n readonly detach: () => void;\n}\n\nexport interface StepButtonHandlers {\n readonly onPointerDown: (event: ReactPointerEvent<HTMLElement>) => void;\n readonly onClick: (event: ReactMouseEvent<HTMLElement>) => void;\n readonly onContextMenu: (event: ReactMouseEvent<HTMLElement>) => void;\n}\n\n/**\n * The press-and-hold of `NumberField.Increment` / `Decrement` (design 4.3): one step at\n * `pointerdown`, a repeat after 400 ms then every 60 ms, one commit at the release\n * (`pointerup`, `pointercancel`, `lostpointercapture`, or `blur` on the window). The press is\n * `preventDefault()`ed so focus stays where it is. A `click` with no pointer gesture before it\n * (assistive technology: `detail === 0`) is a one-step gesture.\n */\nexport function useStepButton(\n ctx: NumberFieldContextValue,\n direction: StepDirection,\n disabled: boolean,\n): StepButtonHandlers {\n const active = useRef<ActiveHold | null>(null);\n /** A pointer gesture started on this button; the click that follows it is not an activation. */\n const pointerSeen = useRef(false);\n const latest = useRef({ ctx, disabled });\n useEffect(() => {\n latest.current = { ctx, disabled };\n });\n\n // An unmount mid-hold leaves no timer or listener behind.\n useEffect(\n () => () => {\n active.current?.detach();\n active.current = null;\n },\n [],\n );\n\n const onPointerDown = (event: ReactPointerEvent<HTMLElement>): void => {\n if (event.button !== 0 || !event.isPrimary) return;\n if (disabled
|
|
1
|
+
{"version":3,"file":"use-step-button.mjs","names":[],"sources":["../../src/number-field/use-step-button.ts"],"sourcesContent":["\"use client\";\nimport { useEffect, useRef } from \"react\";\nimport type { MouseEvent as ReactMouseEvent, PointerEvent as ReactPointerEvent } from \"react\";\nimport type { NumberFieldContextValue, StepDirection } from \"./number-field-context.js\";\n\n/** First repeat after the press, then the interval between repeats (Base UI, React Aria). */\nexport const HOLD_DELAY = 400;\nexport const HOLD_INTERVAL = 60;\n\ninterface ActiveHold {\n readonly pointerId: number;\n readonly detach: () => void;\n}\n\nexport interface StepButtonHandlers {\n readonly onPointerDown: (event: ReactPointerEvent<HTMLElement>) => void;\n readonly onClick: (event: ReactMouseEvent<HTMLElement>) => void;\n readonly onContextMenu: (event: ReactMouseEvent<HTMLElement>) => void;\n}\n\n/**\n * The press-and-hold of `NumberField.Increment` / `Decrement` (design 4.3): one step at\n * `pointerdown`, a repeat after 400 ms then every 60 ms, one commit at the release\n * (`pointerup`, `pointercancel`, `lostpointercapture`, or `blur` on the window). The press is\n * `preventDefault()`ed so focus stays where it is. A `click` with no pointer gesture before it\n * (assistive technology: `detail === 0`) is a one-step gesture.\n */\nexport function useStepButton(\n ctx: NumberFieldContextValue,\n direction: StepDirection,\n disabled: boolean,\n): StepButtonHandlers {\n const active = useRef<ActiveHold | null>(null);\n /** A pointer gesture started on this button; the click that follows it is not an activation. */\n const pointerSeen = useRef(false);\n const latest = useRef({ ctx, disabled });\n useEffect(() => {\n latest.current = { ctx, disabled };\n });\n\n // An unmount mid-hold leaves no timer or listener behind.\n useEffect(\n () => () => {\n active.current?.detach();\n active.current = null;\n },\n [],\n );\n\n const onPointerDown = (event: ReactPointerEvent<HTMLElement>): void => {\n if (event.button !== 0 || !event.isPrimary) return;\n if (disabled) {\n // A focusable disabled render (`aria-disabled`, #83) must not take focus from the input\n // either; native `disabled` focused nothing.\n event.preventDefault();\n return;\n }\n if (active.current !== null) return;\n const element = event.currentTarget;\n const native = event.nativeEvent;\n // No focus move off the input, no touch keyboard, no text selection.\n event.preventDefault();\n pointerSeen.current = true;\n const pointerId = native.pointerId;\n try {\n element.setPointerCapture(pointerId);\n } catch {\n // An inactive pointer id (a synthetic event): the hold still follows the element.\n }\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n const view = element.ownerDocument.defaultView;\n const detach = (): void => {\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n element.removeEventListener(\"pointerup\", onEnd);\n element.removeEventListener(\"pointercancel\", onEnd);\n element.removeEventListener(\"lostpointercapture\", onEnd);\n view?.removeEventListener(\"blur\", onBlur);\n if (active.current?.pointerId === pointerId) active.current = null;\n // A click that follows this release (same task, or a touch tap later) is ignored; an\n // activation without a pointer afterwards is not.\n setTimeout(() => {\n pointerSeen.current = false;\n }, 0);\n };\n function onEnd(endEvent: Event): void {\n if ((endEvent as PointerEvent).pointerId !== pointerId) return;\n detach();\n latest.current.ctx.endHold(direction, endEvent);\n }\n function onBlur(blurEvent: Event): void {\n detach();\n latest.current.ctx.endHold(direction, blurEvent);\n }\n const tick = (): void => {\n timer = setTimeout(() => {\n timer = undefined;\n if (latest.current.ctx.tickHold(direction, undefined)) tick();\n else detach();\n }, HOLD_INTERVAL);\n };\n\n if (!ctx.beginHold(direction, native)) {\n // The first step reached the bound: the hold already committed.\n detach();\n return;\n }\n element.addEventListener(\"pointerup\", onEnd);\n element.addEventListener(\"pointercancel\", onEnd);\n element.addEventListener(\"lostpointercapture\", onEnd);\n view?.addEventListener(\"blur\", onBlur);\n active.current = { pointerId, detach };\n timer = setTimeout(() => {\n timer = undefined;\n if (latest.current.ctx.tickHold(direction, undefined)) tick();\n else detach();\n }, HOLD_DELAY);\n };\n\n const onClick = (event: ReactMouseEvent<HTMLElement>): void => {\n // A disabled link or component render (`aria-disabled`, #83) must not navigate either.\n if (disabled) event.preventDefault();\n if (pointerSeen.current || active.current !== null || event.detail !== 0) {\n pointerSeen.current = false;\n return;\n }\n if (disabled) return;\n ctx.activate(direction, event.nativeEvent);\n };\n\n const onContextMenu = (event: ReactMouseEvent<HTMLElement>): void => {\n // A touch long-press would open the context menu mid-hold.\n if (active.current !== null) event.preventDefault();\n };\n\n return { onPointerDown, onClick, onContextMenu };\n}\n"],"mappings":";;;;;;;;;AA2BA,SAAgB,cACd,KACA,WACA,UACoB;CACpB,MAAM,SAAS,OAA0B,IAAI;;CAE7C,MAAM,cAAc,OAAO,KAAK;CAChC,MAAM,SAAS,OAAO;EAAE;EAAK;CAAS,CAAC;CACvC,gBAAgB;EACd,OAAO,UAAU;GAAE;GAAK;EAAS;CACnC,CAAC;CAGD,sBACc;EACV,OAAO,SAAS,OAAO;EACvB,OAAO,UAAU;CACnB,GACA,CAAC,CACH;CAEA,MAAM,iBAAiB,UAAgD;EACrE,IAAI,MAAM,WAAW,KAAK,CAAC,MAAM,WAAW;EAC5C,IAAI,UAAU;GAGZ,MAAM,eAAe;GACrB;EACF;EACA,IAAI,OAAO,YAAY,MAAM;EAC7B,MAAM,UAAU,MAAM;EACtB,MAAM,SAAS,MAAM;EAErB,MAAM,eAAe;EACrB,YAAY,UAAU;EACtB,MAAM,YAAY,OAAO;EACzB,IAAI;GACF,QAAQ,kBAAkB,SAAS;EACrC,QAAQ,CAER;EAEA,IAAI;EACJ,MAAM,OAAO,QAAQ,cAAc;EACnC,MAAM,eAAqB;GACzB,IAAI,UAAU,KAAA,GAAW,aAAa,KAAK;GAC3C,QAAQ,KAAA;GACR,QAAQ,oBAAoB,aAAa,KAAK;GAC9C,QAAQ,oBAAoB,iBAAiB,KAAK;GAClD,QAAQ,oBAAoB,sBAAsB,KAAK;GACvD,MAAM,oBAAoB,QAAQ,MAAM;GACxC,IAAI,OAAO,SAAS,cAAc,WAAW,OAAO,UAAU;GAG9D,iBAAiB;IACf,YAAY,UAAU;GACxB,GAAG,CAAC;EACN;EACA,SAAS,MAAM,UAAuB;GACpC,IAAK,SAA0B,cAAc,WAAW;GACxD,OAAO;GACP,OAAO,QAAQ,IAAI,QAAQ,WAAW,QAAQ;EAChD;EACA,SAAS,OAAO,WAAwB;GACtC,OAAO;GACP,OAAO,QAAQ,IAAI,QAAQ,WAAW,SAAS;EACjD;EACA,MAAM,aAAmB;GACvB,QAAQ,iBAAiB;IACvB,QAAQ,KAAA;IACR,IAAI,OAAO,QAAQ,IAAI,SAAS,WAAW,KAAA,CAAS,GAAG,KAAK;SACvD,OAAO;GACd,GAAA,EAAgB;EAClB;EAEA,IAAI,CAAC,IAAI,UAAU,WAAW,MAAM,GAAG;GAErC,OAAO;GACP;EACF;EACA,QAAQ,iBAAiB,aAAa,KAAK;EAC3C,QAAQ,iBAAiB,iBAAiB,KAAK;EAC/C,QAAQ,iBAAiB,sBAAsB,KAAK;EACpD,MAAM,iBAAiB,QAAQ,MAAM;EACrC,OAAO,UAAU;GAAE;GAAW;EAAO;EACrC,QAAQ,iBAAiB;GACvB,QAAQ,KAAA;GACR,IAAI,OAAO,QAAQ,IAAI,SAAS,WAAW,KAAA,CAAS,GAAG,KAAK;QACvD,OAAO;EACd,GAAA,GAAa;CACf;CAEA,MAAM,WAAW,UAA8C;EAE7D,IAAI,UAAU,MAAM,eAAe;EACnC,IAAI,YAAY,WAAW,OAAO,YAAY,QAAQ,MAAM,WAAW,GAAG;GACxE,YAAY,UAAU;GACtB;EACF;EACA,IAAI,UAAU;EACd,IAAI,SAAS,WAAW,MAAM,WAAW;CAC3C;CAEA,MAAM,iBAAiB,UAA8C;EAEnE,IAAI,OAAO,YAAY,MAAM,MAAM,eAAe;CACpD;CAEA,OAAO;EAAE;EAAe;EAAS;CAAc;AACjD"}
|
|
@@ -16,7 +16,11 @@ export interface PopoverCloseProps extends Omit<ComponentPropsWithRef<"button">,
|
|
|
16
16
|
* mode the button is a native `command="hide-popover"` invoker.
|
|
17
17
|
*/
|
|
18
18
|
action?: Action<[details: ActionDetails]>;
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Never closes. Native `disabled` on a `<button>`; owned, so a `render` element cannot
|
|
21
|
+
* re-enable it. With a link or component `render` whose click this part handles (library mode,
|
|
22
|
+
* or with `action`), `aria-disabled="true"` instead: the element stays focusable.
|
|
23
|
+
*/
|
|
20
24
|
disabled?: boolean;
|
|
21
25
|
className?: StateValue<string | undefined, PopoverCloseState>;
|
|
22
26
|
style?: StateValue<CSSProperties | undefined, PopoverCloseState>;
|
|
@@ -28,10 +32,12 @@ export interface PopoverCloseProps extends Omit<ComponentPropsWithRef<"button">,
|
|
|
28
32
|
* without `action` the browser hides the popover. In library mode the click is always cancelled
|
|
29
33
|
* and the close goes through the Root (`openChangeAction(false)` awaited, `hidePopover()` in the
|
|
30
34
|
* commit). With `action` the action runs first in this part's own transition (`data-pending` on
|
|
31
|
-
* the button); a rejection leaves the
|
|
35
|
+
* the button, with `aria-disabled`; a second click meanwhile is ignored); a rejection leaves the
|
|
36
|
+
* popover open.
|
|
32
37
|
*
|
|
33
38
|
* Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `commandfor`,
|
|
34
|
-
* `command`, `onClick` (library mode, or with `action`), `
|
|
39
|
+
* `command`, `onClick` (library mode, or with `action`), `aria-disabled`, `data-pending`,
|
|
40
|
+
* `data-disabled`.
|
|
35
41
|
*/
|
|
36
42
|
export declare function PopoverClose({ action, disabled, className, style, children, render, ...rest }: PopoverCloseProps): ReactNode;
|
|
37
43
|
export declare namespace PopoverClose {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"popover-close.d.mts","names":[],"sources":["../../src/popover/popover-close.tsx"],"mappings":";;;iBAWiB;;WAEN;WACA;;iBAGM,0BAA0B,KACzC;;;;;;;;;EAWA,SAAS,QAAQ,SAAS
|
|
1
|
+
{"version":3,"file":"popover-close.d.mts","names":[],"sources":["../../src/popover/popover-close.tsx"],"mappings":";;;iBAWiB;;WAEN;WACA;;iBAGM,0BAA0B,KACzC;;;;;;;;;EAWA,SAAS,QAAQ,SAAS;;;;;;EAM1B;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,iCAAiC;;;;;;;;;;;;;;wBAyBvC,eACd,QACA,UACA,WACA,OACA,UACA,WACG,QACF,oBAAiB;yBARJ"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { createActionDetails } from "../internal/action-details.mjs";
|
|
3
3
|
import { dataAttr } from "../internal/data-attr.mjs";
|
|
4
|
-
import { rendersButton } from "../internal/render-element.mjs";
|
|
5
|
-
import { useRender } from "../internal/use-render.mjs";
|
|
6
4
|
import { useActionTrigger } from "../internal/use-action-trigger.mjs";
|
|
5
|
+
import { isHostButton, rendersButton } from "../internal/render-element.mjs";
|
|
6
|
+
import { useRender } from "../internal/use-render.mjs";
|
|
7
7
|
import { usePopoverContext } from "./popover-context.mjs";
|
|
8
8
|
import { useCallback } from "react";
|
|
9
9
|
//#region src/popover/popover-close.tsx
|
|
@@ -20,17 +20,21 @@ function isPopoverOpen(element) {
|
|
|
20
20
|
* without `action` the browser hides the popover. In library mode the click is always cancelled
|
|
21
21
|
* and the close goes through the Root (`openChangeAction(false)` awaited, `hidePopover()` in the
|
|
22
22
|
* commit). With `action` the action runs first in this part's own transition (`data-pending` on
|
|
23
|
-
* the button); a rejection leaves the
|
|
23
|
+
* the button, with `aria-disabled`; a second click meanwhile is ignored); a rejection leaves the
|
|
24
|
+
* popover open.
|
|
24
25
|
*
|
|
25
26
|
* Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `commandfor`,
|
|
26
|
-
* `command`, `onClick` (library mode, or with `action`), `
|
|
27
|
+
* `command`, `onClick` (library mode, or with `action`), `aria-disabled`, `data-pending`,
|
|
28
|
+
* `data-disabled`.
|
|
27
29
|
*/
|
|
28
30
|
function PopoverClose({ action, disabled = false, className, style, children, render, ...rest }) {
|
|
29
31
|
const ctx = usePopoverContext("Popover.Close");
|
|
30
32
|
const { ids, popupRef, recordCloseIntent, openGeneration } = ctx;
|
|
31
|
-
const { pending, run } = useActionTrigger(openGeneration);
|
|
33
|
+
const { pending, run } = useActionTrigger({ openGeneration });
|
|
32
34
|
const isLibrary = ctx.mode === "library";
|
|
33
35
|
const installClick = isLibrary || action !== void 0;
|
|
36
|
+
const focusable = installClick && !isHostButton(render);
|
|
37
|
+
const noInvoker = focusable && disabled;
|
|
34
38
|
const state = {
|
|
35
39
|
pending,
|
|
36
40
|
disabled
|
|
@@ -65,10 +69,11 @@ function PopoverClose({ action, disabled = false, className, style, children, re
|
|
|
65
69
|
};
|
|
66
70
|
const libraryProps = {
|
|
67
71
|
...rendersButton(render) && { type: "button" },
|
|
68
|
-
disabled,
|
|
69
|
-
commandfor: ids.popup,
|
|
70
|
-
command: "hide-popover",
|
|
72
|
+
disabled: focusable ? void 0 : disabled,
|
|
73
|
+
commandfor: noInvoker ? void 0 : ids.popup,
|
|
74
|
+
command: noInvoker ? void 0 : "hide-popover",
|
|
71
75
|
...installClick && { onClick },
|
|
76
|
+
"aria-disabled": pending || focusable && disabled ? true : void 0,
|
|
72
77
|
"data-pending": dataAttr(pending),
|
|
73
78
|
"data-disabled": dataAttr(disabled)
|
|
74
79
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"popover-close.mjs","names":[],"sources":["../../src/popover/popover-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 { usePopoverContext } from \"./popover-context.js\";\n\nexport interface PopoverCloseState {\n /** The `action` is in flight. */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface PopoverCloseProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"onClick\" | \"className\" | \"style\" | \"children\"\n> {\n /**\n * Runs inside this part's own transition (`data-pending` on the button), then closes the\n * popover: in library mode through the Root (`openChangeAction(false)` awaited, view\n * transition if set), in JS-free mode with `hidePopover()`. A rejection reaches the nearest\n * Error Boundary above the Close part and leaves the popover open. If the popover was\n * dismissed and opened again while the action ran, the close is dropped. Without `action` in JS-free\n * mode the button is a native `command=\"hide-popover\"` invoker.\n */\n action?: Action<[details: ActionDetails]>;\n
|
|
1
|
+
{"version":3,"file":"popover-close.mjs","names":[],"sources":["../../src/popover/popover-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 { usePopoverContext } from \"./popover-context.js\";\n\nexport interface PopoverCloseState {\n /** The `action` is in flight. */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface PopoverCloseProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"onClick\" | \"className\" | \"style\" | \"children\"\n> {\n /**\n * Runs inside this part's own transition (`data-pending` on the button), then closes the\n * popover: in library mode through the Root (`openChangeAction(false)` awaited, view\n * transition if set), in JS-free mode with `hidePopover()`. A rejection reaches the nearest\n * Error Boundary above the Close part and leaves the popover open. If the popover was\n * dismissed and opened again while the action ran, the close is dropped. Without `action` in JS-free\n * mode the button is a native `command=\"hide-popover\"` invoker.\n */\n action?: Action<[details: ActionDetails]>;\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, PopoverCloseState>;\n style?: StateValue<CSSProperties | undefined, PopoverCloseState>;\n children?: StateValue<ReactNode, PopoverCloseState>;\n render?: RenderProp<ComponentPropsWithRef<\"button\">, PopoverCloseState>;\n}\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\nfunction isPopoverOpen(element: Element): boolean {\n try {\n return element.matches(\":popover-open\");\n } catch {\n return false;\n }\n}\n\n/**\n * A close button: `<button type=\"button\" commandfor command=\"hide-popover\">`. In JS-free mode\n * without `action` the browser hides the popover. In library mode the click is always cancelled\n * and the close goes through the Root (`openChangeAction(false)` awaited, `hidePopover()` in the\n * commit). With `action` the action runs first in this part's own transition (`data-pending` on\n * the button, with `aria-disabled`; a second click meanwhile is ignored); a rejection leaves the\n * popover open.\n *\n * Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `commandfor`,\n * `command`, `onClick` (library mode, or with `action`), `aria-disabled`, `data-pending`,\n * `data-disabled`.\n */\nexport function PopoverClose({\n action,\n disabled = false,\n className,\n style,\n children,\n render,\n ...rest\n}: PopoverCloseProps) {\n const ctx = usePopoverContext(\"Popover.Close\");\n const { ids, popupRef, recordCloseIntent, openGeneration } = ctx;\n const { pending, run } = useActionTrigger({ openGeneration });\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 `hide-popover` 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: PopoverCloseState = { pending, disabled };\n\n const devRef = useCallback(\n (element: HTMLElement) => {\n if (isDev && !installClick && element.tagName !== \"BUTTON\") {\n console.error(\n `Popover.Close: rendered a <${element.tagName.toLowerCase()}> in JS-free mode without ` +\n '`action`. Only a <button> runs `command=\"hide-popover\"`; this element will not ' +\n \"close the popover.\",\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): void => {\n if (isLibrary) {\n // A library-owned close (design 2.3): awaited, then hidden in the commit.\n ctx.requestClose(event, \"activate\");\n return;\n }\n // JS-free mode with `action`: a browser-owned close; there is no Root state to notify.\n const popup = popupRef.current;\n if (popup === null || !popup.isConnected || !isPopoverOpen(popup)) return;\n recordCloseIntent({ reason: \"activate\", event });\n popup.hidePopover();\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 if (action === undefined) {\n close(native);\n return;\n }\n const details = createActionDetails(native, {});\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),\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.popup,\n command: noInvoker ? undefined : \"hide-popover\",\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\">, PopoverCloseState>({\n defaultElement: \"button\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps,\n ref: devRef,\n });\n}\n\nPopoverClose.displayName = \"PopoverClose\";\n"],"mappings":";;;;;;;;;AA0CA,MAAM,QAAQ,QAAQ,IAAI,aAAa;AAEvC,SAAS,cAAc,SAA2B;CAChD,IAAI;EACF,OAAO,QAAQ,QAAQ,eAAe;CACxC,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;;;;;;AAcA,SAAgB,aAAa,EAC3B,QACA,WAAW,OACX,WACA,OACA,UACA,QACA,GAAG,QACiB;CACpB,MAAM,MAAM,kBAAkB,eAAe;CAC7C,MAAM,EAAE,KAAK,UAAU,mBAAmB,mBAAmB;CAC7D,MAAM,EAAE,SAAS,QAAQ,iBAAiB,EAAE,eAAe,CAAC;CAC5D,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,QAA2B;EAAE;EAAS;CAAS;CAErD,MAAM,SAAS,aACZ,YAAyB;EACxB,IAAI,SAAS,CAAC,gBAAgB,QAAQ,YAAY,UAChD,QAAQ,MACN,8BAA8B,QAAQ,QAAQ,YAAY,EAAE,gIAG9D;EAGF,aAAa,CAAC;CAChB,GACA,CAAC,YAAY,CACf;CAEA,MAAM,SAAS,UAAuB;EACpC,IAAI,WAAW;GAEb,IAAI,aAAa,OAAO,UAAU;GAClC;EACF;EAEA,MAAM,QAAQ,SAAS;EACvB,IAAI,UAAU,QAAQ,CAAC,MAAM,eAAe,CAAC,cAAc,KAAK,GAAG;EACnE,kBAAkB;GAAE,QAAQ;GAAY;EAAM,CAAC;EAC/C,MAAM,YAAY;CACpB;CAEA,MAAM,WAAW,UAAmC;EAElD,MAAM,eAAe;EACrB,IAAI,UAAU;EACd,MAAM,SAAS,MAAM;EACrB,IAAI,WAAW,KAAA,GAAW;GACxB,MAAM,MAAM;GACZ;EACF;EACA,MAAM,UAAU,oBAAoB,QAAQ,CAAC,CAAC;EAI9C,UACQ,OAAO,OAAO,SACd,MAAM,MAAM,CACpB;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,GAAI,gBAAgB,EAAE,QAAQ;EAG9B,iBAAiB,WAAY,aAAa,WAAY,OAAO,KAAA;EAC7D,gBAAgB,SAAS,OAAO;EAChC,iBAAiB,SAAS,QAAQ;CACpC;CAEA,OAAO,UAA8D;EACnE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;EACA,KAAK;CACP,CAAC;AACH;AAEA,aAAa,cAAc"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { ToolbarContext } from "../internal/toolbar-context.mjs";
|
|
2
3
|
import { useTransitionBoundary } from "../internal/transition-boundary.mjs";
|
|
4
|
+
import { useDevWarning } from "../internal/use-dev-warning.mjs";
|
|
3
5
|
import { resolveStateValue } from "../internal/render-element.mjs";
|
|
4
6
|
import { useRender } from "../internal/use-render.mjs";
|
|
5
|
-
import { ToolbarContext } from "../internal/toolbar-context.mjs";
|
|
6
|
-
import { useDevWarning } from "../internal/use-dev-warning.mjs";
|
|
7
7
|
import { POSITION_TRY_FALLBACKS, positionArea } from "../internal/anchor-position.mjs";
|
|
8
8
|
import { PopoverPopupContext, usePopoverContext } from "./popover-context.mjs";
|
|
9
9
|
import { usePopoverPopup } from "../internal/use-popover-popup.mjs";
|
|
@@ -9,7 +9,11 @@ export interface PopoverTriggerState {
|
|
|
9
9
|
readonly disabled: boolean;
|
|
10
10
|
}
|
|
11
11
|
export interface PopoverTriggerProps extends Omit<ComponentPropsWithRef<"button">, "onClick" | "aria-expanded" | "className" | "style" | "children"> {
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Never dispatches. Native `disabled` on a `<button>` (no longer an invoker); in library mode
|
|
14
|
+
* with a link or component `render`, `aria-disabled="true"` instead: the element stays
|
|
15
|
+
* focusable.
|
|
16
|
+
*/
|
|
13
17
|
disabled?: boolean;
|
|
14
18
|
className?: StateValue<string | undefined, PopoverTriggerState>;
|
|
15
19
|
style?: StateValue<CSSProperties | undefined, PopoverTriggerState>;
|
|
@@ -25,9 +29,9 @@ export interface PopoverTriggerProps extends Omit<ComponentPropsWithRef<"button"
|
|
|
25
29
|
* Root's transition: an open, or a library-owned close while open. The button shows optimistic
|
|
26
30
|
* `data-open` / `aria-expanded` and `data-pending` until the change has landed.
|
|
27
31
|
*
|
|
28
|
-
* Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `
|
|
29
|
-
*
|
|
30
|
-
* `data-disabled`. `aria-haspopup` and `aria-controls` pass through.
|
|
32
|
+
* Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `aria-disabled`
|
|
33
|
+
* (library mode, link or component `render`), `commandfor`, `command`, `aria-expanded`,
|
|
34
|
+
* `onClick` (library mode), `data-open`, `data-pending`, `data-disabled`. `aria-haspopup` and `aria-controls` pass through.
|
|
31
35
|
*/
|
|
32
36
|
export declare function PopoverTrigger({ disabled, className, style, children, render, ...rest }: PopoverTriggerProps): ReactNode;
|
|
33
37
|
export declare namespace PopoverTrigger {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"popover-trigger.d.mts","names":[],"sources":["../../src/popover/popover-trigger.tsx"],"mappings":";;;iBAOiB;;WAEN;;WAEA;WACA;;iBAGM,4BAA4B,KAC3C
|
|
1
|
+
{"version":3,"file":"popover-trigger.d.mts","names":[],"sources":["../../src/popover/popover-trigger.tsx"],"mappings":";;;iBAOiB;;WAEN;;WAEA;WACA;;iBAGM,4BAA4B,KAC3C;;;;;;EAQA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;EAE9C,WAAW,WAAW,WAAW;;EAEjC,SAAS,WAAW,iCAAiC;;;;;;;;;;;;;wBAcvC,iBACd,UACA,WACA,OACA,UACA,WACG,QACF,sBAAmB;yBAPN"}
|
|
@@ -10,9 +10,9 @@ import { usePopoverTrigger } from "../internal/use-popover-trigger.mjs";
|
|
|
10
10
|
* Root's transition: an open, or a library-owned close while open. The button shows optimistic
|
|
11
11
|
* `data-open` / `aria-expanded` and `data-pending` until the change has landed.
|
|
12
12
|
*
|
|
13
|
-
* Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `
|
|
14
|
-
*
|
|
15
|
-
* `data-disabled`. `aria-haspopup` and `aria-controls` pass through.
|
|
13
|
+
* Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `aria-disabled`
|
|
14
|
+
* (library mode, link or component `render`), `commandfor`, `command`, `aria-expanded`,
|
|
15
|
+
* `onClick` (library mode), `data-open`, `data-pending`, `data-disabled`. `aria-haspopup` and `aria-controls` pass through.
|
|
16
16
|
*/
|
|
17
17
|
function PopoverTrigger({ disabled = false, className, style, children, render, ...rest }) {
|
|
18
18
|
const ctx = usePopoverContext("Popover.Trigger");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"popover-trigger.mjs","names":[],"sources":["../../src/popover/popover-trigger.tsx"],"sourcesContent":["\"use client\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { usePopoverTrigger } from \"../internal/use-popover-trigger.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { usePopoverContext } from \"./popover-context.js\";\n\nexport interface PopoverTriggerState {\n /** Optimistic open state (library mode); `false` in JS-free mode. */\n readonly open: boolean;\n /** The Root's transition is pending (an open, a part-initiated close or a dismissal notification). */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface PopoverTriggerProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"onClick\" | \"aria-expanded\" | \"className\" | \"style\" | \"children\"\n> {\n
|
|
1
|
+
{"version":3,"file":"popover-trigger.mjs","names":[],"sources":["../../src/popover/popover-trigger.tsx"],"sourcesContent":["\"use client\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { usePopoverTrigger } from \"../internal/use-popover-trigger.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { usePopoverContext } from \"./popover-context.js\";\n\nexport interface PopoverTriggerState {\n /** Optimistic open state (library mode); `false` in JS-free mode. */\n readonly open: boolean;\n /** The Root's transition is pending (an open, a part-initiated close or a dismissal notification). */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface PopoverTriggerProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"onClick\" | \"aria-expanded\" | \"className\" | \"style\" | \"children\"\n> {\n /**\n * Never dispatches. Native `disabled` on a `<button>` (no longer an invoker); in library mode\n * with a link or component `render`, `aria-disabled=\"true\"` instead: the element stays\n * focusable.\n */\n disabled?: boolean;\n className?: StateValue<string | undefined, PopoverTriggerState>;\n style?: StateValue<CSSProperties | undefined, PopoverTriggerState>;\n /** Rendered from optimistic state; must not suspend. */\n children?: StateValue<ReactNode, PopoverTriggerState>;\n /** Should render a `<button>`; another element works only in library mode (click handler). */\n render?: RenderProp<ComponentPropsWithRef<\"button\">, PopoverTriggerState>;\n}\n\n/**\n * The toggle: `<button type=\"button\" commandfor command=\"toggle-popover\" aria-expanded>`. In\n * JS-free mode the browser runs the command and the Popup keeps `aria-expanded` current. In\n * library mode the click is cancelled (so the command does not run) and dispatched through the\n * Root's transition: an open, or a library-owned close while open. The button shows optimistic\n * `data-open` / `aria-expanded` and `data-pending` until the change has landed.\n *\n * Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `aria-disabled`\n * (library mode, link or component `render`), `commandfor`, `command`, `aria-expanded`,\n * `onClick` (library mode), `data-open`, `data-pending`, `data-disabled`. `aria-haspopup` and `aria-controls` pass through.\n */\nexport function PopoverTrigger({\n disabled = false,\n className,\n style,\n children,\n render,\n ...rest\n}: PopoverTriggerProps) {\n const ctx = usePopoverContext(\"Popover.Trigger\");\n const { state, libraryProps, ref } = usePopoverTrigger(ctx, {\n part: \"Popover.Trigger\",\n disabled,\n render,\n });\n\n return useRender<ComponentPropsWithRef<\"button\">, PopoverTriggerState>({\n defaultElement: \"button\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps,\n ref,\n });\n}\n\nPopoverTrigger.displayName = \"PopoverTrigger\";\n"],"mappings":";;;;;;;;;;;;;;;;AA4CA,SAAgB,eAAe,EAC7B,WAAW,OACX,WACA,OACA,UACA,QACA,GAAG,QACmB;CACtB,MAAM,MAAM,kBAAkB,iBAAiB;CAC/C,MAAM,EAAE,OAAO,cAAc,QAAQ,kBAAkB,KAAK;EAC1D,MAAM;EACN;EACA;CACF,CAAC;CAED,OAAO,UAAgE;EACrE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;EACA;CACF,CAAC;AACH;AAEA,eAAe,cAAc"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useTransitionBoundary } from "../internal/transition-boundary.mjs";
|
|
3
2
|
import { dataAttr } from "../internal/data-attr.mjs";
|
|
3
|
+
import { useTransitionBoundary } from "../internal/transition-boundary.mjs";
|
|
4
4
|
import { useRender } from "../internal/use-render.mjs";
|
|
5
5
|
import { guardCheckableChange, useNativeChecked } from "../internal/use-native-checked.mjs";
|
|
6
6
|
import { syncPresence } from "../internal/sync-presence.mjs";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { createActionDetails } from "../internal/action-details.mjs";
|
|
3
3
|
import { dataAttr } from "../internal/data-attr.mjs";
|
|
4
|
+
import { useRender } from "../internal/use-render.mjs";
|
|
4
5
|
import { transitionTypes } from "../internal/transition-types.mjs";
|
|
5
6
|
import { useControllableAction } from "../internal/use-controllable-action.mjs";
|
|
6
|
-
import { useRender } from "../internal/use-render.mjs";
|
|
7
7
|
import { useFieldControl, useFieldValueChange, useMergedRef } from "../internal/use-field-control.mjs";
|
|
8
8
|
import { RadioGroupContext } from "./radio-group-context.mjs";
|
|
9
9
|
import { useNativeGroupReconciliation } from "../internal/use-native-group-reconciliation.mjs";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { createActionDetails } from "../internal/action-details.mjs";
|
|
3
3
|
import { dataAttr } from "../internal/data-attr.mjs";
|
|
4
|
+
import { useRender } from "../internal/use-render.mjs";
|
|
4
5
|
import { transitionTypes } from "../internal/transition-types.mjs";
|
|
5
6
|
import { useControllableAction } from "../internal/use-controllable-action.mjs";
|
|
6
|
-
import { useRender } from "../internal/use-render.mjs";
|
|
7
7
|
import { fieldInputProps, useFieldControl, useFieldValueChange, useMergedRef } from "../internal/use-field-control.mjs";
|
|
8
8
|
import { SelectContext } from "./select-context.mjs";
|
|
9
9
|
import { useSelectPartRef } from "./select-dom.mjs";
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { createActionDetails } from "../internal/action-details.mjs";
|
|
3
3
|
import { dataAttr } from "../internal/data-attr.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { useControllableAction } from "../internal/use-controllable-action.mjs";
|
|
4
|
+
import { useActionTrigger } from "../internal/use-action-trigger.mjs";
|
|
5
|
+
import { useDevWarning } from "../internal/use-dev-warning.mjs";
|
|
7
6
|
import { mergeTokenLists } from "../internal/merge-props.mjs";
|
|
8
7
|
import { resolveStateValue } from "../internal/render-element.mjs";
|
|
9
8
|
import { useRender } from "../internal/use-render.mjs";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { transitionTypes } from "../internal/transition-types.mjs";
|
|
10
|
+
import { useLatest } from "../internal/use-latest.mjs";
|
|
11
|
+
import { useControllableAction } from "../internal/use-controllable-action.mjs";
|
|
12
12
|
import { FieldContext } from "../internal/field-context.mjs";
|
|
13
13
|
import { useFieldControl, useFieldValueChange, useMergedRef } from "../internal/use-field-control.mjs";
|
|
14
14
|
import { useNativeGroupReconciliation } from "../internal/use-native-group-reconciliation.mjs";
|
|
@@ -106,7 +106,7 @@ function SliderRootImpl(props) {
|
|
|
106
106
|
});
|
|
107
107
|
const { optimistic, committed, dispatch } = commitState;
|
|
108
108
|
const committedRef = useLatest(committed);
|
|
109
|
-
const { pending: stepPending, run: runStep } = useActionTrigger();
|
|
109
|
+
const { pending: stepPending, run: runStep } = useActionTrigger({ ignoreWhilePending: false });
|
|
110
110
|
const optimisticValues = useMemo(() => toArray(optimistic), [optimistic]);
|
|
111
111
|
const shown = (gestureThumb !== null || stepHeld || stepPending || commitState.pending) && live !== null ? live : optimisticValues;
|
|
112
112
|
const pending = stepPending || commitState.pending;
|