@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
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { createActionDetails } from "./action-details.mjs";
|
|
3
3
|
import { transitionTypes } from "./transition-types.mjs";
|
|
4
|
+
import { useLatest } from "./use-latest.mjs";
|
|
4
5
|
import { useControllableAction } from "./use-controllable-action.mjs";
|
|
5
6
|
import { useDevWarning } from "./use-dev-warning.mjs";
|
|
6
|
-
import { useCallback, useId,
|
|
7
|
+
import { useCallback, useId, useMemo, useRef, useState } from "react";
|
|
7
8
|
//#region src/internal/use-popover-root.ts
|
|
8
9
|
function isPopoverOpen(element) {
|
|
9
10
|
if (element === null) return false;
|
|
@@ -45,10 +46,7 @@ function usePopoverRoot(options) {
|
|
|
45
46
|
const triggerRef = useRef(null);
|
|
46
47
|
const popupRef = useRef(null);
|
|
47
48
|
const closeIntent = useRef(void 0);
|
|
48
|
-
const committedOptimistic =
|
|
49
|
-
useInsertionEffect(() => {
|
|
50
|
-
committedOptimistic.current = optimistic;
|
|
51
|
-
});
|
|
49
|
+
const committedOptimistic = useLatest(optimistic);
|
|
52
50
|
const generation = useRef(0);
|
|
53
51
|
const openGeneration = useCallback(() => generation.current, []);
|
|
54
52
|
const nextOpenGeneration = useCallback(() => {
|
|
@@ -63,33 +61,54 @@ function usePopoverRoot(options) {
|
|
|
63
61
|
closeIntent.current = void 0;
|
|
64
62
|
return intent;
|
|
65
63
|
}, []);
|
|
66
|
-
const intended = () => latestInFlight()?.next ?? committedOptimistic.current;
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
const intended = useCallback(() => latestInFlight()?.next ?? committedOptimistic.current, [latestInFlight, committedOptimistic]);
|
|
65
|
+
const callbacks = useLatest({
|
|
66
|
+
details,
|
|
67
|
+
onDispatch
|
|
68
|
+
});
|
|
69
|
+
const send = useCallback((next, event, reason) => {
|
|
70
|
+
const { details, onDispatch } = callbacks.current;
|
|
71
|
+
const built = details !== void 0 ? details(event, reason) : createActionDetails(event, { reason });
|
|
70
72
|
if (next) nextOpenGeneration();
|
|
71
73
|
onDispatch?.(next, built);
|
|
72
74
|
dispatch(next, built);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
+
}, [
|
|
76
|
+
callbacks,
|
|
77
|
+
nextOpenGeneration,
|
|
78
|
+
dispatch
|
|
79
|
+
]);
|
|
80
|
+
const requestOpen = useCallback((event, reason) => {
|
|
75
81
|
if (!isLibrary || intended()) return;
|
|
76
82
|
send(true, event, reason);
|
|
77
|
-
}
|
|
78
|
-
|
|
83
|
+
}, [
|
|
84
|
+
isLibrary,
|
|
85
|
+
intended,
|
|
86
|
+
send
|
|
87
|
+
]);
|
|
88
|
+
const requestClose = useCallback((event, reason) => {
|
|
79
89
|
if (!isLibrary || !intended()) return;
|
|
80
90
|
send(false, event, reason);
|
|
81
|
-
}
|
|
82
|
-
|
|
91
|
+
}, [
|
|
92
|
+
isLibrary,
|
|
93
|
+
intended,
|
|
94
|
+
send
|
|
95
|
+
]);
|
|
96
|
+
const notifyClosed = useCallback((event, reason) => {
|
|
83
97
|
if (!isLibrary || !intended()) return;
|
|
84
98
|
send(false, event, reason);
|
|
85
|
-
}
|
|
86
|
-
|
|
99
|
+
}, [
|
|
100
|
+
isLibrary,
|
|
101
|
+
intended,
|
|
102
|
+
send
|
|
103
|
+
]);
|
|
104
|
+
const ids = useMemo(() => ({
|
|
105
|
+
popup: id,
|
|
106
|
+
title: `${id}-title`,
|
|
107
|
+
description: `${id}-description`
|
|
108
|
+
}), [id]);
|
|
109
|
+
return useMemo(() => ({
|
|
87
110
|
mode,
|
|
88
|
-
ids
|
|
89
|
-
popup: id,
|
|
90
|
-
title: `${id}-title`,
|
|
91
|
-
description: `${id}-description`
|
|
92
|
-
},
|
|
111
|
+
ids,
|
|
93
112
|
committed: isLibrary && committed,
|
|
94
113
|
optimistic: isLibrary && optimistic,
|
|
95
114
|
pending: isLibrary && pending,
|
|
@@ -104,7 +123,23 @@ function usePopoverRoot(options) {
|
|
|
104
123
|
popupRef,
|
|
105
124
|
recordCloseIntent,
|
|
106
125
|
takeCloseIntent
|
|
107
|
-
}
|
|
126
|
+
}), [
|
|
127
|
+
mode,
|
|
128
|
+
ids,
|
|
129
|
+
isLibrary,
|
|
130
|
+
committed,
|
|
131
|
+
optimistic,
|
|
132
|
+
pending,
|
|
133
|
+
intended,
|
|
134
|
+
openGeneration,
|
|
135
|
+
nextOpenGeneration,
|
|
136
|
+
requestOpen,
|
|
137
|
+
requestClose,
|
|
138
|
+
notifyClosed,
|
|
139
|
+
latestInFlight,
|
|
140
|
+
recordCloseIntent,
|
|
141
|
+
takeCloseIntent
|
|
142
|
+
]);
|
|
108
143
|
}
|
|
109
144
|
//#endregion
|
|
110
145
|
export { usePopoverRoot };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-popover-root.mjs","names":[],"sources":["../../src/internal/use-popover-root.ts"],"sourcesContent":["\"use client\";\nimport { useCallback, useId, useInsertionEffect, useRef, useState } from \"react\";\nimport type { RefObject } from \"react\";\nimport { createActionDetails } from \"./action-details.js\";\nimport { transitionTypes } from \"./transition-types.js\";\nimport type { ActionDetails, ChangeAction, ChangeReason } from \"./types.js\";\nimport { useControllableAction } from \"./use-controllable-action.js\";\nimport { useDevWarning } from \"./use-dev-warning.js\";\n\n/**\n * \"static\": JS-free mode, the browser owns open and close through Invoker Commands. \"library\":\n * opening and part-initiated closing are library-owned (transitions keyed on committed state);\n * dismissals are the browser's and notified (design/components/popover.md section 2).\n */\nexport type PopoverMode = \"static\" | \"library\";\n\n/**\n * What caused the next close, recorded just before it happens (design 2.3). The `toggle` event\n * carries no source; without an intent a close is a `\"dismiss\"`.\n */\nexport interface PopoverCloseIntent {\n readonly reason: ChangeReason;\n readonly event: Event | undefined;\n}\n\n/** The `details` every popover-like `openChangeAction` receives. */\nexport interface PopoverLikeDetails extends ActionDetails {\n readonly reason: ChangeReason;\n}\n\n/**\n * The Root's state and machinery, shared by Popover and Menu (design 14). The component puts it\n * in its own context; `usePopoverTrigger` and `usePopoverPopup` take it as their first argument.\n */\nexport interface PopoverRootValue {\n readonly mode: PopoverMode;\n /** `useId`-based ids: the popup, its Title and its Description. */\n readonly ids: { readonly popup: string; readonly title: string; readonly description: string };\n /** Committed open state (library mode); `false` in JS-free mode. */\n readonly committed: boolean;\n /** Optimistic open state (library mode); `false` in JS-free mode. */\n readonly optimistic: boolean;\n /** The Root's transition is pending (library mode). */\n readonly pending: boolean;\n /**\n * What the user already intends: the latest in-flight dispatch's `next`, else the optimistic\n * value of the last commit. Safe to call from handlers and effects (layout effects included,\n * also in the commit that changed it), and after an `await`. Not during render.\n */\n readonly intended: () => boolean;\n /**\n * A counter that grows with every open (a requested open, or the popup being shown). A part\n * whose close runs after an `await` records it first and drops the close when it changed: the\n * user dismissed and re-opened meanwhile (Popover.Close, design 3.4). Handlers only.\n */\n readonly openGeneration: () => number;\n /** Advance `openGeneration` (the Popup's `beforetoggle` for an open). */\n readonly nextOpenGeneration: () => void;\n /**\n * Open through the Root's transition (library mode): a Trigger click, a foreign open caught at\n * `beforetoggle`, or mount reconciliation. A no-op when `true` is already intended.\n */\n readonly requestOpen: (event: Event | undefined, reason: ChangeReason) => void;\n /**\n * A library-owned close (library mode): a Trigger click while open, a Close part, Menu's item\n * activation. `openChangeAction(false)` is awaited and the popup hides in the commit. A no-op\n * when `false` is already intended.\n */\n readonly requestClose: (event: Event | undefined, reason: ChangeReason) => void;\n /**\n * The browser hid the popup (a dismissal or a foreign invoker): dispatch the `false`\n * notification unless `false` is already intended (the library's own echo, or a dismissal\n * during a pending part-initiated close).\n */\n readonly notifyClosed: (event: Event | undefined, reason: ChangeReason) => void;\n /** From `useControllableAction`: the latest in-flight intent, or `null`. */\n readonly latestInFlight: () => { readonly next: boolean } | null;\n /** The registered Trigger: `showPopover({ source })` and the focus-return fallback. */\n readonly triggerRef: RefObject<HTMLElement | null>;\n /** The popup element, set by the Popup's ref; how Close reaches `hidePopover()`. */\n readonly popupRef: RefObject<HTMLElement | null>;\n /** Record what causes the next close. Cleared after a task if the popover did not close. */\n readonly recordCloseIntent: (intent: PopoverCloseIntent | undefined) => void;\n /** Read and clear the recorded intent (the Popup's `toggle` listener). */\n readonly takeCloseIntent: () => PopoverCloseIntent | undefined;\n}\n\nexport interface UsePopoverRootOptions<Details extends PopoverLikeDetails> {\n /** Transition types and messages: `\"popover\"` gives `fsui-popover`, `fsui-popover-open`, … */\n readonly component: string;\n /** Public name of the Root part for development warnings (`\"Popover.Root\"`). */\n readonly part: string;\n readonly open: boolean | undefined;\n readonly defaultOpen: boolean | undefined;\n readonly openChangeAction: ChangeAction<boolean, Details> | undefined;\n /**\n * Builds the `details` of a dispatch. Default: `createActionDetails(event, { reason })`. A\n * component with more fields (Menu) passes its own.\n */\n readonly details?: (event: Event | undefined, reason: ChangeReason) => Details;\n /**\n * `\"library\"` forces library mode whatever the props (Menu, which has no JS-free mode). Default:\n * detected from `open` / `defaultOpen` / `openChangeAction` on the first render.\n */\n readonly mode?: \"library\";\n /**\n * Called synchronously for every dispatch, before the action runs (Menu: cancel hover timers,\n * read the open cause).\n */\n readonly onDispatch?: (next: boolean, details: Details) => void;\n}\n\nfunction isPopoverOpen(element: Element | null): boolean {\n if (element === null) return false;\n try {\n return element.matches(\":popover-open\");\n } catch {\n return false;\n }\n}\n\n/** How long a recorded close intent survives when the popover did not close (design 2.3). */\nfunction clearLater(\n intentRef: { current: PopoverCloseIntent | undefined },\n intent: PopoverCloseIntent,\n popupRef: { current: HTMLElement | null },\n): void {\n setTimeout(() => {\n // Closed: its `toggle` event (queued as a task) consumes the intent. Still open: the close\n // was cancelled or invalid, so the intent is stale.\n if (intentRef.current === intent && isPopoverOpen(popupRef.current)) {\n intentRef.current = undefined;\n }\n }, 0);\n}\n\n/**\n * The Root of a popover-like component without its public props (design 14.1): mode detection\n * (once, from the first render), `useControllableAction<boolean>`, ids, refs, close intents and\n * the intent-aware `requestOpen` / `requestClose` / `notifyClosed`.\n */\nexport function usePopoverRoot<Details extends PopoverLikeDetails>(\n options: UsePopoverRootOptions<Details>,\n): PopoverRootValue {\n const { component, part, open, defaultOpen, openChangeAction, details, onDispatch } = options;\n\n // Mode is decided once, from the first render's props (design 2).\n const isLibraryNow =\n options.mode === \"library\" ||\n open !== undefined ||\n defaultOpen !== undefined ||\n openChangeAction !== undefined;\n const [mode] = useState<PopoverMode>(isLibraryNow ? \"library\" : \"static\");\n const isLibrary = mode === \"library\";\n useDevWarning(\n isLibrary !== isLibraryNow,\n `${part}: the mode changed after mount (\\`open\\`, \\`defaultOpen\\` and \\`openChangeAction\\` ` +\n \"were all absent on one render and present on another). Switching between JS-free and \" +\n \"library mode is not supported.\",\n );\n\n // Hooks cannot be conditional: JS-free mode calls the hook too and never dispatches.\n const { committed, optimistic, pending, dispatch, latestInFlight } = useControllableAction<\n boolean,\n Details\n >({\n controlled: isLibrary ? open : undefined,\n defaultValue: isLibrary ? defaultOpen : undefined,\n initialValue: false,\n action: isLibrary ? openChangeAction : undefined,\n transitionTypes: (next) => transitionTypes(component, next ? \"open\" : \"close\"),\n warn: { component: part, prop: \"open\" },\n });\n\n const id = useId();\n const triggerRef = useRef<HTMLElement | null>(null);\n const popupRef = useRef<HTMLElement | null>(null);\n const closeIntent = useRef<PopoverCloseIntent | undefined>(undefined);\n\n // The optimistic value of the last commit, for callers that run after an `await` (a Close\n // action's continuation) and would otherwise read the value of the render they were created in.\n // An insertion effect runs in the mutation phase, before any layout effect of the same commit\n // (a child's included), so `intended()` is current there too.\n const committedOptimistic = useRef(optimistic);\n useInsertionEffect(() => {\n committedOptimistic.current = optimistic;\n });\n const generation = useRef(0);\n const openGeneration = useCallback(() => generation.current, []);\n const nextOpenGeneration = useCallback(() => {\n generation.current++;\n }, []);\n\n const recordCloseIntent = useCallback((intent: PopoverCloseIntent | undefined) => {\n closeIntent.current = intent;\n if (intent !== undefined) clearLater(closeIntent, intent, popupRef);\n }, []);\n const takeCloseIntent = useCallback(() => {\n const intent = closeIntent.current;\n closeIntent.current = undefined;\n return intent;\n }, []);\n\n // Conventions 3.8 rule 1: judge no-ops against what the user already intends. That also\n // deduplicates the StrictMode re-run of the mount reconciliation and recognises the library's\n // own `hidePopover()` echoing back through `toggle`.\n const intended = (): boolean => latestInFlight()?.next ?? committedOptimistic.current;\n\n const buildDetails = (event: Event | undefined, reason: ChangeReason): Details =>\n details !== undefined\n ? details(event, reason)\n : (createActionDetails(event, { reason }) as unknown as Details);\n\n const send = (next: boolean, event: Event | undefined, reason: ChangeReason): void => {\n const built = buildDetails(event, reason);\n if (next) nextOpenGeneration();\n onDispatch?.(next, built);\n dispatch(next, built);\n };\n\n const requestOpen = (event: Event | undefined, reason: ChangeReason): void => {\n if (!isLibrary || intended()) return;\n send(true, event, reason);\n };\n\n const requestClose = (event: Event | undefined, reason: ChangeReason): void => {\n if (!isLibrary || !intended()) return;\n send(false, event, reason);\n };\n\n // Same guard as `requestClose`; a separate name because the callers mean different things.\n const notifyClosed = (event: Event | undefined, reason: ChangeReason): void => {\n if (!isLibrary || !intended()) return;\n send(false, event, reason);\n };\n\n // Not memoised: `dispatch` is a new function on every render (as Dialog), so parts re-render\n // with the Root, which they do anyway when its state changes.\n return {\n mode,\n ids: { popup: id, title: `${id}-title`, description: `${id}-description` },\n committed: isLibrary && committed,\n optimistic: isLibrary && optimistic,\n pending: isLibrary && pending,\n intended,\n openGeneration,\n nextOpenGeneration,\n requestOpen,\n requestClose,\n notifyClosed,\n latestInFlight,\n triggerRef,\n popupRef,\n recordCloseIntent,\n takeCloseIntent,\n };\n}\n"],"mappings":";;;;;;;AAgHA,SAAS,cAAc,SAAkC;CACvD,IAAI,YAAY,MAAM,OAAO;CAC7B,IAAI;EACF,OAAO,QAAQ,QAAQ,eAAe;CACxC,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,SAAS,WACP,WACA,QACA,UACM;CACN,iBAAiB;EAGf,IAAI,UAAU,YAAY,UAAU,cAAc,SAAS,OAAO,GAChE,UAAU,UAAU,KAAA;CAExB,GAAG,CAAC;AACN;;;;;;AAOA,SAAgB,eACd,SACkB;CAClB,MAAM,EAAE,WAAW,MAAM,MAAM,aAAa,kBAAkB,SAAS,eAAe;CAGtF,MAAM,eACJ,QAAQ,SAAS,aACjB,SAAS,KAAA,KACT,gBAAgB,KAAA,KAChB,qBAAqB,KAAA;CACvB,MAAM,CAAC,QAAQ,SAAsB,eAAe,YAAY,QAAQ;CACxE,MAAM,YAAY,SAAS;CAC3B,cACE,cAAc,cACd,GAAG,KAAK,uMAGV;CAGA,MAAM,EAAE,WAAW,YAAY,SAAS,UAAU,mBAAmB,sBAGnE;EACA,YAAY,YAAY,OAAO,KAAA;EAC/B,cAAc,YAAY,cAAc,KAAA;EACxC,cAAc;EACd,QAAQ,YAAY,mBAAmB,KAAA;EACvC,kBAAkB,SAAS,gBAAgB,WAAW,OAAO,SAAS,OAAO;EAC7E,MAAM;GAAE,WAAW;GAAM,MAAM;EAAO;CACxC,CAAC;CAED,MAAM,KAAK,MAAM;CACjB,MAAM,aAAa,OAA2B,IAAI;CAClD,MAAM,WAAW,OAA2B,IAAI;CAChD,MAAM,cAAc,OAAuC,KAAA,CAAS;CAMpE,MAAM,sBAAsB,OAAO,UAAU;CAC7C,yBAAyB;EACvB,oBAAoB,UAAU;CAChC,CAAC;CACD,MAAM,aAAa,OAAO,CAAC;CAC3B,MAAM,iBAAiB,kBAAkB,WAAW,SAAS,CAAC,CAAC;CAC/D,MAAM,qBAAqB,kBAAkB;EAC3C,WAAW;CACb,GAAG,CAAC,CAAC;CAEL,MAAM,oBAAoB,aAAa,WAA2C;EAChF,YAAY,UAAU;EACtB,IAAI,WAAW,KAAA,GAAW,WAAW,aAAa,QAAQ,QAAQ;CACpE,GAAG,CAAC,CAAC;CACL,MAAM,kBAAkB,kBAAkB;EACxC,MAAM,SAAS,YAAY;EAC3B,YAAY,UAAU,KAAA;EACtB,OAAO;CACT,GAAG,CAAC,CAAC;CAKL,MAAM,iBAA0B,eAAe,CAAC,EAAE,QAAQ,oBAAoB;CAE9E,MAAM,gBAAgB,OAA0B,WAC9C,YAAY,KAAA,IACR,QAAQ,OAAO,MAAM,IACpB,oBAAoB,OAAO,EAAE,OAAO,CAAC;CAE5C,MAAM,QAAQ,MAAe,OAA0B,WAA+B;EACpF,MAAM,QAAQ,aAAa,OAAO,MAAM;EACxC,IAAI,MAAM,mBAAmB;EAC7B,aAAa,MAAM,KAAK;EACxB,SAAS,MAAM,KAAK;CACtB;CAEA,MAAM,eAAe,OAA0B,WAA+B;EAC5E,IAAI,CAAC,aAAa,SAAS,GAAG;EAC9B,KAAK,MAAM,OAAO,MAAM;CAC1B;CAEA,MAAM,gBAAgB,OAA0B,WAA+B;EAC7E,IAAI,CAAC,aAAa,CAAC,SAAS,GAAG;EAC/B,KAAK,OAAO,OAAO,MAAM;CAC3B;CAGA,MAAM,gBAAgB,OAA0B,WAA+B;EAC7E,IAAI,CAAC,aAAa,CAAC,SAAS,GAAG;EAC/B,KAAK,OAAO,OAAO,MAAM;CAC3B;CAIA,OAAO;EACL;EACA,KAAK;GAAE,OAAO;GAAI,OAAO,GAAG,GAAG;GAAS,aAAa,GAAG,GAAG;EAAc;EACzE,WAAW,aAAa;EACxB,YAAY,aAAa;EACzB,SAAS,aAAa;EACtB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"use-popover-root.mjs","names":[],"sources":["../../src/internal/use-popover-root.ts"],"sourcesContent":["\"use client\";\nimport { useCallback, useId, useMemo, useRef, useState } from \"react\";\nimport type { RefObject } from \"react\";\nimport { createActionDetails } from \"./action-details.js\";\nimport { transitionTypes } from \"./transition-types.js\";\nimport type { ActionDetails, ChangeAction, ChangeReason } from \"./types.js\";\nimport { useControllableAction } from \"./use-controllable-action.js\";\nimport { useDevWarning } from \"./use-dev-warning.js\";\nimport { useLatest } from \"./use-latest.js\";\n\n/**\n * \"static\": JS-free mode, the browser owns open and close through Invoker Commands. \"library\":\n * opening and part-initiated closing are library-owned (transitions keyed on committed state);\n * dismissals are the browser's and notified (design/components/popover.md section 2).\n */\nexport type PopoverMode = \"static\" | \"library\";\n\n/**\n * What caused the next close, recorded just before it happens (design 2.3). The `toggle` event\n * carries no source; without an intent a close is a `\"dismiss\"`.\n */\nexport interface PopoverCloseIntent {\n readonly reason: ChangeReason;\n readonly event: Event | undefined;\n}\n\n/** The `details` every popover-like `openChangeAction` receives. */\nexport interface PopoverLikeDetails extends ActionDetails {\n readonly reason: ChangeReason;\n}\n\n/**\n * The Root's state and machinery, shared by Popover and Menu (design 14). The component puts it\n * in its own context; `usePopoverTrigger` and `usePopoverPopup` take it as their first argument.\n */\nexport interface PopoverRootValue {\n readonly mode: PopoverMode;\n /** `useId`-based ids: the popup, its Title and its Description. */\n readonly ids: { readonly popup: string; readonly title: string; readonly description: string };\n /** Committed open state (library mode); `false` in JS-free mode. */\n readonly committed: boolean;\n /** Optimistic open state (library mode); `false` in JS-free mode. */\n readonly optimistic: boolean;\n /** The Root's transition is pending (library mode). */\n readonly pending: boolean;\n /**\n * What the user already intends: the latest in-flight dispatch's `next`, else the optimistic\n * value of the last commit. Safe to call from handlers and effects (layout effects included,\n * also in the commit that changed it), and after an `await`. Not during render.\n */\n readonly intended: () => boolean;\n /**\n * A counter that grows with every open (a requested open, or the popup being shown). A part\n * whose close runs after an `await` records it first and drops the close when it changed: the\n * user dismissed and re-opened meanwhile (Popover.Close, design 3.4). Handlers only.\n */\n readonly openGeneration: () => number;\n /** Advance `openGeneration` (the Popup's `beforetoggle` for an open). */\n readonly nextOpenGeneration: () => void;\n /**\n * Open through the Root's transition (library mode): a Trigger click, a foreign open caught at\n * `beforetoggle`, or mount reconciliation. A no-op when `true` is already intended.\n */\n readonly requestOpen: (event: Event | undefined, reason: ChangeReason) => void;\n /**\n * A library-owned close (library mode): a Trigger click while open, a Close part, Menu's item\n * activation. `openChangeAction(false)` is awaited and the popup hides in the commit. A no-op\n * when `false` is already intended.\n */\n readonly requestClose: (event: Event | undefined, reason: ChangeReason) => void;\n /**\n * The browser hid the popup (a dismissal or a foreign invoker): dispatch the `false`\n * notification unless `false` is already intended (the library's own echo, or a dismissal\n * during a pending part-initiated close).\n */\n readonly notifyClosed: (event: Event | undefined, reason: ChangeReason) => void;\n /** From `useControllableAction`: the latest in-flight intent, or `null`. */\n readonly latestInFlight: () => { readonly next: boolean } | null;\n /** The registered Trigger: `showPopover({ source })` and the focus-return fallback. */\n readonly triggerRef: RefObject<HTMLElement | null>;\n /** The popup element, set by the Popup's ref; how Close reaches `hidePopover()`. */\n readonly popupRef: RefObject<HTMLElement | null>;\n /** Record what causes the next close. Cleared after a task if the popover did not close. */\n readonly recordCloseIntent: (intent: PopoverCloseIntent | undefined) => void;\n /** Read and clear the recorded intent (the Popup's `toggle` listener). */\n readonly takeCloseIntent: () => PopoverCloseIntent | undefined;\n}\n\nexport interface UsePopoverRootOptions<Details extends PopoverLikeDetails> {\n /** Transition types and messages: `\"popover\"` gives `fsui-popover`, `fsui-popover-open`, … */\n readonly component: string;\n /** Public name of the Root part for development warnings (`\"Popover.Root\"`). */\n readonly part: string;\n readonly open: boolean | undefined;\n readonly defaultOpen: boolean | undefined;\n readonly openChangeAction: ChangeAction<boolean, Details> | undefined;\n /**\n * Builds the `details` of a dispatch. Default: `createActionDetails(event, { reason })`. A\n * component with more fields (Menu) passes its own.\n */\n readonly details?: (event: Event | undefined, reason: ChangeReason) => Details;\n /**\n * `\"library\"` forces library mode whatever the props (Menu, which has no JS-free mode). Default:\n * detected from `open` / `defaultOpen` / `openChangeAction` on the first render.\n */\n readonly mode?: \"library\";\n /**\n * Called synchronously for every dispatch, before the action runs (Menu: cancel hover timers,\n * read the open cause).\n */\n readonly onDispatch?: (next: boolean, details: Details) => void;\n}\n\nfunction isPopoverOpen(element: Element | null): boolean {\n if (element === null) return false;\n try {\n return element.matches(\":popover-open\");\n } catch {\n return false;\n }\n}\n\n/** How long a recorded close intent survives when the popover did not close (design 2.3). */\nfunction clearLater(\n intentRef: { current: PopoverCloseIntent | undefined },\n intent: PopoverCloseIntent,\n popupRef: { current: HTMLElement | null },\n): void {\n setTimeout(() => {\n // Closed: its `toggle` event (queued as a task) consumes the intent. Still open: the close\n // was cancelled or invalid, so the intent is stale.\n if (intentRef.current === intent && isPopoverOpen(popupRef.current)) {\n intentRef.current = undefined;\n }\n }, 0);\n}\n\n/**\n * The Root of a popover-like component without its public props (design 14.1): mode detection\n * (once, from the first render), `useControllableAction<boolean>`, ids, refs, close intents and\n * the intent-aware `requestOpen` / `requestClose` / `notifyClosed`.\n */\nexport function usePopoverRoot<Details extends PopoverLikeDetails>(\n options: UsePopoverRootOptions<Details>,\n): PopoverRootValue {\n const { component, part, open, defaultOpen, openChangeAction, details, onDispatch } = options;\n\n // Mode is decided once, from the first render's props (design 2).\n const isLibraryNow =\n options.mode === \"library\" ||\n open !== undefined ||\n defaultOpen !== undefined ||\n openChangeAction !== undefined;\n const [mode] = useState<PopoverMode>(isLibraryNow ? \"library\" : \"static\");\n const isLibrary = mode === \"library\";\n useDevWarning(\n isLibrary !== isLibraryNow,\n `${part}: the mode changed after mount (\\`open\\`, \\`defaultOpen\\` and \\`openChangeAction\\` ` +\n \"were all absent on one render and present on another). Switching between JS-free and \" +\n \"library mode is not supported.\",\n );\n\n // Hooks cannot be conditional: JS-free mode calls the hook too and never dispatches.\n const { committed, optimistic, pending, dispatch, latestInFlight } = useControllableAction<\n boolean,\n Details\n >({\n controlled: isLibrary ? open : undefined,\n defaultValue: isLibrary ? defaultOpen : undefined,\n initialValue: false,\n action: isLibrary ? openChangeAction : undefined,\n transitionTypes: (next) => transitionTypes(component, next ? \"open\" : \"close\"),\n warn: { component: part, prop: \"open\" },\n });\n\n const id = useId();\n const triggerRef = useRef<HTMLElement | null>(null);\n const popupRef = useRef<HTMLElement | null>(null);\n const closeIntent = useRef<PopoverCloseIntent | undefined>(undefined);\n\n // The optimistic value of the last commit, for callers that run after an `await` (a Close\n // action's continuation) and would otherwise read the value of the render they were created in.\n // An insertion effect runs in the mutation phase, before any layout effect of the same commit\n // (a child's included), so `intended()` is current there too.\n const committedOptimistic = useLatest(optimistic);\n const generation = useRef(0);\n const openGeneration = useCallback(() => generation.current, []);\n const nextOpenGeneration = useCallback(() => {\n generation.current++;\n }, []);\n\n const recordCloseIntent = useCallback((intent: PopoverCloseIntent | undefined) => {\n closeIntent.current = intent;\n if (intent !== undefined) clearLater(closeIntent, intent, popupRef);\n }, []);\n const takeCloseIntent = useCallback(() => {\n const intent = closeIntent.current;\n closeIntent.current = undefined;\n return intent;\n }, []);\n\n // Conventions 3.8 rule 1: judge no-ops against what the user already intends. That also\n // deduplicates the StrictMode re-run of the mount reconciliation and recognises the library's\n // own `hidePopover()` echoing back through `toggle`.\n const intended = useCallback(\n (): boolean => latestInFlight()?.next ?? committedOptimistic.current,\n [latestInFlight, committedOptimistic],\n );\n\n // `details` and `onDispatch` are usually inline functions: read the committed render's through a\n // ref so that the functions below, and with them the returned value, stay stable (#48).\n const callbacks = useLatest({ details, onDispatch });\n\n const send = useCallback(\n (next: boolean, event: Event | undefined, reason: ChangeReason): void => {\n const { details, onDispatch } = callbacks.current;\n const built =\n details !== undefined\n ? details(event, reason)\n : (createActionDetails(event, { reason }) as unknown as Details);\n if (next) nextOpenGeneration();\n onDispatch?.(next, built);\n dispatch(next, built);\n },\n [callbacks, nextOpenGeneration, dispatch],\n );\n\n const requestOpen = useCallback(\n (event: Event | undefined, reason: ChangeReason): void => {\n if (!isLibrary || intended()) return;\n send(true, event, reason);\n },\n [isLibrary, intended, send],\n );\n\n const requestClose = useCallback(\n (event: Event | undefined, reason: ChangeReason): void => {\n if (!isLibrary || !intended()) return;\n send(false, event, reason);\n },\n [isLibrary, intended, send],\n );\n\n // Same guard as `requestClose`; a separate name because the callers mean different things.\n const notifyClosed = useCallback(\n (event: Event | undefined, reason: ChangeReason): void => {\n if (!isLibrary || !intended()) return;\n send(false, event, reason);\n },\n [isLibrary, intended, send],\n );\n\n const ids = useMemo(\n () => ({ popup: id, title: `${id}-title`, description: `${id}-description` }),\n [id],\n );\n\n // Memoised: every function above is stable, so the value changes only with the open state and\n // pending, and the components' context values built on it are reused otherwise (#48).\n return useMemo(\n () => ({\n mode,\n ids,\n committed: isLibrary && committed,\n optimistic: isLibrary && optimistic,\n pending: isLibrary && pending,\n intended,\n openGeneration,\n nextOpenGeneration,\n requestOpen,\n requestClose,\n notifyClosed,\n latestInFlight,\n triggerRef,\n popupRef,\n recordCloseIntent,\n takeCloseIntent,\n }),\n [\n mode,\n ids,\n isLibrary,\n committed,\n optimistic,\n pending,\n intended,\n openGeneration,\n nextOpenGeneration,\n requestOpen,\n requestClose,\n notifyClosed,\n latestInFlight,\n recordCloseIntent,\n takeCloseIntent,\n ],\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { dataAttr } from "./data-attr.mjs";
|
|
3
|
-
import { rendersButton } from "./
|
|
3
|
+
import { rendersButton } from "./render-element.mjs";
|
|
4
4
|
import { useCallback } from "react";
|
|
5
5
|
//#region src/internal/use-popover-trigger.ts
|
|
6
6
|
const isDev = process.env.NODE_ENV !== "production";
|
|
@@ -1,49 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { mergeRefs } from "./merge-props.mjs";
|
|
3
|
+
import { renderElement, rendersButton, resolveStateValue } from "./render-element.mjs";
|
|
4
|
+
import { isValidElement, useMemo } from "react";
|
|
4
5
|
//#region src/internal/use-render.tsx
|
|
5
|
-
function resolveStateValue(value, state) {
|
|
6
|
-
return typeof value === "function" ? value(state) : value;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Whether a part whose default element is a `<button>` should add `type="button"`: no `render`,
|
|
10
|
-
* a `render` element whose type is the intrinsic `"button"`, or the function form (it receives
|
|
11
|
-
* button props and decides itself what to spread). An element of any other type (an `<a>`, a
|
|
12
|
-
* router `Link`) does not get it: `type` means something else, or nothing, there.
|
|
13
|
-
*/
|
|
14
|
-
function rendersButton(render) {
|
|
15
|
-
if (render === void 0 || typeof render === "function") return true;
|
|
16
|
-
return isValidElement(render) && render.type === "button";
|
|
17
|
-
}
|
|
18
6
|
/**
|
|
19
7
|
* Resolves `render` and the function-form `className` / `style` / `children` of a part against
|
|
20
|
-
* its state and produces the element (conventions 1.2, 1.3, 1.4).
|
|
8
|
+
* its state and produces the element (conventions 1.2, 1.3, 1.4). The pure work is
|
|
9
|
+
* `renderElement` (`render-element.ts`); this hook adds the memoised ref merge.
|
|
21
10
|
*/
|
|
22
11
|
function useRender(options) {
|
|
23
|
-
const {
|
|
12
|
+
const { render, props, ref } = options;
|
|
24
13
|
const consumerRef = props.ref;
|
|
25
|
-
const
|
|
26
|
-
const elementRef = renderElement?.props.ref;
|
|
14
|
+
const elementRef = isValidElement(render) ? render.props.ref : void 0;
|
|
27
15
|
const mergedRef = useMemo(() => mergeRefs(ref, consumerRef, elementRef), [
|
|
28
16
|
ref,
|
|
29
17
|
consumerRef,
|
|
30
18
|
elementRef
|
|
31
19
|
]);
|
|
32
|
-
|
|
33
|
-
...
|
|
34
|
-
ref: mergedRef
|
|
35
|
-
|
|
36
|
-
style: resolveStateValue(style, state),
|
|
37
|
-
children: resolveStateValue(children, state)
|
|
38
|
-
};
|
|
39
|
-
const controlled = libraryProps ?? {};
|
|
40
|
-
if (renderElement !== void 0) {
|
|
41
|
-
const { ref: _ignored, ...elementProps } = renderElement.props;
|
|
42
|
-
return cloneElement(renderElement, mergeProps(partProps, elementProps, controlled));
|
|
43
|
-
}
|
|
44
|
-
const resolved = mergeProps(partProps, {}, controlled);
|
|
45
|
-
if (typeof render === "function") return render(resolved, state);
|
|
46
|
-
return createElement(defaultElement, resolved);
|
|
20
|
+
return renderElement({
|
|
21
|
+
...options,
|
|
22
|
+
ref: mergedRef
|
|
23
|
+
});
|
|
47
24
|
}
|
|
48
25
|
//#endregion
|
|
49
26
|
export { rendersButton, resolveStateValue, useRender };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-render.mjs","names":[],"sources":["../../src/internal/use-render.tsx"],"sourcesContent":["\"use client\";\nimport {
|
|
1
|
+
{"version":3,"file":"use-render.mjs","names":[],"sources":["../../src/internal/use-render.tsx"],"sourcesContent":["\"use client\";\nimport { isValidElement, useMemo } from \"react\";\nimport type { ReactNode, Ref } from \"react\";\nimport { mergeRefs } from \"./merge-props.js\";\nimport { renderElement } from \"./render-element.js\";\nimport type { ElementLikeProps, RenderElementOptions } from \"./render-element.js\";\n\nexport { rendersButton, resolveStateValue } from \"./render-element.js\";\nexport type { DataAttributes } from \"./render-element.js\";\n\nexport interface UseRenderOptions<Props extends ElementLikeProps, State> extends Omit<\n RenderElementOptions<Props, State>,\n \"ref\"\n> {\n /** The library's own ref, if the part needs the DOM node. */\n ref?: Ref<unknown> | undefined;\n}\n\n/**\n * Resolves `render` and the function-form `className` / `style` / `children` of a part against\n * its state and produces the element (conventions 1.2, 1.3, 1.4). The pure work is\n * `renderElement` (`render-element.ts`); this hook adds the memoised ref merge.\n */\nexport function useRender<Props extends ElementLikeProps, State>(\n options: UseRenderOptions<Props, State>,\n): ReactNode {\n const { render, props, ref } = options;\n\n const consumerRef = (props as ElementLikeProps).ref;\n const elementRef = isValidElement<ElementLikeProps>(render) ? render.props.ref : undefined;\n // Memoised so that the callback ref keeps its identity across renders and React does not\n // detach and re-attach the node every time.\n const mergedRef = useMemo(\n () => mergeRefs(ref, consumerRef, elementRef),\n [ref, consumerRef, elementRef],\n );\n\n return renderElement<Props, State>({ ...options, ref: mergedRef });\n}\n"],"mappings":";;;;;;;;;;AAuBA,SAAgB,UACd,SACW;CACX,MAAM,EAAE,QAAQ,OAAO,QAAQ;CAE/B,MAAM,cAAe,MAA2B;CAChD,MAAM,aAAa,eAAiC,MAAM,IAAI,OAAO,MAAM,MAAM,KAAA;CAGjF,MAAM,YAAY,cACV,UAAU,KAAK,aAAa,UAAU,GAC5C;EAAC;EAAK;EAAa;CAAU,CAC/B;CAEA,OAAO,cAA4B;EAAE,GAAG;EAAS,KAAK;CAAU,CAAC;AACnE"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useInsertionEffect, useLayoutEffect, useState } from "react";
|
|
3
|
+
//#region src/internal/use-roving-focus.ts
|
|
4
|
+
const FOCUSABLE = [
|
|
5
|
+
"button",
|
|
6
|
+
"a[href]",
|
|
7
|
+
"input:not([type=\"hidden\"])",
|
|
8
|
+
"select",
|
|
9
|
+
"textarea",
|
|
10
|
+
"summary",
|
|
11
|
+
"[contenteditable]:not([contenteditable=\"false\"])",
|
|
12
|
+
"[tabindex]"
|
|
13
|
+
].join(",");
|
|
14
|
+
function isVisible(el) {
|
|
15
|
+
if (typeof el.checkVisibility === "function") return el.checkVisibility();
|
|
16
|
+
return el.closest("[hidden]") === null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Whether `el` or an ancestor below `root` takes it out of `root`'s sequence: a nested toolbar
|
|
20
|
+
* (its own sequence), a popover or a dialog rendered inside the container (a `Menu.Popup`'s items
|
|
21
|
+
* are the menu's).
|
|
22
|
+
*/
|
|
23
|
+
function isNestedOut(el, root) {
|
|
24
|
+
for (let node = el; node !== null && node !== root; node = node.parentElement) if (node.getAttribute("role") === "toolbar" || node.hasAttribute("popover") || node.tagName === "DIALOG") return true;
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The items of `root` in DOM order (design 6.2): its focusable descendants minus `:disabled`,
|
|
29
|
+
* `inert`, invisible, nested-toolbar / popover / dialog content, and elements whose own
|
|
30
|
+
* `tabindex="-1"` was not written by this engine (`managed`). `aria-disabled` elements are items.
|
|
31
|
+
*/
|
|
32
|
+
function itemsOf(root, managed) {
|
|
33
|
+
const items = [];
|
|
34
|
+
for (const el of root.querySelectorAll(FOCUSABLE)) {
|
|
35
|
+
if (el.matches(":disabled")) continue;
|
|
36
|
+
if (el.closest("[inert]") !== null) continue;
|
|
37
|
+
if (el.getAttribute("tabindex") === "-1" && !managed.has(el)) continue;
|
|
38
|
+
if (isNestedOut(el, root)) continue;
|
|
39
|
+
if (!isVisible(el)) continue;
|
|
40
|
+
items.push(el);
|
|
41
|
+
}
|
|
42
|
+
return items;
|
|
43
|
+
}
|
|
44
|
+
const BUTTON_LIKE_INPUTS = /* @__PURE__ */ new Set([
|
|
45
|
+
"button",
|
|
46
|
+
"submit",
|
|
47
|
+
"reset",
|
|
48
|
+
"checkbox",
|
|
49
|
+
"radio",
|
|
50
|
+
"image",
|
|
51
|
+
"color",
|
|
52
|
+
"file"
|
|
53
|
+
]);
|
|
54
|
+
const ARROW_OWNING_ROLES = /* @__PURE__ */ new Set([
|
|
55
|
+
"textbox",
|
|
56
|
+
"combobox",
|
|
57
|
+
"spinbutton",
|
|
58
|
+
"slider"
|
|
59
|
+
]);
|
|
60
|
+
/**
|
|
61
|
+
* An item that owns the arrow keys (design 6.3): text-like `input`s, `textarea`, `select`,
|
|
62
|
+
* `[contenteditable]`, and `role` `textbox` / `combobox` / `spinbutton` / `slider`.
|
|
63
|
+
*/
|
|
64
|
+
function isTextEntryItem(el) {
|
|
65
|
+
const role = el.getAttribute("role");
|
|
66
|
+
if (role !== null && ARROW_OWNING_ROLES.has(role)) return true;
|
|
67
|
+
if (el instanceof HTMLInputElement) return !BUTTON_LIKE_INPUTS.has(el.type);
|
|
68
|
+
if (el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) return true;
|
|
69
|
+
const editable = el.getAttribute("contenteditable");
|
|
70
|
+
if (editable !== null && editable !== "false") return true;
|
|
71
|
+
return el instanceof HTMLElement && el.isContentEditable === true;
|
|
72
|
+
}
|
|
73
|
+
function resolveKey(key, orientation, rtl) {
|
|
74
|
+
switch (key) {
|
|
75
|
+
case "Home": return "first";
|
|
76
|
+
case "End": return "last";
|
|
77
|
+
case "ArrowRight": return orientation === "horizontal" ? rtl ? "prev" : "next" : null;
|
|
78
|
+
case "ArrowLeft": return orientation === "horizontal" ? rtl ? "next" : "prev" : null;
|
|
79
|
+
case "ArrowDown": return orientation === "vertical" ? "next" : null;
|
|
80
|
+
case "ArrowUp": return orientation === "vertical" ? "prev" : null;
|
|
81
|
+
default: return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function focusItem(item) {
|
|
85
|
+
item.focus({ focusVisible: true });
|
|
86
|
+
}
|
|
87
|
+
function createEngine() {
|
|
88
|
+
let current = null;
|
|
89
|
+
let latestOptions = null;
|
|
90
|
+
/**
|
|
91
|
+
* Elements this engine wrote `tabindex` on, with the attribute value they had before the first
|
|
92
|
+
* write (`null`: none). Restored when the engine lets go of an element (native path, element
|
|
93
|
+
* left the container): React does not re-write a `tabIndex` prop it believes it rendered, so a
|
|
94
|
+
* `[tabindex="0"]` widget would otherwise become unreachable.
|
|
95
|
+
*/
|
|
96
|
+
const managed = /* @__PURE__ */ new Map();
|
|
97
|
+
const restore = (el) => {
|
|
98
|
+
const original = managed.get(el);
|
|
99
|
+
managed.delete(el);
|
|
100
|
+
if (!el.isConnected || original === void 0) return;
|
|
101
|
+
if (original === null) el.removeAttribute("tabindex");
|
|
102
|
+
else el.setAttribute("tabindex", original);
|
|
103
|
+
};
|
|
104
|
+
const active = (options) => options !== null && options.enabled !== false;
|
|
105
|
+
/** Native path: give back every `tabindex` this engine wrote (a hydration render may have). */
|
|
106
|
+
const release = () => {
|
|
107
|
+
for (const el of [...managed.keys()]) restore(el);
|
|
108
|
+
current = null;
|
|
109
|
+
};
|
|
110
|
+
const apply = (root, focused) => {
|
|
111
|
+
const options = latestOptions;
|
|
112
|
+
if (root === null || !active(options)) return;
|
|
113
|
+
if (options.native) {
|
|
114
|
+
release();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const items = itemsOf(root, managed);
|
|
118
|
+
if (!options.memory) current = focused instanceof HTMLElement && items.includes(focused) ? focused : options.entry(items) ?? null;
|
|
119
|
+
else if (current === null || !items.includes(current)) current = options.entry(items) ?? null;
|
|
120
|
+
for (const el of items) {
|
|
121
|
+
if (!managed.has(el)) managed.set(el, el.getAttribute("tabindex"));
|
|
122
|
+
const want = el === current ? "0" : "-1";
|
|
123
|
+
if (el.getAttribute("tabindex") !== want) el.setAttribute("tabindex", want);
|
|
124
|
+
}
|
|
125
|
+
for (const el of [...managed.keys()]) {
|
|
126
|
+
if (items.includes(el)) continue;
|
|
127
|
+
if (!root.contains(el)) restore(el);
|
|
128
|
+
else if (el.getAttribute("tabindex") !== "-1") el.setAttribute("tabindex", "-1");
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
const onFocus = (event) => {
|
|
132
|
+
const options = latestOptions;
|
|
133
|
+
if (!active(options) || options.native) return;
|
|
134
|
+
const target = event.target;
|
|
135
|
+
if (!(target instanceof HTMLElement)) return;
|
|
136
|
+
if (!itemsOf(event.currentTarget, managed).includes(target)) return;
|
|
137
|
+
current = target;
|
|
138
|
+
apply(event.currentTarget, target);
|
|
139
|
+
};
|
|
140
|
+
const onBlur = (event) => {
|
|
141
|
+
const options = latestOptions;
|
|
142
|
+
if (!active(options) || options.native || options.memory) return;
|
|
143
|
+
const next = event.relatedTarget;
|
|
144
|
+
if (next instanceof Node && event.currentTarget.contains(next)) return;
|
|
145
|
+
apply(event.currentTarget, null);
|
|
146
|
+
};
|
|
147
|
+
const onPointerDown = (event) => {
|
|
148
|
+
const options = latestOptions;
|
|
149
|
+
if (!active(options) || options.native) return;
|
|
150
|
+
const target = event.target;
|
|
151
|
+
if (!(target instanceof Node)) return;
|
|
152
|
+
const item = itemsOf(event.currentTarget, managed).find((el) => el.contains(target));
|
|
153
|
+
if (item === void 0) return;
|
|
154
|
+
current = item;
|
|
155
|
+
apply(event.currentTarget, options.memory ? item : event.currentTarget.ownerDocument.activeElement);
|
|
156
|
+
};
|
|
157
|
+
const onKeyDown = (event) => {
|
|
158
|
+
if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey) return;
|
|
159
|
+
const options = latestOptions;
|
|
160
|
+
if (!active(options) || options.native) return;
|
|
161
|
+
const root = event.currentTarget;
|
|
162
|
+
const target = event.target;
|
|
163
|
+
if (!(target instanceof HTMLElement)) return;
|
|
164
|
+
const items = itemsOf(root, managed);
|
|
165
|
+
const index = items.indexOf(target);
|
|
166
|
+
if (index < 0) return;
|
|
167
|
+
if (isTextEntryItem(target)) {
|
|
168
|
+
if (event.key !== "Tab") return;
|
|
169
|
+
const next = items[index + (event.shiftKey ? -1 : 1)];
|
|
170
|
+
if (next === void 0) return;
|
|
171
|
+
event.preventDefault();
|
|
172
|
+
focusItem(next);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const rtl = options.orientation === "horizontal" && getComputedStyle(root).direction === "rtl";
|
|
176
|
+
const move = resolveKey(event.key, options.orientation, rtl);
|
|
177
|
+
if (move === null) return;
|
|
178
|
+
event.preventDefault();
|
|
179
|
+
const last = items.length - 1;
|
|
180
|
+
let nextIndex;
|
|
181
|
+
switch (move) {
|
|
182
|
+
case "first":
|
|
183
|
+
nextIndex = 0;
|
|
184
|
+
break;
|
|
185
|
+
case "last":
|
|
186
|
+
nextIndex = last;
|
|
187
|
+
break;
|
|
188
|
+
case "next":
|
|
189
|
+
nextIndex = index >= last ? options.loop ? 0 : last : index + 1;
|
|
190
|
+
break;
|
|
191
|
+
case "prev": nextIndex = index <= 0 ? options.loop ? last : 0 : index - 1;
|
|
192
|
+
}
|
|
193
|
+
const next = items[nextIndex];
|
|
194
|
+
if (next !== void 0 && next !== target) focusItem(next);
|
|
195
|
+
};
|
|
196
|
+
return {
|
|
197
|
+
setOptions: (options) => {
|
|
198
|
+
latestOptions = options;
|
|
199
|
+
},
|
|
200
|
+
apply,
|
|
201
|
+
handlers: {
|
|
202
|
+
onFocus,
|
|
203
|
+
onBlur,
|
|
204
|
+
onPointerDown,
|
|
205
|
+
onKeyDown
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Roving focus over the focusable descendants of `ref`'s element (design 6.2). Writes `tabindex`
|
|
211
|
+
* (`0` on the Tab stop, `-1` on the other items) from a layout effect after every commit of the
|
|
212
|
+
* calling component, from a `MutationObserver` on the subtree and from focus / pointer events;
|
|
213
|
+
* the items render none. Returns the container's handlers for its library props.
|
|
214
|
+
*/
|
|
215
|
+
function useRovingFocus(ref, options) {
|
|
216
|
+
const [engine] = useState(createEngine);
|
|
217
|
+
useInsertionEffect(() => {
|
|
218
|
+
engine.setOptions(options);
|
|
219
|
+
});
|
|
220
|
+
const enabled = options.enabled !== false;
|
|
221
|
+
useLayoutEffect(() => {
|
|
222
|
+
const root = ref.current;
|
|
223
|
+
engine.apply(root, root?.ownerDocument.activeElement ?? null);
|
|
224
|
+
});
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
const root = ref.current;
|
|
227
|
+
if (!enabled || root === null) return;
|
|
228
|
+
const observer = new MutationObserver(() => {
|
|
229
|
+
engine.apply(root, root.ownerDocument.activeElement);
|
|
230
|
+
});
|
|
231
|
+
observer.observe(root, {
|
|
232
|
+
childList: true,
|
|
233
|
+
subtree: true,
|
|
234
|
+
attributes: true,
|
|
235
|
+
attributeFilter: [
|
|
236
|
+
"disabled",
|
|
237
|
+
"hidden",
|
|
238
|
+
"inert",
|
|
239
|
+
"aria-disabled"
|
|
240
|
+
]
|
|
241
|
+
});
|
|
242
|
+
return () => {
|
|
243
|
+
observer.disconnect();
|
|
244
|
+
};
|
|
245
|
+
}, [
|
|
246
|
+
engine,
|
|
247
|
+
enabled,
|
|
248
|
+
ref
|
|
249
|
+
]);
|
|
250
|
+
return engine.handlers;
|
|
251
|
+
}
|
|
252
|
+
//#endregion
|
|
253
|
+
export { isTextEntryItem, itemsOf, useRovingFocus };
|
|
254
|
+
|
|
255
|
+
//# sourceMappingURL=use-roving-focus.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-roving-focus.mjs","names":[],"sources":["../../src/internal/use-roving-focus.ts"],"sourcesContent":["\"use client\";\nimport { useEffect, useInsertionEffect, useLayoutEffect, useState } from \"react\";\nimport type { FocusEvent, KeyboardEvent, PointerEvent, RefObject } from \"react\";\n\n// The roving-focus engine shared by `Toolbar.Root` and a standalone `ToggleGroup.Root`\n// (design/components/toggle-group.md section 6, conventions 3.2 and 7.4). Items are the focusable\n// descendants of the container, found in the DOM; which one is the Tab stop is urgent library\n// state kept outside React and written to the items as `tabindex` through the DOM.\n\nexport type RovingOrientation = \"horizontal\" | \"vertical\";\n\nexport interface RovingFocusOptions {\n /** `false`: the engine writes nothing and handles nothing (a ToggleGroup inside a Toolbar). Default `true`. */\n readonly enabled?: boolean;\n readonly orientation: RovingOrientation;\n readonly loop: boolean;\n /** The native `focusgroup` path is active: write no `tabindex`, handle no navigation key. */\n readonly native: boolean;\n /** Which item is the Tab stop when there is no usable memory. */\n readonly entry: (items: readonly HTMLElement[]) => HTMLElement | undefined;\n /**\n * `true`: the last focused item stays the Tab stop after focus leaves (Toolbar). `false`: while\n * focus is inside, the focused item is the stop (so Tab and Shift+Tab leave the container);\n * once it leaves, the stop is re-derived from `entry` (ToggleGroup: the pressed Item).\n */\n readonly memory: boolean;\n}\n\nexport interface RovingFocusHandlers {\n readonly onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;\n readonly onFocus: (event: FocusEvent<HTMLElement>) => void;\n readonly onBlur: (event: FocusEvent<HTMLElement>) => void;\n readonly onPointerDown: (event: PointerEvent<HTMLElement>) => void;\n}\n\nconst FOCUSABLE = [\n \"button\",\n \"a[href]\",\n 'input:not([type=\"hidden\"])',\n \"select\",\n \"textarea\",\n \"summary\",\n '[contenteditable]:not([contenteditable=\"false\"])',\n \"[tabindex]\",\n].join(\",\");\n\nfunction isVisible(el: HTMLElement): boolean {\n // jsdom has no `checkVisibility`; `hidden` is the part of it a test can observe there.\n if (typeof el.checkVisibility === \"function\") return el.checkVisibility();\n return el.closest(\"[hidden]\") === null;\n}\n\n/**\n * Whether `el` or an ancestor below `root` takes it out of `root`'s sequence: a nested toolbar\n * (its own sequence), a popover or a dialog rendered inside the container (a `Menu.Popup`'s items\n * are the menu's).\n */\nfunction isNestedOut(el: HTMLElement, root: HTMLElement): boolean {\n for (let node: Element | null = el; node !== null && node !== root; node = node.parentElement) {\n if (\n node.getAttribute(\"role\") === \"toolbar\" ||\n node.hasAttribute(\"popover\") ||\n node.tagName === \"DIALOG\"\n ) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * The items of `root` in DOM order (design 6.2): its focusable descendants minus `:disabled`,\n * `inert`, invisible, nested-toolbar / popover / dialog content, and elements whose own\n * `tabindex=\"-1\"` was not written by this engine (`managed`). `aria-disabled` elements are items.\n */\nexport function itemsOf(root: HTMLElement, managed: { has(el: Element): boolean }): HTMLElement[] {\n const items: HTMLElement[] = [];\n for (const el of root.querySelectorAll<HTMLElement>(FOCUSABLE)) {\n if (el.matches(\":disabled\")) continue;\n if (el.closest(\"[inert]\") !== null) continue;\n if (el.getAttribute(\"tabindex\") === \"-1\" && !managed.has(el)) continue;\n if (isNestedOut(el, root)) continue;\n if (!isVisible(el)) continue;\n items.push(el);\n }\n return items;\n}\n\nconst BUTTON_LIKE_INPUTS = new Set([\n \"button\",\n \"submit\",\n \"reset\",\n \"checkbox\",\n \"radio\",\n \"image\",\n \"color\",\n \"file\",\n]);\nconst ARROW_OWNING_ROLES = new Set([\"textbox\", \"combobox\", \"spinbutton\", \"slider\"]);\n\n/**\n * An item that owns the arrow keys (design 6.3): text-like `input`s, `textarea`, `select`,\n * `[contenteditable]`, and `role` `textbox` / `combobox` / `spinbutton` / `slider`.\n */\nexport function isTextEntryItem(el: Element): boolean {\n const role = el.getAttribute(\"role\");\n if (role !== null && ARROW_OWNING_ROLES.has(role)) return true;\n if (el instanceof HTMLInputElement) return !BUTTON_LIKE_INPUTS.has(el.type);\n if (el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) return true;\n const editable = el.getAttribute(\"contenteditable\");\n if (editable !== null && editable !== \"false\") return true;\n return el instanceof HTMLElement && el.isContentEditable === true;\n}\n\ntype Move = \"next\" | \"prev\" | \"first\" | \"last\";\n\nfunction resolveKey(key: string, orientation: RovingOrientation, rtl: boolean): Move | null {\n switch (key) {\n case \"Home\":\n return \"first\";\n case \"End\":\n return \"last\";\n case \"ArrowRight\":\n return orientation === \"horizontal\" ? (rtl ? \"prev\" : \"next\") : null;\n case \"ArrowLeft\":\n return orientation === \"horizontal\" ? (rtl ? \"next\" : \"prev\") : null;\n case \"ArrowDown\":\n return orientation === \"vertical\" ? \"next\" : null;\n case \"ArrowUp\":\n return orientation === \"vertical\" ? \"prev\" : null;\n default:\n return null;\n }\n}\n\nfunction focusItem(item: HTMLElement): void {\n // Keyboard focus: the ring shows even where the engine's heuristic would not (as Tabs).\n item.focus({ focusVisible: true } as FocusOptions);\n}\n\ninterface Engine {\n /** The options of the last committed render (written from an insertion effect). */\n readonly setOptions: (options: RovingFocusOptions) => void;\n /**\n * Re-derives the Tab stop of `root` and writes `tabindex`. `focused`: the element focus is on,\n * if any.\n */\n readonly apply: (root: HTMLElement | null, focused: Element | null) => void;\n readonly handlers: RovingFocusHandlers;\n}\n\nfunction createEngine(): Engine {\n let current: HTMLElement | null = null;\n let latestOptions: RovingFocusOptions | null = null;\n /**\n * Elements this engine wrote `tabindex` on, with the attribute value they had before the first\n * write (`null`: none). Restored when the engine lets go of an element (native path, element\n * left the container): React does not re-write a `tabIndex` prop it believes it rendered, so a\n * `[tabindex=\"0\"]` widget would otherwise become unreachable.\n */\n const managed = new Map<HTMLElement, string | null>();\n\n const restore = (el: HTMLElement) => {\n const original = managed.get(el);\n managed.delete(el);\n if (!el.isConnected || original === undefined) return;\n if (original === null) el.removeAttribute(\"tabindex\");\n else el.setAttribute(\"tabindex\", original);\n };\n\n const active = (options: RovingFocusOptions | null): options is RovingFocusOptions =>\n options !== null && options.enabled !== false;\n\n /** Native path: give back every `tabindex` this engine wrote (a hydration render may have). */\n const release = () => {\n for (const el of [...managed.keys()]) restore(el);\n current = null;\n };\n\n const apply = (root: HTMLElement | null, focused: Element | null) => {\n const options = latestOptions;\n if (root === null || !active(options)) return;\n if (options.native) {\n release();\n return;\n }\n const items = itemsOf(root, managed);\n if (!options.memory) {\n current =\n focused instanceof HTMLElement && items.includes(focused)\n ? focused\n : (options.entry(items) ?? null);\n } else if (current === null || !items.includes(current)) {\n current = options.entry(items) ?? null;\n }\n for (const el of items) {\n if (!managed.has(el)) managed.set(el, el.getAttribute(\"tabindex\"));\n const want = el === current ? \"0\" : \"-1\";\n if (el.getAttribute(\"tabindex\") !== want) el.setAttribute(\"tabindex\", want);\n }\n // Elements written before that are no longer items: a stale `0` must not stay a Tab stop\n // (it would be one again when re-enabled); one that left the container gets its own back.\n for (const el of [...managed.keys()]) {\n if (items.includes(el)) continue;\n if (!root.contains(el)) {\n restore(el);\n } else if (el.getAttribute(\"tabindex\") !== \"-1\") {\n el.setAttribute(\"tabindex\", \"-1\");\n }\n }\n };\n\n const onFocus = (event: FocusEvent<HTMLElement>) => {\n const options = latestOptions;\n if (!active(options) || options.native) return;\n const target = event.target;\n if (!(target instanceof HTMLElement)) return;\n const items = itemsOf(event.currentTarget, managed);\n if (!items.includes(target)) return;\n current = target;\n apply(event.currentTarget, target);\n };\n\n const onBlur = (event: FocusEvent<HTMLElement>) => {\n const options = latestOptions;\n if (!active(options) || options.native || options.memory) return;\n // Focus moving to another item is handled by its focus event.\n const next = event.relatedTarget;\n if (next instanceof Node && event.currentTarget.contains(next)) return;\n apply(event.currentTarget, null);\n };\n\n const onPointerDown = (event: PointerEvent<HTMLElement>) => {\n const options = latestOptions;\n if (!active(options) || options.native) return;\n const target = event.target;\n if (!(target instanceof Node)) return;\n const items = itemsOf(event.currentTarget, managed);\n // Safari does not focus a button on click; the pointer still makes it the stop.\n const item = items.find((el) => el.contains(target));\n if (item === undefined) return;\n current = item;\n apply(\n event.currentTarget,\n options.memory ? item : event.currentTarget.ownerDocument.activeElement,\n );\n };\n\n const onKeyDown = (event: KeyboardEvent<HTMLElement>) => {\n if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey) return;\n const options = latestOptions;\n // The browser moves focus itself where `focusgroup` is implemented.\n if (!active(options) || options.native) return;\n const root = event.currentTarget;\n const target = event.target;\n if (!(target instanceof HTMLElement)) return;\n const items = itemsOf(root, managed);\n const index = items.indexOf(target);\n if (index < 0) return;\n\n if (isTextEntryItem(target)) {\n // The item keeps its arrows, Home and End; Tab / Shift+Tab move to the next / previous item\n // while one exists, as Chromium's `focusgroup=\"toolbar\"` does (design 6.3).\n if (event.key !== \"Tab\") return;\n const next = items[index + (event.shiftKey ? -1 : 1)];\n if (next === undefined) return;\n event.preventDefault();\n focusItem(next);\n return;\n }\n\n const rtl = options.orientation === \"horizontal\" && getComputedStyle(root).direction === \"rtl\";\n const move = resolveKey(event.key, options.orientation, rtl);\n if (move === null) return;\n event.preventDefault();\n const last = items.length - 1;\n let nextIndex: number;\n switch (move) {\n case \"first\":\n nextIndex = 0;\n break;\n case \"last\":\n nextIndex = last;\n break;\n case \"next\":\n nextIndex = index >= last ? (options.loop ? 0 : last) : index + 1;\n break;\n case \"prev\":\n nextIndex = index <= 0 ? (options.loop ? last : 0) : index - 1;\n break;\n }\n const next = items[nextIndex];\n if (next !== undefined && next !== target) focusItem(next);\n };\n\n return {\n setOptions: (options) => {\n latestOptions = options;\n },\n apply,\n handlers: { onFocus, onBlur, onPointerDown, onKeyDown },\n };\n}\n\n/**\n * Roving focus over the focusable descendants of `ref`'s element (design 6.2). Writes `tabindex`\n * (`0` on the Tab stop, `-1` on the other items) from a layout effect after every commit of the\n * calling component, from a `MutationObserver` on the subtree and from focus / pointer events;\n * the items render none. Returns the container's handlers for its library props.\n */\nexport function useRovingFocus(\n ref: RefObject<HTMLElement | null>,\n options: RovingFocusOptions,\n): RovingFocusHandlers {\n const [engine] = useState(createEngine);\n // Before every layout effect of the same commit (as `useLatest`): handlers and the observer\n // read the options of the last committed render.\n useInsertionEffect(() => {\n engine.setOptions(options);\n });\n const enabled = options.enabled !== false;\n\n // After every commit of the container (a deps-less layout effect).\n useLayoutEffect(() => {\n const root = ref.current;\n engine.apply(root, root?.ownerDocument.activeElement ?? null);\n });\n\n // Children that change without the container re-rendering.\n useEffect(() => {\n const root = ref.current;\n if (!enabled || root === null) return;\n const observer = new MutationObserver(() => {\n engine.apply(root, root.ownerDocument.activeElement);\n });\n observer.observe(root, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"disabled\", \"hidden\", \"inert\", \"aria-disabled\"],\n });\n return () => {\n observer.disconnect();\n };\n }, [engine, enabled, ref]);\n\n return engine.handlers;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/internal/validity.d.ts
|
|
2
|
+
/** A field error: a message, several messages, or nothing (`""`, `[]`, `null`, `undefined`). */
|
|
3
|
+
export type FieldErrorValue = string | readonly string[] | null | undefined;
|
|
4
|
+
/** Field errors keyed by field `name` (usually `state.errors` from `useActionState`). */
|
|
5
|
+
export type FormErrors = Readonly<Record<string, FieldErrorValue>>;
|
|
6
|
+
/**
|
|
7
|
+
* A plain, serialisable snapshot of the control's `ValidityState` (all `false` and
|
|
8
|
+
* `valid: true` on the server and before the first read).
|
|
9
|
+
*/
|
|
10
|
+
export interface FieldValidity {
|
|
11
|
+
readonly valueMissing: boolean;
|
|
12
|
+
readonly typeMismatch: boolean;
|
|
13
|
+
readonly patternMismatch: boolean;
|
|
14
|
+
readonly tooLong: boolean;
|
|
15
|
+
readonly tooShort: boolean;
|
|
16
|
+
readonly rangeUnderflow: boolean;
|
|
17
|
+
readonly rangeOverflow: boolean;
|
|
18
|
+
readonly stepMismatch: boolean;
|
|
19
|
+
readonly badInput: boolean;
|
|
20
|
+
readonly customError: boolean;
|
|
21
|
+
readonly valid: boolean;
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
//# sourceMappingURL=validity.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validity.d.mts","names":[],"sources":["../../src/internal/validity.ts"],"mappings":";;YAIY;;YAGA,aAAa,SAAS,eAAe;;;;;iBAMhC;WACN;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA"}
|