@funstack/ui 0.1.0-alpha.2 → 0.1.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accordion/accordion-context.mjs.map +1 -1
- package/dist/accordion/accordion-item.mjs +18 -4
- package/dist/accordion/accordion-item.mjs.map +1 -1
- package/dist/accordion/accordion-panel.mjs +2 -3
- package/dist/accordion/accordion-panel.mjs.map +1 -1
- package/dist/accordion/accordion-root.d.mts.map +1 -1
- package/dist/accordion/accordion-root.mjs +34 -15
- package/dist/accordion/accordion-root.mjs.map +1 -1
- package/dist/accordion/accordion-trigger.mjs +4 -5
- package/dist/accordion/accordion-trigger.mjs.map +1 -1
- package/dist/checkbox/checkbox.d.mts +5 -3
- package/dist/checkbox/checkbox.d.mts.map +1 -1
- package/dist/checkbox/checkbox.mjs +25 -9
- package/dist/checkbox/checkbox.mjs.map +1 -1
- package/dist/checkbox-group/checkbox-group-context.mjs.map +1 -1
- package/dist/checkbox-group/checkbox-group-item.d.mts +2 -1
- package/dist/checkbox-group/checkbox-group-item.d.mts.map +1 -1
- package/dist/checkbox-group/checkbox-group-item.mjs +3 -1
- package/dist/checkbox-group/checkbox-group-item.mjs.map +1 -1
- package/dist/checkbox-group/checkbox-group-root.d.mts +5 -2
- package/dist/checkbox-group/checkbox-group-root.d.mts.map +1 -1
- package/dist/checkbox-group/checkbox-group-root.mjs +24 -5
- package/dist/checkbox-group/checkbox-group-root.mjs.map +1 -1
- package/dist/combobox/combobox-clear.mjs +2 -1
- package/dist/combobox/combobox-clear.mjs.map +1 -1
- package/dist/combobox/combobox-context.mjs.map +1 -1
- package/dist/combobox/combobox-input.d.mts +3 -1
- package/dist/combobox/combobox-input.d.mts.map +1 -1
- package/dist/combobox/combobox-input.mjs +16 -2
- package/dist/combobox/combobox-input.mjs.map +1 -1
- package/dist/combobox/combobox-list.mjs +3 -2
- package/dist/combobox/combobox-list.mjs.map +1 -1
- package/dist/combobox/combobox-popup.d.mts.map +1 -1
- package/dist/combobox/combobox-popup.mjs +7 -2
- package/dist/combobox/combobox-popup.mjs.map +1 -1
- package/dist/combobox/combobox-root.d.mts.map +1 -1
- package/dist/combobox/combobox-root.mjs +20 -5
- package/dist/combobox/combobox-root.mjs.map +1 -1
- package/dist/combobox/combobox-trigger.mjs +2 -1
- package/dist/combobox/combobox-trigger.mjs.map +1 -1
- package/dist/dialog/dialog-close.d.mts +17 -10
- package/dist/dialog/dialog-close.d.mts.map +1 -1
- package/dist/dialog/dialog-close.mjs +33 -21
- package/dist/dialog/dialog-close.mjs.map +1 -1
- package/dist/dialog/dialog-context.mjs.map +1 -1
- package/dist/dialog/dialog-form.d.mts +23 -10
- package/dist/dialog/dialog-form.d.mts.map +1 -1
- package/dist/dialog/dialog-form.mjs +51 -32
- package/dist/dialog/dialog-form.mjs.map +1 -1
- package/dist/dialog/dialog-popup.d.mts +10 -4
- package/dist/dialog/dialog-popup.d.mts.map +1 -1
- package/dist/dialog/dialog-popup.mjs +117 -44
- package/dist/dialog/dialog-popup.mjs.map +1 -1
- package/dist/dialog/dialog-root.d.mts +14 -8
- package/dist/dialog/dialog-root.d.mts.map +1 -1
- package/dist/dialog/dialog-root.mjs +67 -12
- package/dist/dialog/dialog-root.mjs.map +1 -1
- package/dist/dialog/dialog-trigger.d.mts +3 -2
- package/dist/dialog/dialog-trigger.d.mts.map +1 -1
- package/dist/dialog/dialog-trigger.mjs +2 -1
- package/dist/dialog/dialog-trigger.mjs.map +1 -1
- package/dist/dialog/index.d.mts +4 -2
- package/dist/dialog/index.d.mts.map +1 -1
- package/dist/dialog/index.mjs +4 -2
- package/dist/dialog/index.mjs.map +1 -1
- package/dist/field/field-control.d.mts +33 -0
- package/dist/field/field-control.d.mts.map +1 -0
- package/dist/field/field-control.mjs +68 -0
- package/dist/field/field-control.mjs.map +1 -0
- package/dist/field/field-description.d.mts +24 -0
- package/dist/field/field-description.d.mts.map +1 -0
- package/dist/field/field-description.mjs +34 -0
- package/dist/field/field-description.mjs.map +1 -0
- package/dist/field/field-error.d.mts +30 -0
- package/dist/field/field-error.d.mts.map +1 -0
- package/dist/field/field-error.mjs +42 -0
- package/dist/field/field-error.mjs.map +1 -0
- package/dist/field/field-label.d.mts +25 -0
- package/dist/field/field-label.d.mts.map +1 -0
- package/dist/field/field-label.mjs +46 -0
- package/dist/field/field-label.mjs.map +1 -0
- package/dist/field/field-root.d.mts +53 -0
- package/dist/field/field-root.d.mts.map +1 -0
- package/dist/field/field-root.mjs +319 -0
- package/dist/field/field-root.mjs.map +1 -0
- package/dist/field/index.d.mts +18 -0
- package/dist/field/index.d.mts.map +1 -0
- package/dist/field/index.mjs +17 -0
- package/dist/field/index.mjs.map +1 -0
- package/dist/form/form-root.d.mts +58 -0
- package/dist/form/form-root.d.mts.map +1 -0
- package/dist/form/form-root.mjs +147 -0
- package/dist/form/form-root.mjs.map +1 -0
- package/dist/form/form-submit.d.mts +31 -0
- package/dist/form/form-submit.d.mts.map +1 -0
- package/dist/form/form-submit.mjs +53 -0
- package/dist/form/form-submit.mjs.map +1 -0
- package/dist/form/index.d.mts +11 -0
- package/dist/form/index.d.mts.map +1 -0
- package/dist/form/index.mjs +11 -0
- package/dist/form/index.mjs.map +1 -0
- package/dist/index.d.mts +53 -1
- package/dist/index.mjs +11 -1
- package/dist/internal/announce.mjs +53 -0
- package/dist/internal/announce.mjs.map +1 -0
- package/dist/internal/content-focus-probe.mjs +42 -0
- package/dist/internal/content-focus-probe.mjs.map +1 -0
- package/dist/internal/decimal.mjs +27 -0
- package/dist/internal/decimal.mjs.map +1 -0
- package/dist/internal/dev-warn.mjs +17 -0
- package/dist/internal/dev-warn.mjs.map +1 -0
- package/dist/internal/field-context.d.mts +19 -0
- package/dist/internal/field-context.d.mts.map +1 -0
- package/dist/internal/field-context.mjs +14 -0
- package/dist/internal/field-context.mjs.map +1 -0
- package/dist/internal/focus-first-invalid.mjs +67 -0
- package/dist/internal/focus-first-invalid.mjs.map +1 -0
- package/dist/internal/form-status-watcher.mjs +31 -0
- package/dist/internal/form-status-watcher.mjs.map +1 -0
- package/dist/internal/modal-registry.mjs +39 -0
- package/dist/internal/modal-registry.mjs.map +1 -0
- package/dist/internal/range-value.mjs +51 -0
- package/dist/internal/range-value.mjs.map +1 -0
- package/dist/internal/render-element.mjs +54 -0
- package/dist/internal/render-element.mjs.map +1 -0
- package/dist/internal/toolbar-context.mjs +17 -0
- package/dist/internal/toolbar-context.mjs.map +1 -0
- package/dist/internal/types.d.mts +3 -1
- package/dist/internal/types.d.mts.map +1 -1
- package/dist/internal/use-controllable-action.mjs +33 -15
- package/dist/internal/use-controllable-action.mjs.map +1 -1
- package/dist/internal/use-exit-animation.mjs +26 -6
- package/dist/internal/use-exit-animation.mjs.map +1 -1
- package/dist/internal/use-field-control.mjs +109 -0
- package/dist/internal/use-field-control.mjs.map +1 -0
- package/dist/internal/use-form-owner-event.mjs +44 -0
- package/dist/internal/use-form-owner-event.mjs.map +1 -0
- package/dist/internal/use-form-reset.mjs +10 -33
- package/dist/internal/use-form-reset.mjs.map +1 -1
- package/dist/internal/use-latest.mjs +32 -0
- package/dist/internal/use-latest.mjs.map +1 -0
- package/dist/internal/use-modal-registration.mjs +47 -0
- package/dist/internal/use-modal-registration.mjs.map +1 -0
- package/dist/internal/use-native-group-reconciliation.mjs +6 -5
- package/dist/internal/use-native-group-reconciliation.mjs.map +1 -1
- package/dist/internal/use-native-select-value.mjs +7 -5
- package/dist/internal/use-native-select-value.mjs.map +1 -1
- package/dist/internal/use-popover-popup.mjs +11 -24
- package/dist/internal/use-popover-popup.mjs.map +1 -1
- package/dist/internal/use-popover-root.mjs +58 -23
- package/dist/internal/use-popover-root.mjs.map +1 -1
- package/dist/internal/use-popover-trigger.mjs +1 -1
- package/dist/internal/use-render.mjs +11 -34
- package/dist/internal/use-render.mjs.map +1 -1
- package/dist/internal/use-roving-focus.mjs +255 -0
- package/dist/internal/use-roving-focus.mjs.map +1 -0
- package/dist/internal/validity.d.mts +24 -0
- package/dist/internal/validity.d.mts.map +1 -0
- package/dist/internal/validity.mjs +108 -0
- package/dist/internal/validity.mjs.map +1 -0
- package/dist/menu/menu-checkbox-item.mjs +2 -1
- package/dist/menu/menu-checkbox-item.mjs.map +1 -1
- package/dist/menu/menu-group.d.mts.map +1 -1
- package/dist/menu/menu-group.mjs +3 -2
- package/dist/menu/menu-group.mjs.map +1 -1
- package/dist/menu/menu-item.mjs +2 -1
- package/dist/menu/menu-item.mjs.map +1 -1
- package/dist/menu/menu-popup.d.mts.map +1 -1
- package/dist/menu/menu-popup.mjs +7 -2
- package/dist/menu/menu-popup.mjs.map +1 -1
- package/dist/menu/menu-radio-group.mjs +9 -4
- package/dist/menu/menu-radio-group.mjs.map +1 -1
- package/dist/menu/menu-radio-item.mjs +2 -1
- package/dist/menu/menu-radio-item.mjs.map +1 -1
- package/dist/menu/menu-root.mjs +1 -1
- package/dist/menu/menu-root.mjs.map +1 -1
- package/dist/menu/menu-trigger.d.mts +2 -1
- package/dist/menu/menu-trigger.d.mts.map +1 -1
- package/dist/menu/menu-trigger.mjs +15 -4
- package/dist/menu/menu-trigger.mjs.map +1 -1
- package/dist/meter/index.d.mts +24 -0
- package/dist/meter/index.d.mts.map +1 -0
- package/dist/meter/index.mjs +23 -0
- package/dist/meter/index.mjs.map +1 -0
- package/dist/meter/meter-context.mjs +13 -0
- package/dist/meter/meter-context.mjs.map +1 -0
- package/dist/meter/meter-indicator.d.mts +19 -0
- package/dist/meter/meter-indicator.d.mts.map +1 -0
- package/dist/meter/meter-indicator.mjs +25 -0
- package/dist/meter/meter-indicator.mjs.map +1 -0
- package/dist/meter/meter-label.d.mts +19 -0
- package/dist/meter/meter-label.d.mts.map +1 -0
- package/dist/meter/meter-label.mjs +28 -0
- package/dist/meter/meter-label.mjs.map +1 -0
- package/dist/meter/meter-region.d.mts +4 -0
- package/dist/meter/meter-region.d.mts.map +1 -0
- package/dist/meter/meter-region.mjs +39 -0
- package/dist/meter/meter-region.mjs.map +1 -0
- package/dist/meter/meter-root.d.mts +71 -0
- package/dist/meter/meter-root.d.mts.map +1 -0
- package/dist/meter/meter-root.mjs +83 -0
- package/dist/meter/meter-root.mjs.map +1 -0
- package/dist/meter/meter-track.d.mts +19 -0
- package/dist/meter/meter-track.d.mts.map +1 -0
- package/dist/meter/meter-track.mjs +25 -0
- package/dist/meter/meter-track.mjs.map +1 -0
- package/dist/meter/meter-value.d.mts +20 -0
- package/dist/meter/meter-value.d.mts.map +1 -0
- package/dist/meter/meter-value.mjs +29 -0
- package/dist/meter/meter-value.mjs.map +1 -0
- package/dist/number-field/index.d.mts +26 -0
- package/dist/number-field/index.d.mts.map +1 -0
- package/dist/number-field/index.mjs +26 -0
- package/dist/number-field/index.mjs.map +1 -0
- package/dist/number-field/number-field-arithmetic.mjs +78 -0
- package/dist/number-field/number-field-arithmetic.mjs.map +1 -0
- package/dist/number-field/number-field-context.d.mts +2 -0
- package/dist/number-field/number-field-context.mjs +13 -0
- package/dist/number-field/number-field-context.mjs.map +1 -0
- package/dist/number-field/number-field-decrement.d.mts +16 -0
- package/dist/number-field/number-field-decrement.d.mts.map +1 -0
- package/dist/number-field/number-field-decrement.mjs +19 -0
- package/dist/number-field/number-field-decrement.mjs.map +1 -0
- package/dist/number-field/number-field-format.mjs +279 -0
- package/dist/number-field/number-field-format.mjs.map +1 -0
- package/dist/number-field/number-field-increment.d.mts +32 -0
- package/dist/number-field/number-field-increment.d.mts.map +1 -0
- package/dist/number-field/number-field-increment.mjs +68 -0
- package/dist/number-field/number-field-increment.mjs.map +1 -0
- package/dist/number-field/number-field-input.d.mts +29 -0
- package/dist/number-field/number-field-input.d.mts.map +1 -0
- package/dist/number-field/number-field-input.mjs +163 -0
- package/dist/number-field/number-field-input.mjs.map +1 -0
- package/dist/number-field/number-field-label.d.mts +23 -0
- package/dist/number-field/number-field-label.d.mts.map +1 -0
- package/dist/number-field/number-field-label.mjs +47 -0
- package/dist/number-field/number-field-label.mjs.map +1 -0
- package/dist/number-field/number-field-root.d.mts +108 -0
- package/dist/number-field/number-field-root.d.mts.map +1 -0
- package/dist/number-field/number-field-root.mjs +523 -0
- package/dist/number-field/number-field-root.mjs.map +1 -0
- package/dist/number-field/use-step-button.mjs +107 -0
- package/dist/number-field/use-step-button.mjs.map +1 -0
- package/dist/popover/popover-close.mjs +2 -1
- package/dist/popover/popover-close.mjs.map +1 -1
- package/dist/popover/popover-popup.d.mts.map +1 -1
- package/dist/popover/popover-popup.mjs +7 -2
- package/dist/popover/popover-popup.mjs.map +1 -1
- package/dist/popover/popover-root.mjs +1 -1
- package/dist/progress/index.d.mts +24 -0
- package/dist/progress/index.d.mts.map +1 -0
- package/dist/progress/index.mjs +24 -0
- package/dist/progress/index.mjs.map +1 -0
- package/dist/progress/progress-context.mjs +13 -0
- package/dist/progress/progress-context.mjs.map +1 -0
- package/dist/progress/progress-indicator.d.mts +19 -0
- package/dist/progress/progress-indicator.d.mts.map +1 -0
- package/dist/progress/progress-indicator.mjs +29 -0
- package/dist/progress/progress-indicator.mjs.map +1 -0
- package/dist/progress/progress-label.d.mts +19 -0
- package/dist/progress/progress-label.d.mts.map +1 -0
- package/dist/progress/progress-label.mjs +30 -0
- package/dist/progress/progress-label.mjs.map +1 -0
- package/dist/progress/progress-root.d.mts +68 -0
- package/dist/progress/progress-root.d.mts.map +1 -0
- package/dist/progress/progress-root.mjs +80 -0
- package/dist/progress/progress-root.mjs.map +1 -0
- package/dist/progress/progress-track.d.mts +19 -0
- package/dist/progress/progress-track.d.mts.map +1 -0
- package/dist/progress/progress-track.mjs +29 -0
- package/dist/progress/progress-track.mjs.map +1 -0
- package/dist/progress/progress-value.d.mts +20 -0
- package/dist/progress/progress-value.d.mts.map +1 -0
- package/dist/progress/progress-value.mjs +33 -0
- package/dist/progress/progress-value.mjs.map +1 -0
- package/dist/radio-group/radio-group-root.d.mts +4 -2
- package/dist/radio-group/radio-group-root.d.mts.map +1 -1
- package/dist/radio-group/radio-group-root.mjs +34 -8
- package/dist/radio-group/radio-group-root.mjs.map +1 -1
- package/dist/select/select-group.d.mts.map +1 -1
- package/dist/select/select-group.mjs +6 -4
- package/dist/select/select-group.mjs.map +1 -1
- package/dist/select/select-root.d.mts +4 -2
- package/dist/select/select-root.d.mts.map +1 -1
- package/dist/select/select-root.mjs +26 -6
- package/dist/select/select-root.mjs.map +1 -1
- package/dist/slider/index.d.mts +32 -0
- package/dist/slider/index.d.mts.map +1 -0
- package/dist/slider/index.mjs +27 -0
- package/dist/slider/index.mjs.map +1 -0
- package/dist/slider/slider-arithmetic.mjs +104 -0
- package/dist/slider/slider-arithmetic.mjs.map +1 -0
- package/dist/slider/slider-context.mjs +13 -0
- package/dist/slider/slider-context.mjs.map +1 -0
- package/dist/slider/slider-indicator.d.mts +22 -0
- package/dist/slider/slider-indicator.d.mts.map +1 -0
- package/dist/slider/slider-indicator.mjs +36 -0
- package/dist/slider/slider-indicator.mjs.map +1 -0
- package/dist/slider/slider-label.d.mts +23 -0
- package/dist/slider/slider-label.d.mts.map +1 -0
- package/dist/slider/slider-label.mjs +44 -0
- package/dist/slider/slider-label.mjs.map +1 -0
- package/dist/slider/slider-root.d.mts +111 -0
- package/dist/slider/slider-root.d.mts.map +1 -0
- package/dist/slider/slider-root.mjs +583 -0
- package/dist/slider/slider-root.mjs.map +1 -0
- package/dist/slider/slider-thumb.d.mts +58 -0
- package/dist/slider/slider-thumb.d.mts.map +1 -0
- package/dist/slider/slider-thumb.mjs +146 -0
- package/dist/slider/slider-thumb.mjs.map +1 -0
- package/dist/slider/slider-track.d.mts +29 -0
- package/dist/slider/slider-track.d.mts.map +1 -0
- package/dist/slider/slider-track.mjs +55 -0
- package/dist/slider/slider-track.mjs.map +1 -0
- package/dist/slider/slider-value.d.mts +34 -0
- package/dist/slider/slider-value.d.mts.map +1 -0
- package/dist/slider/slider-value.mjs +38 -0
- package/dist/slider/slider-value.mjs.map +1 -0
- package/dist/switch/switch.d.mts +5 -3
- package/dist/switch/switch.d.mts.map +1 -1
- package/dist/switch/switch.mjs +24 -8
- package/dist/switch/switch.mjs.map +1 -1
- package/dist/tabs/tabs-indicator.mjs +2 -1
- package/dist/tabs/tabs-indicator.mjs.map +1 -1
- package/dist/tabs/tabs-root.mjs +13 -3
- package/dist/tabs/tabs-root.mjs.map +1 -1
- package/dist/tabs/tabs-tab.mjs +2 -1
- package/dist/tabs/tabs-tab.mjs.map +1 -1
- package/dist/toast/index.d.mts +25 -0
- package/dist/toast/index.d.mts.map +1 -0
- package/dist/toast/index.mjs +26 -0
- package/dist/toast/index.mjs.map +1 -0
- package/dist/toast/toast-action.d.mts +39 -0
- package/dist/toast/toast-action.d.mts.map +1 -0
- package/dist/toast/toast-action.mjs +65 -0
- package/dist/toast/toast-action.mjs.map +1 -0
- package/dist/toast/toast-close.d.mts +25 -0
- package/dist/toast/toast-close.d.mts.map +1 -0
- package/dist/toast/toast-close.mjs +47 -0
- package/dist/toast/toast-close.mjs.map +1 -0
- package/dist/toast/toast-context.mjs +19 -0
- package/dist/toast/toast-context.mjs.map +1 -0
- package/dist/toast/toast-description.d.mts +24 -0
- package/dist/toast/toast-description.d.mts.map +1 -0
- package/dist/toast/toast-description.mjs +35 -0
- package/dist/toast/toast-description.mjs.map +1 -0
- package/dist/toast/toast-root.d.mts +43 -0
- package/dist/toast/toast-root.d.mts.map +1 -0
- package/dist/toast/toast-root.mjs +205 -0
- package/dist/toast/toast-root.mjs.map +1 -0
- package/dist/toast/toast-title.d.mts +24 -0
- package/dist/toast/toast-title.d.mts.map +1 -0
- package/dist/toast/toast-title.mjs +33 -0
- package/dist/toast/toast-title.mjs.map +1 -0
- package/dist/toast/toast-viewport.d.mts +52 -0
- package/dist/toast/toast-viewport.d.mts.map +1 -0
- package/dist/toast/toast-viewport.mjs +644 -0
- package/dist/toast/toast-viewport.mjs.map +1 -0
- package/dist/toast/toast.d.mts +119 -0
- package/dist/toast/toast.d.mts.map +1 -0
- package/dist/toast/toast.mjs +157 -0
- package/dist/toast/toast.mjs.map +1 -0
- package/dist/toggle/toggle.mjs +2 -1
- package/dist/toggle/toggle.mjs.map +1 -1
- package/dist/toggle-group/index.d.mts +16 -0
- package/dist/toggle-group/index.d.mts.map +1 -0
- package/dist/toggle-group/index.mjs +15 -0
- package/dist/toggle-group/index.mjs.map +1 -0
- package/dist/toggle-group/toggle-group-context.d.mts +4 -0
- package/dist/toggle-group/toggle-group-context.d.mts.map +1 -0
- package/dist/toggle-group/toggle-group-context.mjs +13 -0
- package/dist/toggle-group/toggle-group-context.mjs.map +1 -0
- package/dist/toggle-group/toggle-group-item.d.mts +47 -0
- package/dist/toggle-group/toggle-group-item.d.mts.map +1 -0
- package/dist/toggle-group/toggle-group-item.mjs +63 -0
- package/dist/toggle-group/toggle-group-item.mjs.map +1 -0
- package/dist/toggle-group/toggle-group-root.d.mts +95 -0
- package/dist/toggle-group/toggle-group-root.d.mts.map +1 -0
- package/dist/toggle-group/toggle-group-root.mjs +211 -0
- package/dist/toggle-group/toggle-group-root.mjs.map +1 -0
- package/dist/toolbar/index.d.mts +18 -0
- package/dist/toolbar/index.d.mts.map +1 -0
- package/dist/toolbar/index.mjs +18 -0
- package/dist/toolbar/index.mjs.map +1 -0
- package/dist/toolbar/toolbar-button.d.mts +42 -0
- package/dist/toolbar/toolbar-button.d.mts.map +1 -0
- package/dist/toolbar/toolbar-button.mjs +50 -0
- package/dist/toolbar/toolbar-button.mjs.map +1 -0
- package/dist/toolbar/toolbar-root.d.mts +38 -0
- package/dist/toolbar/toolbar-root.d.mts.map +1 -0
- package/dist/toolbar/toolbar-root.mjs +66 -0
- package/dist/toolbar/toolbar-root.mjs.map +1 -0
- package/dist/toolbar/toolbar-separator.d.mts +25 -0
- package/dist/toolbar/toolbar-separator.d.mts.map +1 -0
- package/dist/toolbar/toolbar-separator.mjs +31 -0
- package/dist/toolbar/toolbar-separator.mjs.map +1 -0
- package/dist/tooltip/tooltip-popup.mjs +2 -1
- package/dist/tooltip/tooltip-popup.mjs.map +1 -1
- package/dist/tooltip/tooltip-root.mjs +16 -2
- package/dist/tooltip/tooltip-root.mjs.map +1 -1
- package/dist/tooltip/tooltip-trigger.mjs +2 -1
- package/dist/tooltip/tooltip-trigger.mjs.map +1 -1
- package/package.json +10 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-root.mjs","names":[],"sources":["../../src/form/form-root.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback, useMemo, useRef, useState, useTransition } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, FormEvent, ReactNode } from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport { FormContext } from \"../internal/field-context.js\";\nimport type { FormContextValue, FormFieldRegistration } from \"../internal/field-context.js\";\nimport { announce } from \"../internal/focus-first-invalid.js\";\nimport { FormStatusWatcher } from \"../internal/form-status-watcher.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useLatest } from \"../internal/use-latest.js\";\nimport { resolveStateValue, useRender } from \"../internal/use-render.js\";\nimport { hasAnyError, sortByDocumentOrder } from \"../internal/validity.js\";\nimport type { FormErrors } from \"../internal/validity.js\";\n\nexport type { FieldErrorValue, FormErrors } from \"../internal/validity.js\";\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\nexport interface FormRootState {\n /** A submission is in flight: the Form's own transition, or the form's status (`useFormStatus`), including the Suspense wait of the result. */\n readonly pending: boolean;\n}\n\nexport interface FormRootProps extends Omit<\n ComponentPropsWithRef<\"form\">,\n \"onSubmit\" | \"method\" | \"action\" | \"className\" | \"style\" | \"children\"\n> {\n /**\n * React form action (`(formData) => unknown`: a Server Function, a `useActionState` dispatch,\n * or a client function), rendered on the form unwrapped so React's progressive enhancement is\n * kept, and run by the Form inside its own transition after hydration (no automatic reset; see\n * `autoReset`). A string is a navigating form. A submitter with its own `formAction` is left to\n * React (automatic reset applies to it).\n */\n action?: string | ((formData: FormData) => unknown);\n /**\n * Field errors keyed by field `name` (usually `state.errors` from `useActionState`). Each\n * `Field.Root` with that name shows its entry, writes it into the control's custom validity,\n * and clears it on the user's next edit; the next settled submission shows the new entries.\n * Errors present at mount are shown without moving focus.\n */\n errors?: FormErrors;\n /**\n * Reset the form (`form.reset()`) when a submission the Form ran settles with no non-empty\n * entry in `errors`. Default `true`. A submission that reports errors never resets, so the\n * user's input, `:user-invalid` and custom validity stay. `false`: never reset.\n */\n autoReset?: boolean;\n /** Only with a string `action`. Owned otherwise (React's own with a function action). */\n method?: ComponentPropsWithRef<\"form\">[\"method\"];\n className?: StateValue<string | undefined, FormRootState>;\n style?: StateValue<CSSProperties | undefined, FormRootState>;\n children?: StateValue<ReactNode, FormRootState>;\n /** Must render a `<form>`. */\n render?: RenderProp<ComponentPropsWithRef<\"form\">, FormRootState>;\n}\n\ninterface Submission {\n /** The Form ran the action itself (`onSubmit` + its own transition). */\n readonly intercepted: boolean;\n readonly formData: FormData | null;\n}\n\n/**\n * A `<form>` that runs its function `action` itself after hydration (design/components/field-form.md\n * 3): `onSubmit` cancels the native submission, so react-dom neither runs the action nor schedules\n * its automatic reset, and the Form awaits `action(formData)` inside its own transition, which\n * still starts the form's status (`useFormStatus`). The action stays rendered unwrapped on the\n * element, so a Server Function posts before hydration.\n *\n * When a submission settles, the Form focuses the first Field that shows a server error (from\n * `errors`), or, when `errors` has no entry and `autoReset` is on, resets the form.\n *\n * Owned keys (conventions 1.2): `action`, `method` (except with a string `action`), `onSubmit`,\n * `data-pending`.\n */\nexport function FormRoot({\n action,\n errors,\n autoReset = true,\n method,\n className,\n style,\n children,\n render,\n ...rest\n}: FormRootProps) {\n const [isPending, startTransition] = useTransition();\n const [statusPending, setStatusPending] = useState(false);\n const pending = isPending || statusPending;\n\n const formRef = useRef<HTMLFormElement | null>(null);\n const submission = useRef<Submission | null>(null);\n const fields = useRef(new Set<FormFieldRegistration>());\n const warnedElement = useRef(false);\n const latest = useLatest({ errors, autoReset });\n const isFunction = typeof action === \"function\";\n\n // A native capture listener sees every submission, React's path included (a submitter with its\n // own `formAction`), before React's `onSubmit` runs and marks it intercepted.\n const libraryRef = useCallback((form: HTMLFormElement) => {\n formRef.current = form;\n if (isDev && !warnedElement.current && form.tagName !== \"FORM\") {\n warnedElement.current = true;\n console.error(\n `Form.Root: expected to render a <form> element but got <${form.tagName.toLowerCase()}>.`,\n );\n }\n const onSubmit = (event: Event) => {\n if (event.target !== form) return;\n submission.current = { intercepted: false, formData: null };\n };\n form.addEventListener(\"submit\", onSubmit, true);\n return () => {\n form.removeEventListener(\"submit\", onSubmit, true);\n if (formRef.current === form) formRef.current = null;\n };\n }, []);\n\n const onSubmit = (event: FormEvent<HTMLFormElement>) => {\n if (typeof action !== \"function\") return; // a navigating form: the browser's\n // A `render` element's own `onSubmit` (merged, it runs first) vetoed the submission\n // (conventions: a `preventDefault()` in the consumer's handler cancels the library's work).\n if (event.defaultPrevented) {\n submission.current = null;\n return;\n }\n const native = event.nativeEvent as SubmitEvent;\n const submitter = native.submitter;\n // A per-button action: React's own path (and its automatic reset).\n if (submitter?.hasAttribute(\"formaction\") === true) return;\n // react-dom now skips its action run and its reset, and still starts the form's status.\n event.preventDefault();\n const formData = new FormData(event.currentTarget, submitter);\n submission.current = { intercepted: true, formData };\n startTransition(async () => {\n await action(formData);\n });\n };\n\n const registerField = useCallback((registration: FormFieldRegistration) => {\n fields.current.add(registration);\n return () => {\n fields.current.delete(registration);\n };\n }, []);\n\n // The settle (design 3.2): the form's status went `true → false`. The Fields rendered the\n // result's errors in that commit; this effect runs after it.\n const onSettled = useCallback(() => {\n const settled = submission.current;\n submission.current = null;\n const form = formRef.current;\n if (form === null) return;\n const shown = [...fields.current]\n .filter((field) => field.showsServerError())\n .map((field) => ({ field, target: field.getFocusTarget() }))\n .filter(\n (entry): entry is { field: FormFieldRegistration; target: HTMLElement } =>\n entry.target !== null && entry.target.isConnected,\n );\n if (shown.length > 0) {\n const first = sortByDocumentOrder(shown, (entry) => entry.target)[0]!;\n // Focus already there (the user pressed Enter in that field): `focus()` does nothing and\n // nothing reads the new description, so the message is announced instead (design 5.3).\n if (first.target.ownerDocument.activeElement === first.target) {\n announce(first.target, first.field.serverMessages().join(\" \"));\n } else {\n first.target.focus();\n }\n return;\n }\n const { errors, autoReset } = latest.current;\n if (autoReset && settled?.intercepted === true && !hasAnyError(errors)) form.reset();\n }, [latest]);\n\n const submittedData = useCallback(() => {\n const current = submission.current;\n return current?.intercepted === true ? current.formData : null;\n }, []);\n\n const refreshFields = useCallback((source: FormFieldRegistration | null) => {\n for (const field of fields.current) if (field !== source) field.refresh();\n }, []);\n\n const context = useMemo<FormContextValue>(\n () => ({ errors, registerField, submittedData, refreshFields }),\n [errors, registerField, submittedData, refreshFields],\n );\n\n const state: FormRootState = { pending };\n\n const element = useRender<ComponentPropsWithRef<\"form\">, FormRootState>({\n defaultElement: \"form\",\n render,\n state,\n className,\n style,\n children: (s) => (\n <>\n {resolveStateValue(children, s)}\n {isFunction && (\n <FormStatusWatcher onPendingChange={setStatusPending} onSettled={onSettled} />\n )}\n </>\n ),\n props: rest,\n libraryProps: {\n method: typeof action === \"string\" ? method : undefined,\n // React's own type wants `void | Promise<void>`; the library's actions return `unknown`\n // (conventions 2.2) and React ignores the result.\n action: action as ComponentPropsWithRef<\"form\">[\"action\"],\n onSubmit: isFunction ? onSubmit : undefined,\n \"data-pending\": dataAttr(pending),\n },\n ref: libraryRef,\n });\n\n return <FormContext value={context}>{element}</FormContext>;\n}\n\nFormRoot.displayName = \"FormRoot\";\n"],"mappings":";;;;;;;;;;;;AAgBA,MAAM,QAAQ,QAAQ,IAAI,aAAa;;;;;;;;;;;;;;AA4DvC,SAAgB,SAAS,EACvB,QACA,QACA,YAAY,MACZ,QACA,WACA,OACA,UACA,QACA,GAAG,QACa;CAChB,MAAM,CAAC,WAAW,mBAAmB,cAAc;CACnD,MAAM,CAAC,eAAe,oBAAoB,SAAS,KAAK;CACxD,MAAM,UAAU,aAAa;CAE7B,MAAM,UAAU,OAA+B,IAAI;CACnD,MAAM,aAAa,OAA0B,IAAI;CACjD,MAAM,SAAS,uBAAO,IAAI,IAA2B,CAAC;CACtD,MAAM,gBAAgB,OAAO,KAAK;CAClC,MAAM,SAAS,UAAU;EAAE;EAAQ;CAAU,CAAC;CAC9C,MAAM,aAAa,OAAO,WAAW;CAIrC,MAAM,aAAa,aAAa,SAA0B;EACxD,QAAQ,UAAU;EAClB,IAAI,SAAS,CAAC,cAAc,WAAW,KAAK,YAAY,QAAQ;GAC9D,cAAc,UAAU;GACxB,QAAQ,MACN,2DAA2D,KAAK,QAAQ,YAAY,EAAE,GACxF;EACF;EACA,MAAM,YAAY,UAAiB;GACjC,IAAI,MAAM,WAAW,MAAM;GAC3B,WAAW,UAAU;IAAE,aAAa;IAAO,UAAU;GAAK;EAC5D;EACA,KAAK,iBAAiB,UAAU,UAAU,IAAI;EAC9C,aAAa;GACX,KAAK,oBAAoB,UAAU,UAAU,IAAI;GACjD,IAAI,QAAQ,YAAY,MAAM,QAAQ,UAAU;EAClD;CACF,GAAG,CAAC,CAAC;CAEL,MAAM,YAAY,UAAsC;EACtD,IAAI,OAAO,WAAW,YAAY;EAGlC,IAAI,MAAM,kBAAkB;GAC1B,WAAW,UAAU;GACrB;EACF;EAEA,MAAM,YADS,MAAM,YACI;EAEzB,IAAI,WAAW,aAAa,YAAY,MAAM,MAAM;EAEpD,MAAM,eAAe;EACrB,MAAM,WAAW,IAAI,SAAS,MAAM,eAAe,SAAS;EAC5D,WAAW,UAAU;GAAE,aAAa;GAAM;EAAS;EACnD,gBAAgB,YAAY;GAC1B,MAAM,OAAO,QAAQ;EACvB,CAAC;CACH;CAEA,MAAM,gBAAgB,aAAa,iBAAwC;EACzE,OAAO,QAAQ,IAAI,YAAY;EAC/B,aAAa;GACX,OAAO,QAAQ,OAAO,YAAY;EACpC;CACF,GAAG,CAAC,CAAC;CAIL,MAAM,YAAY,kBAAkB;EAClC,MAAM,UAAU,WAAW;EAC3B,WAAW,UAAU;EACrB,MAAM,OAAO,QAAQ;EACrB,IAAI,SAAS,MAAM;EACnB,MAAM,QAAQ,CAAC,GAAG,OAAO,OAAO,CAAC,CAC9B,QAAQ,UAAU,MAAM,iBAAiB,CAAC,CAAC,CAC3C,KAAK,WAAW;GAAE;GAAO,QAAQ,MAAM,eAAe;EAAE,EAAE,CAAC,CAC3D,QACE,UACC,MAAM,WAAW,QAAQ,MAAM,OAAO,WAC1C;EACF,IAAI,MAAM,SAAS,GAAG;GACpB,MAAM,QAAQ,oBAAoB,QAAQ,UAAU,MAAM,MAAM,CAAC,CAAC;GAGlE,IAAI,MAAM,OAAO,cAAc,kBAAkB,MAAM,QACrD,SAAS,MAAM,QAAQ,MAAM,MAAM,eAAe,CAAC,CAAC,KAAK,GAAG,CAAC;QAE7D,MAAM,OAAO,MAAM;GAErB;EACF;EACA,MAAM,EAAE,QAAQ,cAAc,OAAO;EACrC,IAAI,aAAa,SAAS,gBAAgB,QAAQ,CAAC,YAAY,MAAM,GAAG,KAAK,MAAM;CACrF,GAAG,CAAC,MAAM,CAAC;CAEX,MAAM,gBAAgB,kBAAkB;EACtC,MAAM,UAAU,WAAW;EAC3B,OAAO,SAAS,gBAAgB,OAAO,QAAQ,WAAW;CAC5D,GAAG,CAAC,CAAC;CAEL,MAAM,gBAAgB,aAAa,WAAyC;EAC1E,KAAK,MAAM,SAAS,OAAO,SAAS,IAAI,UAAU,QAAQ,MAAM,QAAQ;CAC1E,GAAG,CAAC,CAAC;CAEL,MAAM,UAAU,eACP;EAAE;EAAQ;EAAe;EAAe;CAAc,IAC7D;EAAC;EAAQ;EAAe;EAAe;CAAa,CACtD;CAIA,MAAM,UAAU,UAAwD;EACtE,gBAAgB;EAChB;EACA,OAAA,EAL6B,QAKzB;EACJ;EACA;EACA,WAAW,MACT,qBAAA,UAAA,EAAA,UAAA,CACG,kBAAkB,UAAU,CAAC,GAC7B,cACC,oBAAC,mBAAD;GAAmB,iBAAiB;GAA6B;EAAY,CAAA,CAE/E,EAAA,CAAA;EAEJ,OAAO;EACP,cAAc;GACZ,QAAQ,OAAO,WAAW,WAAW,SAAS,KAAA;GAGtC;GACR,UAAU,aAAa,WAAW,KAAA;GAClC,gBAAgB,SAAS,OAAO;EAClC;EACA,KAAK;CACP,CAAC;CAED,OAAO,oBAAC,aAAD;EAAa,OAAO;EAAU,UAAA;CAAqB,CAAA;AAC5D;AAEA,SAAS,cAAc"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { RenderProp, StateValue } from "../internal/types.mjs";
|
|
2
|
+
import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
|
|
3
|
+
//#region src/form/form-submit.d.ts
|
|
4
|
+
export interface FormSubmitState {
|
|
5
|
+
/** The enclosing form's submission is in flight (`useFormStatus().pending`). */
|
|
6
|
+
readonly pending: boolean;
|
|
7
|
+
readonly disabled: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface FormSubmitProps extends Omit<ComponentPropsWithRef<"button">, "type" | "className" | "style" | "children"> {
|
|
10
|
+
/** Native `disabled`; owned. Never set by the library (conventions 3.6). */
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
className?: StateValue<string | undefined, FormSubmitState>;
|
|
13
|
+
style?: StateValue<CSSProperties | undefined, FormSubmitState>;
|
|
14
|
+
children?: StateValue<ReactNode, FormSubmitState>;
|
|
15
|
+
render?: RenderProp<ComponentPropsWithRef<"button">, FormSubmitState>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The form's submit button: `<button type="submit">` with `data-pending` while the enclosing
|
|
19
|
+
* form's submission is in flight (`useFormStatus`). Works in any React-rendered form
|
|
20
|
+
* (`Form.Root`, `Dialog.Form`, a plain `<form action>`). Never disabled while pending: block
|
|
21
|
+
* repeat clicks with CSS on `[data-pending]` if you need to.
|
|
22
|
+
*
|
|
23
|
+
* Owned keys (conventions 1.2): `type` (on a `<button>`), `disabled`, `data-pending`,
|
|
24
|
+
* `data-disabled`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function FormSubmit({ disabled, className, style, children, render, ...rest }: FormSubmitProps): ReactNode;
|
|
27
|
+
export declare namespace FormSubmit {
|
|
28
|
+
var displayName: string;
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
//# sourceMappingURL=form-submit.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-submit.d.mts","names":[],"sources":["../../src/form/form-submit.tsx"],"mappings":";;;iBAUiB;;WAEN;WACA;;iBAGM,wBAAwB,KACvC;;EAIA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,iCAAiC;;;;;;;;;;;wBAYvC,aACd,UACA,WACA,OACA,UACA,WACG,QACF,kBAAe;yBAPF"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { dataAttr } from "../internal/data-attr.mjs";
|
|
3
|
+
import { rendersButton } from "../internal/render-element.mjs";
|
|
4
|
+
import { useRender } from "../internal/use-render.mjs";
|
|
5
|
+
import { useCallback, useRef } from "react";
|
|
6
|
+
import { useFormStatus } from "react-dom";
|
|
7
|
+
//#region src/form/form-submit.tsx
|
|
8
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
9
|
+
/**
|
|
10
|
+
* The form's submit button: `<button type="submit">` with `data-pending` while the enclosing
|
|
11
|
+
* form's submission is in flight (`useFormStatus`). Works in any React-rendered form
|
|
12
|
+
* (`Form.Root`, `Dialog.Form`, a plain `<form action>`). Never disabled while pending: block
|
|
13
|
+
* repeat clicks with CSS on `[data-pending]` if you need to.
|
|
14
|
+
*
|
|
15
|
+
* Owned keys (conventions 1.2): `type` (on a `<button>`), `disabled`, `data-pending`,
|
|
16
|
+
* `data-disabled`.
|
|
17
|
+
*/
|
|
18
|
+
function FormSubmit({ disabled = false, className, style, children, render, ...rest }) {
|
|
19
|
+
const { pending } = useFormStatus();
|
|
20
|
+
const warned = useRef(false);
|
|
21
|
+
const ref = useCallback((el) => {
|
|
22
|
+
const submitter = el.tagName === "BUTTON" || el.tagName === "INPUT" && ["submit", "image"].includes(el.type);
|
|
23
|
+
if (isDev && !warned.current && !submitter) {
|
|
24
|
+
warned.current = true;
|
|
25
|
+
console.error(`Form.Submit: expected to render a <button> (or <input type="submit">) but got <${el.tagName.toLowerCase()}>. Only a submit button submits the form natively.`);
|
|
26
|
+
}
|
|
27
|
+
return () => {};
|
|
28
|
+
}, []);
|
|
29
|
+
return useRender({
|
|
30
|
+
defaultElement: "button",
|
|
31
|
+
render,
|
|
32
|
+
state: {
|
|
33
|
+
pending,
|
|
34
|
+
disabled
|
|
35
|
+
},
|
|
36
|
+
className,
|
|
37
|
+
style,
|
|
38
|
+
children,
|
|
39
|
+
props: rest,
|
|
40
|
+
libraryProps: {
|
|
41
|
+
...rendersButton(render) && { type: "submit" },
|
|
42
|
+
disabled,
|
|
43
|
+
"data-pending": dataAttr(pending),
|
|
44
|
+
"data-disabled": dataAttr(disabled)
|
|
45
|
+
},
|
|
46
|
+
ref
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
FormSubmit.displayName = "FormSubmit";
|
|
50
|
+
//#endregion
|
|
51
|
+
export { FormSubmit };
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=form-submit.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-submit.mjs","names":[],"sources":["../../src/form/form-submit.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback, useRef } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { useFormStatus } from \"react-dom\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { rendersButton, useRender } from \"../internal/use-render.js\";\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\nexport interface FormSubmitState {\n /** The enclosing form's submission is in flight (`useFormStatus().pending`). */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface FormSubmitProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"type\" | \"className\" | \"style\" | \"children\"\n> {\n /** Native `disabled`; owned. Never set by the library (conventions 3.6). */\n disabled?: boolean;\n className?: StateValue<string | undefined, FormSubmitState>;\n style?: StateValue<CSSProperties | undefined, FormSubmitState>;\n children?: StateValue<ReactNode, FormSubmitState>;\n render?: RenderProp<ComponentPropsWithRef<\"button\">, FormSubmitState>;\n}\n\n/**\n * The form's submit button: `<button type=\"submit\">` with `data-pending` while the enclosing\n * form's submission is in flight (`useFormStatus`). Works in any React-rendered form\n * (`Form.Root`, `Dialog.Form`, a plain `<form action>`). Never disabled while pending: block\n * repeat clicks with CSS on `[data-pending]` if you need to.\n *\n * Owned keys (conventions 1.2): `type` (on a `<button>`), `disabled`, `data-pending`,\n * `data-disabled`.\n */\nexport function FormSubmit({\n disabled = false,\n className,\n style,\n children,\n render,\n ...rest\n}: FormSubmitProps) {\n const { pending } = useFormStatus();\n const warned = useRef(false);\n const ref = useCallback((el: HTMLElement) => {\n const submitter =\n el.tagName === \"BUTTON\" ||\n (el.tagName === \"INPUT\" && [\"submit\", \"image\"].includes((el as HTMLInputElement).type));\n if (isDev && !warned.current && !submitter) {\n warned.current = true;\n console.error(\n `Form.Submit: expected to render a <button> (or <input type=\"submit\">) but got ` +\n `<${el.tagName.toLowerCase()}>. Only a submit button submits the form natively.`,\n );\n }\n // A cleanup, so the merged ref never calls this with `null`.\n return () => {};\n }, []);\n const state: FormSubmitState = { pending, disabled };\n return useRender<ComponentPropsWithRef<\"button\">, FormSubmitState>({\n defaultElement: \"button\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps: {\n ...(rendersButton(render) && { type: \"submit\" as const }),\n disabled,\n \"data-pending\": dataAttr(pending),\n \"data-disabled\": dataAttr(disabled),\n },\n ref,\n });\n}\n\nFormSubmit.displayName = \"FormSubmit\";\n"],"mappings":";;;;;;;AAQA,MAAM,QAAQ,QAAQ,IAAI,aAAa;;;;;;;;;;AA6BvC,SAAgB,WAAW,EACzB,WAAW,OACX,WACA,OACA,UACA,QACA,GAAG,QACe;CAClB,MAAM,EAAE,YAAY,cAAc;CAClC,MAAM,SAAS,OAAO,KAAK;CAC3B,MAAM,MAAM,aAAa,OAAoB;EAC3C,MAAM,YACJ,GAAG,YAAY,YACd,GAAG,YAAY,WAAW,CAAC,UAAU,OAAO,CAAC,CAAC,SAAU,GAAwB,IAAI;EACvF,IAAI,SAAS,CAAC,OAAO,WAAW,CAAC,WAAW;GAC1C,OAAO,UAAU;GACjB,QAAQ,MACN,kFACM,GAAG,QAAQ,YAAY,EAAE,mDACjC;EACF;EAEA,aAAa,CAAC;CAChB,GAAG,CAAC,CAAC;CAEL,OAAO,UAA4D;EACjE,gBAAgB;EAChB;EACA,OAAA;GAJ+B;GAAS;EAIpC;EACJ;EACA;EACA;EACA,OAAO;EACP,cAAc;GACZ,GAAI,cAAc,MAAM,KAAK,EAAE,MAAM,SAAkB;GACvD;GACA,gBAAgB,SAAS,OAAO;GAChC,iBAAiB,SAAS,QAAQ;EACpC;EACA;CACF,CAAC;AACH;AAEA,WAAW,cAAc"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FieldErrorValue, FormErrors } from "../internal/validity.mjs";
|
|
2
|
+
import { FormRoot, FormRootProps, FormRootState } from "./form-root.mjs";
|
|
3
|
+
import { FormSubmit, FormSubmitProps, FormSubmitState } from "./form-submit.mjs";
|
|
4
|
+
//#region src/form/index.d.ts
|
|
5
|
+
export declare const Form: {
|
|
6
|
+
readonly Root: typeof FormRoot;
|
|
7
|
+
readonly Submit: typeof FormSubmit;
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export type { FieldErrorValue, FormErrors, FormRootProps, FormRootState, FormSubmitProps, FormSubmitState };
|
|
11
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/form/index.ts"],"mappings":";;;;qBAGa;WAAS,aAAI;WAAY,eAAM"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FormRoot } from "./form-root.mjs";
|
|
2
|
+
import { FormSubmit } from "./form-submit.mjs";
|
|
3
|
+
//#region src/form/index.ts
|
|
4
|
+
const Form = {
|
|
5
|
+
Root: FormRoot,
|
|
6
|
+
Submit: FormSubmit
|
|
7
|
+
};
|
|
8
|
+
//#endregion
|
|
9
|
+
export { Form };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/form/index.ts"],"sourcesContent":["import { FormRoot } from \"./form-root.js\";\nimport { FormSubmit } from \"./form-submit.js\";\n\nexport const Form = { Root: FormRoot, Submit: FormSubmit } as const;\n\nexport type { FormRootProps, FormRootState, FormErrors, FieldErrorValue } from \"./form-root.js\";\nexport type { FormSubmitProps, FormSubmitState } from \"./form-submit.js\";\n"],"mappings":";;;AAGA,MAAa,OAAO;CAAE,MAAM;CAAU,QAAQ;AAAW"}
|
package/dist/index.d.mts
CHANGED
|
@@ -30,8 +30,27 @@ import { DialogOpenChangeDetails, DialogRootProps, DialogRootState } from "./dia
|
|
|
30
30
|
import { DialogTitleProps, DialogTitleState } from "./dialog/dialog-title.mjs";
|
|
31
31
|
import { DialogTriggerProps, DialogTriggerState } from "./dialog/dialog-trigger.mjs";
|
|
32
32
|
import { Dialog } from "./dialog/index.mjs";
|
|
33
|
+
import { FieldErrorValue, FieldValidity, FormErrors } from "./internal/validity.mjs";
|
|
34
|
+
import { FieldState } from "./internal/field-context.mjs";
|
|
35
|
+
import { FieldControlProps } from "./field/field-control.mjs";
|
|
36
|
+
import { FieldDescriptionProps } from "./field/field-description.mjs";
|
|
37
|
+
import { FieldErrorProps } from "./field/field-error.mjs";
|
|
38
|
+
import { FieldLabelProps } from "./field/field-label.mjs";
|
|
39
|
+
import { FieldRootProps, FieldValidate } from "./field/field-root.mjs";
|
|
40
|
+
import { Field } from "./field/index.mjs";
|
|
41
|
+
import { FormRootProps, FormRootState } from "./form/form-root.mjs";
|
|
42
|
+
import { FormSubmitProps, FormSubmitState } from "./form/form-submit.mjs";
|
|
43
|
+
import { Form } from "./form/index.mjs";
|
|
33
44
|
import { Toggle, TogglePressedChangeDetails, ToggleProps, ToggleState } from "./toggle/toggle.mjs";
|
|
34
45
|
import "./toggle/index.mjs";
|
|
46
|
+
import { ToggleGroupOrientation } from "./toggle-group/toggle-group-context.mjs";
|
|
47
|
+
import { ToggleGroupItemProps, ToggleGroupItemState } from "./toggle-group/toggle-group-item.mjs";
|
|
48
|
+
import { ToggleGroupExclusiveProps, ToggleGroupMultipleProps, ToggleGroupRootProps, ToggleGroupRootState, ToggleGroupValueChangeDetails } from "./toggle-group/toggle-group-root.mjs";
|
|
49
|
+
import { ToggleGroup } from "./toggle-group/index.mjs";
|
|
50
|
+
import { ToolbarButtonActionDetails, ToolbarButtonProps, ToolbarButtonState } from "./toolbar/toolbar-button.mjs";
|
|
51
|
+
import { ToolbarOrientation, ToolbarRootProps, ToolbarRootState } from "./toolbar/toolbar-root.mjs";
|
|
52
|
+
import { ToolbarSeparatorProps, ToolbarSeparatorState } from "./toolbar/toolbar-separator.mjs";
|
|
53
|
+
import { Toolbar } from "./toolbar/index.mjs";
|
|
35
54
|
import { TabsActivationMode, TabsOrientation } from "./tabs/tabs-context.mjs";
|
|
36
55
|
import { TabsIndicatorProps, TabsIndicatorState } from "./tabs/tabs-indicator.mjs";
|
|
37
56
|
import { TabsListProps, TabsListState } from "./tabs/tabs-list.mjs";
|
|
@@ -75,4 +94,37 @@ import { SelectSeparatorProps } from "./select/select-separator.mjs";
|
|
|
75
94
|
import { SelectTriggerProps } from "./select/select-trigger.mjs";
|
|
76
95
|
import { SelectValueProps } from "./select/select-value.mjs";
|
|
77
96
|
import { Select } from "./select/index.mjs";
|
|
78
|
-
|
|
97
|
+
import { ToastActionDetails, ToastActionOptions, ToastCloseDetails, ToastItem, ToastManager, ToastOptions, ToastPriority, ToastPromiseOptions, createToastManager, toast } from "./toast/toast.mjs";
|
|
98
|
+
import { ToastActionProps, ToastActionState } from "./toast/toast-action.mjs";
|
|
99
|
+
import { ToastCloseProps, ToastCloseState } from "./toast/toast-close.mjs";
|
|
100
|
+
import { ToastRootProps, ToastRootState } from "./toast/toast-root.mjs";
|
|
101
|
+
import { ToastDescriptionProps } from "./toast/toast-description.mjs";
|
|
102
|
+
import { ToastTitleProps } from "./toast/toast-title.mjs";
|
|
103
|
+
import { ToastViewportProps, ToastViewportState } from "./toast/toast-viewport.mjs";
|
|
104
|
+
import { Toast } from "./toast/index.mjs";
|
|
105
|
+
import { SliderOrientation, SliderRootProps, SliderRootState, SliderValue, SliderValueChangeDetails, SliderValueCommitDetails } from "./slider/slider-root.mjs";
|
|
106
|
+
import { SliderTrackProps, SliderTrackState } from "./slider/slider-track.mjs";
|
|
107
|
+
import { SliderIndicatorProps, SliderIndicatorState } from "./slider/slider-indicator.mjs";
|
|
108
|
+
import { SliderLabelProps } from "./slider/slider-label.mjs";
|
|
109
|
+
import { SliderThumbProps, SliderThumbState } from "./slider/slider-thumb.mjs";
|
|
110
|
+
import { SliderValueProps, SliderValueState } from "./slider/slider-value.mjs";
|
|
111
|
+
import { Slider } from "./slider/index.mjs";
|
|
112
|
+
import { NumberFieldRootProps, NumberFieldState, NumberFieldValueChangeDetails } from "./number-field/number-field-root.mjs";
|
|
113
|
+
import { NumberFieldButtonState, NumberFieldDecrementProps, NumberFieldIncrementProps } from "./number-field/number-field-increment.mjs";
|
|
114
|
+
import { NumberFieldInputProps } from "./number-field/number-field-input.mjs";
|
|
115
|
+
import { NumberFieldLabelProps } from "./number-field/number-field-label.mjs";
|
|
116
|
+
import { NumberField } from "./number-field/index.mjs";
|
|
117
|
+
import { ProgressRootProps, ProgressState } from "./progress/progress-root.mjs";
|
|
118
|
+
import { ProgressIndicatorProps } from "./progress/progress-indicator.mjs";
|
|
119
|
+
import { ProgressLabelProps } from "./progress/progress-label.mjs";
|
|
120
|
+
import { ProgressTrackProps } from "./progress/progress-track.mjs";
|
|
121
|
+
import { ProgressValueProps } from "./progress/progress-value.mjs";
|
|
122
|
+
import { Progress } from "./progress/index.mjs";
|
|
123
|
+
import { MeterRegion } from "./meter/meter-region.mjs";
|
|
124
|
+
import { MeterRootProps, MeterState } from "./meter/meter-root.mjs";
|
|
125
|
+
import { MeterIndicatorProps } from "./meter/meter-indicator.mjs";
|
|
126
|
+
import { MeterLabelProps } from "./meter/meter-label.mjs";
|
|
127
|
+
import { MeterTrackProps } from "./meter/meter-track.mjs";
|
|
128
|
+
import { MeterValueProps } from "./meter/meter-value.mjs";
|
|
129
|
+
import { Meter } from "./meter/index.mjs";
|
|
130
|
+
export { Accordion, type AccordionItemProps, type AccordionItemState, type AccordionPanelProps, type AccordionPanelState, type AccordionRootExclusiveProps, type AccordionRootMultipleProps, type AccordionRootProps, type AccordionRootState, type AccordionTriggerProps, type AccordionTriggerState, type AccordionValueChangeDetails, type Action, type ActionDetails, type ChangeAction, type ChangeReason, Checkbox, type CheckboxCheckedChangeDetails, CheckboxGroup, type CheckboxGroupItemProps, type CheckboxGroupItemState, type CheckboxGroupLabelProps, type CheckboxGroupParentProps, type CheckboxGroupParentState, type CheckboxGroupRootProps, type CheckboxGroupRootState, type CheckboxGroupValueChangeDetails, type CheckboxProps, type CheckboxState, Combobox, type ComboboxClearProps, type ComboboxClearState, type ComboboxEmptyProps, type ComboboxEmptyState, type ComboboxGroupLabelProps, type ComboboxGroupLabelState, type ComboboxGroupProps, type ComboboxGroupState, type ComboboxInputProps, type ComboboxInputState, type ComboboxInputValueChangeDetails, type ComboboxLabelProps, type ComboboxLabelState, type ComboboxListProps, type ComboboxListState, type ComboboxOpenChangeDetails, type ComboboxOptionProps, type ComboboxOptionState, type ComboboxPopupProps, type ComboboxPopupState, type ComboboxRootProps, type ComboboxRootState, type ComboboxStatusProps, type ComboboxStatusState, type ComboboxTriggerProps, type ComboboxTriggerState, type ComboboxValueChangeDetails, Dialog, type DialogCloseDetails, type DialogCloseProps, type DialogCloseState, type DialogDescriptionProps, type DialogDescriptionState, type DialogFormProps, type DialogFormState, type DialogOpenChangeDetails, type DialogPopupProps, type DialogPopupState, type DialogRootProps, type DialogRootState, type DialogTitleProps, type DialogTitleState, type DialogTriggerProps, type DialogTriggerState, Field, type FieldControlProps, type FieldDescriptionProps, type FieldErrorProps, type FieldErrorValue, type FieldLabelProps, type FieldRootProps, type FieldState, type FieldValidate, type FieldValidity, Form, type FormErrors, type FormRootProps, type FormRootState, type FormSubmitProps, type FormSubmitState, Menu, type MenuAlign, type MenuCheckboxItemProps, type MenuCheckboxItemState, type MenuGroupLabelProps, type MenuGroupLabelState, type MenuGroupProps, type MenuGroupState, type MenuItemDetails, type MenuItemProps, type MenuItemState, type MenuOpenChangeDetails, type MenuPopupProps, type MenuPopupState, type MenuRadioGroupProps, type MenuRadioGroupState, type MenuRadioItemProps, type MenuRadioItemState, type MenuRootProps, type MenuRootState, type MenuSeparatorProps, type MenuSeparatorState, type MenuSide, type MenuSubmenuTriggerProps, type MenuSubmenuTriggerState, type MenuTriggerProps, type MenuTriggerState, Meter, type MeterIndicatorProps, type MeterLabelProps, type MeterRegion, type MeterRootProps, type MeterState, type MeterTrackProps, type MeterValueProps, NumberField, type NumberFieldButtonState, type NumberFieldDecrementProps, type NumberFieldIncrementProps, type NumberFieldInputProps, type NumberFieldLabelProps, type NumberFieldRootProps, type NumberFieldState, type NumberFieldValueChangeDetails, Popover, type PopoverAlign, type PopoverCloseProps, type PopoverCloseState, type PopoverDescriptionProps, type PopoverDescriptionState, type PopoverOpenChangeDetails, type PopoverPopupProps, type PopoverPopupState, type PopoverRootProps, type PopoverRootState, type PopoverSide, type PopoverTitleProps, type PopoverTitleState, type PopoverTriggerProps, type PopoverTriggerState, Progress, type ProgressIndicatorProps, type ProgressLabelProps, type ProgressRootProps, type ProgressState, type ProgressTrackProps, type ProgressValueProps, RadioGroup, type RadioGroupItemProps, type RadioGroupItemState, type RadioGroupLabelProps, type RadioGroupRootProps, type RadioGroupRootState, type RadioGroupValueChangeDetails, type RenderProp, Select, type SelectGroupLabelProps, type SelectGroupProps, type SelectOptionProps, type SelectOptionState, type SelectRootProps, type SelectRootState, type SelectSeparatorProps, type SelectTriggerProps, type SelectValueChangeDetails, type SelectValueProps, Slider, type SliderIndicatorProps, type SliderIndicatorState, type SliderLabelProps, type SliderOrientation, type SliderRootProps, type SliderRootState, type SliderThumbProps, type SliderThumbState, type SliderTrackProps, type SliderTrackState, type SliderValue, type SliderValueChangeDetails, type SliderValueCommitDetails, type SliderValueProps, type SliderValueState, type StateValue, Switch, type SwitchCheckedChangeDetails, type SwitchProps, type SwitchState, Tabs, type TabsActivationMode, type TabsIndicatorProps, type TabsIndicatorState, type TabsListProps, type TabsListState, type TabsOrientation, type TabsPanelProps, type TabsPanelState, type TabsRootProps, type TabsRootState, type TabsTabProps, type TabsTabState, type TabsValueChangeDetails, Toast, type ToastActionDetails, type ToastActionOptions, type ToastActionProps, type ToastActionState, type ToastCloseDetails, type ToastCloseProps, type ToastCloseState, type ToastDescriptionProps, type ToastItem, type ToastManager, type ToastOptions, type ToastPriority, type ToastPromiseOptions, type ToastRootProps, type ToastRootState, type ToastTitleProps, type ToastViewportProps, type ToastViewportState, Toggle, ToggleGroup, type ToggleGroupExclusiveProps, type ToggleGroupItemProps, type ToggleGroupItemState, type ToggleGroupMultipleProps, type ToggleGroupOrientation, type ToggleGroupRootProps, type ToggleGroupRootState, type ToggleGroupValueChangeDetails, type TogglePressedChangeDetails, type ToggleProps, type ToggleState, Toolbar, type ToolbarButtonActionDetails, type ToolbarButtonProps, type ToolbarButtonState, type ToolbarOrientation, type ToolbarRootProps, type ToolbarRootState, type ToolbarSeparatorProps, type ToolbarSeparatorState, Tooltip, type TooltipAlign, type TooltipPopupProps, type TooltipPopupState, type TooltipProvides, type TooltipRootProps, type TooltipRootState, type TooltipSide, type TooltipTriggerProps, type TooltipTriggerState, type UrgentChangeAction, createToastManager, toast };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Toggle } from "./toggle/toggle.mjs";
|
|
2
|
+
import { ToggleGroup } from "./toggle-group/index.mjs";
|
|
3
|
+
import { Toolbar } from "./toolbar/index.mjs";
|
|
2
4
|
import { Accordion } from "./accordion/index.mjs";
|
|
3
5
|
import { Tabs } from "./tabs/index.mjs";
|
|
4
6
|
import { Tooltip } from "./tooltip/index.mjs";
|
|
@@ -11,4 +13,12 @@ import { RadioGroup } from "./radio-group/index.mjs";
|
|
|
11
13
|
import { CheckboxGroup } from "./checkbox-group/index.mjs";
|
|
12
14
|
import { Select } from "./select/index.mjs";
|
|
13
15
|
import { Combobox } from "./combobox/index.mjs";
|
|
14
|
-
|
|
16
|
+
import { createToastManager, toast } from "./toast/toast.mjs";
|
|
17
|
+
import { Toast } from "./toast/index.mjs";
|
|
18
|
+
import { Slider } from "./slider/index.mjs";
|
|
19
|
+
import { NumberField } from "./number-field/index.mjs";
|
|
20
|
+
import { Field } from "./field/index.mjs";
|
|
21
|
+
import { Form } from "./form/index.mjs";
|
|
22
|
+
import { Progress } from "./progress/index.mjs";
|
|
23
|
+
import { Meter } from "./meter/index.mjs";
|
|
24
|
+
export { Accordion, Checkbox, CheckboxGroup, Combobox, Dialog, Field, Form, Menu, Meter, NumberField, Popover, Progress, RadioGroup, Select, Slider, Switch, Tabs, Toast, Toggle, ToggleGroup, Toolbar, Tooltip, createToastManager, toast };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
//#region src/internal/announce.ts
|
|
3
|
+
/** How long the fallback region keeps a message before it is cleared, so a repeat is spoken. */
|
|
4
|
+
const ANNOUNCE_CLEAR_DELAY = 1e3;
|
|
5
|
+
const REGION_STYLE = "position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap;";
|
|
6
|
+
let region = null;
|
|
7
|
+
let clearTimer;
|
|
8
|
+
let writeTimer;
|
|
9
|
+
function liveRegion(doc) {
|
|
10
|
+
if (region !== null && region.isConnected && region.ownerDocument === doc) return region;
|
|
11
|
+
const el = doc.createElement("div");
|
|
12
|
+
el.setAttribute("role", "status");
|
|
13
|
+
el.setAttribute("aria-live", "polite");
|
|
14
|
+
el.setAttribute("aria-atomic", "true");
|
|
15
|
+
el.setAttribute("data-fsui-announcer", "");
|
|
16
|
+
el.setAttribute("style", REGION_STYLE);
|
|
17
|
+
doc.body.append(el);
|
|
18
|
+
region = el;
|
|
19
|
+
return el;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Speaks `message` in the context of `element`: `element.ariaNotify(message)` where it exists,
|
|
23
|
+
* else the fallback live region (inserted on first use, cleared, then written
|
|
24
|
+
* `ANNOUNCE_WRITE_DELAY` ms later and cleared again after `ANNOUNCE_CLEAR_DELAY`). An empty
|
|
25
|
+
* message announces nothing; a newer message replaces one not yet written.
|
|
26
|
+
*/
|
|
27
|
+
function announce(element, message) {
|
|
28
|
+
if (message === "") return;
|
|
29
|
+
const notifying = element;
|
|
30
|
+
if (typeof notifying.ariaNotify === "function") {
|
|
31
|
+
notifying.ariaNotify(message);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const doc = element.ownerDocument;
|
|
35
|
+
if (doc.body === null) return;
|
|
36
|
+
const el = liveRegion(doc);
|
|
37
|
+
el.textContent = "";
|
|
38
|
+
if (clearTimer !== void 0) clearTimeout(clearTimer);
|
|
39
|
+
if (writeTimer !== void 0) clearTimeout(writeTimer);
|
|
40
|
+
clearTimer = void 0;
|
|
41
|
+
writeTimer = setTimeout(() => {
|
|
42
|
+
writeTimer = void 0;
|
|
43
|
+
el.textContent = message;
|
|
44
|
+
clearTimer = setTimeout(() => {
|
|
45
|
+
clearTimer = void 0;
|
|
46
|
+
el.textContent = "";
|
|
47
|
+
}, ANNOUNCE_CLEAR_DELAY);
|
|
48
|
+
}, 100);
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { ANNOUNCE_CLEAR_DELAY, announce };
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=announce.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"announce.mjs","names":[],"sources":["../../src/internal/announce.ts"],"sourcesContent":["\"use client\";\n// Screen-reader announcements (design/components/number-field.md 8.3, conventions 8): through\n// `ariaNotify()` where the engine has it, otherwise through one module-level, visually hidden\n// `role=\"status\"` live region appended to `document.body` on first use. No part renders a live\n// region of its own for these announcements.\n\ntype NotifyingElement = Element & { ariaNotify?: (announcement: string) => void };\n\n/** How long the fallback region keeps a message before it is cleared, so a repeat is spoken. */\nexport const ANNOUNCE_CLEAR_DELAY = 1000;\n\nconst REGION_STYLE =\n \"position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0; \" +\n \"overflow: hidden; clip-path: inset(50%); white-space: nowrap;\";\n\n/**\n * The delay between clearing the region and writing a message. A region inserted in the same task\n * as its first message is skipped by some screen readers, and an unchanged text is not spoken\n * again; clearing first and writing a tick later makes every message a fresh change.\n */\nexport const ANNOUNCE_WRITE_DELAY = 100;\n\nlet region: HTMLElement | null = null;\nlet clearTimer: ReturnType<typeof setTimeout> | undefined;\nlet writeTimer: ReturnType<typeof setTimeout> | undefined;\n\nfunction liveRegion(doc: Document): HTMLElement {\n if (region !== null && region.isConnected && region.ownerDocument === doc) return region;\n const el = doc.createElement(\"div\");\n el.setAttribute(\"role\", \"status\");\n el.setAttribute(\"aria-live\", \"polite\");\n el.setAttribute(\"aria-atomic\", \"true\");\n el.setAttribute(\"data-fsui-announcer\", \"\");\n el.setAttribute(\"style\", REGION_STYLE);\n doc.body.append(el);\n region = el;\n return el;\n}\n\n/**\n * Speaks `message` in the context of `element`: `element.ariaNotify(message)` where it exists,\n * else the fallback live region (inserted on first use, cleared, then written\n * `ANNOUNCE_WRITE_DELAY` ms later and cleared again after `ANNOUNCE_CLEAR_DELAY`). An empty\n * message announces nothing; a newer message replaces one not yet written.\n */\nexport function announce(element: Element, message: string): void {\n if (message === \"\") return;\n const notifying = element as NotifyingElement;\n if (typeof notifying.ariaNotify === \"function\") {\n notifying.ariaNotify(message);\n return;\n }\n const doc = element.ownerDocument;\n if (doc.body === null) return;\n const el = liveRegion(doc);\n el.textContent = \"\";\n if (clearTimer !== undefined) clearTimeout(clearTimer);\n if (writeTimer !== undefined) clearTimeout(writeTimer);\n clearTimer = undefined;\n writeTimer = setTimeout(() => {\n writeTimer = undefined;\n el.textContent = message;\n clearTimer = setTimeout(() => {\n clearTimer = undefined;\n el.textContent = \"\";\n }, ANNOUNCE_CLEAR_DELAY);\n }, ANNOUNCE_WRITE_DELAY);\n}\n"],"mappings":";;;AASA,MAAa,uBAAuB;AAEpC,MAAM,eACJ;AAUF,IAAI,SAA6B;AACjC,IAAI;AACJ,IAAI;AAEJ,SAAS,WAAW,KAA4B;CAC9C,IAAI,WAAW,QAAQ,OAAO,eAAe,OAAO,kBAAkB,KAAK,OAAO;CAClF,MAAM,KAAK,IAAI,cAAc,KAAK;CAClC,GAAG,aAAa,QAAQ,QAAQ;CAChC,GAAG,aAAa,aAAa,QAAQ;CACrC,GAAG,aAAa,eAAe,MAAM;CACrC,GAAG,aAAa,uBAAuB,EAAE;CACzC,GAAG,aAAa,SAAS,YAAY;CACrC,IAAI,KAAK,OAAO,EAAE;CAClB,SAAS;CACT,OAAO;AACT;;;;;;;AAQA,SAAgB,SAAS,SAAkB,SAAuB;CAChE,IAAI,YAAY,IAAI;CACpB,MAAM,YAAY;CAClB,IAAI,OAAO,UAAU,eAAe,YAAY;EAC9C,UAAU,WAAW,OAAO;EAC5B;CACF;CACA,MAAM,MAAM,QAAQ;CACpB,IAAI,IAAI,SAAS,MAAM;CACvB,MAAM,KAAK,WAAW,GAAG;CACzB,GAAG,cAAc;CACjB,IAAI,eAAe,KAAA,GAAW,aAAa,UAAU;CACrD,IAAI,eAAe,KAAA,GAAW,aAAa,UAAU;CACrD,aAAa,KAAA;CACb,aAAa,iBAAiB;EAC5B,aAAa,KAAA;EACb,GAAG,cAAc;EACjB,aAAa,iBAAiB;GAC5B,aAAa,KAAA;GACb,GAAG,cAAc;EACnB,GAAG,oBAAoB;CACzB,GAAA,GAAuB;AACzB"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useLayoutEffect, useRef } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/internal/content-focus-probe.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Records, from its layout-effect cleanup, whether focus is inside the popup when the content
|
|
7
|
+
* goes away: React runs a deleted component's layout cleanups before it detaches the host nodes,
|
|
8
|
+
* so `document.activeElement` still names the focused element there.
|
|
9
|
+
*/
|
|
10
|
+
function ContentFocusProbe({ onDetach, children }) {
|
|
11
|
+
useLayoutEffect(() => onDetach, [onDetach]);
|
|
12
|
+
return children;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The content probe shared by the popover-like Popups (Popover, Menu) and Dialog.Popup: a
|
|
16
|
+
* library-driven close with `transition` unmounts the content, the focused Close part included,
|
|
17
|
+
* in the close commit, before the layout effect that hides the popup runs, so focus falls to
|
|
18
|
+
* `<body>` and the platform may restore nothing. The probe tells the owner that this happened.
|
|
19
|
+
*/
|
|
20
|
+
function useContentFocusProbe(popupRef) {
|
|
21
|
+
const lostFocus = useRef(false);
|
|
22
|
+
const record = useCallback(() => {
|
|
23
|
+
const element = popupRef.current;
|
|
24
|
+
if (element === null) return;
|
|
25
|
+
const active = element.ownerDocument.activeElement;
|
|
26
|
+
if (active !== null && active !== element && element.contains(active)) lostFocus.current = true;
|
|
27
|
+
}, [popupRef]);
|
|
28
|
+
return {
|
|
29
|
+
wrapContent: useCallback((children) => /* @__PURE__ */ jsx(ContentFocusProbe, {
|
|
30
|
+
onDetach: record,
|
|
31
|
+
children
|
|
32
|
+
}), [record]),
|
|
33
|
+
hadFocus: useCallback(() => lostFocus.current, []),
|
|
34
|
+
forget: useCallback(() => {
|
|
35
|
+
lostFocus.current = false;
|
|
36
|
+
}, [])
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { useContentFocusProbe };
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=content-focus-probe.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-focus-probe.mjs","names":[],"sources":["../../src/internal/content-focus-probe.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback, useLayoutEffect, useRef } from \"react\";\nimport type { ReactNode, RefObject } from \"react\";\n\n/**\n * Records, from its layout-effect cleanup, whether focus is inside the popup when the content\n * goes away: React runs a deleted component's layout cleanups before it detaches the host nodes,\n * so `document.activeElement` still names the focused element there.\n */\nfunction ContentFocusProbe({\n onDetach,\n children,\n}: {\n onDetach: () => void;\n children?: ReactNode;\n}): ReactNode {\n useLayoutEffect(() => onDetach, [onDetach]);\n return children;\n}\n\nexport interface ContentFocusProbeHook {\n /**\n * Wraps the library-mode content of a top-layer popup: call it only while the content is\n * shown (`visible ? wrapContent(x) : null`), or inside the `<Activity>` for `keepMounted`.\n */\n readonly wrapContent: (children: ReactNode) => ReactNode;\n /**\n * Whether the wrapped content unmounted (or was hidden by `<Activity>`) while it held focus\n * since the last `forget()`. The owner reads it after its close\n * (design/components/popover.md 6.2 rule 2, design/components/dialog.md 6.2 rule 2).\n * Handlers and effects only.\n */\n readonly hadFocus: () => boolean;\n /**\n * Clear the record: on every open, after the owner used it, and whenever the content went\n * away while the popup stays open (not a close).\n */\n readonly forget: () => void;\n}\n\n/**\n * The content probe shared by the popover-like Popups (Popover, Menu) and Dialog.Popup: a\n * library-driven close with `transition` unmounts the content, the focused Close part included,\n * in the close commit, before the layout effect that hides the popup runs, so focus falls to\n * `<body>` and the platform may restore nothing. The probe tells the owner that this happened.\n */\nexport function useContentFocusProbe(\n popupRef: RefObject<HTMLElement | null>,\n): ContentFocusProbeHook {\n const lostFocus = useRef(false);\n\n const record = useCallback(() => {\n const element = popupRef.current;\n if (element === null) return;\n const active = element.ownerDocument.activeElement;\n if (active !== null && active !== element && element.contains(active)) {\n lostFocus.current = true;\n }\n }, [popupRef]);\n\n const wrapContent = useCallback(\n (children: ReactNode): ReactNode => (\n <ContentFocusProbe onDetach={record}>{children}</ContentFocusProbe>\n ),\n [record],\n );\n const hadFocus = useCallback(() => lostFocus.current, []);\n const forget = useCallback(() => {\n lostFocus.current = false;\n }, []);\n\n return { wrapContent, hadFocus, forget };\n}\n"],"mappings":";;;;;;;;;AASA,SAAS,kBAAkB,EACzB,UACA,YAIY;CACZ,sBAAsB,UAAU,CAAC,QAAQ,CAAC;CAC1C,OAAO;AACT;;;;;;;AA4BA,SAAgB,qBACd,UACuB;CACvB,MAAM,YAAY,OAAO,KAAK;CAE9B,MAAM,SAAS,kBAAkB;EAC/B,MAAM,UAAU,SAAS;EACzB,IAAI,YAAY,MAAM;EACtB,MAAM,SAAS,QAAQ,cAAc;EACrC,IAAI,WAAW,QAAQ,WAAW,WAAW,QAAQ,SAAS,MAAM,GAClE,UAAU,UAAU;CAExB,GAAG,CAAC,QAAQ,CAAC;CAab,OAAO;EAAE,aAXW,aACjB,aACC,oBAAC,mBAAD;GAAmB,UAAU;GAAS;EAA4B,CAAA,GAEpE,CAAC,MAAM,CAOU;EAAG,UALL,kBAAkB,UAAU,SAAS,CAAC,CAK1B;EAAG,QAJjB,kBAAkB;GAC/B,UAAU,UAAU;EACtB,GAAG,CAAC,CAEiC;CAAE;AACzC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region src/internal/decimal.ts
|
|
2
|
+
/** Number of decimal places of `n` as written (`0.1` → 1, `1e-7` → 7, `5` → 0). */
|
|
3
|
+
function decimalsOf(n) {
|
|
4
|
+
if (!Number.isFinite(n)) return 0;
|
|
5
|
+
const text = String(n);
|
|
6
|
+
const exponent = /e-(\d+)$/i.exec(text);
|
|
7
|
+
if (exponent !== null) {
|
|
8
|
+
const mantissa = text.slice(0, exponent.index);
|
|
9
|
+
const dot = mantissa.indexOf(".");
|
|
10
|
+
return Number(exponent[1]) + (dot === -1 ? 0 : mantissa.length - dot - 1);
|
|
11
|
+
}
|
|
12
|
+
const dot = text.indexOf(".");
|
|
13
|
+
return dot === -1 ? 0 : text.length - dot - 1;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* `value` rounded to `decimals` places through `toFixed` (decimal, not binary, rounding of the
|
|
17
|
+
* written digits: `roundTo(0.1 + 0.2, 1)` is `0.3`). `-0` becomes `0`.
|
|
18
|
+
*/
|
|
19
|
+
function roundTo(value, decimals) {
|
|
20
|
+
if (!Number.isFinite(value)) return value;
|
|
21
|
+
const result = decimals <= 0 ? Math.round(value) : Number(value.toFixed(Math.min(decimals, 100)));
|
|
22
|
+
return result === 0 ? 0 : result;
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { decimalsOf, roundTo };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=decimal.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decimal.mjs","names":[],"sources":["../../src/internal/decimal.ts"],"sourcesContent":["// Decimal helpers shared by Slider and NumberField (design/components/number-field.md 5.4). No\n// directive: no React, no client-only API.\n\n/** Number of decimal places of `n` as written (`0.1` → 1, `1e-7` → 7, `5` → 0). */\nexport function decimalsOf(n: number): number {\n if (!Number.isFinite(n)) return 0;\n const text = String(n);\n const exponent = /e-(\\d+)$/i.exec(text);\n if (exponent !== null) {\n const mantissa = text.slice(0, exponent.index);\n const dot = mantissa.indexOf(\".\");\n return Number(exponent[1]) + (dot === -1 ? 0 : mantissa.length - dot - 1);\n }\n const dot = text.indexOf(\".\");\n return dot === -1 ? 0 : text.length - dot - 1;\n}\n\n/**\n * `value` rounded to `decimals` places through `toFixed` (decimal, not binary, rounding of the\n * written digits: `roundTo(0.1 + 0.2, 1)` is `0.3`). `-0` becomes `0`.\n */\nexport function roundTo(value: number, decimals: number): number {\n if (!Number.isFinite(value)) return value;\n // `toFixed` takes at most 100 digits; anything near that is already exact.\n const result = decimals <= 0 ? Math.round(value) : Number(value.toFixed(Math.min(decimals, 100)));\n return result === 0 ? 0 : result;\n}\n"],"mappings":";;AAIA,SAAgB,WAAW,GAAmB;CAC5C,IAAI,CAAC,OAAO,SAAS,CAAC,GAAG,OAAO;CAChC,MAAM,OAAO,OAAO,CAAC;CACrB,MAAM,WAAW,YAAY,KAAK,IAAI;CACtC,IAAI,aAAa,MAAM;EACrB,MAAM,WAAW,KAAK,MAAM,GAAG,SAAS,KAAK;EAC7C,MAAM,MAAM,SAAS,QAAQ,GAAG;EAChC,OAAO,OAAO,SAAS,EAAE,KAAK,QAAQ,KAAK,IAAI,SAAS,SAAS,MAAM;CACzE;CACA,MAAM,MAAM,KAAK,QAAQ,GAAG;CAC5B,OAAO,QAAQ,KAAK,IAAI,KAAK,SAAS,MAAM;AAC9C;;;;;AAMA,SAAgB,QAAQ,OAAe,UAA0B;CAC/D,IAAI,CAAC,OAAO,SAAS,KAAK,GAAG,OAAO;CAEpC,MAAM,SAAS,YAAY,IAAI,KAAK,MAAM,KAAK,IAAI,OAAO,MAAM,QAAQ,KAAK,IAAI,UAAU,GAAG,CAAC,CAAC;CAChG,OAAO,WAAW,IAAI,IAAI;AAC5B"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/internal/dev-warn.ts
|
|
2
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
3
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4
|
+
/**
|
|
5
|
+
* Logs `message` through `console.error` once per `key` in development. Called during render,
|
|
6
|
+
* so a discarded render may warn; keys name the part and the condition (a Server Component has
|
|
7
|
+
* no element identity to key on).
|
|
8
|
+
*/
|
|
9
|
+
function warnOnce(key, message) {
|
|
10
|
+
if (!isDev || seen.has(key)) return;
|
|
11
|
+
seen.add(key);
|
|
12
|
+
console.error(message);
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { warnOnce };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=dev-warn.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-warn.mjs","names":[],"sources":["../../src/internal/dev-warn.ts"],"sourcesContent":["// No \"use client\": usable during the render of a directive-free part that may run as a Server\n// Component, where an effect (`useDevWarning`) never runs (design/components/progress.md 8.1,\n// implementation note 14.3).\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\nconst seen = new Set<string>();\n\n/**\n * Logs `message` through `console.error` once per `key` in development. Called during render,\n * so a discarded render may warn; keys name the part and the condition (a Server Component has\n * no element identity to key on).\n */\nexport function warnOnce(key: string, message: string): void {\n if (!isDev || seen.has(key)) return;\n seen.add(key);\n console.error(message);\n}\n\n/** Tests only: forget every key, so the next `warnOnce` warns again. */\nexport function resetWarnOnce(): void {\n seen.clear();\n}\n"],"mappings":";AAIA,MAAM,QAAQ,QAAQ,IAAI,aAAa;AAEvC,MAAM,uBAAO,IAAI,IAAY;;;;;;AAO7B,SAAgB,SAAS,KAAa,SAAuB;CAC3D,IAAI,CAAC,SAAS,KAAK,IAAI,GAAG,GAAG;CAC7B,KAAK,IAAI,GAAG;CACZ,QAAQ,MAAM,OAAO;AACvB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { FieldValidity } from "./validity.mjs";
|
|
2
|
+
//#region src/internal/field-context.d.ts
|
|
3
|
+
/** The state every Field part receives (design/components/field-form.md 4.1). */
|
|
4
|
+
export interface FieldState {
|
|
5
|
+
/** An error is shown: the control matches `:user-invalid`, or a server error for this field is shown. */
|
|
6
|
+
readonly invalid: boolean;
|
|
7
|
+
/** The messages shown: `[validationMessage]` for a native constraint, the strings `validate` returned for a custom one, the server's strings for a server error; `[]` while valid. */
|
|
8
|
+
readonly messages: readonly string[];
|
|
9
|
+
/** A snapshot of the control's `ValidityState` (all valid on the server and before the first read). */
|
|
10
|
+
readonly validity: FieldValidity;
|
|
11
|
+
/** `Field.Root disabled` or the control's own `disabled`. */
|
|
12
|
+
readonly disabled: boolean;
|
|
13
|
+
/** `Field.Root required` or the control's own `required`. */
|
|
14
|
+
readonly required: boolean;
|
|
15
|
+
/** The enclosing form's submission is in flight (`useFormStatus().pending`); `false` outside a form. */
|
|
16
|
+
readonly pending: boolean;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
//# sourceMappingURL=field-context.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-context.d.mts","names":[],"sources":["../../src/internal/field-context.ts"],"mappings":";;;iBAKiB;;WAEN;;WAEA;;WAEA,UAAU;;WAEV;;WAEA;;WAEA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createContext, use } from "react";
|
|
3
|
+
//#region src/internal/field-context.ts
|
|
4
|
+
const FieldContext = createContext(null);
|
|
5
|
+
function useFieldContext(part) {
|
|
6
|
+
const ctx = use(FieldContext);
|
|
7
|
+
if (ctx === null) throw new Error(`${part} must be rendered inside a Field.Root.`);
|
|
8
|
+
return ctx;
|
|
9
|
+
}
|
|
10
|
+
const FormContext = createContext(null);
|
|
11
|
+
//#endregion
|
|
12
|
+
export { FieldContext, FormContext, useFieldContext };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=field-context.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-context.mjs","names":[],"sources":["../../src/internal/field-context.ts"],"sourcesContent":["\"use client\";\nimport { createContext, use } from \"react\";\nimport type { FieldValidity, FormErrors, ValidatableElement } from \"./validity.js\";\n\n/** The state every Field part receives (design/components/field-form.md 4.1). */\nexport interface FieldState {\n /** An error is shown: the control matches `:user-invalid`, or a server error for this field is shown. */\n readonly invalid: boolean;\n /** The messages shown: `[validationMessage]` for a native constraint, the strings `validate` returned for a custom one, the server's strings for a server error; `[]` while valid. */\n readonly messages: readonly string[];\n /** A snapshot of the control's `ValidityState` (all valid on the server and before the first read). */\n readonly validity: FieldValidity;\n /** `Field.Root disabled` or the control's own `disabled`. */\n readonly disabled: boolean;\n /** `Field.Root required` or the control's own `required`. */\n readonly required: boolean;\n /** The enclosing form's submission is in flight (`useFormStatus().pending`); `false` outside a form. */\n readonly pending: boolean;\n}\n\nexport interface FieldIds {\n readonly control: string;\n readonly label: string;\n readonly description: string;\n readonly error: string;\n}\n\n/** What a control reports to its Field (design 7.1). */\nexport interface FieldControlRegistration {\n /** The element the Field labels, describes and listens to (the input, or a group's fieldset). */\n readonly element: HTMLElement;\n /** `<label for>` can target it. Groups report `false`. */\n readonly labelable: boolean;\n /** The control's own `required` / `disabled` props (for the Field's `data-*` state). */\n readonly required: boolean;\n readonly disabled: boolean;\n /**\n * Where `validity`, `validationMessage` and the custom-validity slot are read and written when\n * that is not the registered element (NumberField: the hidden `input[type=number]` after the\n * visible text input). `:user-invalid`, the listeners and the focus target stay on `element`;\n * after writing the slot the Field mirrors this element's `validationMessage` into `element`\n * (design/components/field-form.md, amendment `validityElement`). Resolved on every read.\n */\n readonly validityElement?: (() => ValidatableElement | null) | undefined;\n}\n\nexport interface FieldContextValue {\n readonly ids: FieldIds;\n /** `Field.Root name`. */\n readonly name: string | undefined;\n /** `Field.Root disabled` / `required` (the props, not the merged state). */\n readonly disabled: boolean;\n readonly required: boolean;\n readonly state: FieldState;\n /** The registered control is labelable (default `true`, also before any registration). */\n readonly labelable: boolean;\n /** Registers the Field's control; returns the unregister function. The first one wins. */\n readonly registerControl: (registration: FieldControlRegistration) => () => void;\n /** A `Field.Error` is mounted (the Field may cancel the platform bubble). */\n readonly registerError: () => () => void;\n /**\n * The control's value changed in the DOM without an `input` / `change` event the Field could\n * hear (a Combobox selection, a CheckboxGroup's derived `required`, a rolled-back optimistic\n * value): re-reads the slot and the mirror after the commit that wrote it, and refreshes the\n * other Fields of the `Form.Root` (cross-field rules). `edit`: the user made the change, so a\n * shown server error is dismissed as by an `input` event. Call from a layout effect.\n */\n readonly notifyChange: (edit: boolean) => void;\n}\n\nexport const FieldContext = createContext<FieldContextValue | null>(null);\n\nexport function useFieldContext(part: string): FieldContextValue {\n const ctx = use(FieldContext);\n if (ctx === null) throw new Error(`${part} must be rendered inside a Field.Root.`);\n return ctx;\n}\n\n/** What a Field registers with its Form (design 3.5). */\nexport interface FormFieldRegistration {\n /** The control, or a group's first enabled (or checked) member. */\n readonly getFocusTarget: () => HTMLElement | null;\n /** The Field shows a server error at the latest commit. */\n readonly showsServerError: () => boolean;\n /** The server messages shown, for an announcement when focus does not move. */\n readonly serverMessages: () => readonly string[];\n /** Re-runs `validate` and re-reads validity (another Field's control changed). */\n readonly refresh: () => void;\n}\n\nexport interface FormContextValue {\n readonly errors: FormErrors | undefined;\n readonly registerField: (registration: FormFieldRegistration) => () => void;\n /**\n * The `FormData` of the submission in flight when the Form ran it itself (the intercepted path),\n * else `null`: the stale-value suppression of design 3.2 item 2 is computed against it.\n */\n readonly submittedData: () => FormData | null;\n /** Refreshes every registered Field except `source` (a change no DOM event carried). */\n readonly refreshFields: (source: FormFieldRegistration | null) => void;\n}\n\nexport const FormContext = createContext<FormContextValue | null>(null);\n"],"mappings":";;;AAsEA,MAAa,eAAe,cAAwC,IAAI;AAExE,SAAgB,gBAAgB,MAAiC;CAC/D,MAAM,MAAM,IAAI,YAAY;CAC5B,IAAI,QAAQ,MAAM,MAAM,IAAI,MAAM,GAAG,KAAK,uCAAuC;CACjF,OAAO;AACT;AA0BA,MAAa,cAAc,cAAuC,IAAI"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { sortByDocumentOrder } from "./validity.mjs";
|
|
2
|
+
//#region src/internal/focus-first-invalid.ts
|
|
3
|
+
/**
|
|
4
|
+
* Speaks `message` through `element.ariaNotify()` where the browser has it (design 5.3: the
|
|
5
|
+
* error is read when focus moves to the control; when focus is already there nothing would be
|
|
6
|
+
* read). Does nothing without `ariaNotify` or with an empty message.
|
|
7
|
+
*/
|
|
8
|
+
function announce(element, message) {
|
|
9
|
+
const notifying = element;
|
|
10
|
+
if (message === "" || typeof notifying.ariaNotify !== "function") return;
|
|
11
|
+
notifying.ariaNotify(message);
|
|
12
|
+
}
|
|
13
|
+
/** The submission attempt in progress: every target added during the current task. */
|
|
14
|
+
let batch = null;
|
|
15
|
+
/**
|
|
16
|
+
* Adds a control whose `invalid` event a Field cancelled during a submission attempt, and
|
|
17
|
+
* focuses the first collected control in document order in a microtask, so the result does not
|
|
18
|
+
* depend on the browser's dispatch order or on how many forms are on the page.
|
|
19
|
+
*
|
|
20
|
+
* The batch lives until the end of the task (a `setTimeout`), not just one microtask: when the
|
|
21
|
+
* browser dispatches the `invalid` events itself (a click on a submit button), it runs a
|
|
22
|
+
* microtask checkpoint after each listener, so the microtask of the first event runs before the
|
|
23
|
+
* second event fires. Every add therefore queues a focus of the batch's first target; later ones
|
|
24
|
+
* re-focus the same element, which is a no-op.
|
|
25
|
+
*
|
|
26
|
+
* Skips the focus when focus moved during the attempt to an element that is not one of the
|
|
27
|
+
* collected targets: a plain `<input required>` with no Field kept the platform's behaviour
|
|
28
|
+
* (bubble and focus), and the library does not fight it.
|
|
29
|
+
*/
|
|
30
|
+
function collectInvalid(target, message = "") {
|
|
31
|
+
if (target === null) return;
|
|
32
|
+
if (batch === null) {
|
|
33
|
+
batch = {
|
|
34
|
+
targets: [],
|
|
35
|
+
messages: /* @__PURE__ */ new Map(),
|
|
36
|
+
activeBefore: target.ownerDocument.activeElement,
|
|
37
|
+
moved: false,
|
|
38
|
+
announced: null
|
|
39
|
+
};
|
|
40
|
+
setTimeout(() => {
|
|
41
|
+
batch = null;
|
|
42
|
+
}, 0);
|
|
43
|
+
}
|
|
44
|
+
const current = batch;
|
|
45
|
+
if (!current.targets.includes(target)) current.targets.push(target);
|
|
46
|
+
current.messages.set(target, message);
|
|
47
|
+
queueMicrotask(() => focusFirst(current));
|
|
48
|
+
}
|
|
49
|
+
function focusFirst(current) {
|
|
50
|
+
const { targets, activeBefore } = current;
|
|
51
|
+
const connected = targets.filter((t) => t.isConnected);
|
|
52
|
+
if (connected.length === 0) return;
|
|
53
|
+
const active = connected[0].ownerDocument.activeElement;
|
|
54
|
+
if (active !== activeBefore && active !== null && !connected.includes(active)) return;
|
|
55
|
+
const first = sortByDocumentOrder(connected, (t) => t)[0];
|
|
56
|
+
if (active !== first) {
|
|
57
|
+
current.moved = true;
|
|
58
|
+
first.focus();
|
|
59
|
+
} else if (!current.moved && current.announced !== first) {
|
|
60
|
+
current.announced = first;
|
|
61
|
+
announce(first, current.messages.get(first) ?? "");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
export { announce, collectInvalid };
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=focus-first-invalid.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focus-first-invalid.mjs","names":[],"sources":["../../src/internal/focus-first-invalid.ts"],"sourcesContent":["// Directive-free: the module-level first-invalid collector (design/components/field-form.md 2.5).\nimport { sortByDocumentOrder } from \"./validity.js\";\n\ninterface Batch {\n readonly targets: HTMLElement[];\n /** The message each target's Field shows, for the announcement when focus does not move. */\n readonly messages: Map<HTMLElement, string>;\n /** `document.activeElement` when the first target arrived. */\n readonly activeBefore: Element | null;\n /** The batch moved focus (later microtasks then re-focus, never announce). */\n moved: boolean;\n /** The target already announced. */\n announced: HTMLElement | null;\n}\n\ntype NotifyingElement = HTMLElement & { ariaNotify?: (announcement: string) => void };\n\n/**\n * Speaks `message` through `element.ariaNotify()` where the browser has it (design 5.3: the\n * error is read when focus moves to the control; when focus is already there nothing would be\n * read). Does nothing without `ariaNotify` or with an empty message.\n */\nexport function announce(element: HTMLElement, message: string): void {\n const notifying = element as NotifyingElement;\n if (message === \"\" || typeof notifying.ariaNotify !== \"function\") return;\n notifying.ariaNotify(message);\n}\n\n/** The submission attempt in progress: every target added during the current task. */\nlet batch: Batch | null = null;\n\n/**\n * Adds a control whose `invalid` event a Field cancelled during a submission attempt, and\n * focuses the first collected control in document order in a microtask, so the result does not\n * depend on the browser's dispatch order or on how many forms are on the page.\n *\n * The batch lives until the end of the task (a `setTimeout`), not just one microtask: when the\n * browser dispatches the `invalid` events itself (a click on a submit button), it runs a\n * microtask checkpoint after each listener, so the microtask of the first event runs before the\n * second event fires. Every add therefore queues a focus of the batch's first target; later ones\n * re-focus the same element, which is a no-op.\n *\n * Skips the focus when focus moved during the attempt to an element that is not one of the\n * collected targets: a plain `<input required>` with no Field kept the platform's behaviour\n * (bubble and focus), and the library does not fight it.\n */\nexport function collectInvalid(target: HTMLElement | null, message = \"\"): void {\n if (target === null) return;\n if (batch === null) {\n batch = {\n targets: [],\n messages: new Map(),\n activeBefore: target.ownerDocument.activeElement,\n moved: false,\n announced: null,\n };\n setTimeout(() => {\n batch = null;\n }, 0);\n }\n const current = batch;\n if (!current.targets.includes(target)) current.targets.push(target);\n current.messages.set(target, message);\n queueMicrotask(() => focusFirst(current));\n}\n\nfunction focusFirst(current: Batch): void {\n const { targets, activeBefore } = current;\n const connected = targets.filter((t) => t.isConnected);\n if (connected.length === 0) return;\n const active = connected[0]!.ownerDocument.activeElement;\n if (active !== activeBefore && active !== null && !connected.includes(active as HTMLElement)) {\n return;\n }\n const first = sortByDocumentOrder(connected, (t) => t)[0]!;\n if (active !== first) {\n current.moved = true;\n first.focus();\n } else if (!current.moved && current.announced !== first) {\n // Focus was already on the first invalid control (an implicit submission with Enter): the\n // cancelled bubble was the only announcement, so speak the message instead.\n current.announced = first;\n announce(first, current.messages.get(first) ?? \"\");\n }\n}\n"],"mappings":";;;;;;;AAsBA,SAAgB,SAAS,SAAsB,SAAuB;CACpE,MAAM,YAAY;CAClB,IAAI,YAAY,MAAM,OAAO,UAAU,eAAe,YAAY;CAClE,UAAU,WAAW,OAAO;AAC9B;;AAGA,IAAI,QAAsB;;;;;;;;;;;;;;;;AAiB1B,SAAgB,eAAe,QAA4B,UAAU,IAAU;CAC7E,IAAI,WAAW,MAAM;CACrB,IAAI,UAAU,MAAM;EAClB,QAAQ;GACN,SAAS,CAAC;GACV,0BAAU,IAAI,IAAI;GAClB,cAAc,OAAO,cAAc;GACnC,OAAO;GACP,WAAW;EACb;EACA,iBAAiB;GACf,QAAQ;EACV,GAAG,CAAC;CACN;CACA,MAAM,UAAU;CAChB,IAAI,CAAC,QAAQ,QAAQ,SAAS,MAAM,GAAG,QAAQ,QAAQ,KAAK,MAAM;CAClE,QAAQ,SAAS,IAAI,QAAQ,OAAO;CACpC,qBAAqB,WAAW,OAAO,CAAC;AAC1C;AAEA,SAAS,WAAW,SAAsB;CACxC,MAAM,EAAE,SAAS,iBAAiB;CAClC,MAAM,YAAY,QAAQ,QAAQ,MAAM,EAAE,WAAW;CACrD,IAAI,UAAU,WAAW,GAAG;CAC5B,MAAM,SAAS,UAAU,EAAE,CAAE,cAAc;CAC3C,IAAI,WAAW,gBAAgB,WAAW,QAAQ,CAAC,UAAU,SAAS,MAAqB,GACzF;CAEF,MAAM,QAAQ,oBAAoB,YAAY,MAAM,CAAC,CAAC,CAAC;CACvD,IAAI,WAAW,OAAO;EACpB,QAAQ,QAAQ;EAChB,MAAM,MAAM;CACd,OAAO,IAAI,CAAC,QAAQ,SAAS,QAAQ,cAAc,OAAO;EAGxD,QAAQ,YAAY;EACpB,SAAS,OAAO,QAAQ,SAAS,IAAI,KAAK,KAAK,EAAE;CACnD;AACF"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { useFormStatus } from "react-dom";
|
|
4
|
+
//#region src/internal/form-status-watcher.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Observes React's form channel (Dialog design 8.1, Field/Form design 3.2): rendered inside a
|
|
7
|
+
* `<form>`, `useFormStatus().pending` covers the action's whole transition, including the
|
|
8
|
+
* Suspense wait of what it re-renders. On `true → false` the submission settled without throwing
|
|
9
|
+
* (a rejection unmounts the form through the nearest Error Boundary, so the edge is never seen).
|
|
10
|
+
* Used by `Dialog.Form` and `Form.Root`.
|
|
11
|
+
*/
|
|
12
|
+
function FormStatusWatcher({ onPendingChange, onSettled }) {
|
|
13
|
+
const { pending } = useFormStatus();
|
|
14
|
+
const previous = useRef(false);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const was = previous.current;
|
|
17
|
+
previous.current = pending;
|
|
18
|
+
if (was === pending) return;
|
|
19
|
+
onPendingChange(pending);
|
|
20
|
+
if (was && !pending) onSettled();
|
|
21
|
+
}, [
|
|
22
|
+
pending,
|
|
23
|
+
onPendingChange,
|
|
24
|
+
onSettled
|
|
25
|
+
]);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { FormStatusWatcher };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=form-status-watcher.mjs.map
|