@vielzeug/craftit 2.0.1 → 3.0.1
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/README.md +60 -108
- package/dist/controls/a11y-control.cjs +2 -0
- package/dist/controls/a11y-control.cjs.map +1 -0
- package/dist/controls/a11y-control.d.ts +16 -0
- package/dist/controls/a11y-control.d.ts.map +1 -0
- package/dist/controls/a11y-control.js +2 -0
- package/dist/controls/a11y-control.js.map +1 -0
- package/dist/controls/checkable-control.cjs +2 -0
- package/dist/controls/checkable-control.cjs.map +1 -0
- package/dist/controls/checkable-control.d.ts +15 -0
- package/dist/controls/checkable-control.d.ts.map +1 -0
- package/dist/controls/checkable-control.js +2 -0
- package/dist/controls/checkable-control.js.map +1 -0
- package/dist/controls/choice-field-control.cjs +2 -0
- package/dist/controls/choice-field-control.cjs.map +1 -0
- package/dist/controls/choice-field-control.d.ts +3 -0
- package/dist/controls/choice-field-control.d.ts.map +1 -0
- package/dist/controls/choice-field-control.js +2 -0
- package/dist/controls/choice-field-control.js.map +1 -0
- package/dist/controls/field-control.cjs +2 -0
- package/dist/controls/field-control.cjs.map +1 -0
- package/dist/controls/field-control.d.ts +111 -0
- package/dist/controls/field-control.d.ts.map +1 -0
- package/dist/controls/field-control.js +2 -0
- package/dist/controls/field-control.js.map +1 -0
- package/dist/controls/index.d.ts +12 -0
- package/dist/controls/index.d.ts.map +1 -0
- package/dist/controls/internal/control-state.cjs +2 -0
- package/dist/controls/internal/control-state.cjs.map +1 -0
- package/dist/controls/internal/control-state.d.ts +21 -0
- package/dist/controls/internal/control-state.d.ts.map +1 -0
- package/dist/controls/internal/control-state.js +2 -0
- package/dist/controls/internal/control-state.js.map +1 -0
- package/dist/controls/internal/keyboard-utils.cjs +2 -0
- package/dist/controls/internal/keyboard-utils.cjs.map +1 -0
- package/dist/controls/internal/keyboard-utils.d.ts +7 -0
- package/dist/controls/internal/keyboard-utils.d.ts.map +1 -0
- package/dist/controls/internal/keyboard-utils.js +2 -0
- package/dist/controls/internal/keyboard-utils.js.map +1 -0
- package/dist/controls/internal/number-utils.cjs +2 -0
- package/dist/controls/internal/number-utils.cjs.map +1 -0
- package/dist/controls/internal/number-utils.d.ts +6 -0
- package/dist/controls/internal/number-utils.d.ts.map +1 -0
- package/dist/controls/internal/number-utils.js +2 -0
- package/dist/controls/internal/number-utils.js.map +1 -0
- package/dist/controls/internal/validation-utils.cjs +2 -0
- package/dist/controls/internal/validation-utils.cjs.map +1 -0
- package/dist/controls/internal/validation-utils.d.ts +13 -0
- package/dist/controls/internal/validation-utils.d.ts.map +1 -0
- package/dist/controls/internal/validation-utils.js +2 -0
- package/dist/controls/internal/validation-utils.js.map +1 -0
- package/dist/controls/list-control.cjs +2 -0
- package/dist/controls/list-control.cjs.map +1 -0
- package/dist/controls/list-control.d.ts +23 -0
- package/dist/controls/list-control.d.ts.map +1 -0
- package/dist/controls/list-control.js +2 -0
- package/dist/controls/list-control.js.map +1 -0
- package/dist/controls/overlay-control.cjs +2 -0
- package/dist/controls/overlay-control.cjs.map +1 -0
- package/dist/{labs/overlay.d.ts → controls/overlay-control.d.ts} +19 -14
- package/dist/controls/overlay-control.d.ts.map +1 -0
- package/dist/controls/overlay-control.js +2 -0
- package/dist/controls/overlay-control.js.map +1 -0
- package/dist/controls/popup-list-control.cjs +2 -0
- package/dist/controls/popup-list-control.cjs.map +1 -0
- package/dist/controls/popup-list-control.d.ts +160 -0
- package/dist/controls/popup-list-control.d.ts.map +1 -0
- package/dist/controls/popup-list-control.js +2 -0
- package/dist/controls/popup-list-control.js.map +1 -0
- package/dist/controls/press-control.cjs +2 -0
- package/dist/controls/press-control.cjs.map +1 -0
- package/dist/controls/press-control.d.ts +12 -0
- package/dist/controls/press-control.d.ts.map +1 -0
- package/dist/controls/press-control.js +2 -0
- package/dist/controls/press-control.js.map +1 -0
- package/dist/controls/slider-control.cjs +2 -0
- package/dist/controls/slider-control.cjs.map +1 -0
- package/dist/controls/slider-control.d.ts +19 -0
- package/dist/controls/slider-control.d.ts.map +1 -0
- package/dist/controls/slider-control.js +2 -0
- package/dist/controls/slider-control.js.map +1 -0
- package/dist/controls/spinner-control.cjs +2 -0
- package/dist/controls/spinner-control.cjs.map +1 -0
- package/dist/controls/spinner-control.d.ts +18 -0
- package/dist/controls/spinner-control.d.ts.map +1 -0
- package/dist/controls/spinner-control.js +2 -0
- package/dist/controls/spinner-control.js.map +1 -0
- package/dist/controls/swipe-control.cjs +2 -0
- package/dist/controls/swipe-control.cjs.map +1 -0
- package/dist/controls/swipe-control.d.ts +32 -0
- package/dist/controls/swipe-control.d.ts.map +1 -0
- package/dist/controls/swipe-control.js +2 -0
- package/dist/controls/swipe-control.js.map +1 -0
- package/dist/controls/text-field-control.cjs +2 -0
- package/dist/controls/text-field-control.cjs.map +1 -0
- package/dist/controls/text-field-control.d.ts +3 -0
- package/dist/controls/text-field-control.d.ts.map +1 -0
- package/dist/controls/text-field-control.js +2 -0
- package/dist/controls/text-field-control.js.map +1 -0
- package/dist/controls.cjs +1 -0
- package/dist/controls.js +1 -0
- package/dist/craftit.cjs +1 -1
- package/dist/craftit.cjs.map +1 -1
- package/dist/craftit.js +1 -1
- package/dist/craftit.js.map +1 -1
- package/dist/directives/classMap.cjs +2 -0
- package/dist/directives/classMap.cjs.map +1 -0
- package/dist/directives/classMap.d.ts +19 -0
- package/dist/directives/classMap.d.ts.map +1 -0
- package/dist/directives/classMap.js +2 -0
- package/dist/directives/classMap.js.map +1 -0
- package/dist/directives/each.cjs +1 -1
- package/dist/directives/each.cjs.map +1 -1
- package/dist/directives/each.d.ts +12 -49
- package/dist/directives/each.d.ts.map +1 -1
- package/dist/directives/each.js +1 -1
- package/dist/directives/each.js.map +1 -1
- package/dist/directives/guard.cjs +2 -0
- package/dist/directives/guard.cjs.map +1 -0
- package/dist/directives/guard.d.ts +10 -0
- package/dist/directives/guard.d.ts.map +1 -0
- package/dist/directives/guard.js +2 -0
- package/dist/directives/guard.js.map +1 -0
- package/dist/directives/live.cjs +2 -0
- package/dist/directives/live.cjs.map +1 -0
- package/dist/directives/live.d.ts +23 -0
- package/dist/directives/live.d.ts.map +1 -0
- package/dist/directives/live.js +2 -0
- package/dist/directives/live.js.map +1 -0
- package/dist/directives/raw.cjs +1 -1
- package/dist/directives/raw.cjs.map +1 -1
- package/dist/directives/raw.d.ts +4 -6
- package/dist/directives/raw.d.ts.map +1 -1
- package/dist/directives/raw.js +1 -1
- package/dist/directives/raw.js.map +1 -1
- package/dist/directives/resource.cjs +2 -0
- package/dist/directives/resource.cjs.map +1 -0
- package/dist/directives/resource.d.ts +32 -0
- package/dist/directives/resource.d.ts.map +1 -0
- package/dist/directives/resource.js +2 -0
- package/dist/directives/resource.js.map +1 -0
- package/dist/directives/styleMap.cjs +2 -0
- package/dist/directives/styleMap.cjs.map +1 -0
- package/dist/directives/styleMap.d.ts +11 -0
- package/dist/directives/styleMap.d.ts.map +1 -0
- package/dist/directives/styleMap.js +2 -0
- package/dist/directives/styleMap.js.map +1 -0
- package/dist/directives/when.cjs +1 -1
- package/dist/directives/when.cjs.map +1 -1
- package/dist/directives/when.d.ts +7 -14
- package/dist/directives/when.d.ts.map +1 -1
- package/dist/directives/when.js +1 -1
- package/dist/directives/when.js.map +1 -1
- package/dist/errors.cjs +2 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +2 -0
- package/dist/errors.js.map +1 -0
- package/dist/form.cjs +2 -0
- package/dist/form.cjs.map +1 -0
- package/dist/form.d.ts +15 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +2 -0
- package/dist/form.js.map +1 -0
- package/dist/host.cjs +2 -0
- package/dist/host.cjs.map +1 -0
- package/dist/host.d.ts +78 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +2 -0
- package/dist/host.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +16 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/internal.cjs +2 -0
- package/dist/internal.cjs.map +1 -0
- package/dist/internal.d.ts +111 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +2 -0
- package/dist/internal.js.map +1 -0
- package/dist/observers/index.d.ts +5 -0
- package/dist/observers/index.d.ts.map +1 -0
- package/dist/observers/intersection-observe.cjs +2 -0
- package/dist/observers/intersection-observe.cjs.map +1 -0
- package/dist/observers/intersection-observe.d.ts +9 -0
- package/dist/observers/intersection-observe.d.ts.map +1 -0
- package/dist/observers/intersection-observe.js +2 -0
- package/dist/observers/intersection-observe.js.map +1 -0
- package/dist/observers/media-observe.cjs +2 -0
- package/dist/observers/media-observe.cjs.map +1 -0
- package/dist/observers/media-observe.d.ts +8 -0
- package/dist/observers/media-observe.d.ts.map +1 -0
- package/dist/observers/media-observe.js +2 -0
- package/dist/observers/media-observe.js.map +1 -0
- package/dist/observers/mutation-observe.cjs +2 -0
- package/dist/observers/mutation-observe.cjs.map +1 -0
- package/dist/observers/mutation-observe.d.ts +10 -0
- package/dist/observers/mutation-observe.d.ts.map +1 -0
- package/dist/observers/mutation-observe.js +2 -0
- package/dist/observers/mutation-observe.js.map +1 -0
- package/dist/observers/resize-observe.cjs +2 -0
- package/dist/observers/resize-observe.cjs.map +1 -0
- package/dist/observers/resize-observe.d.ts +11 -0
- package/dist/observers/resize-observe.d.ts.map +1 -0
- package/dist/observers/resize-observe.js +2 -0
- package/dist/observers/resize-observe.js.map +1 -0
- package/dist/observers.cjs +1 -0
- package/dist/observers.js +1 -0
- package/dist/props.cjs +2 -0
- package/dist/props.cjs.map +1 -0
- package/dist/props.d.ts +39 -0
- package/dist/props.d.ts.map +1 -0
- package/dist/props.js +2 -0
- package/dist/props.js.map +1 -0
- package/dist/registration.cjs +2 -0
- package/dist/registration.cjs.map +1 -0
- package/dist/registration.d.ts +38 -0
- package/dist/registration.d.ts.map +1 -0
- package/dist/registration.js +2 -0
- package/dist/registration.js.map +1 -0
- package/dist/runtime.cjs +2 -0
- package/dist/runtime.cjs.map +1 -0
- package/dist/runtime.d.ts +33 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +2 -0
- package/dist/runtime.js.map +1 -0
- package/dist/template-bindings.cjs +2 -0
- package/dist/template-bindings.cjs.map +1 -0
- package/dist/template-bindings.d.ts +25 -0
- package/dist/template-bindings.d.ts.map +1 -0
- package/dist/template-bindings.js +2 -0
- package/dist/template-bindings.js.map +1 -0
- package/dist/template-compiler.cjs +2 -0
- package/dist/template-compiler.cjs.map +1 -0
- package/dist/template-compiler.d.ts +4 -0
- package/dist/template-compiler.d.ts.map +1 -0
- package/dist/template-compiler.js +2 -0
- package/dist/template-compiler.js.map +1 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/testing.cjs +2 -0
- package/dist/testing/testing.cjs.map +1 -0
- package/dist/{test/test.d.ts → testing/testing.d.ts} +18 -11
- package/dist/testing/testing.d.ts.map +1 -0
- package/dist/testing/testing.js +2 -0
- package/dist/testing/testing.js.map +1 -0
- package/dist/testing.cjs +1 -0
- package/dist/testing.js +1 -0
- package/package.json +21 -22
- package/dist/core/component.cjs +0 -2
- package/dist/core/component.cjs.map +0 -1
- package/dist/core/component.d.ts +0 -172
- package/dist/core/component.d.ts.map +0 -1
- package/dist/core/component.js +0 -2
- package/dist/core/component.js.map +0 -1
- package/dist/core/host.cjs +0 -2
- package/dist/core/host.cjs.map +0 -1
- package/dist/core/host.d.ts +0 -77
- package/dist/core/host.d.ts.map +0 -1
- package/dist/core/host.js +0 -2
- package/dist/core/host.js.map +0 -1
- package/dist/core/internal.cjs +0 -2
- package/dist/core/internal.cjs.map +0 -1
- package/dist/core/internal.d.ts +0 -107
- package/dist/core/internal.d.ts.map +0 -1
- package/dist/core/internal.js +0 -2
- package/dist/core/internal.js.map +0 -1
- package/dist/core/runtime-bindings.cjs +0 -2
- package/dist/core/runtime-bindings.cjs.map +0 -1
- package/dist/core/runtime-bindings.d.ts +0 -6
- package/dist/core/runtime-bindings.d.ts.map +0 -1
- package/dist/core/runtime-bindings.js +0 -2
- package/dist/core/runtime-bindings.js.map +0 -1
- package/dist/core/runtime-lifecycle.cjs +0 -2
- package/dist/core/runtime-lifecycle.cjs.map +0 -1
- package/dist/core/runtime-lifecycle.d.ts +0 -116
- package/dist/core/runtime-lifecycle.d.ts.map +0 -1
- package/dist/core/runtime-lifecycle.js +0 -2
- package/dist/core/runtime-lifecycle.js.map +0 -1
- package/dist/core/runtime.cjs +0 -1
- package/dist/core/runtime.d.ts +0 -3
- package/dist/core/runtime.d.ts.map +0 -1
- package/dist/core/runtime.js +0 -1
- package/dist/core/template-bindings.cjs +0 -2
- package/dist/core/template-bindings.cjs.map +0 -1
- package/dist/core/template-bindings.d.ts +0 -59
- package/dist/core/template-bindings.d.ts.map +0 -1
- package/dist/core/template-bindings.js +0 -2
- package/dist/core/template-bindings.js.map +0 -1
- package/dist/core/template-compiler.cjs +0 -2
- package/dist/core/template-compiler.cjs.map +0 -1
- package/dist/core/template-compiler.d.ts +0 -25
- package/dist/core/template-compiler.d.ts.map +0 -1
- package/dist/core/template-compiler.js +0 -2
- package/dist/core/template-compiler.js.map +0 -1
- package/dist/core/template-dom.cjs +0 -2
- package/dist/core/template-dom.cjs.map +0 -1
- package/dist/core/template-dom.d.ts +0 -13
- package/dist/core/template-dom.d.ts.map +0 -1
- package/dist/core/template-dom.js +0 -2
- package/dist/core/template-dom.js.map +0 -1
- package/dist/core/template-html.cjs +0 -2
- package/dist/core/template-html.cjs.map +0 -1
- package/dist/core/template-html.d.ts +0 -26
- package/dist/core/template-html.d.ts.map +0 -1
- package/dist/core/template-html.js +0 -2
- package/dist/core/template-html.js.map +0 -1
- package/dist/core/template.cjs +0 -2
- package/dist/core/template.cjs.map +0 -1
- package/dist/core/template.d.ts +0 -11
- package/dist/core/template.d.ts.map +0 -1
- package/dist/core/template.js +0 -2
- package/dist/core/template.js.map +0 -1
- package/dist/core/utilities.cjs +0 -2
- package/dist/core/utilities.cjs.map +0 -1
- package/dist/core/utilities.d.ts +0 -68
- package/dist/core/utilities.d.ts.map +0 -1
- package/dist/core/utilities.js +0 -2
- package/dist/core/utilities.js.map +0 -1
- package/dist/directives/attr.cjs +0 -2
- package/dist/directives/attr.cjs.map +0 -1
- package/dist/directives/attr.d.ts +0 -14
- package/dist/directives/attr.d.ts.map +0 -1
- package/dist/directives/attr.js +0 -2
- package/dist/directives/attr.js.map +0 -1
- package/dist/directives/bind.cjs +0 -2
- package/dist/directives/bind.cjs.map +0 -1
- package/dist/directives/bind.d.ts +0 -30
- package/dist/directives/bind.d.ts.map +0 -1
- package/dist/directives/bind.js +0 -2
- package/dist/directives/bind.js.map +0 -1
- package/dist/directives/choose.cjs +0 -2
- package/dist/directives/choose.cjs.map +0 -1
- package/dist/directives/choose.d.ts +0 -34
- package/dist/directives/choose.d.ts.map +0 -1
- package/dist/directives/choose.js +0 -2
- package/dist/directives/choose.js.map +0 -1
- package/dist/directives/classes.cjs +0 -2
- package/dist/directives/classes.cjs.map +0 -1
- package/dist/directives/classes.d.ts +0 -20
- package/dist/directives/classes.d.ts.map +0 -1
- package/dist/directives/classes.js +0 -2
- package/dist/directives/classes.js.map +0 -1
- package/dist/directives/index.cjs +0 -1
- package/dist/directives/index.d.ts +0 -14
- package/dist/directives/index.d.ts.map +0 -1
- package/dist/directives/index.js +0 -1
- package/dist/directives/match.cjs +0 -2
- package/dist/directives/match.cjs.map +0 -1
- package/dist/directives/match.d.ts +0 -31
- package/dist/directives/match.d.ts.map +0 -1
- package/dist/directives/match.js +0 -2
- package/dist/directives/match.js.map +0 -1
- package/dist/directives/memo.cjs +0 -2
- package/dist/directives/memo.cjs.map +0 -1
- package/dist/directives/memo.d.ts +0 -23
- package/dist/directives/memo.d.ts.map +0 -1
- package/dist/directives/memo.js +0 -2
- package/dist/directives/memo.js.map +0 -1
- package/dist/directives/on.cjs +0 -2
- package/dist/directives/on.cjs.map +0 -1
- package/dist/directives/on.d.ts +0 -25
- package/dist/directives/on.d.ts.map +0 -1
- package/dist/directives/on.js +0 -2
- package/dist/directives/on.js.map +0 -1
- package/dist/directives/spread.cjs +0 -2
- package/dist/directives/spread.cjs.map +0 -1
- package/dist/directives/spread.d.ts +0 -14
- package/dist/directives/spread.d.ts.map +0 -1
- package/dist/directives/spread.js +0 -2
- package/dist/directives/spread.js.map +0 -1
- package/dist/directives/style.cjs +0 -2
- package/dist/directives/style.cjs.map +0 -1
- package/dist/directives/style.d.ts +0 -22
- package/dist/directives/style.d.ts.map +0 -1
- package/dist/directives/style.js +0 -2
- package/dist/directives/style.js.map +0 -1
- package/dist/directives/until.cjs +0 -2
- package/dist/directives/until.cjs.map +0 -1
- package/dist/directives/until.d.ts +0 -26
- package/dist/directives/until.d.ts.map +0 -1
- package/dist/directives/until.js +0 -2
- package/dist/directives/until.js.map +0 -1
- package/dist/labs/a11y.cjs +0 -2
- package/dist/labs/a11y.cjs.map +0 -1
- package/dist/labs/a11y.d.ts +0 -61
- package/dist/labs/a11y.d.ts.map +0 -1
- package/dist/labs/a11y.js +0 -2
- package/dist/labs/a11y.js.map +0 -1
- package/dist/labs/index.d.ts +0 -8
- package/dist/labs/index.d.ts.map +0 -1
- package/dist/labs/list.cjs +0 -2
- package/dist/labs/list.cjs.map +0 -1
- package/dist/labs/list.d.ts +0 -26
- package/dist/labs/list.d.ts.map +0 -1
- package/dist/labs/list.js +0 -2
- package/dist/labs/list.js.map +0 -1
- package/dist/labs/observers.cjs +0 -2
- package/dist/labs/observers.cjs.map +0 -1
- package/dist/labs/observers.d.ts +0 -42
- package/dist/labs/observers.d.ts.map +0 -1
- package/dist/labs/observers.js +0 -2
- package/dist/labs/observers.js.map +0 -1
- package/dist/labs/overlay.cjs +0 -2
- package/dist/labs/overlay.cjs.map +0 -1
- package/dist/labs/overlay.d.ts.map +0 -1
- package/dist/labs/overlay.js +0 -2
- package/dist/labs/overlay.js.map +0 -1
- package/dist/labs/selectable.cjs +0 -2
- package/dist/labs/selectable.cjs.map +0 -1
- package/dist/labs/selectable.d.ts +0 -70
- package/dist/labs/selectable.d.ts.map +0 -1
- package/dist/labs/selectable.js +0 -2
- package/dist/labs/selectable.js.map +0 -1
- package/dist/labs/selection.cjs +0 -2
- package/dist/labs/selection.cjs.map +0 -1
- package/dist/labs/selection.d.ts +0 -68
- package/dist/labs/selection.d.ts.map +0 -1
- package/dist/labs/selection.js +0 -2
- package/dist/labs/selection.js.map +0 -1
- package/dist/labs.cjs +0 -1
- package/dist/labs.js +0 -1
- package/dist/test/index.d.ts +0 -2
- package/dist/test/index.d.ts.map +0 -1
- package/dist/test/test.cjs +0 -2
- package/dist/test/test.cjs.map +0 -1
- package/dist/test/test.d.ts.map +0 -1
- package/dist/test/test.js +0 -2
- package/dist/test/test.js.map +0 -1
- package/dist/test.cjs +0 -1
- package/dist/test.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control-state.cjs","names":[],"sources":["../../../src/controls/internal/control-state.ts"],"sourcesContent":["import { computed, type ReadonlySignal } from '@vielzeug/stateit';\n\nexport type FormControlValidationTrigger = 'blur' | 'change';\n\nexport type ControlValidationMode = 'blur' | 'change' | 'submit' | undefined;\n\nexport type ValidationReporter = {\n reportValidity: () => void;\n};\n\nexport type ControlContextOptions = {\n disabled?: ReadonlySignal<boolean | undefined>;\n validateOn?: ReadonlySignal<ControlValidationMode>;\n};\n\nexport type ControlStateOptions = {\n context?: ControlContextOptions;\n disabled?: ReadonlySignal<boolean | undefined>;\n validateOn?: ReadonlySignal<ControlValidationMode>;\n};\n\nexport const createControlState = (options: ControlStateOptions) => {\n const disabled = computed(() => Boolean(options.disabled?.value) || Boolean(options.context?.disabled?.value));\n\n const validateOn = options.validateOn ?? options.context?.validateOn;\n\n return {\n disabled,\n triggerValidation: (field: ValidationReporter, on: FormControlValidationTrigger): void => {\n if (validateOn?.value === on) field.reportValidity();\n },\n validateOn,\n };\n};\n"],"mappings":"mCAqBA,IAAa,EAAsB,GAAiC,CAClE,IAAM,GAAA,EAAA,EAAA,cAA0B,EAAQ,EAAQ,UAAU,OAAU,EAAQ,EAAQ,SAAS,UAAU,KAAM,EAEvG,EAAa,EAAQ,YAAc,EAAQ,SAAS,WAE1D,MAAO,CACL,WACA,mBAAoB,EAA2B,IAA2C,CACpF,GAAY,QAAU,GAAI,EAAM,eAAe,CACrD,EACA,YACF,CACF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ReadonlySignal } from '@vielzeug/stateit';
|
|
2
|
+
export type FormControlValidationTrigger = 'blur' | 'change';
|
|
3
|
+
export type ControlValidationMode = 'blur' | 'change' | 'submit' | undefined;
|
|
4
|
+
export type ValidationReporter = {
|
|
5
|
+
reportValidity: () => void;
|
|
6
|
+
};
|
|
7
|
+
export type ControlContextOptions = {
|
|
8
|
+
disabled?: ReadonlySignal<boolean | undefined>;
|
|
9
|
+
validateOn?: ReadonlySignal<ControlValidationMode>;
|
|
10
|
+
};
|
|
11
|
+
export type ControlStateOptions = {
|
|
12
|
+
context?: ControlContextOptions;
|
|
13
|
+
disabled?: ReadonlySignal<boolean | undefined>;
|
|
14
|
+
validateOn?: ReadonlySignal<ControlValidationMode>;
|
|
15
|
+
};
|
|
16
|
+
export declare const createControlState: (options: ControlStateOptions) => {
|
|
17
|
+
disabled: import("@vielzeug/stateit").ComputedSignal<boolean>;
|
|
18
|
+
triggerValidation: (field: ValidationReporter, on: FormControlValidationTrigger) => void;
|
|
19
|
+
validateOn: ReadonlySignal<ControlValidationMode> | undefined;
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=control-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control-state.d.ts","sourceRoot":"","sources":["../../../src/controls/internal/control-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAElE,MAAM,MAAM,4BAA4B,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE7D,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE7E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,EAAE,cAAc,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IAC/C,UAAU,CAAC,EAAE,cAAc,CAAC,qBAAqB,CAAC,CAAC;CACpD,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAChC,QAAQ,CAAC,EAAE,cAAc,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IAC/C,UAAU,CAAC,EAAE,cAAc,CAAC,qBAAqB,CAAC,CAAC;CACpD,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,SAAS,mBAAmB;;+BAOhC,kBAAkB,MAAM,4BAA4B,KAAG,IAAI;;CAKzF,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{computed as e}from"@vielzeug/stateit";var t=t=>{let n=e(()=>!!t.disabled?.value||!!t.context?.disabled?.value),r=t.validateOn??t.context?.validateOn;return{disabled:n,triggerValidation:(e,t)=>{r?.value===t&&e.reportValidity()},validateOn:r}};export{t as createControlState};
|
|
2
|
+
//# sourceMappingURL=control-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control-state.js","names":[],"sources":["../../../src/controls/internal/control-state.ts"],"sourcesContent":["import { computed, type ReadonlySignal } from '@vielzeug/stateit';\n\nexport type FormControlValidationTrigger = 'blur' | 'change';\n\nexport type ControlValidationMode = 'blur' | 'change' | 'submit' | undefined;\n\nexport type ValidationReporter = {\n reportValidity: () => void;\n};\n\nexport type ControlContextOptions = {\n disabled?: ReadonlySignal<boolean | undefined>;\n validateOn?: ReadonlySignal<ControlValidationMode>;\n};\n\nexport type ControlStateOptions = {\n context?: ControlContextOptions;\n disabled?: ReadonlySignal<boolean | undefined>;\n validateOn?: ReadonlySignal<ControlValidationMode>;\n};\n\nexport const createControlState = (options: ControlStateOptions) => {\n const disabled = computed(() => Boolean(options.disabled?.value) || Boolean(options.context?.disabled?.value));\n\n const validateOn = options.validateOn ?? options.context?.validateOn;\n\n return {\n disabled,\n triggerValidation: (field: ValidationReporter, on: FormControlValidationTrigger): void => {\n if (validateOn?.value === on) field.reportValidity();\n },\n validateOn,\n };\n};\n"],"mappings":"6CAqBA,IAAa,EAAsB,GAAiC,CAClE,IAAM,EAAW,MAAe,EAAQ,EAAQ,UAAU,OAAU,EAAQ,EAAQ,SAAS,UAAU,KAAM,EAEvG,EAAa,EAAQ,YAAc,EAAQ,SAAS,WAE1D,MAAO,CACL,WACA,mBAAoB,EAA2B,IAA2C,CACpF,GAAY,QAAU,GAAI,EAAM,eAAe,CACrD,EACA,YACF,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=(e,t)=>{if(t.disabled?.())return!1;let n=t.keymap[e.key];if(!n)return!1;let r=t.preventDefault??`before`;r===`before`&&e.preventDefault();let i=n(e)!==!1;return r===`after`&&i&&e.preventDefault(),i};exports.dispatchKeyboardAction=e;
|
|
2
|
+
//# sourceMappingURL=keyboard-utils.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard-utils.cjs","names":[],"sources":["../../../src/controls/internal/keyboard-utils.ts"],"sourcesContent":["export type KeyboardDispatchOptions = {\n disabled?: () => boolean;\n keymap: Record<string, (event: KeyboardEvent) => boolean | void>;\n preventDefault?: 'after' | 'before' | false;\n};\n\nexport const dispatchKeyboardAction = (event: KeyboardEvent, options: KeyboardDispatchOptions): boolean => {\n if (options.disabled?.()) return false;\n\n const action = options.keymap[event.key];\n\n if (!action) return false;\n\n const preventDefaultMode = options.preventDefault ?? 'before';\n\n if (preventDefaultMode === 'before') event.preventDefault();\n\n const handled = action(event) !== false;\n\n if (preventDefaultMode === 'after' && handled) event.preventDefault();\n\n return handled;\n};\n"],"mappings":"AAMA,IAAa,GAA0B,EAAsB,IAA8C,CACzG,GAAI,EAAQ,WAAW,EAAG,MAAO,GAEjC,IAAM,EAAS,EAAQ,OAAO,EAAM,KAEpC,GAAI,CAAC,EAAQ,MAAO,GAEpB,IAAM,EAAqB,EAAQ,gBAAkB,SAEjD,IAAuB,UAAU,EAAM,eAAe,EAE1D,IAAM,EAAU,EAAO,CAAK,IAAM,GAIlC,OAFI,IAAuB,SAAW,GAAS,EAAM,eAAe,EAE7D,CACT"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type KeyboardDispatchOptions = {
|
|
2
|
+
disabled?: () => boolean;
|
|
3
|
+
keymap: Record<string, (event: KeyboardEvent) => boolean | void>;
|
|
4
|
+
preventDefault?: 'after' | 'before' | false;
|
|
5
|
+
};
|
|
6
|
+
export declare const dispatchKeyboardAction: (event: KeyboardEvent, options: KeyboardDispatchOptions) => boolean;
|
|
7
|
+
//# sourceMappingURL=keyboard-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard-utils.d.ts","sourceRoot":"","sources":["../../../src/controls/internal/keyboard-utils.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,GAAG,IAAI,CAAC,CAAC;IACjE,cAAc,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;CAC7C,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,OAAO,aAAa,EAAE,SAAS,uBAAuB,KAAG,OAgB/F,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=(e,t)=>{if(t.disabled?.())return!1;let n=t.keymap[e.key];if(!n)return!1;let r=t.preventDefault??`before`;r===`before`&&e.preventDefault();let i=n(e)!==!1;return r===`after`&&i&&e.preventDefault(),i};export{e as dispatchKeyboardAction};
|
|
2
|
+
//# sourceMappingURL=keyboard-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard-utils.js","names":[],"sources":["../../../src/controls/internal/keyboard-utils.ts"],"sourcesContent":["export type KeyboardDispatchOptions = {\n disabled?: () => boolean;\n keymap: Record<string, (event: KeyboardEvent) => boolean | void>;\n preventDefault?: 'after' | 'before' | false;\n};\n\nexport const dispatchKeyboardAction = (event: KeyboardEvent, options: KeyboardDispatchOptions): boolean => {\n if (options.disabled?.()) return false;\n\n const action = options.keymap[event.key];\n\n if (!action) return false;\n\n const preventDefaultMode = options.preventDefault ?? 'before';\n\n if (preventDefaultMode === 'before') event.preventDefault();\n\n const handled = action(event) !== false;\n\n if (preventDefaultMode === 'after' && handled) event.preventDefault();\n\n return handled;\n};\n"],"mappings":"AAMA,IAAa,GAA0B,EAAsB,IAA8C,CACzG,GAAI,EAAQ,WAAW,EAAG,MAAO,GAEjC,IAAM,EAAS,EAAQ,OAAO,EAAM,KAEpC,GAAI,CAAC,EAAQ,MAAO,GAEpB,IAAM,EAAqB,EAAQ,gBAAkB,SAEjD,IAAuB,UAAU,EAAM,eAAe,EAE1D,IAAM,EAAU,EAAO,CAAK,IAAM,GAIlC,OAFI,IAAuB,SAAW,GAAS,EAAM,eAAe,EAE7D,CACT"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=e=>{let t=Number(e);return Number.isFinite(t)?t:void 0},t=(t,n)=>e(t)??n,n=(e,n)=>Math.abs(t(e,n))||n,r=(e,t)=>Number.isFinite(e)?e:t,i=(e,t,n)=>t!=null&&e<t?t:n!=null&&e>n?n:e;exports.clampNumber=i,exports.normalizeFinite=r,exports.toFiniteNumber=e,exports.toFiniteNumberOr=t,exports.toPositiveStep=n;
|
|
2
|
+
//# sourceMappingURL=number-utils.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"number-utils.cjs","names":[],"sources":["../../../src/controls/internal/number-utils.ts"],"sourcesContent":["export const toFiniteNumber = (value: unknown): number | undefined => {\n const parsed = Number(value);\n\n return Number.isFinite(parsed) ? parsed : undefined;\n};\n\nexport const toFiniteNumberOr = (value: unknown, fallback: number): number => {\n return toFiniteNumber(value) ?? fallback;\n};\n\nexport const toPositiveStep = (value: unknown, fallback: number): number => {\n return Math.abs(toFiniteNumberOr(value, fallback)) || fallback;\n};\n\nexport const normalizeFinite = (value: number, fallback: number): number => {\n return Number.isFinite(value) ? value : fallback;\n};\n\nexport const clampNumber = (value: number, min?: number, max?: number): number => {\n if (min != null && value < min) return min;\n\n if (max != null && value > max) return max;\n\n return value;\n};\n"],"mappings":"AAAA,IAAa,EAAkB,GAAuC,CACpE,IAAM,EAAS,OAAO,CAAK,EAE3B,OAAO,OAAO,SAAS,CAAM,EAAI,EAAS,IAAA,EAC5C,EAEa,GAAoB,EAAgB,IACxC,EAAe,CAAK,GAAK,EAGrB,GAAkB,EAAgB,IACtC,KAAK,IAAI,EAAiB,EAAO,CAAQ,CAAC,GAAK,EAG3C,GAAmB,EAAe,IACtC,OAAO,SAAS,CAAK,EAAI,EAAQ,EAG7B,GAAe,EAAe,EAAc,IACnD,GAAO,MAAQ,EAAQ,EAAY,EAEnC,GAAO,MAAQ,EAAQ,EAAY,EAEhC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const toFiniteNumber: (value: unknown) => number | undefined;
|
|
2
|
+
export declare const toFiniteNumberOr: (value: unknown, fallback: number) => number;
|
|
3
|
+
export declare const toPositiveStep: (value: unknown, fallback: number) => number;
|
|
4
|
+
export declare const normalizeFinite: (value: number, fallback: number) => number;
|
|
5
|
+
export declare const clampNumber: (value: number, min?: number, max?: number) => number;
|
|
6
|
+
//# sourceMappingURL=number-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"number-utils.d.ts","sourceRoot":"","sources":["../../../src/controls/internal/number-utils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,SAIxD,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,OAAO,OAAO,EAAE,UAAU,MAAM,KAAG,MAEnE,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,EAAE,UAAU,MAAM,KAAG,MAEjE,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,OAAO,MAAM,EAAE,UAAU,MAAM,KAAG,MAEjE,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,EAAE,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,MAMvE,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=e=>{let t=Number(e);return Number.isFinite(t)?t:void 0},t=(t,n)=>e(t)??n,n=(e,n)=>Math.abs(t(e,n))||n,r=(e,t)=>Number.isFinite(e)?e:t,i=(e,t,n)=>t!=null&&e<t?t:n!=null&&e>n?n:e;export{i as clampNumber,r as normalizeFinite,e as toFiniteNumber,t as toFiniteNumberOr,n as toPositiveStep};
|
|
2
|
+
//# sourceMappingURL=number-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"number-utils.js","names":[],"sources":["../../../src/controls/internal/number-utils.ts"],"sourcesContent":["export const toFiniteNumber = (value: unknown): number | undefined => {\n const parsed = Number(value);\n\n return Number.isFinite(parsed) ? parsed : undefined;\n};\n\nexport const toFiniteNumberOr = (value: unknown, fallback: number): number => {\n return toFiniteNumber(value) ?? fallback;\n};\n\nexport const toPositiveStep = (value: unknown, fallback: number): number => {\n return Math.abs(toFiniteNumberOr(value, fallback)) || fallback;\n};\n\nexport const normalizeFinite = (value: number, fallback: number): number => {\n return Number.isFinite(value) ? value : fallback;\n};\n\nexport const clampNumber = (value: number, min?: number, max?: number): number => {\n if (min != null && value < min) return min;\n\n if (max != null && value > max) return max;\n\n return value;\n};\n"],"mappings":"AAAA,IAAa,EAAkB,GAAuC,CACpE,IAAM,EAAS,OAAO,CAAK,EAE3B,OAAO,OAAO,SAAS,CAAM,EAAI,EAAS,IAAA,EAC5C,EAEa,GAAoB,EAAgB,IACxC,EAAe,CAAK,GAAK,EAGrB,GAAkB,EAAgB,IACtC,KAAK,IAAI,EAAiB,EAAO,CAAQ,CAAC,GAAK,EAG3C,GAAmB,EAAe,IACtC,OAAO,SAAS,CAAK,EAAI,EAAQ,EAG7B,GAAe,EAAe,EAAc,IACnD,GAAO,MAAQ,EAAQ,EAAY,EAEnC,GAAO,MAAQ,EAAQ,EAAY,EAEhC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-utils.cjs","names":[],"sources":["../../../src/controls/internal/validation-utils.ts"],"sourcesContent":["/**\n * Shared validation utilities for control factories.\n * Only genuinely useful abstractions - trivial wrappers removed.\n */\n\n/**\n * Find index of first item matching predicate (forward scan).\n */\nexport const findForward = <T>(items: T[], start: number, predicate: (item: T, index: number) => boolean): number => {\n for (let idx = start; idx < items.length; idx++) {\n if (predicate(items[idx], idx)) return idx;\n }\n\n return -1;\n};\n\n/**\n * Find index of first item matching predicate (backward scan).\n */\nexport const findBackward = <T>(items: T[], start: number, predicate: (item: T, index: number) => boolean): number => {\n for (let idx = start; idx >= 0; idx--) {\n if (predicate(items[idx], idx)) return idx;\n }\n\n return -1;\n};\n"],"mappings":"AAQA,IAAa,GAAkB,EAAY,EAAe,IAA2D,CACnH,IAAK,IAAI,EAAM,EAAO,EAAM,EAAM,OAAQ,IACxC,GAAI,EAAU,EAAM,GAAM,CAAG,EAAG,OAAO,EAGzC,MAAO,EACT,EAKa,GAAmB,EAAY,EAAe,IAA2D,CACpH,IAAK,IAAI,EAAM,EAAO,GAAO,EAAG,IAC9B,GAAI,EAAU,EAAM,GAAM,CAAG,EAAG,OAAO,EAGzC,MAAO,EACT"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared validation utilities for control factories.
|
|
3
|
+
* Only genuinely useful abstractions - trivial wrappers removed.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Find index of first item matching predicate (forward scan).
|
|
7
|
+
*/
|
|
8
|
+
export declare const findForward: <T>(items: T[], start: number, predicate: (item: T, index: number) => boolean) => number;
|
|
9
|
+
/**
|
|
10
|
+
* Find index of first item matching predicate (backward scan).
|
|
11
|
+
*/
|
|
12
|
+
export declare const findBackward: <T>(items: T[], start: number, predicate: (item: T, index: number) => boolean) => number;
|
|
13
|
+
//# sourceMappingURL=validation-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-utils.d.ts","sourceRoot":"","sources":["../../../src/controls/internal/validation-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,KAAG,MAM1G,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,KAAG,MAM3G,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-utils.js","names":[],"sources":["../../../src/controls/internal/validation-utils.ts"],"sourcesContent":["/**\n * Shared validation utilities for control factories.\n * Only genuinely useful abstractions - trivial wrappers removed.\n */\n\n/**\n * Find index of first item matching predicate (forward scan).\n */\nexport const findForward = <T>(items: T[], start: number, predicate: (item: T, index: number) => boolean): number => {\n for (let idx = start; idx < items.length; idx++) {\n if (predicate(items[idx], idx)) return idx;\n }\n\n return -1;\n};\n\n/**\n * Find index of first item matching predicate (backward scan).\n */\nexport const findBackward = <T>(items: T[], start: number, predicate: (item: T, index: number) => boolean): number => {\n for (let idx = start; idx >= 0; idx--) {\n if (predicate(items[idx], idx)) return idx;\n }\n\n return -1;\n};\n"],"mappings":"AAQA,IAAa,GAAkB,EAAY,EAAe,IAA2D,CACnH,IAAK,IAAI,EAAM,EAAO,EAAM,EAAM,OAAQ,IACxC,GAAI,EAAU,EAAM,GAAM,CAAG,EAAG,OAAO,EAGzC,MAAO,EACT,EAKa,GAAmB,EAAY,EAAe,IAA2D,CACpH,IAAK,IAAI,EAAM,EAAO,GAAO,EAAG,IAC9B,GAAI,EAAU,EAAM,GAAM,CAAG,EAAG,OAAO,EAGzC,MAAO,EACT"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`./internal/keyboard-utils.cjs`),t=require(`./internal/validation-utils.cjs`);var n={first:[`Home`],last:[`End`],next:[`ArrowDown`],prev:[`ArrowUp`]},r=r=>{let i=(e,t)=>r.isItemDisabled?.(e,t)??e.disabled,a=e=>(e>=0&&r.setIndex(e),e),o=(e,n,r)=>r===`forward`?t.findForward(e,n,(e,t)=>!i(e,t)):t.findBackward(e,n,(e,t)=>!i(e,t)),s=()=>{let e=r.getItems();if(!e.length)return-1;let t=o(e,0,`forward`);return t<0?-1:a(t)},c=()=>{let e=r.getItems();if(!e.length)return-1;let t=o(e,e.length-1,`backward`);return t<0?-1:a(t)},l=e=>{let t=r.getItems();if(!t.length)return-1;let n=Math.min(Math.max(e,0),t.length-1);return i(t[n],n)?r.getIndex():a(n)},u=e=>{let t=r.getItems(),n=r.getIndex();if(!t.length)return-1;let i=o(t,n<0?e===`forward`?0:t.length-1:e===`forward`?n+1:n-1,e);if(i>=0)return a(i);if(r.loop){let n=o(t,e===`forward`?0:t.length-1,e);if(n>=0)return a(n)}return n},d=()=>u(`forward`),f=()=>u(`backward`),p=()=>{let e=r.getItems(),t=r.getIndex();return t>=0&&t<e.length?e[t]:void 0},m=()=>{r.setIndex(-1)},h=()=>!!r.disabled?.(),g=()=>{let e=typeof r.keys==`function`?r.keys():r.keys;return{first:e?.first??n.first,last:e?.last??n.last,next:e?.next??n.next,prev:e?.prev??n.prev}},_=null,v,y=()=>{let e=g(),t={};for(let n of[`next`,`prev`,`first`,`last`])for(let i of e[n])t[i]=e=>{let t={first:s,last:c,next:d,prev:f}[n]();r.onNavigate?.(n,t,e)};return t},b=()=>{if(typeof r.keys==`function`){let e=r.keys();e!==v&&(v=e,_=y())}else _||=y();return _};return{first:s,getActiveItem:p,handleKeydown:t=>e.dispatchKeyboardAction(t,{disabled:h,keymap:b()}),last:c,next:d,prev:f,reset:m,set:l}};exports.createListControl=r;
|
|
2
|
+
//# sourceMappingURL=list-control.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-control.cjs","names":[],"sources":["../../src/controls/list-control.ts"],"sourcesContent":["import { dispatchKeyboardAction } from './internal/keyboard-utils';\nimport { findBackward, findForward } from './internal/validation-utils';\n\nexport type ListKeyAction = 'first' | 'last' | 'next' | 'prev';\n\nconst DEFAULT_KEYS: Record<ListKeyAction, string[]> = {\n first: ['Home'],\n last: ['End'],\n next: ['ArrowDown'],\n prev: ['ArrowUp'],\n};\n\nexport type ListNavigationOptions<T> = {\n disabled?: () => boolean;\n getIndex: () => number;\n getItems: () => T[];\n isItemDisabled?: (item: T, index: number) => boolean;\n keys?: Partial<Record<ListKeyAction, string[]>> | (() => Partial<Record<ListKeyAction, string[]>>);\n loop?: boolean;\n onNavigate?: (action: ListKeyAction, index: number, event: KeyboardEvent) => void;\n setIndex: (index: number) => void;\n};\n\nexport type ListControl<T> = {\n first(): number;\n getActiveItem(): T | undefined;\n handleKeydown(event: KeyboardEvent): boolean;\n last(): number;\n next(): number;\n prev(): number;\n reset(): void;\n set(index: number): number;\n};\n\nexport const createListControl = <T>(options: ListNavigationOptions<T>): ListControl<T> => {\n const isDisabled = (item: T, index: number): boolean =>\n options.isItemDisabled?.(item, index) ?? (item as any).disabled;\n\n const commitIndex = (idx: number): number => {\n if (idx >= 0) options.setIndex(idx);\n\n return idx;\n };\n\n const findEnabledIndex = (items: T[], start: number, direction: 'forward' | 'backward'): number => {\n if (direction === 'forward') return findForward(items, start, (item, i) => !isDisabled(item, i));\n\n return findBackward(items, start, (item, i) => !isDisabled(item, i));\n };\n\n const first = (): number => {\n const items = options.getItems();\n\n if (!items.length) return -1;\n\n const idx = findEnabledIndex(items, 0, 'forward');\n\n if (idx < 0) return -1;\n\n return commitIndex(idx);\n };\n\n const last = (): number => {\n const items = options.getItems();\n\n if (!items.length) return -1;\n\n const idx = findEnabledIndex(items, items.length - 1, 'backward');\n\n if (idx < 0) return -1;\n\n return commitIndex(idx);\n };\n\n const set = (index: number): number => {\n const items = options.getItems();\n\n if (!items.length) return -1;\n\n const clamped = Math.min(Math.max(index, 0), items.length - 1);\n\n if (isDisabled(items[clamped], clamped)) {\n return options.getIndex();\n }\n\n return commitIndex(clamped);\n };\n\n const move = (direction: 'forward' | 'backward'): number => {\n const items = options.getItems();\n const current = options.getIndex();\n\n if (!items.length) return -1;\n\n const start =\n current < 0\n ? direction === 'forward'\n ? 0\n : items.length - 1\n : direction === 'forward'\n ? current + 1\n : current - 1;\n const idx = findEnabledIndex(items, start, direction);\n\n if (idx >= 0) return commitIndex(idx);\n\n if (options.loop) {\n const wrapStart = direction === 'forward' ? 0 : items.length - 1;\n const wrapped = findEnabledIndex(items, wrapStart, direction);\n\n if (wrapped >= 0) return commitIndex(wrapped);\n }\n\n return current;\n };\n\n const next = (): number => move('forward');\n\n const prev = (): number => move('backward');\n\n const getActiveItem = (): T | undefined => {\n const items = options.getItems();\n const index = options.getIndex();\n\n return index >= 0 && index < items.length ? items[index] : undefined;\n };\n\n const reset = (): void => {\n options.setIndex(-1);\n };\n\n // ── Keyboard handling ──────────────────────────────────────────────────────\n\n const isKeyDisabled = (): boolean => Boolean(options.disabled?.());\n\n const resolveKeys = (): Record<ListKeyAction, string[]> => {\n const keys = typeof options.keys === 'function' ? options.keys() : options.keys;\n\n return {\n first: keys?.first ?? DEFAULT_KEYS.first,\n last: keys?.last ?? DEFAULT_KEYS.last,\n next: keys?.next ?? DEFAULT_KEYS.next,\n prev: keys?.prev ?? DEFAULT_KEYS.prev,\n };\n };\n\n let cachedKeymap: Record<string, (keyboardEvent: KeyboardEvent) => void> | null = null;\n let cachedKeysRef: Partial<Record<ListKeyAction, string[]>> | undefined;\n\n const buildKeymap = (): Record<string, (keyboardEvent: KeyboardEvent) => void> => {\n const keys = resolveKeys();\n const keymap: Record<string, (keyboardEvent: KeyboardEvent) => void> = {};\n\n for (const action of ['next', 'prev', 'first', 'last'] as const) {\n for (const key of keys[action]) {\n keymap[key] = (keyboardEvent: KeyboardEvent) => {\n const index = { first, last, next, prev }[action]();\n\n options.onNavigate?.(action, index, keyboardEvent);\n };\n }\n }\n\n return keymap;\n };\n\n const getKeymap = (): Record<string, (keyboardEvent: KeyboardEvent) => void> => {\n if (typeof options.keys === 'function') {\n const currentKeys = options.keys();\n\n if (currentKeys !== cachedKeysRef) {\n cachedKeysRef = currentKeys;\n cachedKeymap = buildKeymap();\n }\n } else if (!cachedKeymap) {\n cachedKeymap = buildKeymap();\n }\n\n return cachedKeymap!;\n };\n\n const handleKeydown = (event: KeyboardEvent): boolean =>\n dispatchKeyboardAction(event, { disabled: isKeyDisabled, keymap: getKeymap() });\n\n return {\n first,\n getActiveItem,\n handleKeydown,\n last,\n next,\n prev,\n reset,\n set,\n };\n};\n"],"mappings":"8FAKA,IAAM,EAAgD,CACpD,MAAO,CAAC,MAAM,EACd,KAAM,CAAC,KAAK,EACZ,KAAM,CAAC,WAAW,EAClB,KAAM,CAAC,SAAS,CAClB,EAwBa,EAAwB,GAAsD,CACzF,IAAM,GAAc,EAAS,IAC3B,EAAQ,iBAAiB,EAAM,CAAK,GAAM,EAAa,SAEnD,EAAe,IACf,GAAO,GAAG,EAAQ,SAAS,CAAG,EAE3B,GAGH,GAAoB,EAAY,EAAe,IAC/C,IAAc,UAAkB,EAAA,YAAY,EAAO,GAAQ,EAAM,IAAM,CAAC,EAAW,EAAM,CAAC,CAAC,EAExF,EAAA,aAAa,EAAO,GAAQ,EAAM,IAAM,CAAC,EAAW,EAAM,CAAC,CAAC,EAG/D,MAAsB,CAC1B,IAAM,EAAQ,EAAQ,SAAS,EAE/B,GAAI,CAAC,EAAM,OAAQ,MAAO,GAE1B,IAAM,EAAM,EAAiB,EAAO,EAAG,SAAS,EAIhD,OAFI,EAAM,EAAU,GAEb,EAAY,CAAG,CACxB,EAEM,MAAqB,CACzB,IAAM,EAAQ,EAAQ,SAAS,EAE/B,GAAI,CAAC,EAAM,OAAQ,MAAO,GAE1B,IAAM,EAAM,EAAiB,EAAO,EAAM,OAAS,EAAG,UAAU,EAIhE,OAFI,EAAM,EAAU,GAEb,EAAY,CAAG,CACxB,EAEM,EAAO,GAA0B,CACrC,IAAM,EAAQ,EAAQ,SAAS,EAE/B,GAAI,CAAC,EAAM,OAAQ,MAAO,GAE1B,IAAM,EAAU,KAAK,IAAI,KAAK,IAAI,EAAO,CAAC,EAAG,EAAM,OAAS,CAAC,EAM7D,OAJI,EAAW,EAAM,GAAU,CAAO,EAC7B,EAAQ,SAAS,EAGnB,EAAY,CAAO,CAC5B,EAEM,EAAQ,GAA8C,CAC1D,IAAM,EAAQ,EAAQ,SAAS,EACzB,EAAU,EAAQ,SAAS,EAEjC,GAAI,CAAC,EAAM,OAAQ,MAAO,GAU1B,IAAM,EAAM,EAAiB,EAP3B,EAAU,EACN,IAAc,UACZ,EACA,EAAM,OAAS,EACjB,IAAc,UACZ,EAAU,EACV,EAAU,EACyB,CAAS,EAEpD,GAAI,GAAO,EAAG,OAAO,EAAY,CAAG,EAEpC,GAAI,EAAQ,KAAM,CAEhB,IAAM,EAAU,EAAiB,EADf,IAAc,UAAY,EAAI,EAAM,OAAS,EACZ,CAAS,EAE5D,GAAI,GAAW,EAAG,OAAO,EAAY,CAAO,CAC9C,CAEA,OAAO,CACT,EAEM,MAAqB,EAAK,SAAS,EAEnC,MAAqB,EAAK,UAAU,EAEpC,MAAqC,CACzC,IAAM,EAAQ,EAAQ,SAAS,EACzB,EAAQ,EAAQ,SAAS,EAE/B,OAAO,GAAS,GAAK,EAAQ,EAAM,OAAS,EAAM,GAAS,IAAA,EAC7D,EAEM,MAAoB,CACxB,EAAQ,SAAS,EAAE,CACrB,EAIM,MAA+B,EAAQ,EAAQ,WAAW,EAE1D,MAAqD,CACzD,IAAM,EAAO,OAAO,EAAQ,MAAS,WAAa,EAAQ,KAAK,EAAI,EAAQ,KAE3E,MAAO,CACL,MAAO,GAAM,OAAS,EAAa,MACnC,KAAM,GAAM,MAAQ,EAAa,KACjC,KAAM,GAAM,MAAQ,EAAa,KACjC,KAAM,GAAM,MAAQ,EAAa,IACnC,CACF,EAEI,EAA8E,KAC9E,EAEE,MAA4E,CAChF,IAAM,EAAO,EAAY,EACnB,EAAiE,CAAC,EAExE,IAAK,IAAM,IAAU,CAAC,OAAQ,OAAQ,QAAS,MAAM,EACnD,IAAK,IAAM,KAAO,EAAK,GACrB,EAAO,GAAQ,GAAiC,CAC9C,IAAM,EAAQ,CAAE,QAAO,OAAM,OAAM,MAAK,EAAE,GAAQ,EAElD,EAAQ,aAAa,EAAQ,EAAO,CAAa,CACnD,EAIJ,OAAO,CACT,EAEM,MAA0E,CAC9E,GAAI,OAAO,EAAQ,MAAS,WAAY,CACtC,IAAM,EAAc,EAAQ,KAAK,EAE7B,IAAgB,IAClB,EAAgB,EAChB,EAAe,EAAY,EAE/B,MAAO,AACL,IAAe,EAAY,EAG7B,OAAO,CACT,EAKA,MAAO,CACL,QACA,gBACA,cANqB,GACrB,EAAA,uBAAuB,EAAO,CAAE,SAAU,EAAe,OAAQ,EAAU,CAAE,CAAC,EAM9E,OACA,OACA,OACA,QACA,KACF,CACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type ListKeyAction = 'first' | 'last' | 'next' | 'prev';
|
|
2
|
+
export type ListNavigationOptions<T> = {
|
|
3
|
+
disabled?: () => boolean;
|
|
4
|
+
getIndex: () => number;
|
|
5
|
+
getItems: () => T[];
|
|
6
|
+
isItemDisabled?: (item: T, index: number) => boolean;
|
|
7
|
+
keys?: Partial<Record<ListKeyAction, string[]>> | (() => Partial<Record<ListKeyAction, string[]>>);
|
|
8
|
+
loop?: boolean;
|
|
9
|
+
onNavigate?: (action: ListKeyAction, index: number, event: KeyboardEvent) => void;
|
|
10
|
+
setIndex: (index: number) => void;
|
|
11
|
+
};
|
|
12
|
+
export type ListControl<T> = {
|
|
13
|
+
first(): number;
|
|
14
|
+
getActiveItem(): T | undefined;
|
|
15
|
+
handleKeydown(event: KeyboardEvent): boolean;
|
|
16
|
+
last(): number;
|
|
17
|
+
next(): number;
|
|
18
|
+
prev(): number;
|
|
19
|
+
reset(): void;
|
|
20
|
+
set(index: number): number;
|
|
21
|
+
};
|
|
22
|
+
export declare const createListControl: <T>(options: ListNavigationOptions<T>) => ListControl<T>;
|
|
23
|
+
//# sourceMappingURL=list-control.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-control.d.ts","sourceRoot":"","sources":["../../src/controls/list-control.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAS/D,MAAM,MAAM,qBAAqB,CAAC,CAAC,IAAI;IACrC,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC;IACzB,QAAQ,EAAE,MAAM,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC;IACpB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACrD,IAAI,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACnG,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAClF,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,KAAK,IAAI,MAAM,CAAC;IAChB,aAAa,IAAI,CAAC,GAAG,SAAS,CAAC;IAC/B,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC;IAC7C,IAAI,IAAI,MAAM,CAAC;IACf,IAAI,IAAI,MAAM,CAAC;IACf,IAAI,IAAI,MAAM,CAAC;IACf,KAAK,IAAI,IAAI,CAAC;IACd,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,CAAC,EAAE,SAAS,qBAAqB,CAAC,CAAC,CAAC,KAAG,WAAW,CAAC,CAAC,CAgKrF,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{dispatchKeyboardAction as e}from"./internal/keyboard-utils.js";import{findBackward as t,findForward as n}from"./internal/validation-utils.js";var r={first:[`Home`],last:[`End`],next:[`ArrowDown`],prev:[`ArrowUp`]},i=i=>{let a=(e,t)=>i.isItemDisabled?.(e,t)??e.disabled,o=e=>(e>=0&&i.setIndex(e),e),s=(e,r,i)=>i===`forward`?n(e,r,(e,t)=>!a(e,t)):t(e,r,(e,t)=>!a(e,t)),c=()=>{let e=i.getItems();if(!e.length)return-1;let t=s(e,0,`forward`);return t<0?-1:o(t)},l=()=>{let e=i.getItems();if(!e.length)return-1;let t=s(e,e.length-1,`backward`);return t<0?-1:o(t)},u=e=>{let t=i.getItems();if(!t.length)return-1;let n=Math.min(Math.max(e,0),t.length-1);return a(t[n],n)?i.getIndex():o(n)},d=e=>{let t=i.getItems(),n=i.getIndex();if(!t.length)return-1;let r=s(t,n<0?e===`forward`?0:t.length-1:e===`forward`?n+1:n-1,e);if(r>=0)return o(r);if(i.loop){let n=s(t,e===`forward`?0:t.length-1,e);if(n>=0)return o(n)}return n},f=()=>d(`forward`),p=()=>d(`backward`),m=()=>{let e=i.getItems(),t=i.getIndex();return t>=0&&t<e.length?e[t]:void 0},h=()=>{i.setIndex(-1)},g=()=>!!i.disabled?.(),_=()=>{let e=typeof i.keys==`function`?i.keys():i.keys;return{first:e?.first??r.first,last:e?.last??r.last,next:e?.next??r.next,prev:e?.prev??r.prev}},v=null,y,b=()=>{let e=_(),t={};for(let n of[`next`,`prev`,`first`,`last`])for(let r of e[n])t[r]=e=>{let t={first:c,last:l,next:f,prev:p}[n]();i.onNavigate?.(n,t,e)};return t},x=()=>{if(typeof i.keys==`function`){let e=i.keys();e!==y&&(y=e,v=b())}else v||=b();return v};return{first:c,getActiveItem:m,handleKeydown:t=>e(t,{disabled:g,keymap:x()}),last:l,next:f,prev:p,reset:h,set:u}};export{i as createListControl};
|
|
2
|
+
//# sourceMappingURL=list-control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-control.js","names":[],"sources":["../../src/controls/list-control.ts"],"sourcesContent":["import { dispatchKeyboardAction } from './internal/keyboard-utils';\nimport { findBackward, findForward } from './internal/validation-utils';\n\nexport type ListKeyAction = 'first' | 'last' | 'next' | 'prev';\n\nconst DEFAULT_KEYS: Record<ListKeyAction, string[]> = {\n first: ['Home'],\n last: ['End'],\n next: ['ArrowDown'],\n prev: ['ArrowUp'],\n};\n\nexport type ListNavigationOptions<T> = {\n disabled?: () => boolean;\n getIndex: () => number;\n getItems: () => T[];\n isItemDisabled?: (item: T, index: number) => boolean;\n keys?: Partial<Record<ListKeyAction, string[]>> | (() => Partial<Record<ListKeyAction, string[]>>);\n loop?: boolean;\n onNavigate?: (action: ListKeyAction, index: number, event: KeyboardEvent) => void;\n setIndex: (index: number) => void;\n};\n\nexport type ListControl<T> = {\n first(): number;\n getActiveItem(): T | undefined;\n handleKeydown(event: KeyboardEvent): boolean;\n last(): number;\n next(): number;\n prev(): number;\n reset(): void;\n set(index: number): number;\n};\n\nexport const createListControl = <T>(options: ListNavigationOptions<T>): ListControl<T> => {\n const isDisabled = (item: T, index: number): boolean =>\n options.isItemDisabled?.(item, index) ?? (item as any).disabled;\n\n const commitIndex = (idx: number): number => {\n if (idx >= 0) options.setIndex(idx);\n\n return idx;\n };\n\n const findEnabledIndex = (items: T[], start: number, direction: 'forward' | 'backward'): number => {\n if (direction === 'forward') return findForward(items, start, (item, i) => !isDisabled(item, i));\n\n return findBackward(items, start, (item, i) => !isDisabled(item, i));\n };\n\n const first = (): number => {\n const items = options.getItems();\n\n if (!items.length) return -1;\n\n const idx = findEnabledIndex(items, 0, 'forward');\n\n if (idx < 0) return -1;\n\n return commitIndex(idx);\n };\n\n const last = (): number => {\n const items = options.getItems();\n\n if (!items.length) return -1;\n\n const idx = findEnabledIndex(items, items.length - 1, 'backward');\n\n if (idx < 0) return -1;\n\n return commitIndex(idx);\n };\n\n const set = (index: number): number => {\n const items = options.getItems();\n\n if (!items.length) return -1;\n\n const clamped = Math.min(Math.max(index, 0), items.length - 1);\n\n if (isDisabled(items[clamped], clamped)) {\n return options.getIndex();\n }\n\n return commitIndex(clamped);\n };\n\n const move = (direction: 'forward' | 'backward'): number => {\n const items = options.getItems();\n const current = options.getIndex();\n\n if (!items.length) return -1;\n\n const start =\n current < 0\n ? direction === 'forward'\n ? 0\n : items.length - 1\n : direction === 'forward'\n ? current + 1\n : current - 1;\n const idx = findEnabledIndex(items, start, direction);\n\n if (idx >= 0) return commitIndex(idx);\n\n if (options.loop) {\n const wrapStart = direction === 'forward' ? 0 : items.length - 1;\n const wrapped = findEnabledIndex(items, wrapStart, direction);\n\n if (wrapped >= 0) return commitIndex(wrapped);\n }\n\n return current;\n };\n\n const next = (): number => move('forward');\n\n const prev = (): number => move('backward');\n\n const getActiveItem = (): T | undefined => {\n const items = options.getItems();\n const index = options.getIndex();\n\n return index >= 0 && index < items.length ? items[index] : undefined;\n };\n\n const reset = (): void => {\n options.setIndex(-1);\n };\n\n // ── Keyboard handling ──────────────────────────────────────────────────────\n\n const isKeyDisabled = (): boolean => Boolean(options.disabled?.());\n\n const resolveKeys = (): Record<ListKeyAction, string[]> => {\n const keys = typeof options.keys === 'function' ? options.keys() : options.keys;\n\n return {\n first: keys?.first ?? DEFAULT_KEYS.first,\n last: keys?.last ?? DEFAULT_KEYS.last,\n next: keys?.next ?? DEFAULT_KEYS.next,\n prev: keys?.prev ?? DEFAULT_KEYS.prev,\n };\n };\n\n let cachedKeymap: Record<string, (keyboardEvent: KeyboardEvent) => void> | null = null;\n let cachedKeysRef: Partial<Record<ListKeyAction, string[]>> | undefined;\n\n const buildKeymap = (): Record<string, (keyboardEvent: KeyboardEvent) => void> => {\n const keys = resolveKeys();\n const keymap: Record<string, (keyboardEvent: KeyboardEvent) => void> = {};\n\n for (const action of ['next', 'prev', 'first', 'last'] as const) {\n for (const key of keys[action]) {\n keymap[key] = (keyboardEvent: KeyboardEvent) => {\n const index = { first, last, next, prev }[action]();\n\n options.onNavigate?.(action, index, keyboardEvent);\n };\n }\n }\n\n return keymap;\n };\n\n const getKeymap = (): Record<string, (keyboardEvent: KeyboardEvent) => void> => {\n if (typeof options.keys === 'function') {\n const currentKeys = options.keys();\n\n if (currentKeys !== cachedKeysRef) {\n cachedKeysRef = currentKeys;\n cachedKeymap = buildKeymap();\n }\n } else if (!cachedKeymap) {\n cachedKeymap = buildKeymap();\n }\n\n return cachedKeymap!;\n };\n\n const handleKeydown = (event: KeyboardEvent): boolean =>\n dispatchKeyboardAction(event, { disabled: isKeyDisabled, keymap: getKeymap() });\n\n return {\n first,\n getActiveItem,\n handleKeydown,\n last,\n next,\n prev,\n reset,\n set,\n };\n};\n"],"mappings":"qJAKA,IAAM,EAAgD,CACpD,MAAO,CAAC,MAAM,EACd,KAAM,CAAC,KAAK,EACZ,KAAM,CAAC,WAAW,EAClB,KAAM,CAAC,SAAS,CAClB,EAwBa,EAAwB,GAAsD,CACzF,IAAM,GAAc,EAAS,IAC3B,EAAQ,iBAAiB,EAAM,CAAK,GAAM,EAAa,SAEnD,EAAe,IACf,GAAO,GAAG,EAAQ,SAAS,CAAG,EAE3B,GAGH,GAAoB,EAAY,EAAe,IAC/C,IAAc,UAAkB,EAAY,EAAO,GAAQ,EAAM,IAAM,CAAC,EAAW,EAAM,CAAC,CAAC,EAExF,EAAa,EAAO,GAAQ,EAAM,IAAM,CAAC,EAAW,EAAM,CAAC,CAAC,EAG/D,MAAsB,CAC1B,IAAM,EAAQ,EAAQ,SAAS,EAE/B,GAAI,CAAC,EAAM,OAAQ,MAAO,GAE1B,IAAM,EAAM,EAAiB,EAAO,EAAG,SAAS,EAIhD,OAFI,EAAM,EAAU,GAEb,EAAY,CAAG,CACxB,EAEM,MAAqB,CACzB,IAAM,EAAQ,EAAQ,SAAS,EAE/B,GAAI,CAAC,EAAM,OAAQ,MAAO,GAE1B,IAAM,EAAM,EAAiB,EAAO,EAAM,OAAS,EAAG,UAAU,EAIhE,OAFI,EAAM,EAAU,GAEb,EAAY,CAAG,CACxB,EAEM,EAAO,GAA0B,CACrC,IAAM,EAAQ,EAAQ,SAAS,EAE/B,GAAI,CAAC,EAAM,OAAQ,MAAO,GAE1B,IAAM,EAAU,KAAK,IAAI,KAAK,IAAI,EAAO,CAAC,EAAG,EAAM,OAAS,CAAC,EAM7D,OAJI,EAAW,EAAM,GAAU,CAAO,EAC7B,EAAQ,SAAS,EAGnB,EAAY,CAAO,CAC5B,EAEM,EAAQ,GAA8C,CAC1D,IAAM,EAAQ,EAAQ,SAAS,EACzB,EAAU,EAAQ,SAAS,EAEjC,GAAI,CAAC,EAAM,OAAQ,MAAO,GAU1B,IAAM,EAAM,EAAiB,EAP3B,EAAU,EACN,IAAc,UACZ,EACA,EAAM,OAAS,EACjB,IAAc,UACZ,EAAU,EACV,EAAU,EACyB,CAAS,EAEpD,GAAI,GAAO,EAAG,OAAO,EAAY,CAAG,EAEpC,GAAI,EAAQ,KAAM,CAEhB,IAAM,EAAU,EAAiB,EADf,IAAc,UAAY,EAAI,EAAM,OAAS,EACZ,CAAS,EAE5D,GAAI,GAAW,EAAG,OAAO,EAAY,CAAO,CAC9C,CAEA,OAAO,CACT,EAEM,MAAqB,EAAK,SAAS,EAEnC,MAAqB,EAAK,UAAU,EAEpC,MAAqC,CACzC,IAAM,EAAQ,EAAQ,SAAS,EACzB,EAAQ,EAAQ,SAAS,EAE/B,OAAO,GAAS,GAAK,EAAQ,EAAM,OAAS,EAAM,GAAS,IAAA,EAC7D,EAEM,MAAoB,CACxB,EAAQ,SAAS,EAAE,CACrB,EAIM,MAA+B,EAAQ,EAAQ,WAAW,EAE1D,MAAqD,CACzD,IAAM,EAAO,OAAO,EAAQ,MAAS,WAAa,EAAQ,KAAK,EAAI,EAAQ,KAE3E,MAAO,CACL,MAAO,GAAM,OAAS,EAAa,MACnC,KAAM,GAAM,MAAQ,EAAa,KACjC,KAAM,GAAM,MAAQ,EAAa,KACjC,KAAM,GAAM,MAAQ,EAAa,IACnC,CACF,EAEI,EAA8E,KAC9E,EAEE,MAA4E,CAChF,IAAM,EAAO,EAAY,EACnB,EAAiE,CAAC,EAExE,IAAK,IAAM,IAAU,CAAC,OAAQ,OAAQ,QAAS,MAAM,EACnD,IAAK,IAAM,KAAO,EAAK,GACrB,EAAO,GAAQ,GAAiC,CAC9C,IAAM,EAAQ,CAAE,QAAO,OAAM,OAAM,MAAK,EAAE,GAAQ,EAElD,EAAQ,aAAa,EAAQ,EAAO,CAAa,CACnD,EAIJ,OAAO,CACT,EAEM,MAA0E,CAC9E,GAAI,OAAO,EAAQ,MAAS,WAAY,CACtC,IAAM,EAAc,EAAQ,KAAK,EAE7B,IAAgB,IAClB,EAAgB,EAChB,EAAe,EAAY,EAE/B,MAAO,AACL,IAAe,EAAY,EAG7B,OAAO,CACT,EAKA,MAAO,CACL,QACA,gBACA,cANqB,GACrB,EAAuB,EAAO,CAAE,SAAU,EAAe,OAAQ,EAAU,CAAE,CAAC,EAM9E,OACA,OACA,OACA,QACA,KACF,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=require(`@vielzeug/floatit`);var t=new Set,n=null,r=e=>e instanceof Error&&e.message.includes(`[stateit] Cannot read disposed computed signal`),i=()=>{if(n)return;let e=e=>{for(let n of[...t])try{n(e)}catch(e){if(r(e)){t.delete(n);continue}throw e}a()};document.addEventListener(`click`,e,{capture:!0}),n=()=>{document.removeEventListener(`click`,e,{capture:!0}),n=null}},a=()=>{t.size===0&&n&&n()},o=n=>{let o=null,s=null,c=()=>typeof n.restoreFocus==`function`?n.restoreFocus():n.restoreFocus??!0,l=(e,o)=>{try{n.setOpen(e,o)}catch(e){if(r(e)&&s){t.delete(s),a();return}throw e}e&&s?(t.add(s),i()):!e&&s&&(t.delete(s),a())},u=(t={})=>{let r=t.reason??`programmatic`;if(!(n.isDisabled?.()||n.isOpen())){if(l(!0,{reason:r}),n.positioner){let t=n.positioner.reference(),r=n.positioner.floating();t&&r&&(o=(0,e.autoUpdate)(t,r,()=>n.positioner?.update())),n.positioner.update()}n.onOpen?.(r)}},d=(e={})=>{let t=e.reason??`programmatic`;n.isOpen()&&(l(!1,{reason:t}),o&&=(o(),null),(e.restoreFocus??c())&&n.getTriggerElement?.()?.focus(),n.onClose?.(t))};return s=e=>{if(!n.isOpen())return;let t=e.composedPath?.()??[],r=n.getBoundaryElement(),i=n.getPanelElement?.()??null,a=t[0]??e.target,o=a instanceof Node?a:null,s=t.some(e=>e===r||e===i),c=o?(r?.contains(o)??!1)||(i?.contains(o)??!1):!1;s||c||d({reason:`outside-click`})},{close:d,open:u,toggle:()=>{if(n.isOpen()){d({reason:`trigger`});return}u({reason:`trigger`})}}};exports.createOverlayControl=o;
|
|
2
|
+
//# sourceMappingURL=overlay-control.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay-control.cjs","names":[],"sources":["../../src/controls/overlay-control.ts"],"sourcesContent":["import { autoUpdate } from '@vielzeug/floatit';\n\nexport type OverlayOpenReason = 'programmatic' | 'trigger';\nexport type OverlayCloseReason = 'escape' | 'outside-click' | 'programmatic' | 'swipe' | 'trigger';\nexport type OverlayCloseDetail = { reason: OverlayCloseReason };\nexport type OverlayOpenDetail = { reason: OverlayOpenReason };\n\ntype OverlayPositioner = {\n floating: () => HTMLElement | null;\n reference: () => HTMLElement | null;\n update: () => void;\n};\n\nexport type OverlayControlOptions = {\n getBoundaryElement: () => HTMLElement | null;\n getPanelElement?: () => HTMLElement | null;\n getTriggerElement?: () => HTMLElement | null;\n isDisabled?: () => boolean;\n isOpen: () => boolean;\n onClose?: (reason: OverlayCloseReason) => void;\n onOpen?: (reason: OverlayOpenReason) => void;\n positioner?: OverlayPositioner;\n restoreFocus?: boolean | (() => boolean);\n setOpen: (next: boolean, context: { reason: OverlayOpenReason | OverlayCloseReason }) => void;\n};\n\nexport type OverlayControl = {\n close(opts?: { reason?: OverlayCloseReason; restoreFocus?: boolean }): void;\n open(opts?: { reason?: OverlayOpenReason }): void;\n toggle(): void;\n};\n\n// Module-level document click listener management for outside-click dismissal\nconst activeOverlayListeners = new Set<(event: Event) => void>();\nlet documentClickUnsubscribe: (() => void) | null = null;\n\nconst isDisposedComputedSignalError = (error: unknown): boolean =>\n error instanceof Error && error.message.includes('[stateit] Cannot read disposed computed signal');\n\nconst ensureDocumentClickListener = (): void => {\n if (documentClickUnsubscribe) return; // Already attached\n\n const handler = (event: Event) => {\n for (const listener of [...activeOverlayListeners]) {\n try {\n listener(event);\n } catch (error) {\n if (isDisposedComputedSignalError(error)) {\n activeOverlayListeners.delete(listener);\n\n continue;\n }\n\n throw error;\n }\n }\n\n removeDocumentClickListener();\n };\n\n document.addEventListener('click', handler, { capture: true });\n documentClickUnsubscribe = () => {\n document.removeEventListener('click', handler, { capture: true });\n documentClickUnsubscribe = null;\n };\n};\n\nconst removeDocumentClickListener = (): void => {\n if (activeOverlayListeners.size === 0 && documentClickUnsubscribe) {\n documentClickUnsubscribe();\n }\n};\n\nexport const createOverlayControl = (options: OverlayControlOptions): OverlayControl => {\n let positionerCleanup: (() => void) | null = null;\n let clickListener: ((event: Event) => void) | null = null;\n\n const shouldRestoreFocus = (): boolean => {\n if (typeof options.restoreFocus === 'function') return options.restoreFocus();\n\n return options.restoreFocus ?? true;\n };\n\n // Local wrapper — handles click-listener registration without mutating options.\n const commitOpen = (next: boolean, context: { reason: OverlayOpenReason | OverlayCloseReason }): void => {\n try {\n options.setOpen(next, context);\n } catch (error) {\n if (isDisposedComputedSignalError(error) && clickListener) {\n activeOverlayListeners.delete(clickListener);\n removeDocumentClickListener();\n\n return;\n }\n\n throw error;\n }\n\n if (next && clickListener) {\n activeOverlayListeners.add(clickListener);\n ensureDocumentClickListener();\n } else if (!next && clickListener) {\n activeOverlayListeners.delete(clickListener);\n removeDocumentClickListener();\n }\n };\n\n const open = (opts: { reason?: OverlayOpenReason } = {}): void => {\n const reason = opts.reason ?? 'programmatic';\n\n if (options.isDisabled?.() || options.isOpen()) return;\n\n commitOpen(true, { reason });\n\n if (options.positioner) {\n const reference = options.positioner.reference();\n const floating = options.positioner.floating();\n\n if (reference && floating) {\n positionerCleanup = autoUpdate(reference, floating, () => options.positioner?.update());\n }\n\n options.positioner.update();\n }\n\n options.onOpen?.(reason);\n };\n\n const close = (opts: { reason?: OverlayCloseReason; restoreFocus?: boolean } = {}): void => {\n const reason = opts.reason ?? 'programmatic';\n\n if (!options.isOpen()) return;\n\n commitOpen(false, { reason });\n\n if (positionerCleanup) {\n positionerCleanup();\n positionerCleanup = null;\n }\n\n const restore = opts.restoreFocus ?? shouldRestoreFocus();\n\n if (restore) options.getTriggerElement?.()?.focus();\n\n options.onClose?.(reason);\n };\n\n const toggle = (): void => {\n if (options.isOpen()) {\n close({ reason: 'trigger' });\n\n return;\n }\n\n open({ reason: 'trigger' });\n };\n\n clickListener = (event: Event) => {\n if (!options.isOpen()) return;\n\n const path = (event as Event & { composedPath?: () => EventTarget[] }).composedPath?.() ?? [];\n const boundary = options.getBoundaryElement();\n const panel = options.getPanelElement?.() ?? null;\n const target = (path[0] ?? event.target) as EventTarget | null;\n const nodeTarget = target instanceof Node ? target : null;\n\n const insideByPath = path.some((entry) => entry === boundary || entry === panel);\n const insideByContainment = nodeTarget\n ? (boundary?.contains(nodeTarget) ?? false) || (panel?.contains(nodeTarget) ?? false)\n : false;\n const inside = insideByPath || insideByContainment;\n\n if (!inside) close({ reason: 'outside-click' });\n };\n\n return {\n close,\n open,\n toggle,\n };\n};\n"],"mappings":"mCAiCA,IAAM,EAAyB,IAAI,IAC/B,EAAgD,KAE9C,EAAiC,GACrC,aAAiB,OAAS,EAAM,QAAQ,SAAS,gDAAgD,EAE7F,MAA0C,CAC9C,GAAI,EAA0B,OAE9B,IAAM,EAAW,GAAiB,CAChC,IAAK,IAAM,IAAY,CAAC,GAAG,CAAsB,EAC/C,GAAI,CACF,EAAS,CAAK,CAChB,OAAS,EAAO,CACd,GAAI,EAA8B,CAAK,EAAG,CACxC,EAAuB,OAAO,CAAQ,EAEtC,QACF,CAEA,MAAM,CACR,CAGF,EAA4B,CAC9B,EAEA,SAAS,iBAAiB,QAAS,EAAS,CAAE,QAAS,EAAK,CAAC,EAC7D,MAAiC,CAC/B,SAAS,oBAAoB,QAAS,EAAS,CAAE,QAAS,EAAK,CAAC,EAChE,EAA2B,IAC7B,CACF,EAEM,MAA0C,CAC1C,EAAuB,OAAS,GAAK,GACvC,EAAyB,CAE7B,EAEa,EAAwB,GAAmD,CACtF,IAAI,EAAyC,KACzC,EAAiD,KAE/C,MACA,OAAO,EAAQ,cAAiB,WAAmB,EAAQ,aAAa,EAErE,EAAQ,cAAgB,GAI3B,GAAc,EAAe,IAAsE,CACvG,GAAI,CACF,EAAQ,QAAQ,EAAM,CAAO,CAC/B,OAAS,EAAO,CACd,GAAI,EAA8B,CAAK,GAAK,EAAe,CACzD,EAAuB,OAAO,CAAa,EAC3C,EAA4B,EAE5B,MACF,CAEA,MAAM,CACR,CAEI,GAAQ,GACV,EAAuB,IAAI,CAAa,EACxC,EAA4B,GACnB,CAAC,GAAQ,IAClB,EAAuB,OAAO,CAAa,EAC3C,EAA4B,EAEhC,EAEM,GAAQ,EAAuC,CAAC,IAAY,CAChE,IAAM,EAAS,EAAK,QAAU,eAE1B,OAAQ,aAAa,GAAK,EAAQ,OAAO,GAI7C,IAFA,EAAW,GAAM,CAAE,QAAO,CAAC,EAEvB,EAAQ,WAAY,CACtB,IAAM,EAAY,EAAQ,WAAW,UAAU,EACzC,EAAW,EAAQ,WAAW,SAAS,EAEzC,GAAa,IACf,GAAA,EAAA,EAAA,YAA+B,EAAW,MAAgB,EAAQ,YAAY,OAAO,CAAC,GAGxF,EAAQ,WAAW,OAAO,CAC5B,CAEA,EAAQ,SAAS,CAAM,CAFvB,CAGF,EAEM,GAAS,EAAgE,CAAC,IAAY,CAC1F,IAAM,EAAS,EAAK,QAAU,eAEzB,EAAQ,OAAO,IAEpB,EAAW,GAAO,CAAE,QAAO,CAAC,EAE5B,AAEE,KADA,EAAkB,EACE,OAGN,EAAK,cAAgB,EAAmB,IAE3C,EAAQ,oBAAoB,GAAG,MAAM,EAElD,EAAQ,UAAU,CAAM,EAC1B,EA8BA,MAlBA,GAAiB,GAAiB,CAChC,GAAI,CAAC,EAAQ,OAAO,EAAG,OAEvB,IAAM,EAAQ,EAAyD,eAAe,GAAK,CAAC,EACtF,EAAW,EAAQ,mBAAmB,EACtC,EAAQ,EAAQ,kBAAkB,GAAK,KACvC,EAAU,EAAK,IAAM,EAAM,OAC3B,EAAa,aAAkB,KAAO,EAAS,KAE/C,EAAe,EAAK,KAAM,GAAU,IAAU,GAAY,IAAU,CAAK,EACzE,EAAsB,GACvB,GAAU,SAAS,CAAU,GAAK,MAAW,GAAO,SAAS,CAAU,GAAK,IAC7E,GACW,GAAgB,GAElB,EAAM,CAAE,OAAQ,eAAgB,CAAC,CAChD,EAEO,CACL,QACA,OACA,WA/ByB,CACzB,GAAI,EAAQ,OAAO,EAAG,CACpB,EAAM,CAAE,OAAQ,SAAU,CAAC,EAE3B,MACF,CAEA,EAAK,CAAE,OAAQ,SAAU,CAAC,CAC5B,CAwBA,CACF"}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type OverlayOpenReason = 'programmatic' | 'trigger';
|
|
2
|
+
export type OverlayCloseReason = 'escape' | 'outside-click' | 'programmatic' | 'swipe' | 'trigger';
|
|
3
|
+
export type OverlayCloseDetail = {
|
|
4
|
+
reason: OverlayCloseReason;
|
|
5
|
+
};
|
|
6
|
+
export type OverlayOpenDetail = {
|
|
7
|
+
reason: OverlayOpenReason;
|
|
8
|
+
};
|
|
9
|
+
type OverlayPositioner = {
|
|
2
10
|
floating: () => HTMLElement | null;
|
|
3
11
|
reference: () => HTMLElement | null;
|
|
4
12
|
update: () => void;
|
|
5
13
|
};
|
|
6
|
-
export type OverlayOpenReason = 'programmatic' | 'toggle' | 'trigger';
|
|
7
|
-
export type OverlayCloseReason = 'escape' | 'outside-click' | 'programmatic' | 'toggle';
|
|
8
|
-
export type OverlayChangeContext = {
|
|
9
|
-
reason: OverlayCloseReason | OverlayOpenReason;
|
|
10
|
-
};
|
|
11
14
|
export type OverlayControlOptions = {
|
|
12
15
|
getBoundaryElement: () => HTMLElement | null;
|
|
13
16
|
getPanelElement?: () => HTMLElement | null;
|
|
@@ -18,18 +21,20 @@ export type OverlayControlOptions = {
|
|
|
18
21
|
onOpen?: (reason: OverlayOpenReason) => void;
|
|
19
22
|
positioner?: OverlayPositioner;
|
|
20
23
|
restoreFocus?: boolean | (() => boolean);
|
|
21
|
-
setOpen: (next: boolean, context:
|
|
24
|
+
setOpen: (next: boolean, context: {
|
|
25
|
+
reason: OverlayOpenReason | OverlayCloseReason;
|
|
26
|
+
}) => void;
|
|
22
27
|
};
|
|
23
28
|
export type OverlayControl = {
|
|
24
|
-
|
|
25
|
-
close: (opts?: {
|
|
29
|
+
close(opts?: {
|
|
26
30
|
reason?: OverlayCloseReason;
|
|
27
31
|
restoreFocus?: boolean;
|
|
28
|
-
})
|
|
29
|
-
open
|
|
32
|
+
}): void;
|
|
33
|
+
open(opts?: {
|
|
30
34
|
reason?: OverlayOpenReason;
|
|
31
|
-
})
|
|
32
|
-
toggle
|
|
35
|
+
}): void;
|
|
36
|
+
toggle(): void;
|
|
33
37
|
};
|
|
34
38
|
export declare const createOverlayControl: (options: OverlayControlOptions) => OverlayControl;
|
|
35
|
-
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=overlay-control.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay-control.d.ts","sourceRoot":"","sources":["../../src/controls/overlay-control.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,SAAS,CAAC;AAC3D,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,eAAe,GAAG,cAAc,GAAG,OAAO,GAAG,SAAS,CAAC;AACnG,MAAM,MAAM,kBAAkB,GAAG;IAAE,MAAM,EAAE,kBAAkB,CAAA;CAAE,CAAC;AAChE,MAAM,MAAM,iBAAiB,GAAG;IAAE,MAAM,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAE9D,KAAK,iBAAiB,GAAG;IACvB,QAAQ,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACpC,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,kBAAkB,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IAC7C,eAAe,CAAC,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IAC3C,iBAAiB,CAAC,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC;IAC3B,MAAM,EAAE,MAAM,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC/C,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,YAAY,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;IACzC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE;QAAE,MAAM,EAAE,iBAAiB,GAAG,kBAAkB,CAAA;KAAE,KAAK,IAAI,CAAC;CAC/F,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,kBAAkB,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5E,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,iBAAiB,CAAA;KAAE,GAAG,IAAI,CAAC;IAClD,MAAM,IAAI,IAAI,CAAC;CAChB,CAAC;AA2CF,eAAO,MAAM,oBAAoB,GAAI,SAAS,qBAAqB,KAAG,cA2GrE,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{autoUpdate as e}from"@vielzeug/floatit";var t=new Set,n=null,r=e=>e instanceof Error&&e.message.includes(`[stateit] Cannot read disposed computed signal`),i=()=>{if(n)return;let e=e=>{for(let n of[...t])try{n(e)}catch(e){if(r(e)){t.delete(n);continue}throw e}a()};document.addEventListener(`click`,e,{capture:!0}),n=()=>{document.removeEventListener(`click`,e,{capture:!0}),n=null}},a=()=>{t.size===0&&n&&n()},o=n=>{let o=null,s=null,c=()=>typeof n.restoreFocus==`function`?n.restoreFocus():n.restoreFocus??!0,l=(e,o)=>{try{n.setOpen(e,o)}catch(e){if(r(e)&&s){t.delete(s),a();return}throw e}e&&s?(t.add(s),i()):!e&&s&&(t.delete(s),a())},u=(t={})=>{let r=t.reason??`programmatic`;if(!(n.isDisabled?.()||n.isOpen())){if(l(!0,{reason:r}),n.positioner){let t=n.positioner.reference(),r=n.positioner.floating();t&&r&&(o=e(t,r,()=>n.positioner?.update())),n.positioner.update()}n.onOpen?.(r)}},d=(e={})=>{let t=e.reason??`programmatic`;n.isOpen()&&(l(!1,{reason:t}),o&&=(o(),null),(e.restoreFocus??c())&&n.getTriggerElement?.()?.focus(),n.onClose?.(t))};return s=e=>{if(!n.isOpen())return;let t=e.composedPath?.()??[],r=n.getBoundaryElement(),i=n.getPanelElement?.()??null,a=t[0]??e.target,o=a instanceof Node?a:null,s=t.some(e=>e===r||e===i),c=o?(r?.contains(o)??!1)||(i?.contains(o)??!1):!1;s||c||d({reason:`outside-click`})},{close:d,open:u,toggle:()=>{if(n.isOpen()){d({reason:`trigger`});return}u({reason:`trigger`})}}};export{o as createOverlayControl};
|
|
2
|
+
//# sourceMappingURL=overlay-control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay-control.js","names":[],"sources":["../../src/controls/overlay-control.ts"],"sourcesContent":["import { autoUpdate } from '@vielzeug/floatit';\n\nexport type OverlayOpenReason = 'programmatic' | 'trigger';\nexport type OverlayCloseReason = 'escape' | 'outside-click' | 'programmatic' | 'swipe' | 'trigger';\nexport type OverlayCloseDetail = { reason: OverlayCloseReason };\nexport type OverlayOpenDetail = { reason: OverlayOpenReason };\n\ntype OverlayPositioner = {\n floating: () => HTMLElement | null;\n reference: () => HTMLElement | null;\n update: () => void;\n};\n\nexport type OverlayControlOptions = {\n getBoundaryElement: () => HTMLElement | null;\n getPanelElement?: () => HTMLElement | null;\n getTriggerElement?: () => HTMLElement | null;\n isDisabled?: () => boolean;\n isOpen: () => boolean;\n onClose?: (reason: OverlayCloseReason) => void;\n onOpen?: (reason: OverlayOpenReason) => void;\n positioner?: OverlayPositioner;\n restoreFocus?: boolean | (() => boolean);\n setOpen: (next: boolean, context: { reason: OverlayOpenReason | OverlayCloseReason }) => void;\n};\n\nexport type OverlayControl = {\n close(opts?: { reason?: OverlayCloseReason; restoreFocus?: boolean }): void;\n open(opts?: { reason?: OverlayOpenReason }): void;\n toggle(): void;\n};\n\n// Module-level document click listener management for outside-click dismissal\nconst activeOverlayListeners = new Set<(event: Event) => void>();\nlet documentClickUnsubscribe: (() => void) | null = null;\n\nconst isDisposedComputedSignalError = (error: unknown): boolean =>\n error instanceof Error && error.message.includes('[stateit] Cannot read disposed computed signal');\n\nconst ensureDocumentClickListener = (): void => {\n if (documentClickUnsubscribe) return; // Already attached\n\n const handler = (event: Event) => {\n for (const listener of [...activeOverlayListeners]) {\n try {\n listener(event);\n } catch (error) {\n if (isDisposedComputedSignalError(error)) {\n activeOverlayListeners.delete(listener);\n\n continue;\n }\n\n throw error;\n }\n }\n\n removeDocumentClickListener();\n };\n\n document.addEventListener('click', handler, { capture: true });\n documentClickUnsubscribe = () => {\n document.removeEventListener('click', handler, { capture: true });\n documentClickUnsubscribe = null;\n };\n};\n\nconst removeDocumentClickListener = (): void => {\n if (activeOverlayListeners.size === 0 && documentClickUnsubscribe) {\n documentClickUnsubscribe();\n }\n};\n\nexport const createOverlayControl = (options: OverlayControlOptions): OverlayControl => {\n let positionerCleanup: (() => void) | null = null;\n let clickListener: ((event: Event) => void) | null = null;\n\n const shouldRestoreFocus = (): boolean => {\n if (typeof options.restoreFocus === 'function') return options.restoreFocus();\n\n return options.restoreFocus ?? true;\n };\n\n // Local wrapper — handles click-listener registration without mutating options.\n const commitOpen = (next: boolean, context: { reason: OverlayOpenReason | OverlayCloseReason }): void => {\n try {\n options.setOpen(next, context);\n } catch (error) {\n if (isDisposedComputedSignalError(error) && clickListener) {\n activeOverlayListeners.delete(clickListener);\n removeDocumentClickListener();\n\n return;\n }\n\n throw error;\n }\n\n if (next && clickListener) {\n activeOverlayListeners.add(clickListener);\n ensureDocumentClickListener();\n } else if (!next && clickListener) {\n activeOverlayListeners.delete(clickListener);\n removeDocumentClickListener();\n }\n };\n\n const open = (opts: { reason?: OverlayOpenReason } = {}): void => {\n const reason = opts.reason ?? 'programmatic';\n\n if (options.isDisabled?.() || options.isOpen()) return;\n\n commitOpen(true, { reason });\n\n if (options.positioner) {\n const reference = options.positioner.reference();\n const floating = options.positioner.floating();\n\n if (reference && floating) {\n positionerCleanup = autoUpdate(reference, floating, () => options.positioner?.update());\n }\n\n options.positioner.update();\n }\n\n options.onOpen?.(reason);\n };\n\n const close = (opts: { reason?: OverlayCloseReason; restoreFocus?: boolean } = {}): void => {\n const reason = opts.reason ?? 'programmatic';\n\n if (!options.isOpen()) return;\n\n commitOpen(false, { reason });\n\n if (positionerCleanup) {\n positionerCleanup();\n positionerCleanup = null;\n }\n\n const restore = opts.restoreFocus ?? shouldRestoreFocus();\n\n if (restore) options.getTriggerElement?.()?.focus();\n\n options.onClose?.(reason);\n };\n\n const toggle = (): void => {\n if (options.isOpen()) {\n close({ reason: 'trigger' });\n\n return;\n }\n\n open({ reason: 'trigger' });\n };\n\n clickListener = (event: Event) => {\n if (!options.isOpen()) return;\n\n const path = (event as Event & { composedPath?: () => EventTarget[] }).composedPath?.() ?? [];\n const boundary = options.getBoundaryElement();\n const panel = options.getPanelElement?.() ?? null;\n const target = (path[0] ?? event.target) as EventTarget | null;\n const nodeTarget = target instanceof Node ? target : null;\n\n const insideByPath = path.some((entry) => entry === boundary || entry === panel);\n const insideByContainment = nodeTarget\n ? (boundary?.contains(nodeTarget) ?? false) || (panel?.contains(nodeTarget) ?? false)\n : false;\n const inside = insideByPath || insideByContainment;\n\n if (!inside) close({ reason: 'outside-click' });\n };\n\n return {\n close,\n open,\n toggle,\n };\n};\n"],"mappings":"+CAiCA,IAAM,EAAyB,IAAI,IAC/B,EAAgD,KAE9C,EAAiC,GACrC,aAAiB,OAAS,EAAM,QAAQ,SAAS,gDAAgD,EAE7F,MAA0C,CAC9C,GAAI,EAA0B,OAE9B,IAAM,EAAW,GAAiB,CAChC,IAAK,IAAM,IAAY,CAAC,GAAG,CAAsB,EAC/C,GAAI,CACF,EAAS,CAAK,CAChB,OAAS,EAAO,CACd,GAAI,EAA8B,CAAK,EAAG,CACxC,EAAuB,OAAO,CAAQ,EAEtC,QACF,CAEA,MAAM,CACR,CAGF,EAA4B,CAC9B,EAEA,SAAS,iBAAiB,QAAS,EAAS,CAAE,QAAS,EAAK,CAAC,EAC7D,MAAiC,CAC/B,SAAS,oBAAoB,QAAS,EAAS,CAAE,QAAS,EAAK,CAAC,EAChE,EAA2B,IAC7B,CACF,EAEM,MAA0C,CAC1C,EAAuB,OAAS,GAAK,GACvC,EAAyB,CAE7B,EAEa,EAAwB,GAAmD,CACtF,IAAI,EAAyC,KACzC,EAAiD,KAE/C,MACA,OAAO,EAAQ,cAAiB,WAAmB,EAAQ,aAAa,EAErE,EAAQ,cAAgB,GAI3B,GAAc,EAAe,IAAsE,CACvG,GAAI,CACF,EAAQ,QAAQ,EAAM,CAAO,CAC/B,OAAS,EAAO,CACd,GAAI,EAA8B,CAAK,GAAK,EAAe,CACzD,EAAuB,OAAO,CAAa,EAC3C,EAA4B,EAE5B,MACF,CAEA,MAAM,CACR,CAEI,GAAQ,GACV,EAAuB,IAAI,CAAa,EACxC,EAA4B,GACnB,CAAC,GAAQ,IAClB,EAAuB,OAAO,CAAa,EAC3C,EAA4B,EAEhC,EAEM,GAAQ,EAAuC,CAAC,IAAY,CAChE,IAAM,EAAS,EAAK,QAAU,eAE1B,OAAQ,aAAa,GAAK,EAAQ,OAAO,GAI7C,IAFA,EAAW,GAAM,CAAE,QAAO,CAAC,EAEvB,EAAQ,WAAY,CACtB,IAAM,EAAY,EAAQ,WAAW,UAAU,EACzC,EAAW,EAAQ,WAAW,SAAS,EAEzC,GAAa,IACf,EAAoB,EAAW,EAAW,MAAgB,EAAQ,YAAY,OAAO,CAAC,GAGxF,EAAQ,WAAW,OAAO,CAC5B,CAEA,EAAQ,SAAS,CAAM,CAFvB,CAGF,EAEM,GAAS,EAAgE,CAAC,IAAY,CAC1F,IAAM,EAAS,EAAK,QAAU,eAEzB,EAAQ,OAAO,IAEpB,EAAW,GAAO,CAAE,QAAO,CAAC,EAE5B,AAEE,KADA,EAAkB,EACE,OAGN,EAAK,cAAgB,EAAmB,IAE3C,EAAQ,oBAAoB,GAAG,MAAM,EAElD,EAAQ,UAAU,CAAM,EAC1B,EA8BA,MAlBA,GAAiB,GAAiB,CAChC,GAAI,CAAC,EAAQ,OAAO,EAAG,OAEvB,IAAM,EAAQ,EAAyD,eAAe,GAAK,CAAC,EACtF,EAAW,EAAQ,mBAAmB,EACtC,EAAQ,EAAQ,kBAAkB,GAAK,KACvC,EAAU,EAAK,IAAM,EAAM,OAC3B,EAAa,aAAkB,KAAO,EAAS,KAE/C,EAAe,EAAK,KAAM,GAAU,IAAU,GAAY,IAAU,CAAK,EACzE,EAAsB,GACvB,GAAU,SAAS,CAAU,GAAK,MAAW,GAAO,SAAS,CAAU,GAAK,IAC7E,GACW,GAAgB,GAElB,EAAM,CAAE,OAAQ,eAAgB,CAAC,CAChD,EAEO,CACL,QACA,OACA,WA/ByB,CACzB,GAAI,EAAQ,OAAO,EAAG,CACpB,EAAM,CAAE,OAAQ,SAAU,CAAC,EAE3B,MACF,CAEA,EAAK,CAAE,OAAQ,SAAU,CAAC,CAC5B,CAwBA,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`./list-control.cjs`),t=require(`./overlay-control.cjs`),n=require(`../host.cjs`);let r=require(`@vielzeug/stateit`);var i=i=>{let a=e.createListControl({disabled:()=>!i.isOpen(),getIndex:i.getIndex,getItems:i.getItems,isItemDisabled:i.isItemDisabled,keys:i.keyboardMapping,loop:i.loop??!0,onNavigate:(e,t,n)=>{t>=0&&i.onNavigate?.(e,t,n)},setIndex:i.setIndex}),o=t.createOverlayControl({getBoundaryElement:i.getBoundaryElement,getPanelElement:i.getPanelElement,getTriggerElement:i.getTriggerElement,isDisabled:i.isDisabled,isOpen:i.isOpen,onClose:i.onClose,onOpen:i.onOpen,positioner:i.positioner,restoreFocus:i.restoreFocus,setOpen:i.setOpen}),s=(e=`listbox`)=>e===`menu`?{defaultTriggerAttributes:{controls:()=>i.listId,haspopup:`menu`},defaultTriggerRole:`button`}:{defaultTriggerAttributes:{controls:()=>i.listId,haspopup:`listbox`},defaultTriggerRole:`combobox`},c=(e,t)=>{let r=s(t?.role??i.ariaSync?.role??`listbox`);return n.syncAria(e,{...r.defaultTriggerAttributes,...t?.additional,...i.ariaSync?.additional,disabled:()=>i.isDisabled?.()??!1,expanded:()=>i.isOpen()?`true`:`false`,role:r.defaultTriggerRole})};return i.triggerRef&&(0,r.watch)(()=>i.triggerRef?.value,e=>{if(e)return c(e,i.ariaSync)}),{close:e=>o.close({reason:e??`programmatic`}),first:a.first,getActiveItem:a.getActiveItem,handleListKeydown:a.handleKeydown,last:a.last,next:a.next,open:e=>o.open({reason:e??`programmatic`}),prev:a.prev,reset:a.reset,set:a.set,toggle:o.toggle}};exports.createPopupListControl=i;
|
|
2
|
+
//# sourceMappingURL=popup-list-control.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popup-list-control.cjs","names":[],"sources":["../../src/controls/popup-list-control.ts"],"sourcesContent":["import { watch } from '@vielzeug/stateit';\n\nimport type { OverlayCloseReason, OverlayOpenReason } from './overlay-control';\n\nimport { syncAria } from '../host';\nimport { createListControl } from './list-control';\nimport { createOverlayControl } from './overlay-control';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * ARIA role for the popup list. Affects which attributes are synced to the trigger.\n * - 'listbox': For select/combobox (role=\"combobox\" on trigger, role=\"listbox\" on panel)\n * - 'menu': For menus (role=\"menu\" on trigger, role=\"menu\" on panel)\n */\nexport type PopupListRole = 'listbox' | 'menu';\n\n/**\n * Configuration for syncing ARIA attributes to the trigger element.\n * Automatically handles role-specific attributes like aria-haspopup.\n */\nexport type PopupListAriaSyncConfig = {\n /**\n * Additional ARIA attributes to sync to the trigger beyond the standard ones.\n * These will be merged with role-specific defaults.\n */\n additional?: Record<\n string,\n boolean | null | number | string | undefined | (() => boolean | null | number | string | undefined)\n >;\n /**\n * The role of the popup list component. Controls which ARIA attributes are synced.\n * @default 'listbox'\n */\n role?: PopupListRole;\n};\n\n/**\n * Configuration for positioning the popup list using floatit.\n */\nexport type PopupListPositioner = {\n /** Gets the floating element (popup panel) */\n floating: () => HTMLElement | null;\n /** Get the reference element (trigger) */\n reference: () => HTMLElement | null;\n /** Update the position (called on open and during auto-update) */\n update: () => void;\n};\n\n/**\n * Main options for createPopupListControl.\n */\nexport type PopupListControlOptions<T> = {\n // ARIA\n /** Configuration for ARIA attributes */\n ariaSync?: PopupListAriaSyncConfig;\n // Overlay\n /** Get the boundary element (for click-outside detection) */\n getBoundaryElement: () => HTMLElement | null;\n\n // List state\n /** Get the current focused index (-1 if none focused) */\n getIndex: () => number;\n /** Get all items in the list */\n getItems: () => T[];\n /** Get the panel element */\n getPanelElement: () => HTMLElement | null;\n\n /** Get the trigger element */\n getTriggerElement: () => HTMLElement | null;\n // Overlay state\n /** Check if disabled */\n isDisabled?: () => boolean;\n\n // List navigation\n /** Check if an item is disabled */\n isItemDisabled?: (item: T, index: number) => boolean;\n\n /** Whether the popup is open */\n isOpen: () => boolean;\n /** Custom keyboard mappings for list navigation. Defaults to arrow keys. */\n keyboardMapping?:\n | (() => Partial<Record<'first' | 'last' | 'next' | 'prev', string[]>>)\n | Partial<Record<'first' | 'last' | 'next' | 'prev', string[]>>;\n\n /** The ID of the list element (for aria-controls/aria-owns) */\n listId?: string;\n\n /** Whether list navigation should loop (wrap around). @default true */\n loop?: boolean;\n\n /** Called when popup closes */\n onClose?: (reason: OverlayCloseReason) => void;\n\n /** Called when keyboard navigation is invoked */\n onNavigate?: (action: 'first' | 'last' | 'next' | 'prev', index: number, event: KeyboardEvent) => void;\n\n /** Called when popup opens */\n onOpen?: (reason: OverlayOpenReason) => void;\n\n /** Positioner for floating/positioning library integration */\n positioner?: PopupListPositioner;\n\n /** Whether to restore focus to trigger when closing. @default true */\n restoreFocus?: boolean | (() => boolean);\n\n /** Set the focused index */\n setIndex: (index: number) => void;\n\n /** Set open state */\n setOpen: (next: boolean, context: { reason: OverlayCloseReason | OverlayOpenReason }) => void;\n\n /** Ref-like trigger element used for internal ARIA syncing. */\n triggerRef?: { value: HTMLElement | null };\n};\n\n/**\n * Handle returned by createPopupListControl.\n * Provides methods to control the popup and list, and exposes underlying controls for advanced use.\n */\nexport type PopupListControl<T> = {\n /** Close the popup */\n close(reason?: OverlayCloseReason): void;\n\n // List navigation methods\n /** Move to first enabled item */\n first(): void;\n\n /** Get currently focused item */\n getActiveItem(): T | undefined;\n\n /** Handle keydown events for list navigation. Returns true if event was handled. */\n handleListKeydown(event: KeyboardEvent): boolean;\n\n /** Move to last enabled item */\n last(): void;\n\n /** Move to next enabled item */\n next(): void;\n\n /** Open the popup */\n open(reason?: OverlayOpenReason): void;\n\n /** Move to previous enabled item */\n prev(): void;\n\n /** Reset to no selection */\n reset(): void;\n\n /** Move to specific index */\n set(index: number): void;\n\n /** Toggle popup open/closed state */\n toggle(): void;\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Creates a unified popup list control that manages overlay positioning, list navigation,\n * keyboard handling, and ARIA attributes for select, combobox, and menu components.\n *\n * This composable eliminates duplication across components by providing a single,\n * flexible API for:\n * - Opening/closing popups with proper positioning\n * - Navigating lists with keyboard (arrow keys, Home, End)\n * - Syncing ARIA attributes for accessibility\n * - Managing focus restoration\n * - Handling outside-click dismissal\n *\n * @example\n * ```ts\n * // In a select component setup\n * const popupList = createPopupListControl({\n * // List state\n * getIndex: () => focusedIndex.value,\n * getItems: () => options.value,\n * setIndex: (idx) => { focusedIndex.value = idx; },\n *\n * // Elements\n * getBoundaryElement: () => host.el,\n * getTriggerElement: () => triggerEl,\n * getPanelElement: () => panelEl,\n * isOpen: () => isOpen.value,\n * setOpen: (next, ctx) => { isOpen.value = next; },\n *\n * // Positioning\n * positioner: {\n * reference: () => triggerEl,\n * floating: () => panelEl,\n * update: () => positioner.updatePosition(),\n * },\n *\n * // ARIA\n * ariaSync: { role: 'listbox' },\n * listId: `${selectId}-listbox`,\n * });\n *\n * // Keyboard events\n * function handleKeydown(e: KeyboardEvent) {\n * if (popupList.handleListKeydown(e)) return; // Consumed by navigation\n * // Custom keydown logic\n * }\n * ```\n */\nexport const createPopupListControl = <T>(options: PopupListControlOptions<T>): PopupListControl<T> => {\n // Create underlying list control\n const list = createListControl<T>({\n disabled: () => !options.isOpen(),\n getIndex: options.getIndex,\n getItems: options.getItems,\n isItemDisabled: options.isItemDisabled,\n keys: options.keyboardMapping,\n loop: options.loop ?? true,\n onNavigate: (action, index, event) => {\n if (index >= 0) {\n options.onNavigate?.(action, index, event);\n }\n },\n setIndex: options.setIndex,\n });\n\n // Create underlying overlay control\n const overlay = createOverlayControl({\n getBoundaryElement: options.getBoundaryElement,\n getPanelElement: options.getPanelElement,\n getTriggerElement: options.getTriggerElement,\n isDisabled: options.isDisabled,\n isOpen: options.isOpen,\n onClose: options.onClose,\n onOpen: options.onOpen,\n positioner: options.positioner,\n restoreFocus: options.restoreFocus,\n setOpen: options.setOpen,\n });\n\n // ─────────────────────────────────────────────────────────────────────────\n // ARIA Syncing\n // ─────────────────────────────────────────────────────────────────────────\n\n const getRoleConfig = (role: PopupListRole = 'listbox') => {\n if (role === 'menu') {\n return {\n defaultTriggerAttributes: {\n controls: () => options.listId,\n haspopup: 'menu' as const,\n },\n defaultTriggerRole: 'button' as const,\n };\n }\n\n return {\n defaultTriggerAttributes: {\n controls: () => options.listId,\n haspopup: 'listbox' as const,\n },\n defaultTriggerRole: 'combobox' as const,\n };\n };\n\n const syncTriggerAria = (trigger: Element, config?: PopupListAriaSyncConfig): (() => void) => {\n const role = config?.role ?? options.ariaSync?.role ?? 'listbox';\n const roleConfig = getRoleConfig(role);\n\n return syncAria(trigger, {\n ...roleConfig.defaultTriggerAttributes,\n ...config?.additional,\n ...options.ariaSync?.additional,\n disabled: () => options.isDisabled?.() ?? false,\n expanded: () => (options.isOpen() ? 'true' : 'false'),\n role: roleConfig.defaultTriggerRole,\n });\n };\n\n if (options.triggerRef) {\n watch(\n () => options.triggerRef?.value,\n (trigger) => {\n if (!trigger) return;\n\n return syncTriggerAria(trigger, options.ariaSync);\n },\n );\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Public API\n // ─────────────────────────────────────────────────────────────────────────\n\n return {\n close: (reason) => overlay.close({ reason: reason ?? 'programmatic' }),\n first: list.first,\n getActiveItem: list.getActiveItem,\n handleListKeydown: list.handleKeydown,\n last: list.last,\n next: list.next,\n open: (reason) => overlay.open({ reason: reason ?? 'programmatic' }),\n prev: list.prev,\n reset: list.reset,\n set: list.set,\n toggle: overlay.toggle,\n };\n};\n"],"mappings":"qIAiNA,IAAa,EAA6B,GAA6D,CAErG,IAAM,EAAO,EAAA,kBAAqB,CAChC,aAAgB,CAAC,EAAQ,OAAO,EAChC,SAAU,EAAQ,SAClB,SAAU,EAAQ,SAClB,eAAgB,EAAQ,eACxB,KAAM,EAAQ,gBACd,KAAM,EAAQ,MAAQ,GACtB,YAAa,EAAQ,EAAO,IAAU,CAChC,GAAS,GACX,EAAQ,aAAa,EAAQ,EAAO,CAAK,CAE7C,EACA,SAAU,EAAQ,QACpB,CAAC,EAGK,EAAU,EAAA,qBAAqB,CACnC,mBAAoB,EAAQ,mBAC5B,gBAAiB,EAAQ,gBACzB,kBAAmB,EAAQ,kBAC3B,WAAY,EAAQ,WACpB,OAAQ,EAAQ,OAChB,QAAS,EAAQ,QACjB,OAAQ,EAAQ,OAChB,WAAY,EAAQ,WACpB,aAAc,EAAQ,aACtB,QAAS,EAAQ,OACnB,CAAC,EAMK,GAAiB,EAAsB,YACvC,IAAS,OACJ,CACL,yBAA0B,CACxB,aAAgB,EAAQ,OACxB,SAAU,MACZ,EACA,mBAAoB,QACtB,EAGK,CACL,yBAA0B,CACxB,aAAgB,EAAQ,OACxB,SAAU,SACZ,EACA,mBAAoB,UACtB,EAGI,GAAmB,EAAkB,IAAmD,CAE5F,IAAM,EAAa,EADN,GAAQ,MAAQ,EAAQ,UAAU,MAAQ,SAClB,EAErC,OAAO,EAAA,SAAS,EAAS,CACvB,GAAG,EAAW,yBACd,GAAG,GAAQ,WACX,GAAG,EAAQ,UAAU,WACrB,aAAgB,EAAQ,aAAa,GAAK,GAC1C,aAAiB,EAAQ,OAAO,EAAI,OAAS,QAC7C,KAAM,EAAW,kBACnB,CAAC,CACH,EAiBA,OAfI,EAAQ,aACV,EAAA,EAAA,WACQ,EAAQ,YAAY,MACzB,GAAY,CACN,KAEL,OAAO,EAAgB,EAAS,EAAQ,QAAQ,CAClD,CACF,EAOK,CACL,MAAQ,GAAW,EAAQ,MAAM,CAAE,OAAQ,GAAU,cAAe,CAAC,EACrE,MAAO,EAAK,MACZ,cAAe,EAAK,cACpB,kBAAmB,EAAK,cACxB,KAAM,EAAK,KACX,KAAM,EAAK,KACX,KAAO,GAAW,EAAQ,KAAK,CAAE,OAAQ,GAAU,cAAe,CAAC,EACnE,KAAM,EAAK,KACX,MAAO,EAAK,MACZ,IAAK,EAAK,IACV,OAAQ,EAAQ,MAClB,CACF"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import type { OverlayCloseReason, OverlayOpenReason } from './overlay-control';
|
|
2
|
+
/**
|
|
3
|
+
* ARIA role for the popup list. Affects which attributes are synced to the trigger.
|
|
4
|
+
* - 'listbox': For select/combobox (role="combobox" on trigger, role="listbox" on panel)
|
|
5
|
+
* - 'menu': For menus (role="menu" on trigger, role="menu" on panel)
|
|
6
|
+
*/
|
|
7
|
+
export type PopupListRole = 'listbox' | 'menu';
|
|
8
|
+
/**
|
|
9
|
+
* Configuration for syncing ARIA attributes to the trigger element.
|
|
10
|
+
* Automatically handles role-specific attributes like aria-haspopup.
|
|
11
|
+
*/
|
|
12
|
+
export type PopupListAriaSyncConfig = {
|
|
13
|
+
/**
|
|
14
|
+
* Additional ARIA attributes to sync to the trigger beyond the standard ones.
|
|
15
|
+
* These will be merged with role-specific defaults.
|
|
16
|
+
*/
|
|
17
|
+
additional?: Record<string, boolean | null | number | string | undefined | (() => boolean | null | number | string | undefined)>;
|
|
18
|
+
/**
|
|
19
|
+
* The role of the popup list component. Controls which ARIA attributes are synced.
|
|
20
|
+
* @default 'listbox'
|
|
21
|
+
*/
|
|
22
|
+
role?: PopupListRole;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Configuration for positioning the popup list using floatit.
|
|
26
|
+
*/
|
|
27
|
+
export type PopupListPositioner = {
|
|
28
|
+
/** Gets the floating element (popup panel) */
|
|
29
|
+
floating: () => HTMLElement | null;
|
|
30
|
+
/** Get the reference element (trigger) */
|
|
31
|
+
reference: () => HTMLElement | null;
|
|
32
|
+
/** Update the position (called on open and during auto-update) */
|
|
33
|
+
update: () => void;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Main options for createPopupListControl.
|
|
37
|
+
*/
|
|
38
|
+
export type PopupListControlOptions<T> = {
|
|
39
|
+
/** Configuration for ARIA attributes */
|
|
40
|
+
ariaSync?: PopupListAriaSyncConfig;
|
|
41
|
+
/** Get the boundary element (for click-outside detection) */
|
|
42
|
+
getBoundaryElement: () => HTMLElement | null;
|
|
43
|
+
/** Get the current focused index (-1 if none focused) */
|
|
44
|
+
getIndex: () => number;
|
|
45
|
+
/** Get all items in the list */
|
|
46
|
+
getItems: () => T[];
|
|
47
|
+
/** Get the panel element */
|
|
48
|
+
getPanelElement: () => HTMLElement | null;
|
|
49
|
+
/** Get the trigger element */
|
|
50
|
+
getTriggerElement: () => HTMLElement | null;
|
|
51
|
+
/** Check if disabled */
|
|
52
|
+
isDisabled?: () => boolean;
|
|
53
|
+
/** Check if an item is disabled */
|
|
54
|
+
isItemDisabled?: (item: T, index: number) => boolean;
|
|
55
|
+
/** Whether the popup is open */
|
|
56
|
+
isOpen: () => boolean;
|
|
57
|
+
/** Custom keyboard mappings for list navigation. Defaults to arrow keys. */
|
|
58
|
+
keyboardMapping?: (() => Partial<Record<'first' | 'last' | 'next' | 'prev', string[]>>) | Partial<Record<'first' | 'last' | 'next' | 'prev', string[]>>;
|
|
59
|
+
/** The ID of the list element (for aria-controls/aria-owns) */
|
|
60
|
+
listId?: string;
|
|
61
|
+
/** Whether list navigation should loop (wrap around). @default true */
|
|
62
|
+
loop?: boolean;
|
|
63
|
+
/** Called when popup closes */
|
|
64
|
+
onClose?: (reason: OverlayCloseReason) => void;
|
|
65
|
+
/** Called when keyboard navigation is invoked */
|
|
66
|
+
onNavigate?: (action: 'first' | 'last' | 'next' | 'prev', index: number, event: KeyboardEvent) => void;
|
|
67
|
+
/** Called when popup opens */
|
|
68
|
+
onOpen?: (reason: OverlayOpenReason) => void;
|
|
69
|
+
/** Positioner for floating/positioning library integration */
|
|
70
|
+
positioner?: PopupListPositioner;
|
|
71
|
+
/** Whether to restore focus to trigger when closing. @default true */
|
|
72
|
+
restoreFocus?: boolean | (() => boolean);
|
|
73
|
+
/** Set the focused index */
|
|
74
|
+
setIndex: (index: number) => void;
|
|
75
|
+
/** Set open state */
|
|
76
|
+
setOpen: (next: boolean, context: {
|
|
77
|
+
reason: OverlayCloseReason | OverlayOpenReason;
|
|
78
|
+
}) => void;
|
|
79
|
+
/** Ref-like trigger element used for internal ARIA syncing. */
|
|
80
|
+
triggerRef?: {
|
|
81
|
+
value: HTMLElement | null;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Handle returned by createPopupListControl.
|
|
86
|
+
* Provides methods to control the popup and list, and exposes underlying controls for advanced use.
|
|
87
|
+
*/
|
|
88
|
+
export type PopupListControl<T> = {
|
|
89
|
+
/** Close the popup */
|
|
90
|
+
close(reason?: OverlayCloseReason): void;
|
|
91
|
+
/** Move to first enabled item */
|
|
92
|
+
first(): void;
|
|
93
|
+
/** Get currently focused item */
|
|
94
|
+
getActiveItem(): T | undefined;
|
|
95
|
+
/** Handle keydown events for list navigation. Returns true if event was handled. */
|
|
96
|
+
handleListKeydown(event: KeyboardEvent): boolean;
|
|
97
|
+
/** Move to last enabled item */
|
|
98
|
+
last(): void;
|
|
99
|
+
/** Move to next enabled item */
|
|
100
|
+
next(): void;
|
|
101
|
+
/** Open the popup */
|
|
102
|
+
open(reason?: OverlayOpenReason): void;
|
|
103
|
+
/** Move to previous enabled item */
|
|
104
|
+
prev(): void;
|
|
105
|
+
/** Reset to no selection */
|
|
106
|
+
reset(): void;
|
|
107
|
+
/** Move to specific index */
|
|
108
|
+
set(index: number): void;
|
|
109
|
+
/** Toggle popup open/closed state */
|
|
110
|
+
toggle(): void;
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Creates a unified popup list control that manages overlay positioning, list navigation,
|
|
114
|
+
* keyboard handling, and ARIA attributes for select, combobox, and menu components.
|
|
115
|
+
*
|
|
116
|
+
* This composable eliminates duplication across components by providing a single,
|
|
117
|
+
* flexible API for:
|
|
118
|
+
* - Opening/closing popups with proper positioning
|
|
119
|
+
* - Navigating lists with keyboard (arrow keys, Home, End)
|
|
120
|
+
* - Syncing ARIA attributes for accessibility
|
|
121
|
+
* - Managing focus restoration
|
|
122
|
+
* - Handling outside-click dismissal
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```ts
|
|
126
|
+
* // In a select component setup
|
|
127
|
+
* const popupList = createPopupListControl({
|
|
128
|
+
* // List state
|
|
129
|
+
* getIndex: () => focusedIndex.value,
|
|
130
|
+
* getItems: () => options.value,
|
|
131
|
+
* setIndex: (idx) => { focusedIndex.value = idx; },
|
|
132
|
+
*
|
|
133
|
+
* // Elements
|
|
134
|
+
* getBoundaryElement: () => host.el,
|
|
135
|
+
* getTriggerElement: () => triggerEl,
|
|
136
|
+
* getPanelElement: () => panelEl,
|
|
137
|
+
* isOpen: () => isOpen.value,
|
|
138
|
+
* setOpen: (next, ctx) => { isOpen.value = next; },
|
|
139
|
+
*
|
|
140
|
+
* // Positioning
|
|
141
|
+
* positioner: {
|
|
142
|
+
* reference: () => triggerEl,
|
|
143
|
+
* floating: () => panelEl,
|
|
144
|
+
* update: () => positioner.updatePosition(),
|
|
145
|
+
* },
|
|
146
|
+
*
|
|
147
|
+
* // ARIA
|
|
148
|
+
* ariaSync: { role: 'listbox' },
|
|
149
|
+
* listId: `${selectId}-listbox`,
|
|
150
|
+
* });
|
|
151
|
+
*
|
|
152
|
+
* // Keyboard events
|
|
153
|
+
* function handleKeydown(e: KeyboardEvent) {
|
|
154
|
+
* if (popupList.handleListKeydown(e)) return; // Consumed by navigation
|
|
155
|
+
* // Custom keydown logic
|
|
156
|
+
* }
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
export declare const createPopupListControl: <T>(options: PopupListControlOptions<T>) => PopupListControl<T>;
|
|
160
|
+
//# sourceMappingURL=popup-list-control.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popup-list-control.d.ts","sourceRoot":"","sources":["../../src/controls/popup-list-control.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createListControl as e}from"./list-control.js";import{createOverlayControl as t}from"./overlay-control.js";import{syncAria as n}from"../host.js";import{watch as r}from"@vielzeug/stateit";var i=i=>{let a=e({disabled:()=>!i.isOpen(),getIndex:i.getIndex,getItems:i.getItems,isItemDisabled:i.isItemDisabled,keys:i.keyboardMapping,loop:i.loop??!0,onNavigate:(e,t,n)=>{t>=0&&i.onNavigate?.(e,t,n)},setIndex:i.setIndex}),o=t({getBoundaryElement:i.getBoundaryElement,getPanelElement:i.getPanelElement,getTriggerElement:i.getTriggerElement,isDisabled:i.isDisabled,isOpen:i.isOpen,onClose:i.onClose,onOpen:i.onOpen,positioner:i.positioner,restoreFocus:i.restoreFocus,setOpen:i.setOpen}),s=(e=`listbox`)=>e===`menu`?{defaultTriggerAttributes:{controls:()=>i.listId,haspopup:`menu`},defaultTriggerRole:`button`}:{defaultTriggerAttributes:{controls:()=>i.listId,haspopup:`listbox`},defaultTriggerRole:`combobox`},c=(e,t)=>{let r=s(t?.role??i.ariaSync?.role??`listbox`);return n(e,{...r.defaultTriggerAttributes,...t?.additional,...i.ariaSync?.additional,disabled:()=>i.isDisabled?.()??!1,expanded:()=>i.isOpen()?`true`:`false`,role:r.defaultTriggerRole})};return i.triggerRef&&r(()=>i.triggerRef?.value,e=>{if(e)return c(e,i.ariaSync)}),{close:e=>o.close({reason:e??`programmatic`}),first:a.first,getActiveItem:a.getActiveItem,handleListKeydown:a.handleKeydown,last:a.last,next:a.next,open:e=>o.open({reason:e??`programmatic`}),prev:a.prev,reset:a.reset,set:a.set,toggle:o.toggle}};export{i as createPopupListControl};
|
|
2
|
+
//# sourceMappingURL=popup-list-control.js.map
|