@vielzeug/refine 1.7.0 → 2.0.0
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 +5 -12
- package/dist/_dev.cjs +1 -1
- package/dist/_dev.cjs.map +1 -1
- package/dist/_dev.js +2 -5
- package/dist/_dev.js.map +1 -1
- package/dist/_virtual/_rolldown/runtime.cjs +1 -1
- package/dist/accordion-item.cjs +26 -24
- package/dist/accordion-item.cjs.map +1 -1
- package/dist/accordion-item.js +53 -51
- package/dist/accordion-item.js.map +1 -1
- package/dist/accordion.cjs +3 -1
- package/dist/accordion.cjs.map +1 -1
- package/dist/accordion.js +5 -3
- package/dist/accordion.js.map +1 -1
- package/dist/alert.cjs +1 -1
- package/dist/alert.cjs.map +1 -1
- package/dist/alert.js +1 -1
- package/dist/alert.js.map +1 -1
- package/dist/async.cjs +14 -12
- package/dist/async.cjs.map +1 -1
- package/dist/async.js +5 -4
- package/dist/async.js.map +1 -1
- package/dist/avatar-group.cjs +7 -0
- package/dist/avatar-group.cjs.map +1 -0
- package/dist/avatar-group.js +34 -0
- package/dist/avatar-group.js.map +1 -0
- package/dist/avatar.cjs +21 -16
- package/dist/avatar.cjs.map +1 -1
- package/dist/avatar.js +38 -54
- package/dist/avatar.js.map +1 -1
- package/dist/badge.cjs +4 -2
- package/dist/badge.cjs.map +1 -1
- package/dist/badge.js +6 -4
- package/dist/badge.js.map +1 -1
- package/dist/box.cjs +3 -1
- package/dist/box.cjs.map +1 -1
- package/dist/box.js +24 -18
- package/dist/box.js.map +1 -1
- package/dist/breadcrumb.cjs +4 -4
- package/dist/breadcrumb.cjs.map +1 -1
- package/dist/breadcrumb.js +4 -4
- package/dist/breadcrumb.js.map +1 -1
- package/dist/button-group.cjs +3 -3
- package/dist/button-group.cjs.map +1 -1
- package/dist/button-group.js +18 -16
- package/dist/button-group.js.map +1 -1
- package/dist/button.cjs +16 -12
- package/dist/button.cjs.map +1 -1
- package/dist/button.js +31 -28
- package/dist/button.js.map +1 -1
- package/dist/calendar.cjs +94 -69
- package/dist/calendar.cjs.map +1 -1
- package/dist/calendar.js +131 -99
- package/dist/calendar.js.map +1 -1
- package/dist/card.cjs +7 -7
- package/dist/card.cjs.map +1 -1
- package/dist/card.js +53 -49
- package/dist/card.js.map +1 -1
- package/dist/carousel.cjs +65 -57
- package/dist/carousel.cjs.map +1 -1
- package/dist/carousel.js +157 -201
- package/dist/carousel.js.map +1 -1
- package/dist/chat-message.cjs +21 -19
- package/dist/chat-message.cjs.map +1 -1
- package/dist/chat-message.js +18 -15
- package/dist/chat-message.js.map +1 -1
- package/dist/checkbox-group.cjs +12 -10
- package/dist/checkbox-group.cjs.map +1 -1
- package/dist/checkbox-group.js +75 -77
- package/dist/checkbox-group.js.map +1 -1
- package/dist/checkbox.cjs +4 -4
- package/dist/checkbox.cjs.map +1 -1
- package/dist/checkbox.js +55 -55
- package/dist/checkbox.js.map +1 -1
- package/dist/chip.cjs +22 -20
- package/dist/chip.cjs.map +1 -1
- package/dist/chip.js +15 -12
- package/dist/chip.js.map +1 -1
- package/dist/code-window.cjs +11 -5
- package/dist/code-window.cjs.map +1 -1
- package/dist/code-window.js +9 -4
- package/dist/code-window.js.map +1 -1
- package/dist/combobox.cjs +63 -58
- package/dist/combobox.cjs.map +1 -1
- package/dist/combobox.js +199 -197
- package/dist/combobox.js.map +1 -1
- package/dist/command-palette.cjs +49 -29
- package/dist/command-palette.cjs.map +1 -1
- package/dist/command-palette.js +150 -119
- package/dist/command-palette.js.map +1 -1
- package/dist/content/avatar/avatar-group.d.ts +33 -0
- package/dist/content/avatar/avatar-group.d.ts.map +1 -0
- package/dist/content/avatar/avatar.d.ts +0 -32
- package/dist/content/avatar/avatar.d.ts.map +1 -1
- package/dist/content/card/card.css.cjs +1 -1
- package/dist/content/card/card.css.cjs.map +1 -1
- package/dist/content/card/card.css.js +1 -1
- package/dist/content/card/card.css.js.map +1 -1
- package/dist/content/card/card.d.ts.map +1 -1
- package/dist/content/carousel/carousel-slide.cjs +3 -1
- package/dist/content/carousel/carousel-slide.cjs.map +1 -1
- package/dist/content/carousel/carousel-slide.css.cjs +1 -1
- package/dist/content/carousel/carousel-slide.css.cjs.map +1 -1
- package/dist/content/carousel/carousel-slide.css.js +1 -1
- package/dist/content/carousel/carousel-slide.css.js.map +1 -1
- package/dist/content/carousel/carousel-slide.js +3 -1
- package/dist/content/carousel/carousel-slide.js.map +1 -1
- package/dist/content/carousel/carousel.css.cjs +1 -1
- package/dist/content/carousel/carousel.css.cjs.map +1 -1
- package/dist/content/carousel/carousel.css.js +1 -1
- package/dist/content/carousel/carousel.css.js.map +1 -1
- package/dist/content/carousel/carousel.d.ts +5 -19
- package/dist/content/carousel/carousel.d.ts.map +1 -1
- package/dist/content/chat-message/chat-message.d.ts.map +1 -1
- package/dist/content/code-window/code-window.d.ts.map +1 -1
- package/dist/content/icon/icon.d.ts.map +1 -1
- package/dist/content/index.d.ts +10 -2
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/list/_list-item-reveal.cjs +1 -1
- package/dist/content/list/_list-item-reveal.cjs.map +1 -1
- package/dist/content/list/_list-item-reveal.d.ts +2 -2
- package/dist/content/list/_list-item-reveal.d.ts.map +1 -1
- package/dist/content/list/_list-item-reveal.js +2 -2
- package/dist/content/list/_list-item-reveal.js.map +1 -1
- package/dist/content/list/list.d.ts.map +1 -1
- package/dist/content/marquee/index.d.ts +2 -0
- package/dist/content/marquee/index.d.ts.map +1 -0
- package/dist/content/marquee/marquee.css.cjs +2 -0
- package/dist/content/marquee/marquee.css.cjs.map +1 -0
- package/dist/content/marquee/marquee.css.js +6 -0
- package/dist/content/marquee/marquee.css.js.map +1 -0
- package/dist/content/marquee/marquee.d.ts +44 -0
- package/dist/content/marquee/marquee.d.ts.map +1 -0
- package/dist/content/stepper/_is-step-navigable.cjs +2 -0
- package/dist/content/stepper/_is-step-navigable.cjs.map +1 -0
- package/dist/content/stepper/_is-step-navigable.d.ts +14 -0
- package/dist/content/stepper/_is-step-navigable.d.ts.map +1 -0
- package/dist/content/stepper/_is-step-navigable.js +8 -0
- package/dist/content/stepper/_is-step-navigable.js.map +1 -0
- package/dist/content/stepper/step.css.cjs +2 -0
- package/dist/content/stepper/step.css.cjs.map +1 -0
- package/dist/content/stepper/step.css.js +6 -0
- package/dist/content/stepper/step.css.js.map +1 -0
- package/dist/content/stepper/step.d.ts +63 -0
- package/dist/content/stepper/step.d.ts.map +1 -0
- package/dist/content/stepper/stepper.css.cjs +2 -0
- package/dist/content/stepper/stepper.css.cjs.map +1 -0
- package/dist/content/stepper/stepper.css.js +6 -0
- package/dist/content/stepper/stepper.css.js.map +1 -0
- package/dist/content/stepper/stepper.d.ts +88 -0
- package/dist/content/stepper/stepper.d.ts.map +1 -0
- package/dist/copy-command.cjs +9 -3
- package/dist/copy-command.cjs.map +1 -1
- package/dist/copy-command.js +9 -3
- package/dist/copy-command.js.map +1 -1
- package/dist/{headless → core}/_internal.d.ts +3 -5
- package/dist/core/_internal.d.ts.map +1 -0
- package/dist/core/announcer.cjs.map +1 -0
- package/dist/core/announcer.d.ts.map +1 -0
- package/dist/{headless → core}/announcer.js +1 -1
- package/dist/core/announcer.js.map +1 -0
- package/dist/core/aria-reflection.cjs.map +1 -0
- package/dist/core/aria-reflection.d.ts.map +1 -0
- package/dist/{headless → core}/aria-reflection.js +1 -1
- package/dist/core/aria-reflection.js.map +1 -0
- package/dist/core/auto-resize.cjs.map +1 -0
- package/dist/core/auto-resize.d.ts.map +1 -0
- package/dist/{headless → core}/auto-resize.js +1 -1
- package/dist/core/auto-resize.js.map +1 -0
- package/dist/core/checkable.cjs.map +1 -0
- package/dist/core/checkable.d.ts.map +1 -0
- package/dist/{headless → core}/checkable.js +1 -1
- package/dist/core/checkable.js.map +1 -0
- package/dist/core/choice-field.cjs +2 -0
- package/dist/core/choice-field.cjs.map +1 -0
- package/dist/core/choice-field.d.ts.map +1 -0
- package/dist/{headless → core}/choice-field.js +3 -2
- package/dist/core/choice-field.js.map +1 -0
- package/dist/core/composer.cjs.map +1 -0
- package/dist/core/composer.d.ts.map +1 -0
- package/dist/{headless → core}/composer.js +1 -1
- package/dist/core/composer.js.map +1 -0
- package/dist/{headless → core}/date-picker.cjs +1 -1
- package/dist/core/date-picker.cjs.map +1 -0
- package/dist/core/date-picker.d.ts.map +1 -0
- package/dist/{headless → core}/date-picker.js +3 -3
- package/dist/core/date-picker.js.map +1 -0
- package/dist/core/direction.cjs.map +1 -0
- package/dist/core/direction.d.ts.map +1 -0
- package/dist/{headless → core}/direction.js +1 -1
- package/dist/core/direction.js.map +1 -0
- package/dist/core/field-base.cjs +2 -0
- package/dist/core/field-base.cjs.map +1 -0
- package/dist/{headless → core}/field-base.d.ts +3 -3
- package/dist/core/field-base.d.ts.map +1 -0
- package/dist/core/field-base.js +66 -0
- package/dist/core/field-base.js.map +1 -0
- package/dist/core/focus.cjs.map +1 -0
- package/dist/core/focus.d.ts.map +1 -0
- package/dist/{headless → core}/focus.js +1 -1
- package/dist/core/focus.js.map +1 -0
- package/dist/{headless → core}/index.d.ts +7 -6
- package/dist/core/index.d.ts.map +1 -0
- package/dist/{headless → core}/keyboard.cjs +1 -1
- package/dist/core/keyboard.cjs.map +1 -0
- package/dist/core/keyboard.d.ts.map +1 -0
- package/dist/{headless → core}/keyboard.js +2 -2
- package/dist/core/keyboard.js.map +1 -0
- package/dist/core/light-dom.cjs.map +1 -0
- package/dist/core/light-dom.d.ts.map +1 -0
- package/dist/{headless → core}/light-dom.js +1 -1
- package/dist/core/light-dom.js.map +1 -0
- package/dist/core/nav.cjs.map +1 -0
- package/dist/core/nav.d.ts.map +1 -0
- package/dist/{headless → core}/nav.js +1 -1
- package/dist/core/nav.js.map +1 -0
- package/dist/core/numbers.cjs.map +1 -0
- package/dist/core/numbers.d.ts.map +1 -0
- package/dist/{headless → core}/numbers.js +1 -1
- package/dist/core/numbers.js.map +1 -0
- package/dist/core/option-list.cjs +2 -0
- package/dist/core/option-list.cjs.map +1 -0
- package/dist/core/option-list.d.ts +49 -0
- package/dist/core/option-list.d.ts.map +1 -0
- package/dist/core/option-list.js +65 -0
- package/dist/core/option-list.js.map +1 -0
- package/dist/core/ore.cjs.map +1 -0
- package/dist/{headless → core}/ore.d.ts +2 -2
- package/dist/core/ore.d.ts.map +1 -0
- package/dist/{headless → core}/ore.js +1 -1
- package/dist/core/ore.js.map +1 -0
- package/dist/core/overlay.cjs +2 -0
- package/dist/core/overlay.cjs.map +1 -0
- package/dist/core/overlay.d.ts +41 -0
- package/dist/core/overlay.d.ts.map +1 -0
- package/dist/core/overlay.js +17 -0
- package/dist/core/overlay.js.map +1 -0
- package/dist/core/paginated-list.d.ts.map +1 -0
- package/dist/core/parse.cjs.map +1 -0
- package/dist/core/parse.d.ts.map +1 -0
- package/dist/{headless → core}/parse.js +1 -1
- package/dist/core/parse.js.map +1 -0
- package/dist/core/positioner.cjs +2 -0
- package/dist/core/positioner.cjs.map +1 -0
- package/dist/core/positioner.d.ts.map +1 -0
- package/dist/{headless → core}/positioner.js +7 -7
- package/dist/core/positioner.js.map +1 -0
- package/dist/core/ref-callback.cjs +2 -0
- package/dist/core/ref-callback.cjs.map +1 -0
- package/dist/core/ref-callback.d.ts.map +1 -0
- package/dist/{headless → core}/ref-callback.js +2 -2
- package/dist/core/ref-callback.js.map +1 -0
- package/dist/core/signals.cjs +2 -0
- package/dist/core/signals.cjs.map +1 -0
- package/dist/core/signals.d.ts.map +1 -0
- package/dist/{headless → core}/signals.js +3 -2
- package/dist/core/signals.js.map +1 -0
- package/dist/core/slider.cjs +2 -0
- package/dist/core/slider.cjs.map +1 -0
- package/dist/core/slider.d.ts.map +1 -0
- package/dist/{headless → core}/slider.js +3 -3
- package/dist/{headless/slider.cjs.map → core/slider.js.map} +1 -1
- package/dist/core/spinner.cjs +2 -0
- package/dist/core/spinner.cjs.map +1 -0
- package/dist/core/spinner.d.ts.map +1 -0
- package/dist/{headless → core}/spinner.js +3 -3
- package/dist/{headless/spinner.cjs.map → core/spinner.js.map} +1 -1
- package/dist/core/swipe.cjs.map +1 -0
- package/dist/core/swipe.d.ts.map +1 -0
- package/dist/{headless → core}/swipe.js +1 -1
- package/dist/core/swipe.js.map +1 -0
- package/dist/core/text-field.cjs.map +1 -0
- package/dist/core/text-field.d.ts.map +1 -0
- package/dist/{headless → core}/text-field.js +1 -1
- package/dist/core/text-field.js.map +1 -0
- package/dist/core/typeahead.cjs.map +1 -0
- package/dist/core/typeahead.d.ts.map +1 -0
- package/dist/{headless → core}/typeahead.js +1 -1
- package/dist/core/typeahead.js.map +1 -0
- package/dist/custom-elements.json +4433 -3949
- package/dist/datagrid.cjs +320 -271
- package/dist/datagrid.cjs.map +1 -1
- package/dist/datagrid.js +489 -435
- package/dist/datagrid.js.map +1 -1
- package/dist/date-picker.cjs +77 -62
- package/dist/date-picker.cjs.map +1 -1
- package/dist/date-picker.js +169 -146
- package/dist/date-picker.js.map +1 -1
- package/dist/dialog.cjs +3 -3
- package/dist/dialog.cjs.map +1 -1
- package/dist/dialog.js +18 -7
- package/dist/dialog.js.map +1 -1
- package/dist/disclosure/tab-panel/tab-panel.d.ts.map +1 -1
- package/dist/disclosure/tabs/tabs.d.ts.map +1 -1
- package/dist/drawer.cjs +5 -5
- package/dist/drawer.cjs.map +1 -1
- package/dist/drawer.js +52 -47
- package/dist/drawer.js.map +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/feedback/async/async.d.ts.map +1 -1
- package/dist/feedback/chip/chip.d.ts.map +1 -1
- package/dist/feedback/progress/progress.d.ts.map +1 -1
- package/dist/feedback/skeleton/skeleton.d.ts.map +1 -1
- package/dist/feedback/toast/index.d.ts +1 -1
- package/dist/feedback/toast/index.d.ts.map +1 -1
- package/dist/feedback/toast/toast.d.ts +16 -68
- package/dist/feedback/toast/toast.d.ts.map +1 -1
- package/dist/feedback/typing-indicator/typing-indicator.d.ts +1 -1
- package/dist/file-input.cjs +111 -78
- package/dist/file-input.cjs.map +1 -1
- package/dist/file-input.js +220 -184
- package/dist/file-input.js.map +1 -1
- package/dist/grid-item.cjs +3 -1
- package/dist/grid-item.cjs.map +1 -1
- package/dist/grid-item.js +3 -1
- package/dist/grid-item.js.map +1 -1
- package/dist/grid.cjs +3 -1
- package/dist/grid.cjs.map +1 -1
- package/dist/grid.js +6 -5
- package/dist/grid.js.map +1 -1
- package/dist/icon.cjs +5 -1
- package/dist/icon.cjs.map +1 -1
- package/dist/icon.js +50 -47
- package/dist/icon.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +7 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -67
- package/dist/input.cjs +44 -40
- package/dist/input.cjs.map +1 -1
- package/dist/input.js +132 -130
- package/dist/input.js.map +1 -1
- package/dist/inputs/button-group/button-group.css.cjs +1 -1
- package/dist/inputs/button-group/button-group.css.cjs.map +1 -1
- package/dist/inputs/button-group/button-group.css.js +1 -1
- package/dist/inputs/button-group/button-group.css.js.map +1 -1
- package/dist/inputs/button-group/button-group.d.ts +6 -3
- package/dist/inputs/button-group/button-group.d.ts.map +1 -1
- package/dist/inputs/calendar/calendar.d.ts.map +1 -1
- package/dist/inputs/checkbox/checkbox.d.ts +6 -5
- package/dist/inputs/checkbox/checkbox.d.ts.map +1 -1
- package/dist/inputs/checkbox-group/checkbox-group.d.ts +1 -1
- package/dist/inputs/checkbox-group/checkbox-group.d.ts.map +1 -1
- package/dist/inputs/combobox/combobox.d.ts +4 -2
- package/dist/inputs/combobox/combobox.d.ts.map +1 -1
- package/dist/inputs/combobox/combobox.types.d.ts +5 -7
- package/dist/inputs/combobox/combobox.types.d.ts.map +1 -1
- package/dist/inputs/datagrid/datagrid-column.cjs +1 -1
- package/dist/inputs/datagrid/datagrid-column.cjs.map +1 -1
- package/dist/inputs/datagrid/datagrid-column.d.ts +1 -1
- package/dist/inputs/datagrid/datagrid-column.d.ts.map +1 -1
- package/dist/inputs/datagrid/datagrid-column.js +5 -5
- package/dist/inputs/datagrid/datagrid-column.js.map +1 -1
- package/dist/inputs/datagrid/datagrid-model.cjs +2 -0
- package/dist/inputs/datagrid/datagrid-model.cjs.map +1 -0
- package/dist/inputs/datagrid/datagrid-model.d.ts +101 -0
- package/dist/inputs/datagrid/datagrid-model.d.ts.map +1 -0
- package/dist/inputs/datagrid/datagrid-model.js +202 -0
- package/dist/inputs/datagrid/datagrid-model.js.map +1 -0
- package/dist/inputs/datagrid/datagrid-nav.cjs.map +1 -1
- package/dist/inputs/datagrid/datagrid.d.ts +44 -45
- package/dist/inputs/datagrid/datagrid.d.ts.map +1 -1
- package/dist/inputs/date-picker/date-picker.d.ts +4 -4
- package/dist/inputs/date-picker/date-picker.d.ts.map +1 -1
- package/dist/inputs/file-input/file-input-upload.cjs +1 -1
- package/dist/inputs/file-input/file-input-upload.cjs.map +1 -1
- package/dist/inputs/file-input/file-input-upload.d.ts.map +1 -1
- package/dist/inputs/file-input/file-input-upload.js +6 -3
- package/dist/inputs/file-input/file-input-upload.js.map +1 -1
- package/dist/inputs/file-input/file-input.d.ts.map +1 -1
- package/dist/inputs/index.d.ts +0 -4
- package/dist/inputs/index.d.ts.map +1 -1
- package/dist/inputs/input/input.d.ts +4 -10
- package/dist/inputs/input/input.d.ts.map +1 -1
- package/dist/inputs/message-composer/message-composer.d.ts +6 -10
- package/dist/inputs/message-composer/message-composer.d.ts.map +1 -1
- package/dist/inputs/number-input/number-input.d.ts +4 -10
- package/dist/inputs/number-input/number-input.d.ts.map +1 -1
- package/dist/inputs/otp-input/otp-input.d.ts +4 -6
- package/dist/inputs/otp-input/otp-input.d.ts.map +1 -1
- package/dist/inputs/radio/radio.d.ts +4 -3
- package/dist/inputs/radio/radio.d.ts.map +1 -1
- package/dist/inputs/radio-group/radio-group.d.ts +1 -1
- package/dist/inputs/radio-group/radio-group.d.ts.map +1 -1
- package/dist/inputs/rating/rating.d.ts +4 -5
- package/dist/inputs/rating/rating.d.ts.map +1 -1
- package/dist/inputs/select/select.d.ts +7 -9
- package/dist/inputs/select/select.d.ts.map +1 -1
- package/dist/inputs/shared/field-binding.cjs.map +1 -1
- package/dist/inputs/shared/field-binding.d.ts +1 -1
- package/dist/inputs/shared/field-binding.js.map +1 -1
- package/dist/inputs/shared/index.d.ts +0 -1
- package/dist/inputs/shared/index.d.ts.map +1 -1
- package/dist/inputs/shared/native-field-event.cjs +2 -0
- package/dist/inputs/shared/native-field-event.cjs.map +1 -0
- package/dist/inputs/shared/native-field-event.d.ts +6 -0
- package/dist/inputs/shared/native-field-event.d.ts.map +1 -0
- package/dist/inputs/shared/native-field-event.js +29 -0
- package/dist/inputs/shared/native-field-event.js.map +1 -0
- package/dist/inputs/shared/templates.cjs +18 -12
- package/dist/inputs/shared/templates.cjs.map +1 -1
- package/dist/inputs/shared/templates.d.ts +1 -1
- package/dist/inputs/shared/templates.d.ts.map +1 -1
- package/dist/inputs/shared/templates.js +19 -13
- package/dist/inputs/shared/templates.js.map +1 -1
- package/dist/inputs/slider/slider.css.cjs +1 -1
- package/dist/inputs/slider/slider.css.cjs.map +1 -1
- package/dist/inputs/slider/slider.css.js +1 -1
- package/dist/inputs/slider/slider.css.js.map +1 -1
- package/dist/inputs/slider/slider.d.ts +7 -10
- package/dist/inputs/slider/slider.d.ts.map +1 -1
- package/dist/inputs/switch/switch.d.ts +4 -3
- package/dist/inputs/switch/switch.d.ts.map +1 -1
- package/dist/inputs/textarea/textarea.d.ts +4 -10
- package/dist/inputs/textarea/textarea.d.ts.map +1 -1
- package/dist/inputs/time-picker/time-picker.d.ts +4 -4
- package/dist/inputs/time-picker/time-picker.d.ts.map +1 -1
- package/dist/layout/box/box.d.ts.map +1 -1
- package/dist/layout/navbar/navbar.css.cjs +1 -1
- package/dist/layout/navbar/navbar.css.cjs.map +1 -1
- package/dist/layout/navbar/navbar.css.js +1 -1
- package/dist/layout/navbar/navbar.css.js.map +1 -1
- package/dist/layout/navbar/navbar.d.ts +10 -0
- package/dist/layout/navbar/navbar.d.ts.map +1 -1
- package/dist/layout/sidebar/sidebar-item.css.cjs +1 -1
- package/dist/layout/sidebar/sidebar-item.css.cjs.map +1 -1
- package/dist/layout/sidebar/sidebar-item.css.js +1 -1
- package/dist/layout/sidebar/sidebar-item.css.js.map +1 -1
- package/dist/layout/sidebar/sidebar.css.cjs +1 -1
- package/dist/layout/sidebar/sidebar.css.cjs.map +1 -1
- package/dist/layout/sidebar/sidebar.css.js +1 -1
- package/dist/layout/sidebar/sidebar.css.js.map +1 -1
- package/dist/layout/sidebar/sidebar.d.ts.map +1 -1
- package/dist/list-item.cjs +2 -2
- package/dist/list-item.cjs.map +1 -1
- package/dist/list-item.js +4 -4
- package/dist/list-item.js.map +1 -1
- package/dist/list.cjs +3 -1
- package/dist/list.cjs.map +1 -1
- package/dist/list.js +28 -25
- package/dist/list.js.map +1 -1
- package/dist/marquee.cjs +32 -0
- package/dist/marquee.cjs.map +1 -0
- package/dist/marquee.js +93 -0
- package/dist/marquee.js.map +1 -0
- package/dist/menu.cjs +7 -7
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.js +135 -103
- package/dist/menu.js.map +1 -1
- package/dist/message-composer.cjs +33 -31
- package/dist/message-composer.cjs.map +1 -1
- package/dist/message-composer.js +123 -123
- package/dist/message-composer.js.map +1 -1
- package/dist/navbar.cjs +17 -15
- package/dist/navbar.cjs.map +1 -1
- package/dist/navbar.js +108 -89
- package/dist/navbar.js.map +1 -1
- package/dist/number-input.cjs +19 -19
- package/dist/number-input.cjs.map +1 -1
- package/dist/number-input.js +94 -97
- package/dist/number-input.js.map +1 -1
- package/dist/otp-input.cjs +15 -13
- package/dist/otp-input.cjs.map +1 -1
- package/dist/otp-input.js +83 -82
- package/dist/otp-input.js.map +1 -1
- package/dist/overlay/command-palette/command-palette.d.ts +2 -2
- package/dist/overlay/command-palette/command-palette.d.ts.map +1 -1
- package/dist/overlay/command-palette/command-palette.types.d.ts +4 -5
- package/dist/overlay/command-palette/command-palette.types.d.ts.map +1 -1
- package/dist/overlay/dialog/dialog.d.ts +7 -6
- package/dist/overlay/dialog/dialog.d.ts.map +1 -1
- package/dist/overlay/drawer/drawer.d.ts +7 -10
- package/dist/overlay/drawer/drawer.d.ts.map +1 -1
- package/dist/overlay/index.d.ts +1 -1
- package/dist/overlay/index.d.ts.map +1 -1
- package/dist/overlay/menu/menu.d.ts +9 -7
- package/dist/overlay/menu/menu.d.ts.map +1 -1
- package/dist/overlay/popover/popover.d.ts +6 -11
- package/dist/overlay/popover/popover.d.ts.map +1 -1
- package/dist/overlay/shared/await-exit.cjs +1 -1
- package/dist/overlay/shared/await-exit.cjs.map +1 -1
- package/dist/overlay/shared/await-exit.js +1 -1
- package/dist/overlay/shared/await-exit.js.map +1 -1
- package/dist/overlay/shared/use-dialog.cjs +1 -1
- package/dist/overlay/shared/use-dialog.cjs.map +1 -1
- package/dist/overlay/shared/use-dialog.d.ts +13 -3
- package/dist/overlay/shared/use-dialog.d.ts.map +1 -1
- package/dist/overlay/shared/use-dialog.js +80 -72
- package/dist/overlay/shared/use-dialog.js.map +1 -1
- package/dist/overlay/shared/use-floating-trigger.cjs +1 -1
- package/dist/overlay/shared/use-floating-trigger.cjs.map +1 -1
- package/dist/overlay/shared/use-floating-trigger.d.ts +3 -1
- package/dist/overlay/shared/use-floating-trigger.d.ts.map +1 -1
- package/dist/overlay/shared/use-floating-trigger.js +64 -70
- package/dist/overlay/shared/use-floating-trigger.js.map +1 -1
- package/dist/overlay/tooltip/tooltip.d.ts +9 -0
- package/dist/overlay/tooltip/tooltip.d.ts.map +1 -1
- package/dist/pagination.cjs +69 -55
- package/dist/pagination.cjs.map +1 -1
- package/dist/pagination.js +70 -56
- package/dist/pagination.js.map +1 -1
- package/dist/password-strength.cjs +6 -4
- package/dist/password-strength.cjs.map +1 -1
- package/dist/password-strength.js +6 -4
- package/dist/password-strength.js.map +1 -1
- package/dist/popover.cjs +4 -4
- package/dist/popover.cjs.map +1 -1
- package/dist/popover.js +28 -17
- package/dist/popover.js.map +1 -1
- package/dist/progress.cjs +25 -17
- package/dist/progress.cjs.map +1 -1
- package/dist/progress.js +29 -22
- package/dist/progress.js.map +1 -1
- package/dist/radio-group.cjs +14 -12
- package/dist/radio-group.cjs.map +1 -1
- package/dist/radio-group.js +88 -90
- package/dist/radio-group.js.map +1 -1
- package/dist/radio.cjs +4 -4
- package/dist/radio.cjs.map +1 -1
- package/dist/radio.js +90 -90
- package/dist/radio.js.map +1 -1
- package/dist/rating.cjs +18 -16
- package/dist/rating.cjs.map +1 -1
- package/dist/rating.js +69 -69
- package/dist/rating.js.map +1 -1
- package/dist/refine.cjs +1670 -1325
- package/dist/refine.cjs.map +1 -1
- package/dist/refine.iife.js +1673 -1328
- package/dist/refine.iife.js.map +1 -1
- package/dist/refine.js +1758 -1413
- package/dist/refine.js.map +1 -1
- package/dist/register.d.ts +21 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/select.cjs +59 -51
- package/dist/select.cjs.map +1 -1
- package/dist/select.js +189 -182
- package/dist/select.js.map +1 -1
- package/dist/separator.cjs +9 -9
- package/dist/separator.cjs.map +1 -1
- package/dist/separator.js +9 -9
- package/dist/separator.js.map +1 -1
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/prop-bundles.cjs +1 -1
- package/dist/shared/prop-bundles.cjs.map +1 -1
- package/dist/shared/prop-bundles.js +4 -2
- package/dist/shared/prop-bundles.js.map +1 -1
- package/dist/shared/size-presets.cjs +1 -1
- package/dist/shared/size-presets.cjs.map +1 -1
- package/dist/shared/size-presets.d.ts +3 -0
- package/dist/shared/size-presets.d.ts.map +1 -1
- package/dist/shared/size-presets.js +4 -1
- package/dist/shared/size-presets.js.map +1 -1
- package/dist/sidebar.cjs +8 -8
- package/dist/sidebar.cjs.map +1 -1
- package/dist/sidebar.js +25 -25
- package/dist/sidebar.js.map +1 -1
- package/dist/skeleton.cjs +4 -6
- package/dist/skeleton.cjs.map +1 -1
- package/dist/skeleton.js +21 -22
- package/dist/skeleton.js.map +1 -1
- package/dist/slider.cjs +14 -14
- package/dist/slider.cjs.map +1 -1
- package/dist/slider.js +160 -168
- package/dist/slider.js.map +1 -1
- package/dist/step.cjs +47 -0
- package/dist/step.cjs.map +1 -0
- package/dist/step.js +127 -0
- package/dist/step.js.map +1 -0
- package/dist/stepper.cjs +8 -0
- package/dist/stepper.cjs.map +1 -0
- package/dist/stepper.js +101 -0
- package/dist/stepper.js.map +1 -0
- package/dist/styles/index.d.ts +0 -28
- package/dist/styles/index.d.ts.map +1 -1
- package/dist/styles/mixins/shape.css.cjs +2 -2
- package/dist/styles/mixins/shape.css.cjs.map +1 -1
- package/dist/styles/mixins/shape.css.js +6 -6
- package/dist/styles/mixins/shape.css.js.map +1 -1
- package/dist/styles/tokens.css +4 -0
- package/dist/switch.cjs +4 -4
- package/dist/switch.cjs.map +1 -1
- package/dist/switch.js +44 -44
- package/dist/switch.js.map +1 -1
- package/dist/tab-item.cjs +3 -3
- package/dist/tab-item.cjs.map +1 -1
- package/dist/tab-item.js +24 -24
- package/dist/tab-item.js.map +1 -1
- package/dist/tab-panel.cjs +9 -7
- package/dist/tab-panel.cjs.map +1 -1
- package/dist/tab-panel.js +15 -14
- package/dist/tab-panel.js.map +1 -1
- package/dist/table.cjs +3 -1
- package/dist/table.cjs.map +1 -1
- package/dist/table.js +3 -1
- package/dist/table.js.map +1 -1
- package/dist/tabs.cjs +6 -6
- package/dist/tabs.cjs.map +1 -1
- package/dist/tabs.js +30 -29
- package/dist/tabs.js.map +1 -1
- package/dist/text.cjs +3 -1
- package/dist/text.cjs.map +1 -1
- package/dist/text.js +3 -1
- package/dist/text.js.map +1 -1
- package/dist/textarea.cjs +22 -22
- package/dist/textarea.cjs.map +1 -1
- package/dist/textarea.js +110 -112
- package/dist/textarea.js.map +1 -1
- package/dist/time-picker.cjs +69 -61
- package/dist/time-picker.cjs.map +1 -1
- package/dist/time-picker.js +146 -132
- package/dist/time-picker.js.map +1 -1
- package/dist/toast.cjs +22 -22
- package/dist/toast.cjs.map +1 -1
- package/dist/toast.js +280 -173
- package/dist/toast.js.map +1 -1
- package/dist/tooltip.cjs +5 -5
- package/dist/tooltip.cjs.map +1 -1
- package/dist/tooltip.js +61 -51
- package/dist/tooltip.js.map +1 -1
- package/dist/typing-indicator.cjs +1 -1
- package/dist/typing-indicator.cjs.map +1 -1
- package/dist/typing-indicator.js +5 -5
- package/dist/typing-indicator.js.map +1 -1
- package/dist/utils/link.cjs.map +1 -1
- package/dist/utils/link.d.ts +2 -2
- package/package.json +36 -117
- package/dist/form.cjs +0 -11
- package/dist/form.cjs.map +0 -1
- package/dist/form.js +0 -59
- package/dist/form.js.map +0 -1
- package/dist/headless/_internal.d.ts.map +0 -1
- package/dist/headless/announcer.cjs.map +0 -1
- package/dist/headless/announcer.d.ts.map +0 -1
- package/dist/headless/announcer.js.map +0 -1
- package/dist/headless/aria-reflection.cjs.map +0 -1
- package/dist/headless/aria-reflection.d.ts.map +0 -1
- package/dist/headless/aria-reflection.js.map +0 -1
- package/dist/headless/auto-resize.cjs.map +0 -1
- package/dist/headless/auto-resize.d.ts.map +0 -1
- package/dist/headless/auto-resize.js.map +0 -1
- package/dist/headless/checkable.cjs.map +0 -1
- package/dist/headless/checkable.d.ts.map +0 -1
- package/dist/headless/checkable.js.map +0 -1
- package/dist/headless/choice-field.cjs +0 -2
- package/dist/headless/choice-field.cjs.map +0 -1
- package/dist/headless/choice-field.d.ts.map +0 -1
- package/dist/headless/choice-field.js.map +0 -1
- package/dist/headless/composer.cjs.map +0 -1
- package/dist/headless/composer.d.ts.map +0 -1
- package/dist/headless/composer.js.map +0 -1
- package/dist/headless/datagrid.cjs +0 -2
- package/dist/headless/datagrid.cjs.map +0 -1
- package/dist/headless/datagrid.d.ts +0 -158
- package/dist/headless/datagrid.d.ts.map +0 -1
- package/dist/headless/datagrid.js +0 -55
- package/dist/headless/datagrid.js.map +0 -1
- package/dist/headless/date-picker.cjs.map +0 -1
- package/dist/headless/date-picker.d.ts.map +0 -1
- package/dist/headless/date-picker.js.map +0 -1
- package/dist/headless/direction.cjs.map +0 -1
- package/dist/headless/direction.d.ts.map +0 -1
- package/dist/headless/direction.js.map +0 -1
- package/dist/headless/field-base.cjs +0 -2
- package/dist/headless/field-base.cjs.map +0 -1
- package/dist/headless/field-base.d.ts.map +0 -1
- package/dist/headless/field-base.js +0 -72
- package/dist/headless/field-base.js.map +0 -1
- package/dist/headless/focus.cjs.map +0 -1
- package/dist/headless/focus.d.ts.map +0 -1
- package/dist/headless/focus.js.map +0 -1
- package/dist/headless/index.d.ts.map +0 -1
- package/dist/headless/keyboard.cjs.map +0 -1
- package/dist/headless/keyboard.d.ts.map +0 -1
- package/dist/headless/keyboard.js.map +0 -1
- package/dist/headless/light-dom.cjs.map +0 -1
- package/dist/headless/light-dom.d.ts.map +0 -1
- package/dist/headless/light-dom.js.map +0 -1
- package/dist/headless/nav.cjs.map +0 -1
- package/dist/headless/nav.d.ts.map +0 -1
- package/dist/headless/nav.js.map +0 -1
- package/dist/headless/numbers.cjs.map +0 -1
- package/dist/headless/numbers.d.ts.map +0 -1
- package/dist/headless/numbers.js.map +0 -1
- package/dist/headless/option-list.cjs +0 -2
- package/dist/headless/option-list.cjs.map +0 -1
- package/dist/headless/option-list.d.ts +0 -107
- package/dist/headless/option-list.d.ts.map +0 -1
- package/dist/headless/option-list.js +0 -66
- package/dist/headless/option-list.js.map +0 -1
- package/dist/headless/ore.cjs.map +0 -1
- package/dist/headless/ore.d.ts.map +0 -1
- package/dist/headless/ore.js.map +0 -1
- package/dist/headless/overlay.cjs +0 -2
- package/dist/headless/overlay.cjs.map +0 -1
- package/dist/headless/overlay.d.ts +0 -64
- package/dist/headless/overlay.d.ts.map +0 -1
- package/dist/headless/overlay.js +0 -32
- package/dist/headless/overlay.js.map +0 -1
- package/dist/headless/paginated-list.cjs +0 -2
- package/dist/headless/paginated-list.cjs.map +0 -1
- package/dist/headless/paginated-list.d.ts.map +0 -1
- package/dist/headless/paginated-list.js +0 -34
- package/dist/headless/paginated-list.js.map +0 -1
- package/dist/headless/parse.cjs.map +0 -1
- package/dist/headless/parse.d.ts.map +0 -1
- package/dist/headless/parse.js.map +0 -1
- package/dist/headless/positioner.cjs +0 -2
- package/dist/headless/positioner.cjs.map +0 -1
- package/dist/headless/positioner.d.ts.map +0 -1
- package/dist/headless/positioner.js.map +0 -1
- package/dist/headless/ref-callback.cjs +0 -2
- package/dist/headless/ref-callback.cjs.map +0 -1
- package/dist/headless/ref-callback.d.ts.map +0 -1
- package/dist/headless/ref-callback.js.map +0 -1
- package/dist/headless/selection-control.cjs +0 -2
- package/dist/headless/selection-control.cjs.map +0 -1
- package/dist/headless/selection-control.d.ts +0 -30
- package/dist/headless/selection-control.d.ts.map +0 -1
- package/dist/headless/selection-control.js +0 -44
- package/dist/headless/selection-control.js.map +0 -1
- package/dist/headless/signals.cjs +0 -2
- package/dist/headless/signals.cjs.map +0 -1
- package/dist/headless/signals.d.ts.map +0 -1
- package/dist/headless/signals.js.map +0 -1
- package/dist/headless/slider.cjs +0 -2
- package/dist/headless/slider.d.ts.map +0 -1
- package/dist/headless/slider.js.map +0 -1
- package/dist/headless/sort-control.cjs +0 -2
- package/dist/headless/sort-control.cjs.map +0 -1
- package/dist/headless/sort-control.d.ts +0 -22
- package/dist/headless/sort-control.d.ts.map +0 -1
- package/dist/headless/sort-control.js +0 -36
- package/dist/headless/sort-control.js.map +0 -1
- package/dist/headless/spinner.cjs +0 -2
- package/dist/headless/spinner.d.ts.map +0 -1
- package/dist/headless/spinner.js.map +0 -1
- package/dist/headless/swipe.cjs.map +0 -1
- package/dist/headless/swipe.d.ts.map +0 -1
- package/dist/headless/swipe.js.map +0 -1
- package/dist/headless/text-field.cjs.map +0 -1
- package/dist/headless/text-field.d.ts.map +0 -1
- package/dist/headless/text-field.js.map +0 -1
- package/dist/headless/typeahead.cjs.map +0 -1
- package/dist/headless/typeahead.d.ts.map +0 -1
- package/dist/headless/typeahead.js.map +0 -1
- package/dist/headless.cjs +0 -1
- package/dist/headless.js +0 -30
- package/dist/inputs/datagrid/datagrid-controls.cjs +0 -2
- package/dist/inputs/datagrid/datagrid-controls.cjs.map +0 -1
- package/dist/inputs/datagrid/datagrid-controls.d.ts +0 -98
- package/dist/inputs/datagrid/datagrid-controls.d.ts.map +0 -1
- package/dist/inputs/datagrid/datagrid-controls.js +0 -147
- package/dist/inputs/datagrid/datagrid-controls.js.map +0 -1
- package/dist/inputs/form/form.css.cjs +0 -2
- package/dist/inputs/form/form.css.cjs.map +0 -1
- package/dist/inputs/form/form.css.js +0 -6
- package/dist/inputs/form/form.css.js.map +0 -1
- package/dist/inputs/form/form.d.ts +0 -59
- package/dist/inputs/form/form.d.ts.map +0 -1
- package/dist/inputs/shared/form-context.cjs +0 -2
- package/dist/inputs/shared/form-context.cjs.map +0 -1
- package/dist/inputs/shared/form-context.d.ts +0 -55
- package/dist/inputs/shared/form-context.d.ts.map +0 -1
- package/dist/inputs/shared/form-context.js +0 -15
- package/dist/inputs/shared/form-context.js.map +0 -1
- package/dist/keymap/dist/_dev.cjs +0 -2
- package/dist/keymap/dist/_dev.cjs.map +0 -1
- package/dist/keymap/dist/_dev.js +0 -4
- package/dist/keymap/dist/_dev.js.map +0 -1
- package/dist/keymap/dist/format.cjs +0 -1
- package/dist/keymap/dist/format.js +0 -1
- package/dist/keymap/dist/index.cjs +0 -1
- package/dist/keymap/dist/index.js +0 -4
- package/dist/keymap/dist/keymap.cjs +0 -2
- package/dist/keymap/dist/keymap.cjs.map +0 -1
- package/dist/keymap/dist/keymap.js +0 -221
- package/dist/keymap/dist/keymap.js.map +0 -1
- package/dist/keymap/dist/layer.cjs +0 -1
- package/dist/keymap/dist/layer.js +0 -1
- package/dist/styles/index.cjs +0 -2
- package/dist/styles/index.cjs.map +0 -1
- package/dist/styles/index.js +0 -25
- package/dist/styles/index.js.map +0 -1
- package/dist/styles/styles.css +0 -5
- package/dist/testing/index.d.ts +0 -233
- package/dist/testing/index.d.ts.map +0 -1
- package/dist/testing.cjs +0 -2
- package/dist/testing.cjs.map +0 -1
- package/dist/testing.js +0 -43
- package/dist/testing.js.map +0 -1
- /package/dist/{headless → core}/announcer.cjs +0 -0
- /package/dist/{headless → core}/announcer.d.ts +0 -0
- /package/dist/{headless → core}/aria-reflection.cjs +0 -0
- /package/dist/{headless → core}/aria-reflection.d.ts +0 -0
- /package/dist/{headless → core}/auto-resize.cjs +0 -0
- /package/dist/{headless → core}/auto-resize.d.ts +0 -0
- /package/dist/{headless → core}/checkable.cjs +0 -0
- /package/dist/{headless → core}/checkable.d.ts +0 -0
- /package/dist/{headless → core}/choice-field.d.ts +0 -0
- /package/dist/{headless → core}/composer.cjs +0 -0
- /package/dist/{headless → core}/composer.d.ts +0 -0
- /package/dist/{headless → core}/date-picker.d.ts +0 -0
- /package/dist/{headless → core}/direction.cjs +0 -0
- /package/dist/{headless → core}/direction.d.ts +0 -0
- /package/dist/{headless → core}/focus.cjs +0 -0
- /package/dist/{headless → core}/focus.d.ts +0 -0
- /package/dist/{headless → core}/keyboard.d.ts +0 -0
- /package/dist/{headless → core}/light-dom.cjs +0 -0
- /package/dist/{headless → core}/light-dom.d.ts +0 -0
- /package/dist/{headless → core}/nav.cjs +0 -0
- /package/dist/{headless → core}/nav.d.ts +0 -0
- /package/dist/{headless → core}/numbers.cjs +0 -0
- /package/dist/{headless → core}/numbers.d.ts +0 -0
- /package/dist/{headless → core}/ore.cjs +0 -0
- /package/dist/{headless → core}/paginated-list.d.ts +0 -0
- /package/dist/{headless → core}/parse.cjs +0 -0
- /package/dist/{headless → core}/parse.d.ts +0 -0
- /package/dist/{headless → core}/positioner.d.ts +0 -0
- /package/dist/{headless → core}/ref-callback.d.ts +0 -0
- /package/dist/{headless → core}/signals.d.ts +0 -0
- /package/dist/{headless → core}/slider.d.ts +0 -0
- /package/dist/{headless → core}/spinner.d.ts +0 -0
- /package/dist/{headless → core}/swipe.cjs +0 -0
- /package/dist/{headless → core}/swipe.d.ts +0 -0
- /package/dist/{headless → core}/text-field.cjs +0 -0
- /package/dist/{headless → core}/text-field.d.ts +0 -0
- /package/dist/{headless → core}/typeahead.cjs +0 -0
- /package/dist/{headless → core}/typeahead.d.ts +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"announcer.js","names":[],"sources":["../../src/core/announcer.ts"],"sourcesContent":["// ── Live-Region Announcer ─────────────────────────────────────────────────────\n// Programmatic announcements to screen readers without requiring a visible\n// DOM element. Used for status messages, selection feedback, and count updates\n// that live in components where the visible element is hidden or absent.\n//\n// Two visually-hidden live regions are lazily created in `<body>` — one polite,\n// one assertive. The clear-then-set pattern forces AT to re-read even identical\n// consecutive messages.\n//\n// Usage:\n// ```ts\n// import { announce } from './announcer';\n// announce(`${selectedCount} items selected`);\n// announce('Error: field is required', { politeness: 'assertive' });\n// ```\n\nimport { SR_ONLY_INLINE_STYLE } from '../styles';\n\n// ── Visually-hidden style ─────────────────────────────────────────────────────\n// Shared with the CSS-side `.sr-only` mixin — see `src/styles/mixins/accessibility.css.ts`.\n\n// ── Per-document live regions (WeakMap for automatic GC + test isolation) ────\n\ntype DocumentRegions = { assertive?: HTMLElement; polite?: HTMLElement };\n\nconst _regions = new WeakMap<Document, DocumentRegions>();\nconst _timers = new WeakMap<HTMLElement, ReturnType<typeof setTimeout>>();\n\n/** Creates and mounts a visually-hidden live region with the given politeness. */\nconst createRegion = (politeness: 'assertive' | 'polite', doc: Document): HTMLElement => {\n const el = doc.createElement('div');\n\n el.setAttribute('aria-live', politeness);\n el.setAttribute('aria-atomic', 'true');\n el.setAttribute('data-block-announcer', politeness);\n el.style.cssText = SR_ONLY_INLINE_STYLE;\n\n doc.body.appendChild(el);\n\n return el;\n};\n\n/**\n * Returns the live region for the requested politeness in the given document,\n * creating it on first access. Returns `null` outside a browser context.\n */\nconst getRegion = (politeness: 'assertive' | 'polite', doc: Document): HTMLElement | null => {\n let regions = _regions.get(doc);\n\n // Re-create if this specific region was removed from the DOM (e.g. by test teardown).\n if (regions?.[politeness] && !doc.body.contains(regions[politeness])) {\n regions[politeness] = undefined;\n }\n\n if (!regions?.[politeness]) {\n if (!regions) {\n regions = {};\n _regions.set(doc, regions);\n }\n\n regions[politeness] = createRegion(politeness, doc);\n }\n\n return regions[politeness] ?? null;\n};\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\nexport type AnnouncePoliteness = 'assertive' | 'polite';\n\nexport type AnnounceOptions = {\n /**\n * `'polite'` — queues the announcement; does not interrupt ongoing speech.\n * `'assertive'` — interrupts immediately. Use sparingly; reserve for errors.\n *\n * Default: `'polite'`.\n */\n politeness?: AnnouncePoliteness;\n};\n\n/**\n * Announces a message to screen readers via a singleton visually-hidden\n * live region. Safe to call outside a component lifecycle (no cleanup needed).\n *\n * The clear-then-set pattern (50 ms delay) forces AT to re-read even when the\n * message text hasn't changed.\n *\n * @example\n * ```ts\n * announce('3 results found');\n * announce('Session expired', { politeness: 'assertive' });\n * ```\n */\nexport const announce = (message: string, options: AnnounceOptions = {}): void => {\n if (typeof document === 'undefined') return;\n\n const politeness = options.politeness ?? 'polite';\n const region = getRegion(politeness, document);\n\n if (!region) return;\n\n // Clear immediately so AT registers a content change, then set after a short\n // delay. The 50 ms window is the established minimum for most AT/browser combos.\n region.textContent = '';\n\n // Use a per-element timer so concurrent polite + assertive announcements don't\n // clobber each other's pending writes.\n const existing = _timers.get(region);\n\n if (existing !== undefined) clearTimeout(existing);\n\n const timer = setTimeout(() => {\n region.textContent = message;\n _timers.delete(region);\n }, 50);\n\n _timers.set(region, timer);\n};\n"],"mappings":";;AAyBA,IAAM,oBAAW,IAAI,QAAmC,GAClD,oBAAU,IAAI,QAAoD,GAGlE,KAAgB,GAAoC,MAA+B;CACvF,IAAM,IAAK,EAAI,cAAc,KAAK;CASlC,OAPA,EAAG,aAAa,aAAa,CAAU,GACvC,EAAG,aAAa,eAAe,MAAM,GACrC,EAAG,aAAa,wBAAwB,CAAU,GAClD,EAAG,MAAM,UAAU,GAEnB,EAAI,KAAK,YAAY,CAAE,GAEhB;AACT,GAMM,KAAa,GAAoC,MAAsC;CAC3F,IAAI,IAAU,EAAS,IAAI,CAAG;CAgB9B,OAbI,IAAU,MAAe,CAAC,EAAI,KAAK,SAAS,EAAQ,EAAW,MACjE,EAAQ,KAAc,KAAA,IAGnB,IAAU,OACR,MACH,IAAU,CAAC,GACX,EAAS,IAAI,GAAK,CAAO,IAG3B,EAAQ,KAAc,EAAa,GAAY,CAAG,IAG7C,EAAQ,MAAe;AAChC,GA6Ba,KAAY,GAAiB,IAA2B,CAAC,MAAY;CAChF,IAAI,OAAO,WAAa,KAAa;CAGrC,IAAM,IAAS,EADI,EAAQ,cAAc,UACJ,QAAQ;CAE7C,IAAI,CAAC,GAAQ;CAIb,EAAO,cAAc;CAIrB,IAAM,IAAW,EAAQ,IAAI,CAAM;CAEnC,AAAI,MAAa,KAAA,KAAW,aAAa,CAAQ;CAEjD,IAAM,IAAQ,iBAAiB;EAE7B,AADA,EAAO,cAAc,GACrB,EAAQ,OAAO,CAAM;CACvB,GAAG,EAAE;CAEL,EAAQ,IAAI,GAAQ,CAAK;AAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aria-reflection.cjs","names":[],"sources":["../../src/core/aria-reflection.ts"],"sourcesContent":["/**\n * Cross-shadow-root ARIA relationship helper.\n *\n * Plain IDREF attributes (`aria-controls`, `aria-describedby`, `aria-labelledby`, …) cannot\n * resolve across shadow-tree boundaries — the browser only looks up an `id` within the same\n * root as the referencing element, so `<div id=\"x\">` inside one component's shadow root is\n * invisible to `aria-controls=\"x\"` set inside another component's shadow root, even when both\n * custom elements are siblings in the light DOM. This is a platform limitation, not a bug —\n * see the WICG \"reference target\" explainer for the full rationale.\n *\n * Modern browsers instead expose element-reflection IDL properties\n * (`ariaControlsElements`, `ariaDescribedByElements`, `ariaLabelledByElements`, …) that accept\n * live `Element` references directly, sidestepping ID lookup entirely — these work across\n * shadow roots by design. Where unsupported, this falls back to the plain IDREF attribute,\n * which is still correct for same-root pairings and degrades gracefully (no relationship)\n * for cross-root ones instead of throwing.\n */\n\nexport type AriaReflectionProperty = 'ariaControlsElements' | 'ariaDescribedByElements' | 'ariaLabelledByElements';\n\nconst ATTR_BY_PROPERTY: Record<AriaReflectionProperty, string> = {\n ariaControlsElements: 'aria-controls',\n ariaDescribedByElements: 'aria-describedby',\n ariaLabelledByElements: 'aria-labelledby',\n};\n\n/**\n * Associates `el` with `targets` via the given ARIA relationship. Prefers the\n * cross-shadow-root-safe element-reflection property; falls back to a plain IDREF attribute\n * built from `targets`' own `id`s when the browser doesn't support element reflection yet.\n *\n * Pass an empty array to clear the relationship.\n */\nexport function setAriaReflection(el: Element, property: AriaReflectionProperty, targets: readonly Element[]): void {\n if (property in el) {\n (el as unknown as Record<AriaReflectionProperty, readonly Element[] | null>)[property] =\n targets.length > 0 ? targets : null;\n\n return;\n }\n\n const attr = ATTR_BY_PROPERTY[property];\n const ids = targets.map((target) => target.id).filter(Boolean);\n\n if (ids.length === 0) el.removeAttribute(attr);\n else el.setAttribute(attr, ids.join(' '));\n}\n"],"mappings":"AAoBA,IAAM,EAA2D,CAC/D,qBAAsB,gBACtB,wBAAyB,mBACzB,uBAAwB,iBAC1B,EASA,SAAgB,EAAkB,EAAa,EAAkC,EAAmC,CAClH,GAAI,KAAY,EAAI,CAClB,EAA6E,GAC3E,EAAQ,OAAS,EAAI,EAAU,KAEjC,MACF,CAEA,IAAM,EAAO,EAAiB,GACxB,EAAM,EAAQ,IAAK,GAAW,EAAO,EAAE,CAAC,CAAC,OAAO,OAAO,EAEzD,EAAI,SAAW,EAAG,EAAG,gBAAgB,CAAI,EACxC,EAAG,aAAa,EAAM,EAAI,KAAK,GAAG,CAAC,CAC1C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aria-reflection.d.ts","sourceRoot":"","sources":["../../src/core/aria-reflection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,MAAM,sBAAsB,GAAG,sBAAsB,GAAG,yBAAyB,GAAG,wBAAwB,CAAC;AAQnH;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,sBAAsB,EAAE,OAAO,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI,CAalH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aria-reflection.js","names":[],"sources":["../../src/core/aria-reflection.ts"],"sourcesContent":["/**\n * Cross-shadow-root ARIA relationship helper.\n *\n * Plain IDREF attributes (`aria-controls`, `aria-describedby`, `aria-labelledby`, …) cannot\n * resolve across shadow-tree boundaries — the browser only looks up an `id` within the same\n * root as the referencing element, so `<div id=\"x\">` inside one component's shadow root is\n * invisible to `aria-controls=\"x\"` set inside another component's shadow root, even when both\n * custom elements are siblings in the light DOM. This is a platform limitation, not a bug —\n * see the WICG \"reference target\" explainer for the full rationale.\n *\n * Modern browsers instead expose element-reflection IDL properties\n * (`ariaControlsElements`, `ariaDescribedByElements`, `ariaLabelledByElements`, …) that accept\n * live `Element` references directly, sidestepping ID lookup entirely — these work across\n * shadow roots by design. Where unsupported, this falls back to the plain IDREF attribute,\n * which is still correct for same-root pairings and degrades gracefully (no relationship)\n * for cross-root ones instead of throwing.\n */\n\nexport type AriaReflectionProperty = 'ariaControlsElements' | 'ariaDescribedByElements' | 'ariaLabelledByElements';\n\nconst ATTR_BY_PROPERTY: Record<AriaReflectionProperty, string> = {\n ariaControlsElements: 'aria-controls',\n ariaDescribedByElements: 'aria-describedby',\n ariaLabelledByElements: 'aria-labelledby',\n};\n\n/**\n * Associates `el` with `targets` via the given ARIA relationship. Prefers the\n * cross-shadow-root-safe element-reflection property; falls back to a plain IDREF attribute\n * built from `targets`' own `id`s when the browser doesn't support element reflection yet.\n *\n * Pass an empty array to clear the relationship.\n */\nexport function setAriaReflection(el: Element, property: AriaReflectionProperty, targets: readonly Element[]): void {\n if (property in el) {\n (el as unknown as Record<AriaReflectionProperty, readonly Element[] | null>)[property] =\n targets.length > 0 ? targets : null;\n\n return;\n }\n\n const attr = ATTR_BY_PROPERTY[property];\n const ids = targets.map((target) => target.id).filter(Boolean);\n\n if (ids.length === 0) el.removeAttribute(attr);\n else el.setAttribute(attr, ids.join(' '));\n}\n"],"mappings":";AAoBA,IAAM,IAA2D;CAC/D,sBAAsB;CACtB,yBAAyB;CACzB,wBAAwB;AAC1B;AASA,SAAgB,EAAkB,GAAa,GAAkC,GAAmC;CAClH,IAAI,KAAY,GAAI;EAClB,EAA6E,KAC3E,EAAQ,SAAS,IAAI,IAAU;EAEjC;CACF;CAEA,IAAM,IAAO,EAAiB,IACxB,IAAM,EAAQ,KAAK,MAAW,EAAO,EAAE,CAAC,CAAC,OAAO,OAAO;CAE7D,AAAI,EAAI,WAAW,IAAG,EAAG,gBAAgB,CAAI,IACxC,EAAG,aAAa,GAAM,EAAI,KAAK,GAAG,CAAC;AAC1C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-resize.cjs","names":[],"sources":["../../src/core/auto-resize.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nexport type AutoResizeOptions = {\n /** Gates growth; omit for an always-on composer field, or pass a signal for a toggleable `auto-resize` attribute. */\n enabled?: Readable<boolean | undefined>;\n};\n\nexport type AutoResizeControl = {\n /** Recomputes height from the wired element's current content. No-ops when `enabled` is false or nothing is wired. */\n recompute: () => void;\n /** Wire onto the raw `<textarea>` when it mounts. Returns a detach function to call on unmount. */\n wire: (el: HTMLTextAreaElement) => () => void;\n};\n\n/**\n * Grows a `<textarea>`'s height to fit its content, shared by every component that offers\n * auto-resize (`ore-textarea`, `ore-message-composer`) instead of each reimplementing it.\n *\n * Owns only height — manual-resize cursor/direction is a per-component styling concern, not\n * shared behavior, so it stays local to whichever component renders a resize handle.\n *\n * @example\n * ```ts\n * const autoResize = createAutoResize({ enabled: props['auto-resize'] });\n *\n * onElement(textareaRef, (el) => autoResize.wire(el));\n * // after a programmatic value change that doesn't fire a native `input` event:\n * autoResize.recompute();\n * ```\n */\nexport const createAutoResize = (options: AutoResizeOptions = {}): AutoResizeControl => {\n let wiredEl: HTMLTextAreaElement | null = null;\n\n const recompute = (): void => {\n if (!wiredEl || options.enabled?.value === false) return;\n\n wiredEl.style.height = 'auto';\n wiredEl.style.height = `${wiredEl.scrollHeight}px`;\n };\n\n const wire = (el: HTMLTextAreaElement): (() => void) => {\n wiredEl = el;\n el.addEventListener('input', recompute);\n\n // Deferred a frame: right after mount, the browser hasn't necessarily finished laying out\n // the textarea yet, so a synchronous `scrollHeight` read here can be stale — often reading\n // as 0 and collapsing the field until the next real `input` event happens to correct it.\n // `ore-textarea` has always worked around this locally with its own extra `requestAnimationFrame`\n // call; folding the deferral into `wire()` itself means every consumer gets it for free.\n requestAnimationFrame(recompute);\n\n return () => {\n el.removeEventListener('input', recompute);\n\n if (wiredEl === el) wiredEl = null;\n };\n };\n\n return { recompute, wire };\n};\n"],"mappings":"AA8BA,IAAa,GAAoB,EAA6B,CAAC,IAAyB,CACtF,IAAI,EAAsC,KAEpC,MAAwB,CACxB,CAAC,GAAW,EAAQ,SAAS,QAAU,KAE3C,EAAQ,MAAM,OAAS,OACvB,EAAQ,MAAM,OAAS,GAAG,EAAQ,aAAa,IACjD,EAoBA,MAAO,CAAE,YAAW,KAlBN,IACZ,EAAU,EACV,EAAG,iBAAiB,QAAS,CAAS,EAOtC,sBAAsB,CAAS,MAElB,CACX,EAAG,oBAAoB,QAAS,CAAS,EAErC,IAAY,IAAI,EAAU,KAChC,EAGuB,CAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-resize.d.ts","sourceRoot":"","sources":["../../src/core/auto-resize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,qHAAqH;IACrH,OAAO,CAAC,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,sHAAsH;IACtH,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,mGAAmG;IACnG,IAAI,EAAE,CAAC,EAAE,EAAE,mBAAmB,KAAK,MAAM,IAAI,CAAC;CAC/C,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,gBAAgB,GAAI,UAAS,iBAAsB,KAAG,iBA6BlE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-resize.js","names":[],"sources":["../../src/core/auto-resize.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nexport type AutoResizeOptions = {\n /** Gates growth; omit for an always-on composer field, or pass a signal for a toggleable `auto-resize` attribute. */\n enabled?: Readable<boolean | undefined>;\n};\n\nexport type AutoResizeControl = {\n /** Recomputes height from the wired element's current content. No-ops when `enabled` is false or nothing is wired. */\n recompute: () => void;\n /** Wire onto the raw `<textarea>` when it mounts. Returns a detach function to call on unmount. */\n wire: (el: HTMLTextAreaElement) => () => void;\n};\n\n/**\n * Grows a `<textarea>`'s height to fit its content, shared by every component that offers\n * auto-resize (`ore-textarea`, `ore-message-composer`) instead of each reimplementing it.\n *\n * Owns only height — manual-resize cursor/direction is a per-component styling concern, not\n * shared behavior, so it stays local to whichever component renders a resize handle.\n *\n * @example\n * ```ts\n * const autoResize = createAutoResize({ enabled: props['auto-resize'] });\n *\n * onElement(textareaRef, (el) => autoResize.wire(el));\n * // after a programmatic value change that doesn't fire a native `input` event:\n * autoResize.recompute();\n * ```\n */\nexport const createAutoResize = (options: AutoResizeOptions = {}): AutoResizeControl => {\n let wiredEl: HTMLTextAreaElement | null = null;\n\n const recompute = (): void => {\n if (!wiredEl || options.enabled?.value === false) return;\n\n wiredEl.style.height = 'auto';\n wiredEl.style.height = `${wiredEl.scrollHeight}px`;\n };\n\n const wire = (el: HTMLTextAreaElement): (() => void) => {\n wiredEl = el;\n el.addEventListener('input', recompute);\n\n // Deferred a frame: right after mount, the browser hasn't necessarily finished laying out\n // the textarea yet, so a synchronous `scrollHeight` read here can be stale — often reading\n // as 0 and collapsing the field until the next real `input` event happens to correct it.\n // `ore-textarea` has always worked around this locally with its own extra `requestAnimationFrame`\n // call; folding the deferral into `wire()` itself means every consumer gets it for free.\n requestAnimationFrame(recompute);\n\n return () => {\n el.removeEventListener('input', recompute);\n\n if (wiredEl === el) wiredEl = null;\n };\n };\n\n return { recompute, wire };\n};\n"],"mappings":";AA8BA,IAAa,KAAoB,IAA6B,CAAC,MAAyB;CACtF,IAAI,IAAsC,MAEpC,UAAwB;EACxB,CAAC,KAAW,EAAQ,SAAS,UAAU,OAE3C,EAAQ,MAAM,SAAS,QACvB,EAAQ,MAAM,SAAS,GAAG,EAAQ,aAAa;CACjD;CAoBA,OAAO;EAAE;EAAW,OAlBN,OACZ,IAAU,GACV,EAAG,iBAAiB,SAAS,CAAS,GAOtC,sBAAsB,CAAS,SAElB;GAGX,AAFA,EAAG,oBAAoB,SAAS,CAAS,GAErC,MAAY,MAAI,IAAU;EAChC;CAGuB;AAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkable.cjs","names":[],"sources":["../../src/core/checkable.ts"],"sourcesContent":["import { computed, type Readable, type Signal, signal } from '@vielzeug/ripple';\n\nimport { createDirtyTracker, createField, type ControlValidationMode, type FieldHandle } from './field-base';\nimport { createInteraction } from './keyboard';\nimport { syncedSignal } from './signals';\n\n// ── Checkable control ─────────────────────────────────────────────────────────\n\nexport type CheckableChangePayload = {\n checked: boolean;\n originalEvent?: Event;\n value: string;\n};\n\nexport type CheckableOptions = {\n checked: Readable<boolean | undefined>;\n clearIndeterminateFirst?: boolean;\n disabled?: Readable<boolean | undefined>;\n error?: Readable<string | undefined>;\n group?: { toggle: (value: string, originalEvent?: Event) => void };\n helper?: Readable<string | undefined>;\n indeterminate?: Readable<boolean | undefined>;\n onToggle?: (payload: CheckableChangePayload) => void;\n prefix?: string;\n /** Marks an unchecked control invalid — feeds `validity`/`validationMessage` (see below). */\n required?: Readable<boolean | undefined>;\n /** Message for the unchecked+required case. Defaults to `'This field is required.'`. */\n requiredMessage?: Readable<string | undefined>;\n /** `AbortSignal` from the component lifecycle. All reactive subscriptions are torn down on abort. */\n signal: AbortSignal;\n validateOn?: Readable<ControlValidationMode>;\n value: Readable<string | undefined>;\n};\n\nexport type CheckableHandle = FieldHandle & {\n /** The form submission value: null when unchecked/indeterminate, the value string when checked. */\n checkableFormValue: Readable<string | null>;\n checked: Signal<boolean>;\n handleClick: (event: MouseEvent) => boolean;\n handleKeydown: (event: KeyboardEvent) => boolean;\n indeterminate: Signal<boolean>;\n /**\n * Restores `checked`/`indeterminate` to their native \"default\" value. See `DirtyTracker`\n * for the two-state rationale. Wire into `useField({ onReset: checkable.reset })`.\n */\n reset: () => void;\n toggle: (event: Event) => void;\n /** Reactive validation message paired with `validity`. Empty string when valid. */\n validationMessage: Readable<string>;\n /**\n * Reactive `ValidityStateFlags` — `{ valueMissing: true }` while `required` and unchecked\n * (indeterminate counts as unchecked), `null` (valid) otherwise. Pass straight to\n * `useField({ validity: checkable.validity })`.\n */\n validity: Readable<ValidityStateFlags | null>;\n};\n\nexport const createCheckable = (options: CheckableOptions): CheckableHandle => {\n const checked = syncedSignal(options.checked, options.signal, Boolean);\n const indeterminate = options.indeterminate\n ? syncedSignal(options.indeterminate, options.signal, Boolean)\n : signal(false);\n const initialChecked = checked.value;\n const initialIndeterminate = indeterminate.value;\n const dirtyTracker = createDirtyTracker();\n\n const checkableFormValue = computed<string | null>(() =>\n indeterminate.value ? null : checked.value ? (options.value.value ?? '') : null,\n );\n\n const field = createField(options);\n\n const validity = computed<ValidityStateFlags | null>(() =>\n options.required?.value && (!checked.value || indeterminate.value) ? { valueMissing: true } : null,\n );\n const validationMessage = computed(() =>\n validity.value ? options.requiredMessage?.value || 'This field is required.' : '',\n );\n\n const reset = (): void => {\n checked.value = dirtyTracker.isDirty ? initialChecked : Boolean(options.checked.value);\n indeterminate.value = dirtyTracker.isDirty ? initialIndeterminate : Boolean(options.indeterminate?.value);\n dirtyTracker.clear();\n field.triggerValidation('change');\n };\n\n const createPayload = (event: Event): CheckableChangePayload => ({\n checked: checked.value,\n originalEvent: event,\n value: options.value.value ?? '',\n });\n\n const toggle = (event: Event): void => {\n if (field.disabled.value) return;\n\n dirtyTracker.markDirty();\n\n if (options.group) {\n indeterminate.value = false;\n options.group.toggle(options.value.value ?? '', event);\n options.onToggle?.(createPayload(event));\n\n return;\n }\n\n if (options.clearIndeterminateFirst && indeterminate.value) {\n indeterminate.value = false;\n } else {\n checked.value = !checked.value;\n indeterminate.value = false;\n }\n\n options.onToggle?.(createPayload(event));\n };\n\n const interaction = createInteraction({\n disabled: field.disabled,\n onPress: (event) => toggle(event),\n });\n\n return {\n ...field,\n checkableFormValue,\n checked,\n handleClick: interaction.handleClick,\n handleKeydown: interaction.handleKeydown,\n indeterminate,\n reset,\n toggle,\n validationMessage,\n validity,\n };\n};\n"],"mappings":"6HAyDA,IAAa,EAAmB,GAA+C,CAC7E,IAAM,EAAU,EAAA,aAAa,EAAQ,QAAS,EAAQ,OAAQ,OAAO,EAC/D,EAAgB,EAAQ,cAC1B,EAAA,aAAa,EAAQ,cAAe,EAAQ,OAAQ,OAAO,GAAA,EAC3D,EAAA,OAAA,CAAO,EAAK,EACV,EAAiB,EAAQ,MACzB,EAAuB,EAAc,MACrC,EAAe,EAAA,mBAAmB,EAElC,GAAA,EAAqB,EAAA,SAAA,KACzB,EAAc,MAAQ,KAAO,EAAQ,MAAS,EAAQ,MAAM,OAAS,GAAM,IAC7E,EAEM,EAAQ,EAAA,YAAY,CAAO,EAE3B,GAAA,EAAW,EAAA,SAAA,KACf,EAAQ,UAAU,QAAU,CAAC,EAAQ,OAAS,EAAc,OAAS,CAAE,aAAc,EAAK,EAAI,IAChG,EACM,GAAA,EAAoB,EAAA,SAAA,KACxB,EAAS,MAAQ,EAAQ,iBAAiB,OAAS,0BAA4B,EACjF,EAEM,MAAoB,CACxB,EAAQ,MAAQ,EAAa,QAAU,EAAiB,EAAQ,EAAQ,QAAQ,MAChF,EAAc,MAAQ,EAAa,QAAU,EAAuB,EAAQ,EAAQ,eAAe,MACnG,EAAa,MAAM,EACnB,EAAM,kBAAkB,QAAQ,CAClC,EAEM,EAAiB,IAA0C,CAC/D,QAAS,EAAQ,MACjB,cAAe,EACf,MAAO,EAAQ,MAAM,OAAS,EAChC,GAEM,EAAU,GAAuB,CACjC,MAAM,SAAS,MAInB,IAFA,EAAa,UAAU,EAEnB,EAAQ,MAAO,CACjB,EAAc,MAAQ,GACtB,EAAQ,MAAM,OAAO,EAAQ,MAAM,OAAS,GAAI,CAAK,EACrD,EAAQ,WAAW,EAAc,CAAK,CAAC,EAEvC,MACF,CAEI,EAAQ,yBAA2B,EAAc,QAGnD,EAAQ,MAAQ,CAAC,EAAQ,OAFzB,EAAc,MAAQ,GAMxB,EAAQ,WAAW,EAAc,CAAK,CAAC,CATvC,CAUF,EAEM,EAAc,EAAA,kBAAkB,CACpC,SAAU,EAAM,SAChB,QAAU,GAAU,EAAO,CAAK,CAClC,CAAC,EAED,MAAO,CACL,GAAG,EACH,qBACA,UACA,YAAa,EAAY,YACzB,cAAe,EAAY,cAC3B,gBACA,QACA,SACA,oBACA,UACF,CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkable.d.ts","sourceRoot":"","sources":["../../src/core/checkable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAU,MAAM,kBAAkB,CAAC;AAEhF,OAAO,EAAmC,KAAK,qBAAqB,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAM7G,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,KAAK,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACvC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACrC,KAAK,CAAC,EAAE;QAAE,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,KAAK,KAAK,IAAI,CAAA;KAAE,CAAC;IACnE,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACtC,aAAa,CAAC,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IAC9C,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACzC,wFAAwF;IACxF,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC/C,qGAAqG;IACrG,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,CAAC,EAAE,QAAQ,CAAC,qBAAqB,CAAC,CAAC;IAC7C,KAAK,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG;IAC1C,mGAAmG;IACnG,kBAAkB,EAAE,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5C,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACzB,WAAW,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC;IAC5C,aAAa,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC;IACjD,aAAa,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAC/B;;;OAGG;IACH,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAC/B,mFAAmF;IACnF,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACpC;;;;OAIG;IACH,QAAQ,EAAE,QAAQ,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;CAC/C,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,SAAS,gBAAgB,KAAG,eA2E3D,CAAC"}
|
|
@@ -2,7 +2,7 @@ import { createDirtyTracker as e, createField as t } from "./field-base.js";
|
|
|
2
2
|
import { syncedSignal as n } from "./signals.js";
|
|
3
3
|
import { createInteraction as r } from "./keyboard.js";
|
|
4
4
|
import { computed as i, signal as a } from "@vielzeug/ripple";
|
|
5
|
-
//#region src/
|
|
5
|
+
//#region src/core/checkable.ts
|
|
6
6
|
var o = (o) => {
|
|
7
7
|
let s = n(o.checked, o.signal, Boolean), c = o.indeterminate ? n(o.indeterminate, o.signal, Boolean) : a(!1), l = s.value, u = c.value, d = e(), f = i(() => c.value ? null : s.value ? o.value.value ?? "" : null), p = t(o), m = i(() => o.required?.value && (!s.value || c.value) ? { valueMissing: !0 } : null), h = i(() => m.value ? o.requiredMessage?.value || "This field is required." : ""), g = () => {
|
|
8
8
|
s.value = d.isDirty ? l : !!o.checked.value, c.value = d.isDirty ? u : !!o.indeterminate?.value, d.clear(), p.triggerValidation("change");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkable.js","names":[],"sources":["../../src/core/checkable.ts"],"sourcesContent":["import { computed, type Readable, type Signal, signal } from '@vielzeug/ripple';\n\nimport { createDirtyTracker, createField, type ControlValidationMode, type FieldHandle } from './field-base';\nimport { createInteraction } from './keyboard';\nimport { syncedSignal } from './signals';\n\n// ── Checkable control ─────────────────────────────────────────────────────────\n\nexport type CheckableChangePayload = {\n checked: boolean;\n originalEvent?: Event;\n value: string;\n};\n\nexport type CheckableOptions = {\n checked: Readable<boolean | undefined>;\n clearIndeterminateFirst?: boolean;\n disabled?: Readable<boolean | undefined>;\n error?: Readable<string | undefined>;\n group?: { toggle: (value: string, originalEvent?: Event) => void };\n helper?: Readable<string | undefined>;\n indeterminate?: Readable<boolean | undefined>;\n onToggle?: (payload: CheckableChangePayload) => void;\n prefix?: string;\n /** Marks an unchecked control invalid — feeds `validity`/`validationMessage` (see below). */\n required?: Readable<boolean | undefined>;\n /** Message for the unchecked+required case. Defaults to `'This field is required.'`. */\n requiredMessage?: Readable<string | undefined>;\n /** `AbortSignal` from the component lifecycle. All reactive subscriptions are torn down on abort. */\n signal: AbortSignal;\n validateOn?: Readable<ControlValidationMode>;\n value: Readable<string | undefined>;\n};\n\nexport type CheckableHandle = FieldHandle & {\n /** The form submission value: null when unchecked/indeterminate, the value string when checked. */\n checkableFormValue: Readable<string | null>;\n checked: Signal<boolean>;\n handleClick: (event: MouseEvent) => boolean;\n handleKeydown: (event: KeyboardEvent) => boolean;\n indeterminate: Signal<boolean>;\n /**\n * Restores `checked`/`indeterminate` to their native \"default\" value. See `DirtyTracker`\n * for the two-state rationale. Wire into `useField({ onReset: checkable.reset })`.\n */\n reset: () => void;\n toggle: (event: Event) => void;\n /** Reactive validation message paired with `validity`. Empty string when valid. */\n validationMessage: Readable<string>;\n /**\n * Reactive `ValidityStateFlags` — `{ valueMissing: true }` while `required` and unchecked\n * (indeterminate counts as unchecked), `null` (valid) otherwise. Pass straight to\n * `useField({ validity: checkable.validity })`.\n */\n validity: Readable<ValidityStateFlags | null>;\n};\n\nexport const createCheckable = (options: CheckableOptions): CheckableHandle => {\n const checked = syncedSignal(options.checked, options.signal, Boolean);\n const indeterminate = options.indeterminate\n ? syncedSignal(options.indeterminate, options.signal, Boolean)\n : signal(false);\n const initialChecked = checked.value;\n const initialIndeterminate = indeterminate.value;\n const dirtyTracker = createDirtyTracker();\n\n const checkableFormValue = computed<string | null>(() =>\n indeterminate.value ? null : checked.value ? (options.value.value ?? '') : null,\n );\n\n const field = createField(options);\n\n const validity = computed<ValidityStateFlags | null>(() =>\n options.required?.value && (!checked.value || indeterminate.value) ? { valueMissing: true } : null,\n );\n const validationMessage = computed(() =>\n validity.value ? options.requiredMessage?.value || 'This field is required.' : '',\n );\n\n const reset = (): void => {\n checked.value = dirtyTracker.isDirty ? initialChecked : Boolean(options.checked.value);\n indeterminate.value = dirtyTracker.isDirty ? initialIndeterminate : Boolean(options.indeterminate?.value);\n dirtyTracker.clear();\n field.triggerValidation('change');\n };\n\n const createPayload = (event: Event): CheckableChangePayload => ({\n checked: checked.value,\n originalEvent: event,\n value: options.value.value ?? '',\n });\n\n const toggle = (event: Event): void => {\n if (field.disabled.value) return;\n\n dirtyTracker.markDirty();\n\n if (options.group) {\n indeterminate.value = false;\n options.group.toggle(options.value.value ?? '', event);\n options.onToggle?.(createPayload(event));\n\n return;\n }\n\n if (options.clearIndeterminateFirst && indeterminate.value) {\n indeterminate.value = false;\n } else {\n checked.value = !checked.value;\n indeterminate.value = false;\n }\n\n options.onToggle?.(createPayload(event));\n };\n\n const interaction = createInteraction({\n disabled: field.disabled,\n onPress: (event) => toggle(event),\n });\n\n return {\n ...field,\n checkableFormValue,\n checked,\n handleClick: interaction.handleClick,\n handleKeydown: interaction.handleKeydown,\n indeterminate,\n reset,\n toggle,\n validationMessage,\n validity,\n };\n};\n"],"mappings":";;;;;AAyDA,IAAa,KAAmB,MAA+C;CAC7E,IAAM,IAAU,EAAa,EAAQ,SAAS,EAAQ,QAAQ,OAAO,GAC/D,IAAgB,EAAQ,gBAC1B,EAAa,EAAQ,eAAe,EAAQ,QAAQ,OAAO,IAC3D,EAAO,EAAK,GACV,IAAiB,EAAQ,OACzB,IAAuB,EAAc,OACrC,IAAe,EAAmB,GAElC,IAAqB,QACzB,EAAc,QAAQ,OAAO,EAAQ,QAAS,EAAQ,MAAM,SAAS,KAAM,IAC7E,GAEM,IAAQ,EAAY,CAAO,GAE3B,IAAW,QACf,EAAQ,UAAU,UAAU,CAAC,EAAQ,SAAS,EAAc,SAAS,EAAE,cAAc,GAAK,IAAI,IAChG,GACM,IAAoB,QACxB,EAAS,QAAQ,EAAQ,iBAAiB,SAAS,4BAA4B,EACjF,GAEM,UAAoB;EAIxB,AAHA,EAAQ,QAAQ,EAAa,UAAU,IAAiB,EAAQ,EAAQ,QAAQ,OAChF,EAAc,QAAQ,EAAa,UAAU,IAAuB,EAAQ,EAAQ,eAAe,OACnG,EAAa,MAAM,GACnB,EAAM,kBAAkB,QAAQ;CAClC,GAEM,KAAiB,OAA0C;EAC/D,SAAS,EAAQ;EACjB,eAAe;EACf,OAAO,EAAQ,MAAM,SAAS;CAChC,IAEM,KAAU,MAAuB;EACjC,OAAM,SAAS,OAInB;OAFA,EAAa,UAAU,GAEnB,EAAQ,OAAO;IAGjB,AAFA,EAAc,QAAQ,IACtB,EAAQ,MAAM,OAAO,EAAQ,MAAM,SAAS,IAAI,CAAK,GACrD,EAAQ,WAAW,EAAc,CAAK,CAAC;IAEvC;GACF;GASA,AAPI,EAAQ,2BAA2B,EAAc,UAGnD,EAAQ,QAAQ,CAAC,EAAQ,QAFzB,EAAc,QAAQ,IAMxB,EAAQ,WAAW,EAAc,CAAK,CAAC;EATvC;CAUF,GAEM,IAAc,EAAkB;EACpC,UAAU,EAAM;EAChB,UAAU,MAAU,EAAO,CAAK;CAClC,CAAC;CAED,OAAO;EACL,GAAG;EACH;EACA;EACA,aAAa,EAAY;EACzB,eAAe,EAAY;EAC3B;EACA;EACA;EACA;EACA;CACF;AACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./field-base.cjs");let t=require("@vielzeug/ripple"),n=require("@vielzeug/ripple/watch");var r=e=>e?Array.isArray(e)?e.filter(Boolean):e.split(`,`).map(e=>e.trim()).filter(Boolean):[],i=i=>{let a=(0,t.signal)([]),o=(0,t.computed)(()=>!!i.multiple?.value),s=(0,t.computed)(()=>o.value?a.value.join(`,`):a.value[0]??``),c=e=>{let t=o.value?e:e.slice(0,1);return[...new Set(t.filter(Boolean))]},l=e.createDirtyTracker(),u=e=>{a.value=c(e)},d=e=>{l.markDirty(),u(e)},f=()=>d([]),p=e=>{l.markDirty(),a.value=a.value.filter(t=>t!==e)},m=e=>{if(o.value){a.value.includes(e)||d([...a.value,e]);return}d([e])},h=e=>{o.value&&a.value.includes(e)?p(e):m(e)},g=e=>{u(r(e))},_=e.createField(i),v=(0,t.computed)(()=>i.required?.value&&a.value.length===0?{valueMissing:!0}:null),y=(0,t.computed)(()=>v.value?i.requiredMessage?.value||`Please make a selection.`:``),b=(0,n.watch)(i.value,g,{immediate:!0});if(i.signal.addEventListener(`abort`,()=>b.dispose(),{once:!0}),i.multiple){let e=(0,n.watch)(i.multiple,()=>g(i.value.value));i.signal.addEventListener(`abort`,()=>e.dispose(),{once:!0})}let x=a.value,S=()=>{u(l.isDirty?x:r(i.value.value)),l.clear(),_.triggerValidation(`change`)};return{..._,clear:f,formValue:s,removeValue:p,reset:S,selectedValue:(0,t.computed)(()=>a.value[0]),selectedValues:a,selectValue:m,setValues:d,toggleValue:h,validationMessage:y,validity:v}};exports.createChoiceField=i;
|
|
2
|
+
//# sourceMappingURL=choice-field.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"choice-field.cjs","names":[],"sources":["../../src/core/choice-field.ts"],"sourcesContent":["import { computed, type Readable, signal } from '@vielzeug/ripple';\nimport { watch } from '@vielzeug/ripple/watch';\n\nimport { createDirtyTracker, createField, type FieldHandle, type FieldOptions } from './field-base';\n\n// ── Choice change event payload ──────────────────────────────────────────────────────────────\n\nexport type ChoiceChangeDetail = {\n labels: string[];\n originalEvent?: Event;\n values: string[];\n};\n\n// ── Choice field ──────────────────────────────────────────────────────────────────────\n\nexport type ChoiceFieldOptions = FieldOptions & {\n multiple?: Readable<boolean | undefined>;\n /** Marks an empty selection invalid — feeds `validity`/`validationMessage` (see below). */\n required?: Readable<boolean | undefined>;\n /** Message for the empty-selection+required case. Defaults to `'Please make a selection.'`. */\n requiredMessage?: Readable<string | undefined>;\n /** `AbortSignal` from the component lifecycle. All internal subscriptions are disposed on abort. */\n signal: AbortSignal;\n /**\n * Current selection value(s). Accepts a comma-separated string\n * (legacy/single-select form serialisation) or a `string[]` array.\n * The field normalises both formats internally via `parseChoiceValues`.\n */\n value: Readable<string | string[] | undefined>;\n};\n\nexport type ChoiceFieldHandle = FieldHandle & {\n clear: () => void;\n formValue: Readable<string>;\n removeValue: (value: string) => void;\n /**\n * Restores the selection to its native \"default\". See `DirtyTracker` for the two-state\n * rationale.\n */\n reset: () => void;\n selectedValue: Readable<string | undefined>;\n selectedValues: Readable<string[]>;\n selectValue: (value: string) => void;\n setValues: (values: string[]) => void;\n /**\n * Toggles `value` in the selection.\n * - **Multiple mode**: adds if absent, removes if present.\n * - **Single mode**: always selects `value` (acts like `selectValue` — cannot\n * deselect; use `clear()` to reset a single-select field).\n */\n toggleValue: (value: string) => void;\n /** Reactive validation message paired with `validity`. Empty string when valid. */\n validationMessage: Readable<string>;\n /**\n * Reactive `ValidityStateFlags` — `{ valueMissing: true }` while `required` and no option is\n * selected, `null` (valid) otherwise. Pass straight to `useField({ validity: choice.validity })`.\n */\n validity: Readable<ValidityStateFlags | null>;\n};\n\nconst parseChoiceValues = (raw: string | string[] | undefined): string[] => {\n if (!raw) return [];\n\n if (Array.isArray(raw)) return raw.filter(Boolean);\n\n return raw\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean);\n};\n\nexport const createChoiceField = (options: ChoiceFieldOptions): ChoiceFieldHandle => {\n const selectedValues = signal<string[]>([]);\n const isMultiple = computed(() => Boolean(options.multiple?.value));\n const formValue = computed(() =>\n isMultiple.value ? selectedValues.value.join(',') : (selectedValues.value[0] ?? ''),\n );\n\n const normalizeValues = (values: string[]): string[] => {\n const normalized = isMultiple.value ? values : values.slice(0, 1);\n\n return [...new Set(normalized.filter(Boolean))];\n };\n\n const dirtyTracker = createDirtyTracker();\n\n // The actual state setter, shared by every mutation path. Deliberately *not* where the dirty\n // tracker gets marked — `syncFromProp` (below) calls this directly to stay exempt from it,\n // since mirroring an external prop change is never \"the user changed the selection\".\n const applyValues = (values: string[]): void => {\n selectedValues.value = normalizeValues(values);\n };\n\n const setValues = (values: string[]): void => {\n dirtyTracker.markDirty();\n applyValues(values);\n };\n const clear = (): void => setValues([]);\n const removeValue = (value: string): void => {\n dirtyTracker.markDirty();\n selectedValues.value = selectedValues.value.filter((v) => v !== value);\n };\n\n const selectValue = (value: string): void => {\n if (isMultiple.value) {\n if (!selectedValues.value.includes(value)) setValues([...selectedValues.value, value]);\n\n return;\n }\n\n setValues([value]);\n };\n\n const toggleValue = (value: string): void => {\n if (isMultiple.value) {\n if (selectedValues.value.includes(value)) removeValue(value);\n else selectValue(value);\n } else {\n selectValue(value);\n }\n };\n\n const syncFromProp = (nextValue: string | string[] | undefined): void => {\n applyValues(parseChoiceValues(nextValue));\n };\n\n const field = createField(options);\n\n const validity = computed<ValidityStateFlags | null>(() =>\n options.required?.value && selectedValues.value.length === 0 ? { valueMissing: true } : null,\n );\n const validationMessage = computed(() =>\n validity.value ? options.requiredMessage?.value || 'Please make a selection.' : '',\n );\n\n const valueSub = watch(options.value, syncFromProp, { immediate: true });\n\n options.signal.addEventListener('abort', () => valueSub.dispose(), { once: true });\n\n if (options.multiple) {\n const multipleSub = watch(options.multiple, () => syncFromProp(options.value.value));\n\n options.signal.addEventListener('abort', () => multipleSub.dispose(), { once: true });\n }\n\n const initialValues = selectedValues.value;\n const reset = (): void => {\n applyValues(dirtyTracker.isDirty ? initialValues : parseChoiceValues(options.value.value));\n dirtyTracker.clear();\n field.triggerValidation('change');\n };\n\n return {\n ...field,\n clear,\n formValue,\n removeValue,\n reset,\n selectedValue: computed(() => selectedValues.value[0]),\n selectedValues,\n selectValue,\n setValues,\n toggleValue,\n validationMessage,\n validity,\n };\n};\n"],"mappings":"0GA4DA,IAAM,EAAqB,GACpB,EAED,MAAM,QAAQ,CAAG,EAAU,EAAI,OAAO,OAAO,EAE1C,EACJ,MAAM,GAAG,CAAC,CACV,IAAK,GAAU,EAAM,KAAK,CAAC,CAAC,CAC5B,OAAO,OAAO,EAPA,CAAC,EAUP,EAAqB,GAAmD,CACnF,IAAM,GAAA,EAAiB,EAAA,OAAA,CAAiB,CAAC,CAAC,EACpC,GAAA,EAAa,EAAA,SAAA,KAAe,EAAQ,EAAQ,UAAU,KAAM,EAC5D,GAAA,EAAY,EAAA,SAAA,KAChB,EAAW,MAAQ,EAAe,MAAM,KAAK,GAAG,EAAK,EAAe,MAAM,IAAM,EAClF,EAEM,EAAmB,GAA+B,CACtD,IAAM,EAAa,EAAW,MAAQ,EAAS,EAAO,MAAM,EAAG,CAAC,EAEhE,MAAO,CAAC,GAAG,IAAI,IAAI,EAAW,OAAO,OAAO,CAAC,CAAC,CAChD,EAEM,EAAe,EAAA,mBAAmB,EAKlC,EAAe,GAA2B,CAC9C,EAAe,MAAQ,EAAgB,CAAM,CAC/C,EAEM,EAAa,GAA2B,CAC5C,EAAa,UAAU,EACvB,EAAY,CAAM,CACpB,EACM,MAAoB,EAAU,CAAC,CAAC,EAChC,EAAe,GAAwB,CAC3C,EAAa,UAAU,EACvB,EAAe,MAAQ,EAAe,MAAM,OAAQ,GAAM,IAAM,CAAK,CACvE,EAEM,EAAe,GAAwB,CAC3C,GAAI,EAAW,MAAO,CACf,EAAe,MAAM,SAAS,CAAK,GAAG,EAAU,CAAC,GAAG,EAAe,MAAO,CAAK,CAAC,EAErF,MACF,CAEA,EAAU,CAAC,CAAK,CAAC,CACnB,EAEM,EAAe,GAAwB,CACvC,EAAW,OACT,EAAe,MAAM,SAAS,CAAK,EAAG,EAAY,CAAK,EACtD,EAAY,CAAK,CAI1B,EAEM,EAAgB,GAAmD,CACvE,EAAY,EAAkB,CAAS,CAAC,CAC1C,EAEM,EAAQ,EAAA,YAAY,CAAO,EAE3B,GAAA,EAAW,EAAA,SAAA,KACf,EAAQ,UAAU,OAAS,EAAe,MAAM,SAAW,EAAI,CAAE,aAAc,EAAK,EAAI,IAC1F,EACM,GAAA,EAAoB,EAAA,SAAA,KACxB,EAAS,MAAQ,EAAQ,iBAAiB,OAAS,2BAA6B,EAClF,EAEM,GAAA,EAAW,EAAA,MAAA,CAAM,EAAQ,MAAO,EAAc,CAAE,UAAW,EAAK,CAAC,EAIvE,GAFA,EAAQ,OAAO,iBAAiB,YAAe,EAAS,QAAQ,EAAG,CAAE,KAAM,EAAK,CAAC,EAE7E,EAAQ,SAAU,CACpB,IAAM,GAAA,EAAc,EAAA,MAAA,CAAM,EAAQ,aAAgB,EAAa,EAAQ,MAAM,KAAK,CAAC,EAEnF,EAAQ,OAAO,iBAAiB,YAAe,EAAY,QAAQ,EAAG,CAAE,KAAM,EAAK,CAAC,CACtF,CAEA,IAAM,EAAgB,EAAe,MAC/B,MAAoB,CACxB,EAAY,EAAa,QAAU,EAAgB,EAAkB,EAAQ,MAAM,KAAK,CAAC,EACzF,EAAa,MAAM,EACnB,EAAM,kBAAkB,QAAQ,CAClC,EAEA,MAAO,CACL,GAAG,EACH,QACA,YACA,cACA,QACA,eAAA,EAAe,EAAA,SAAA,KAAe,EAAe,MAAM,EAAE,EACrD,iBACA,cACA,YACA,cACA,oBACA,UACF,CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"choice-field.d.ts","sourceRoot":"","sources":["../../src/core/choice-field.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,QAAQ,EAAU,MAAM,kBAAkB,CAAC;AAGnE,OAAO,EAAmC,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAIpG,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,CAAC,EAAE,KAAK,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAIF,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG;IAC9C,QAAQ,CAAC,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACzC,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACzC,+FAA+F;IAC/F,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC/C,oGAAoG;IACpG,MAAM,EAAE,WAAW,CAAC;IACpB;;;;OAIG;IACH,KAAK,EAAE,QAAQ,CAAC,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG;IAC5C,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5B,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC;;;OAGG;IACH,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,aAAa,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC5C,cAAc,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACnC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACtC;;;;;OAKG;IACH,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,mFAAmF;IACnF,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACpC;;;OAGG;IACH,QAAQ,EAAE,QAAQ,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;CAC/C,CAAC;AAaF,eAAO,MAAM,iBAAiB,GAAI,SAAS,kBAAkB,KAAG,iBA+F/D,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createDirtyTracker as e, createField as t } from "./field-base.js";
|
|
2
|
-
import { computed as n, signal as r
|
|
3
|
-
|
|
2
|
+
import { computed as n, signal as r } from "@vielzeug/ripple";
|
|
3
|
+
import { watch as i } from "@vielzeug/ripple/watch";
|
|
4
|
+
//#region src/core/choice-field.ts
|
|
4
5
|
var a = (e) => e ? Array.isArray(e) ? e.filter(Boolean) : e.split(",").map((e) => e.trim()).filter(Boolean) : [], o = (o) => {
|
|
5
6
|
let s = r([]), c = n(() => !!o.multiple?.value), l = n(() => c.value ? s.value.join(",") : s.value[0] ?? ""), u = (e) => {
|
|
6
7
|
let t = c.value ? e : e.slice(0, 1);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"choice-field.js","names":[],"sources":["../../src/core/choice-field.ts"],"sourcesContent":["import { computed, type Readable, signal } from '@vielzeug/ripple';\nimport { watch } from '@vielzeug/ripple/watch';\n\nimport { createDirtyTracker, createField, type FieldHandle, type FieldOptions } from './field-base';\n\n// ── Choice change event payload ──────────────────────────────────────────────────────────────\n\nexport type ChoiceChangeDetail = {\n labels: string[];\n originalEvent?: Event;\n values: string[];\n};\n\n// ── Choice field ──────────────────────────────────────────────────────────────────────\n\nexport type ChoiceFieldOptions = FieldOptions & {\n multiple?: Readable<boolean | undefined>;\n /** Marks an empty selection invalid — feeds `validity`/`validationMessage` (see below). */\n required?: Readable<boolean | undefined>;\n /** Message for the empty-selection+required case. Defaults to `'Please make a selection.'`. */\n requiredMessage?: Readable<string | undefined>;\n /** `AbortSignal` from the component lifecycle. All internal subscriptions are disposed on abort. */\n signal: AbortSignal;\n /**\n * Current selection value(s). Accepts a comma-separated string\n * (legacy/single-select form serialisation) or a `string[]` array.\n * The field normalises both formats internally via `parseChoiceValues`.\n */\n value: Readable<string | string[] | undefined>;\n};\n\nexport type ChoiceFieldHandle = FieldHandle & {\n clear: () => void;\n formValue: Readable<string>;\n removeValue: (value: string) => void;\n /**\n * Restores the selection to its native \"default\". See `DirtyTracker` for the two-state\n * rationale.\n */\n reset: () => void;\n selectedValue: Readable<string | undefined>;\n selectedValues: Readable<string[]>;\n selectValue: (value: string) => void;\n setValues: (values: string[]) => void;\n /**\n * Toggles `value` in the selection.\n * - **Multiple mode**: adds if absent, removes if present.\n * - **Single mode**: always selects `value` (acts like `selectValue` — cannot\n * deselect; use `clear()` to reset a single-select field).\n */\n toggleValue: (value: string) => void;\n /** Reactive validation message paired with `validity`. Empty string when valid. */\n validationMessage: Readable<string>;\n /**\n * Reactive `ValidityStateFlags` — `{ valueMissing: true }` while `required` and no option is\n * selected, `null` (valid) otherwise. Pass straight to `useField({ validity: choice.validity })`.\n */\n validity: Readable<ValidityStateFlags | null>;\n};\n\nconst parseChoiceValues = (raw: string | string[] | undefined): string[] => {\n if (!raw) return [];\n\n if (Array.isArray(raw)) return raw.filter(Boolean);\n\n return raw\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean);\n};\n\nexport const createChoiceField = (options: ChoiceFieldOptions): ChoiceFieldHandle => {\n const selectedValues = signal<string[]>([]);\n const isMultiple = computed(() => Boolean(options.multiple?.value));\n const formValue = computed(() =>\n isMultiple.value ? selectedValues.value.join(',') : (selectedValues.value[0] ?? ''),\n );\n\n const normalizeValues = (values: string[]): string[] => {\n const normalized = isMultiple.value ? values : values.slice(0, 1);\n\n return [...new Set(normalized.filter(Boolean))];\n };\n\n const dirtyTracker = createDirtyTracker();\n\n // The actual state setter, shared by every mutation path. Deliberately *not* where the dirty\n // tracker gets marked — `syncFromProp` (below) calls this directly to stay exempt from it,\n // since mirroring an external prop change is never \"the user changed the selection\".\n const applyValues = (values: string[]): void => {\n selectedValues.value = normalizeValues(values);\n };\n\n const setValues = (values: string[]): void => {\n dirtyTracker.markDirty();\n applyValues(values);\n };\n const clear = (): void => setValues([]);\n const removeValue = (value: string): void => {\n dirtyTracker.markDirty();\n selectedValues.value = selectedValues.value.filter((v) => v !== value);\n };\n\n const selectValue = (value: string): void => {\n if (isMultiple.value) {\n if (!selectedValues.value.includes(value)) setValues([...selectedValues.value, value]);\n\n return;\n }\n\n setValues([value]);\n };\n\n const toggleValue = (value: string): void => {\n if (isMultiple.value) {\n if (selectedValues.value.includes(value)) removeValue(value);\n else selectValue(value);\n } else {\n selectValue(value);\n }\n };\n\n const syncFromProp = (nextValue: string | string[] | undefined): void => {\n applyValues(parseChoiceValues(nextValue));\n };\n\n const field = createField(options);\n\n const validity = computed<ValidityStateFlags | null>(() =>\n options.required?.value && selectedValues.value.length === 0 ? { valueMissing: true } : null,\n );\n const validationMessage = computed(() =>\n validity.value ? options.requiredMessage?.value || 'Please make a selection.' : '',\n );\n\n const valueSub = watch(options.value, syncFromProp, { immediate: true });\n\n options.signal.addEventListener('abort', () => valueSub.dispose(), { once: true });\n\n if (options.multiple) {\n const multipleSub = watch(options.multiple, () => syncFromProp(options.value.value));\n\n options.signal.addEventListener('abort', () => multipleSub.dispose(), { once: true });\n }\n\n const initialValues = selectedValues.value;\n const reset = (): void => {\n applyValues(dirtyTracker.isDirty ? initialValues : parseChoiceValues(options.value.value));\n dirtyTracker.clear();\n field.triggerValidation('change');\n };\n\n return {\n ...field,\n clear,\n formValue,\n removeValue,\n reset,\n selectedValue: computed(() => selectedValues.value[0]),\n selectedValues,\n selectValue,\n setValues,\n toggleValue,\n validationMessage,\n validity,\n };\n};\n"],"mappings":";;;;AA4DA,IAAM,KAAqB,MACpB,IAED,MAAM,QAAQ,CAAG,IAAU,EAAI,OAAO,OAAO,IAE1C,EACJ,MAAM,GAAG,CAAC,CACV,KAAK,MAAU,EAAM,KAAK,CAAC,CAAC,CAC5B,OAAO,OAAO,IAPA,CAAC,GAUP,KAAqB,MAAmD;CACnF,IAAM,IAAiB,EAAiB,CAAC,CAAC,GACpC,IAAa,QAAe,EAAQ,EAAQ,UAAU,KAAM,GAC5D,IAAY,QAChB,EAAW,QAAQ,EAAe,MAAM,KAAK,GAAG,IAAK,EAAe,MAAM,MAAM,EAClF,GAEM,KAAmB,MAA+B;EACtD,IAAM,IAAa,EAAW,QAAQ,IAAS,EAAO,MAAM,GAAG,CAAC;EAEhE,OAAO,CAAC,GAAG,IAAI,IAAI,EAAW,OAAO,OAAO,CAAC,CAAC;CAChD,GAEM,IAAe,EAAmB,GAKlC,KAAe,MAA2B;EAC9C,EAAe,QAAQ,EAAgB,CAAM;CAC/C,GAEM,KAAa,MAA2B;EAE5C,AADA,EAAa,UAAU,GACvB,EAAY,CAAM;CACpB,GACM,UAAoB,EAAU,CAAC,CAAC,GAChC,KAAe,MAAwB;EAE3C,AADA,EAAa,UAAU,GACvB,EAAe,QAAQ,EAAe,MAAM,QAAQ,MAAM,MAAM,CAAK;CACvE,GAEM,KAAe,MAAwB;EAC3C,IAAI,EAAW,OAAO;GACpB,AAAK,EAAe,MAAM,SAAS,CAAK,KAAG,EAAU,CAAC,GAAG,EAAe,OAAO,CAAK,CAAC;GAErF;EACF;EAEA,EAAU,CAAC,CAAK,CAAC;CACnB,GAEM,KAAe,MAAwB;EAC3C,AAAI,EAAW,SACT,EAAe,MAAM,SAAS,CAAK,IAAG,EAAY,CAAK,IACtD,EAAY,CAAK;CAI1B,GAEM,KAAgB,MAAmD;EACvE,EAAY,EAAkB,CAAS,CAAC;CAC1C,GAEM,IAAQ,EAAY,CAAO,GAE3B,IAAW,QACf,EAAQ,UAAU,SAAS,EAAe,MAAM,WAAW,IAAI,EAAE,cAAc,GAAK,IAAI,IAC1F,GACM,IAAoB,QACxB,EAAS,QAAQ,EAAQ,iBAAiB,SAAS,6BAA6B,EAClF,GAEM,IAAW,EAAM,EAAQ,OAAO,GAAc,EAAE,WAAW,GAAK,CAAC;CAIvE,IAFA,EAAQ,OAAO,iBAAiB,eAAe,EAAS,QAAQ,GAAG,EAAE,MAAM,GAAK,CAAC,GAE7E,EAAQ,UAAU;EACpB,IAAM,IAAc,EAAM,EAAQ,gBAAgB,EAAa,EAAQ,MAAM,KAAK,CAAC;EAEnF,EAAQ,OAAO,iBAAiB,eAAe,EAAY,QAAQ,GAAG,EAAE,MAAM,GAAK,CAAC;CACtF;CAEA,IAAM,IAAgB,EAAe,OAC/B,UAAoB;EAGxB,AAFA,EAAY,EAAa,UAAU,IAAgB,EAAkB,EAAQ,MAAM,KAAK,CAAC,GACzF,EAAa,MAAM,GACnB,EAAM,kBAAkB,QAAQ;CAClC;CAEA,OAAO;EACL,GAAG;EACH;EACA;EACA;EACA;EACA,eAAe,QAAe,EAAe,MAAM,EAAE;EACrD;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composer.cjs","names":[],"sources":["../../src/core/composer.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nimport { computed } from '@vielzeug/ripple';\n\n/**\n * Keyboard shortcut that triggers a send:\n * - `'enter'` (default) — Enter sends, Shift+Enter inserts a newline.\n * - `'mod+enter'` — Enter always inserts a newline; Ctrl/Cmd+Enter sends.\n */\nexport type SendShortcut = 'enter' | 'mod+enter';\n\nexport type ComposerControlOptions = {\n disabled?: Readable<boolean | undefined>;\n /** Blocks further sends (e.g. a send is already in flight) without disabling editing. */\n loading?: Readable<boolean | undefined>;\n /** Called for a send attempt that passed the `canSend` guard. */\n onSend: (event: KeyboardEvent | MouseEvent) => void;\n sendShortcut?: Readable<SendShortcut | undefined>;\n value: Readable<string | undefined>;\n};\n\nexport type ComposerControl = {\n /** Whether the current value is non-blank and the control isn't disabled/loading. */\n canSend: Readable<boolean>;\n /** Wire onto the raw `<textarea>`'s `keydown` — intercepts the resolved send shortcut. */\n handleKeydown: (event: KeyboardEvent) => void;\n /** `aria-keyshortcuts` value describing the resolved shortcut. */\n keyShortcutsHint: Readable<string>;\n /** Attempts a send; no-ops when `canSend` is false. Wire onto the send button's `click`. */\n send: (event: KeyboardEvent | MouseEvent) => void;\n};\n\nconst isBlank = (value: string | undefined): boolean => !value || value.trim() === '';\n\n/**\n * Send-gesture logic for a message/comment composer — decides *when* a send is allowed\n * and *whether* a given keydown counts as one. Owns no DOM, value state, or event dispatch;\n * the component wires `handleKeydown`/`send` and decides what a send actually does.\n *\n * @example\n * ```ts\n * const composer = createComposerControl({\n * onSend: (event) => attemptSend(event),\n * sendShortcut: props['send-shortcut'],\n * value,\n * });\n * // <textarea @keydown=\"${composer.handleKeydown}\" aria-keyshortcuts=\"${composer.keyShortcutsHint}\">\n * // <button ?disabled=\"${() => !composer.canSend.value}\" @click=\"${composer.send}\">\n * ```\n */\nexport const createComposerControl = (options: ComposerControlOptions): ComposerControl => {\n const canSend = computed(() => !isBlank(options.value.value) && !options.disabled?.value && !options.loading?.value);\n\n const send = (event: KeyboardEvent | MouseEvent): void => {\n if (!canSend.value) return;\n\n options.onSend(event);\n };\n\n const handleKeydown = (event: KeyboardEvent): void => {\n // A composing IME candidate selection also fires `Enter` — never treat that as a send.\n if (event.key !== 'Enter' || event.isComposing) return;\n\n const mode = options.sendShortcut?.value ?? 'enter';\n const hasModifier = event.metaKey || event.ctrlKey;\n\n if (mode === 'mod+enter' ? !hasModifier : event.shiftKey) return;\n\n event.preventDefault();\n send(event);\n };\n\n const keyShortcutsHint = computed(() =>\n (options.sendShortcut?.value ?? 'enter') === 'mod+enter' ? 'Control+Enter Meta+Enter' : 'Enter',\n );\n\n return { canSend, handleKeydown, keyShortcutsHint, send };\n};\n"],"mappings":"kCAgCA,IAAM,EAAW,GAAuC,CAAC,GAAS,EAAM,KAAK,IAAM,GAkBtE,EAAyB,GAAqD,CACzF,IAAM,GAAA,EAAU,EAAA,SAAA,KAAe,CAAC,EAAQ,EAAQ,MAAM,KAAK,GAAK,CAAC,EAAQ,UAAU,OAAS,CAAC,EAAQ,SAAS,KAAK,EAE7G,EAAQ,GAA4C,CACnD,EAAQ,OAEb,EAAQ,OAAO,CAAK,CACtB,EAmBA,MAAO,CAAE,UAAS,cAjBK,GAA+B,CAEpD,GAAI,EAAM,MAAQ,SAAW,EAAM,YAAa,OAEhD,IAAM,EAAO,EAAQ,cAAc,OAAS,QACtC,EAAc,EAAM,SAAW,EAAM,SAEvC,IAAS,YAAc,CAAC,EAAc,EAAM,YAEhD,EAAM,eAAe,EACrB,EAAK,CAAK,EACZ,EAMiC,kBAAA,EAJR,EAAA,SAAA,MACtB,EAAQ,cAAc,OAAS,WAAa,YAAc,2BAA6B,OAGzD,EAAkB,MAAK,CAC1D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composer.d.ts","sourceRoot":"","sources":["../../src/core/composer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAIjD;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,WAAW,CAAC;AAEjD,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACzC,yFAAyF;IACzF,OAAO,CAAC,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACxC,iEAAiE;IACjE,MAAM,EAAE,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,KAAK,IAAI,CAAC;IACpD,YAAY,CAAC,EAAE,QAAQ,CAAC,YAAY,GAAG,SAAS,CAAC,CAAC;IAClD,KAAK,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,qFAAqF;IACrF,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC3B,0FAA0F;IAC1F,aAAa,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC9C,kEAAkE;IAClE,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACnC,4FAA4F;IAC5F,IAAI,EAAE,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,KAAK,IAAI,CAAC;CACnD,CAAC;AAIF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,qBAAqB,GAAI,SAAS,sBAAsB,KAAG,eA2BvE,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { computed as e } from "@vielzeug/ripple";
|
|
2
|
-
//#region src/
|
|
2
|
+
//#region src/core/composer.ts
|
|
3
3
|
var t = (e) => !e || e.trim() === "", n = (n) => {
|
|
4
4
|
let r = e(() => !t(n.value.value) && !n.disabled?.value && !n.loading?.value), i = (e) => {
|
|
5
5
|
r.value && n.onSend(e);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composer.js","names":[],"sources":["../../src/core/composer.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nimport { computed } from '@vielzeug/ripple';\n\n/**\n * Keyboard shortcut that triggers a send:\n * - `'enter'` (default) — Enter sends, Shift+Enter inserts a newline.\n * - `'mod+enter'` — Enter always inserts a newline; Ctrl/Cmd+Enter sends.\n */\nexport type SendShortcut = 'enter' | 'mod+enter';\n\nexport type ComposerControlOptions = {\n disabled?: Readable<boolean | undefined>;\n /** Blocks further sends (e.g. a send is already in flight) without disabling editing. */\n loading?: Readable<boolean | undefined>;\n /** Called for a send attempt that passed the `canSend` guard. */\n onSend: (event: KeyboardEvent | MouseEvent) => void;\n sendShortcut?: Readable<SendShortcut | undefined>;\n value: Readable<string | undefined>;\n};\n\nexport type ComposerControl = {\n /** Whether the current value is non-blank and the control isn't disabled/loading. */\n canSend: Readable<boolean>;\n /** Wire onto the raw `<textarea>`'s `keydown` — intercepts the resolved send shortcut. */\n handleKeydown: (event: KeyboardEvent) => void;\n /** `aria-keyshortcuts` value describing the resolved shortcut. */\n keyShortcutsHint: Readable<string>;\n /** Attempts a send; no-ops when `canSend` is false. Wire onto the send button's `click`. */\n send: (event: KeyboardEvent | MouseEvent) => void;\n};\n\nconst isBlank = (value: string | undefined): boolean => !value || value.trim() === '';\n\n/**\n * Send-gesture logic for a message/comment composer — decides *when* a send is allowed\n * and *whether* a given keydown counts as one. Owns no DOM, value state, or event dispatch;\n * the component wires `handleKeydown`/`send` and decides what a send actually does.\n *\n * @example\n * ```ts\n * const composer = createComposerControl({\n * onSend: (event) => attemptSend(event),\n * sendShortcut: props['send-shortcut'],\n * value,\n * });\n * // <textarea @keydown=\"${composer.handleKeydown}\" aria-keyshortcuts=\"${composer.keyShortcutsHint}\">\n * // <button ?disabled=\"${() => !composer.canSend.value}\" @click=\"${composer.send}\">\n * ```\n */\nexport const createComposerControl = (options: ComposerControlOptions): ComposerControl => {\n const canSend = computed(() => !isBlank(options.value.value) && !options.disabled?.value && !options.loading?.value);\n\n const send = (event: KeyboardEvent | MouseEvent): void => {\n if (!canSend.value) return;\n\n options.onSend(event);\n };\n\n const handleKeydown = (event: KeyboardEvent): void => {\n // A composing IME candidate selection also fires `Enter` — never treat that as a send.\n if (event.key !== 'Enter' || event.isComposing) return;\n\n const mode = options.sendShortcut?.value ?? 'enter';\n const hasModifier = event.metaKey || event.ctrlKey;\n\n if (mode === 'mod+enter' ? !hasModifier : event.shiftKey) return;\n\n event.preventDefault();\n send(event);\n };\n\n const keyShortcutsHint = computed(() =>\n (options.sendShortcut?.value ?? 'enter') === 'mod+enter' ? 'Control+Enter Meta+Enter' : 'Enter',\n );\n\n return { canSend, handleKeydown, keyShortcutsHint, send };\n};\n"],"mappings":";;AAgCA,IAAM,KAAW,MAAuC,CAAC,KAAS,EAAM,KAAK,MAAM,IAkBtE,KAAyB,MAAqD;CACzF,IAAM,IAAU,QAAe,CAAC,EAAQ,EAAQ,MAAM,KAAK,KAAK,CAAC,EAAQ,UAAU,SAAS,CAAC,EAAQ,SAAS,KAAK,GAE7G,KAAQ,MAA4C;EACnD,EAAQ,SAEb,EAAQ,OAAO,CAAK;CACtB;CAmBA,OAAO;EAAE;EAAS,gBAjBK,MAA+B;GAEpD,IAAI,EAAM,QAAQ,WAAW,EAAM,aAAa;GAEhD,IAAM,IAAO,EAAQ,cAAc,SAAS,SACtC,IAAc,EAAM,WAAW,EAAM;GAEvC,OAAS,cAAc,CAAC,IAAc,EAAM,cAEhD,EAAM,eAAe,GACrB,EAAK,CAAK;EACZ;EAMiC,kBAJR,SACtB,EAAQ,cAAc,SAAS,aAAa,cAAc,6BAA6B,OAGzD;EAAkB;CAAK;AAC1D"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
let e=require("@vielzeug/tempo");function t(t){if(!t)return null;try{return(0,e.parsePlainDate)(t)}catch{return null}}function n(e){return e?e.toString():null}function r(t,n){return(0,e.format)(t,{intl:{day:`numeric`,month:`short`,year:`numeric`},locale:n,tz:`UTC`})}function i(){return e.Temporal.Now.plainDateISO()}function a(e){return e%7}function o(t){let n=t.value??null,r=`day`,o=n??i(),s=o.year,c=o.month;function l(){return t.locale??(typeof navigator<`u`?navigator.language:`en`)}function u(){return t.weekendDays??[]}function d(n){return!!(t.min&&e.Temporal.PlainDate.compare(n,t.min)<0||t.max&&e.Temporal.PlainDate.compare(n,t.max)>0)}function f(e){return!!(d(e)||u().includes(a(e.dayOfWeek)))}function p(){let t=i(),r=e.Temporal.PlainDate.from({day:1,month:c,year:s}),o=a(r.dayOfWeek),l=[];for(let i=o-1;i>=0;i--){let a=r.subtract({days:i+1});l.push({day:a.day,isDisabled:f(a),iso:a.toString(),isOutsideMonth:!0,isSelected:n!==null&&e.Temporal.PlainDate.compare(a,n)===0,isToday:e.Temporal.PlainDate.compare(a,t)===0,plain:a})}let u=r.daysInMonth;for(let r=1;r<=u;r++){let i=e.Temporal.PlainDate.from({day:r,month:c,year:s});l.push({day:r,isDisabled:f(i),iso:i.toString(),isOutsideMonth:!1,isSelected:n!==null&&e.Temporal.PlainDate.compare(i,n)===0,isToday:e.Temporal.PlainDate.compare(i,t)===0,plain:i})}let d=(7-l.length%7)%7,p=r.add({months:1});for(let r=0;r<d;r++){let i=p.add({days:r});l.push({day:i.day,isDisabled:f(i),iso:i.toString(),isOutsideMonth:!0,isSelected:n!==null&&e.Temporal.PlainDate.compare(i,n)===0,isToday:e.Temporal.PlainDate.compare(i,t)===0,plain:i})}return l}function m(){let r=[],i=l();for(let a=1;a<=12;a++){let o=e.Temporal.PlainDate.from({day:1,month:a,year:s}),c=o.with({day:o.daysInMonth}),l=
|
|
1
|
+
let e=require("@vielzeug/tempo");function t(t){if(!t)return null;try{return(0,e.parsePlainDate)(t)}catch{return null}}function n(e){return e?e.toString():null}function r(t,n){return(0,e.format)(t,{intl:{day:`numeric`,month:`short`,year:`numeric`},locale:n,tz:`UTC`})}function i(){return e.Temporal.Now.plainDateISO()}function a(e){return e%7}function o(t){let n=t.value??null,r=`day`,o=n??i(),s=o.year,c=o.month;function l(){return t.locale??(typeof navigator<`u`?navigator.language:`en`)}function u(){return t.weekendDays??[]}function d(n){return!!(t.min&&e.Temporal.PlainDate.compare(n,t.min)<0||t.max&&e.Temporal.PlainDate.compare(n,t.max)>0)}function f(e){return!!(d(e)||u().includes(a(e.dayOfWeek)))}function p(){let t=i(),r=e.Temporal.PlainDate.from({day:1,month:c,year:s}),o=a(r.dayOfWeek),l=[];for(let i=o-1;i>=0;i--){let a=r.subtract({days:i+1});l.push({day:a.day,isDisabled:f(a),iso:a.toString(),isOutsideMonth:!0,isSelected:n!==null&&e.Temporal.PlainDate.compare(a,n)===0,isToday:e.Temporal.PlainDate.compare(a,t)===0,plain:a})}let u=r.daysInMonth;for(let r=1;r<=u;r++){let i=e.Temporal.PlainDate.from({day:r,month:c,year:s});l.push({day:r,isDisabled:f(i),iso:i.toString(),isOutsideMonth:!1,isSelected:n!==null&&e.Temporal.PlainDate.compare(i,n)===0,isToday:e.Temporal.PlainDate.compare(i,t)===0,plain:i})}let d=(7-l.length%7)%7,p=r.add({months:1});for(let r=0;r<d;r++){let i=p.add({days:r});l.push({day:i.day,isDisabled:f(i),iso:i.toString(),isOutsideMonth:!0,isSelected:n!==null&&e.Temporal.PlainDate.compare(i,n)===0,isToday:e.Temporal.PlainDate.compare(i,t)===0,plain:i})}return l}function m(){let r=[],i=l();for(let a=1;a<=12;a++){let o=e.Temporal.PlainDate.from({day:1,month:a,year:s}),c=o.with({day:o.daysInMonth}),l=t.min!==null&&t.min!==void 0&&e.Temporal.PlainDate.compare(c,t.min)<0||t.max!==null&&t.max!==void 0&&e.Temporal.PlainDate.compare(o,t.max)>0;r.push({isDisabled:l,isSelected:n!==null&&n.year===s&&n.month===a,label:(0,e.format)(o,{intl:{month:`long`},locale:i,tz:`UTC`}),month:a,shortLabel:(0,e.format)(o,{intl:{month:`short`},locale:i,tz:`UTC`})})}return r}function h(){let r=Math.floor(s/10)*10,i=[];for(let a=r;a<r+12;a++){let r=e.Temporal.PlainDate.from({day:1,month:1,year:a}),o=e.Temporal.PlainDate.from({day:31,month:12,year:a}),s=t.min!==null&&t.min!==void 0&&e.Temporal.PlainDate.compare(o,t.min)<0||t.max!==null&&t.max!==void 0&&e.Temporal.PlainDate.compare(r,t.max)>0;i.push({isDisabled:s,isSelected:n!==null&&n.year===a,year:a})}return i}function g(){let t=(0,e.parsePlainDate)(`2024-01-07`),n=l();return Array.from({length:7},(r,i)=>(0,e.format)(t.add({days:i}),{intl:{weekday:`short`},locale:n,tz:`UTC`}))}return{dayCells:p,displayMonth:()=>c,displayYear:()=>s,goTo(e,t){Number.isFinite(e)&&(s=e,c=Math.max(1,Math.min(12,t)))},monthCells:m,nextMonth(){c===12?(c=1,s++):c++},nextYear(){s++},prevMonth(){c===1?(c=12,s--):c--},prevYear(){s--},select(e){e!==null&&f(e)||(n=e,n&&(s=n.year,c=n.month),t.onChange(n))},selected:()=>n,setView(e){r=e},view:()=>r,weekdayLabels:g,yearCells:h}}exports.createDatePickerControl=o,exports.formatDisplayDate=r,exports.parseIso=t,exports.toIsoString=n;
|
|
2
2
|
//# sourceMappingURL=date-picker.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-picker.cjs","names":[],"sources":["../../src/core/date-picker.ts"],"sourcesContent":["import { Temporal, format, parsePlainDate } from '@vielzeug/tempo';\n\n// ── Public ISO helpers (single source of truth for both components) ───────────\n\n/**\n * Parses an ISO 8601 date string (`yyyy-MM-dd`) into a `Temporal.PlainDate`.\n * Returns `null` for any invalid / empty input — never throws.\n */\nexport function parseIso(iso: string | undefined | null): Temporal.PlainDate | null {\n if (!iso) return null;\n\n try {\n return parsePlainDate(iso);\n } catch {\n return null;\n }\n}\n\n/**\n * Serialises a `Temporal.PlainDate` (or `null`) to an ISO 8601 string (`yyyy-MM-dd`).\n * Returns `null` when the input is `null`.\n */\nexport function toIsoString(date: Temporal.PlainDate | null): string | null {\n return date ? date.toString() : null;\n}\n\n/**\n * Formats a `Temporal.PlainDate` for display in a given locale.\n * e.g. \"15 Jun 2025\" (default medium pattern).\n */\nexport function formatDisplayDate(date: Temporal.PlainDate, locale: string): string {\n return format(date, { intl: { day: 'numeric', month: 'short', year: 'numeric' }, locale, tz: 'UTC' });\n}\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type DatePickerView = 'day' | 'month' | 'year';\n\nexport type DateCell = {\n /** Day-of-month number (1–31) */\n day: number;\n isDisabled: boolean;\n /** ISO date string yyyy-MM-dd — stable key for rendering */\n iso: string;\n isOutsideMonth: boolean;\n isSelected: boolean;\n isToday: boolean;\n plain: Temporal.PlainDate;\n};\n\nexport type MonthCell = {\n isDisabled: boolean;\n isSelected: boolean;\n label: string;\n /** 1-based month number (Temporal convention: 1 = January … 12 = December) */\n month: number;\n shortLabel: string;\n};\n\nexport type YearCell = {\n isDisabled: boolean;\n isSelected: boolean;\n year: number;\n};\n\nexport type DatePickerControlOptions = {\n /** Locale for day/month names, defaults to `navigator.language` or `'en'` */\n locale?: string;\n /** Maximum selectable date (inclusive) */\n max?: Temporal.PlainDate | null;\n /** Minimum selectable date (inclusive) */\n min?: Temporal.PlainDate | null;\n /** Called when a date is committed by the user */\n onChange: (date: Temporal.PlainDate | null) => void;\n /** Currently selected date */\n value?: Temporal.PlainDate | null;\n /**\n * Which days of the week to disable (0 = Sunday … 6 = Saturday).\n * @example [0, 6] disables weekends\n */\n weekendDays?: number[];\n};\n\nexport type DatePickerControl = {\n /** Ordered day cells for the visible month grid (includes leading/trailing days) */\n dayCells(): DateCell[];\n /** Month currently shown in the header (1-indexed, Temporal convention) */\n displayMonth(): number;\n /** Year currently shown in the header */\n displayYear(): number;\n /** Jump to a specific display month/year without selecting */\n goTo(year: number, month: number): void;\n /** All 12 month cells */\n monthCells(): MonthCell[];\n /** Move display month forward by one */\n nextMonth(): void;\n /** Move display year forward by one */\n nextYear(): void;\n /** Move display month backward by one */\n prevMonth(): void;\n /** Move display year backward by one */\n prevYear(): void;\n /** Select a date. Pass null to clear. */\n select(date: Temporal.PlainDate | null): void;\n /** The currently selected date, or null */\n selected(): Temporal.PlainDate | null;\n /** Switch the calendar view */\n setView(view: DatePickerView): void;\n /** Currently rendered calendar view */\n view(): DatePickerView;\n /** Short week-day labels in locale order e.g. [\"Su\",\"Mo\",…] */\n weekdayLabels(): string[];\n /** Year cells for the visible decade window */\n yearCells(): YearCell[];\n};\n\n// ── Internal helpers ──────────────────────────────────────────────────────────\n\n/**\n * Returns today as a `Temporal.PlainDate` in the ISO calendar.\n * @internal\n */\nfunction todayPlain(): Temporal.PlainDate {\n return Temporal.Now.plainDateISO();\n}\n\n/**\n * Converts a `Temporal.PlainDate.dayOfWeek` value (1=Mon…7=Sun) to the\n * 0-based weekday index (0=Sun…6=Sat) used in the `weekendDays` prop.\n * @internal\n */\nfunction temporalDowToIndex(dow: number): number {\n // Temporal: 1=Mon, 2=Tue, …, 7=Sun → 0=Sun, 1=Mon, …, 6=Sat\n return dow % 7;\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Pure, framework-agnostic date-picker state machine backed by `Temporal.PlainDate`.\n *\n * All state is held in plain mutable variables — suitable for wrapping in any\n * reactive layer (ore `signal`, Vue ref, etc.). The factory returns a stable\n * handle object; callers are responsible for reactivity.\n *\n * Options with getter-based live bindings (e.g. `get min() { ... }`) are read\n * on every call so the control always reflects the latest reactive state.\n *\n * @example\n * ```ts\n * const ctrl = createDatePickerControl({\n * value: Temporal.PlainDate.from('2025-06-15'),\n * locale: 'en-US',\n * onChange: (date) => console.log(date?.toString()),\n * });\n *\n * ctrl.nextMonth();\n * ctrl.select(Temporal.PlainDate.from('2025-07-04'));\n * ```\n */\nexport function createDatePickerControl(options: DatePickerControlOptions): DatePickerControl {\n // ── Mutable state ─────────────────────────────────────────────────────────\n\n let _selected: Temporal.PlainDate | null = options.value ?? null;\n let _view: DatePickerView = 'day';\n\n const initial = _selected ?? todayPlain();\n\n let _displayYear = initial.year;\n let _displayMonth = initial.month; // 1-indexed (Temporal convention)\n\n // ── Live option accessors (always read from options for reactive compat) ───\n\n function locale(): string {\n return options.locale ?? (typeof navigator !== 'undefined' ? navigator.language : 'en');\n }\n\n function weekendDays(): number[] {\n return options.weekendDays ?? [];\n }\n\n // ── Range / disabled helpers ───────────────────────────────────────────────\n\n function isOutOfRange(date: Temporal.PlainDate): boolean {\n if (options.min && Temporal.PlainDate.compare(date, options.min) < 0) return true;\n\n if (options.max && Temporal.PlainDate.compare(date, options.max) > 0) return true;\n\n return false;\n }\n\n function isDayDisabled(date: Temporal.PlainDate): boolean {\n if (isOutOfRange(date)) return true;\n\n if (weekendDays().includes(temporalDowToIndex(date.dayOfWeek))) return true;\n\n return false;\n }\n\n // ── Grid builders ─────────────────────────────────────────────────────────\n\n function buildDayCells(): DateCell[] {\n const todayDate = todayPlain();\n const firstOfMonth = Temporal.PlainDate.from({ day: 1, month: _displayMonth, year: _displayYear });\n\n // Sunday-start grid: Temporal dayOfWeek is 1=Mon…7=Sun, convert to 0=Sun offset\n const startOffset = temporalDowToIndex(firstOfMonth.dayOfWeek);\n const cells: DateCell[] = [];\n\n // Leading days from previous month\n for (let i = startOffset - 1; i >= 0; i--) {\n const d = firstOfMonth.subtract({ days: i + 1 });\n\n cells.push({\n day: d.day,\n isDisabled: isDayDisabled(d),\n iso: d.toString(),\n isOutsideMonth: true,\n isSelected: _selected !== null && Temporal.PlainDate.compare(d, _selected) === 0,\n isToday: Temporal.PlainDate.compare(d, todayDate) === 0,\n plain: d,\n });\n }\n\n // Days in current month\n const daysInMonth = firstOfMonth.daysInMonth;\n\n for (let day = 1; day <= daysInMonth; day++) {\n const d = Temporal.PlainDate.from({ day, month: _displayMonth, year: _displayYear });\n\n cells.push({\n day,\n isDisabled: isDayDisabled(d),\n iso: d.toString(),\n isOutsideMonth: false,\n isSelected: _selected !== null && Temporal.PlainDate.compare(d, _selected) === 0,\n isToday: Temporal.PlainDate.compare(d, todayDate) === 0,\n plain: d,\n });\n }\n\n // Trailing days to fill last row (always end at multiple of 7)\n const remaining = (7 - (cells.length % 7)) % 7;\n const firstOfNext = firstOfMonth.add({ months: 1 });\n\n for (let i = 0; i < remaining; i++) {\n const d = firstOfNext.add({ days: i });\n\n cells.push({\n day: d.day,\n isDisabled: isDayDisabled(d),\n iso: d.toString(),\n isOutsideMonth: true,\n isSelected: _selected !== null && Temporal.PlainDate.compare(d, _selected) === 0,\n isToday: Temporal.PlainDate.compare(d, todayDate) === 0,\n plain: d,\n });\n }\n\n return cells;\n }\n\n function buildMonthCells(): MonthCell[] {\n const cells: MonthCell[] = [];\n const loc = locale();\n\n for (let m = 1; m <= 12; m++) {\n const plain = Temporal.PlainDate.from({ day: 1, month: m, year: _displayYear });\n const lastOfMonth = plain.with({ day: plain.daysInMonth });\n\n const isDisabled =\n (options.min !== null && options.min !== undefined\n ? Temporal.PlainDate.compare(lastOfMonth, options.min) < 0\n : false) ||\n (options.max !== null && options.max !== undefined\n ? Temporal.PlainDate.compare(plain, options.max) > 0\n : false);\n\n cells.push({\n isDisabled,\n isSelected: _selected !== null && _selected.year === _displayYear && _selected.month === m,\n label: format(plain, { intl: { month: 'long' }, locale: loc, tz: 'UTC' }),\n month: m,\n shortLabel: format(plain, { intl: { month: 'short' }, locale: loc, tz: 'UTC' }),\n });\n }\n\n return cells;\n }\n\n function buildYearCells(): YearCell[] {\n const decadeStart = Math.floor(_displayYear / 10) * 10;\n const cells: YearCell[] = [];\n\n for (let y = decadeStart; y < decadeStart + 12; y++) {\n const firstOfYear = Temporal.PlainDate.from({ day: 1, month: 1, year: y });\n const lastOfYear = Temporal.PlainDate.from({ day: 31, month: 12, year: y });\n\n const isDisabled =\n (options.min !== null && options.min !== undefined\n ? Temporal.PlainDate.compare(lastOfYear, options.min) < 0\n : false) ||\n (options.max !== null && options.max !== undefined\n ? Temporal.PlainDate.compare(firstOfYear, options.max) > 0\n : false);\n\n cells.push({\n isDisabled,\n isSelected: _selected !== null && _selected.year === y,\n year: y,\n });\n }\n\n return cells;\n }\n\n function buildWeekdayLabels(): string[] {\n // 2024-01-07 is a Sunday — use it as the Sunday anchor\n const sunday = parsePlainDate('2024-01-07');\n const loc = locale();\n\n return Array.from({ length: 7 }, (_, i) =>\n format(sunday.add({ days: i }), { intl: { weekday: 'short' }, locale: loc, tz: 'UTC' }),\n );\n }\n\n // ── Handle ────────────────────────────────────────────────────────────────\n\n return {\n dayCells: buildDayCells,\n displayMonth: () => _displayMonth,\n displayYear: () => _displayYear,\n\n goTo(year: number, month: number): void {\n if (!Number.isFinite(year)) return;\n\n _displayYear = year;\n _displayMonth = Math.max(1, Math.min(12, month));\n },\n\n monthCells: buildMonthCells,\n\n nextMonth(): void {\n if (_displayMonth === 12) {\n _displayMonth = 1;\n _displayYear++;\n } else {\n _displayMonth++;\n }\n },\n\n nextYear(): void {\n _displayYear++;\n },\n\n prevMonth(): void {\n if (_displayMonth === 1) {\n _displayMonth = 12;\n _displayYear--;\n } else {\n _displayMonth--;\n }\n },\n\n prevYear(): void {\n _displayYear--;\n },\n\n select(date: Temporal.PlainDate | null): void {\n if (date !== null && isDayDisabled(date)) return;\n\n _selected = date;\n\n if (_selected) {\n _displayYear = _selected.year;\n _displayMonth = _selected.month;\n }\n\n options.onChange(_selected);\n },\n\n selected: () => _selected,\n\n setView(view: DatePickerView): void {\n _view = view;\n },\n\n view: () => _view,\n\n weekdayLabels: buildWeekdayLabels,\n\n yearCells: buildYearCells,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-picker.d.ts","sourceRoot":"","sources":["../../src/core/date-picker.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Temporal as e, format as t, parsePlainDate as n } from "@vielzeug/tempo";
|
|
2
|
-
//#region src/
|
|
2
|
+
//#region src/core/date-picker.ts
|
|
3
3
|
function r(e) {
|
|
4
4
|
if (!e) return null;
|
|
5
5
|
try {
|
|
@@ -99,7 +99,7 @@ function c(r) {
|
|
|
99
99
|
day: 1,
|
|
100
100
|
month: o,
|
|
101
101
|
year: l
|
|
102
|
-
}), c = s.with({ day: s.daysInMonth }), u =
|
|
102
|
+
}), c = s.with({ day: s.daysInMonth }), u = r.min !== null && r.min !== void 0 && e.PlainDate.compare(c, r.min) < 0 || r.max !== null && r.max !== void 0 && e.PlainDate.compare(s, r.max) > 0;
|
|
103
103
|
n.push({
|
|
104
104
|
isDisabled: u,
|
|
105
105
|
isSelected: i !== null && i.year === l && i.month === o,
|
|
@@ -129,7 +129,7 @@ function c(r) {
|
|
|
129
129
|
day: 31,
|
|
130
130
|
month: 12,
|
|
131
131
|
year: a
|
|
132
|
-
}), s =
|
|
132
|
+
}), s = r.min !== null && r.min !== void 0 && e.PlainDate.compare(o, r.min) < 0 || r.max !== null && r.max !== void 0 && e.PlainDate.compare(t, r.max) > 0;
|
|
133
133
|
n.push({
|
|
134
134
|
isDisabled: s,
|
|
135
135
|
isSelected: i !== null && i.year === a,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-picker.js","names":[],"sources":["../../src/core/date-picker.ts"],"sourcesContent":["import { Temporal, format, parsePlainDate } from '@vielzeug/tempo';\n\n// ── Public ISO helpers (single source of truth for both components) ───────────\n\n/**\n * Parses an ISO 8601 date string (`yyyy-MM-dd`) into a `Temporal.PlainDate`.\n * Returns `null` for any invalid / empty input — never throws.\n */\nexport function parseIso(iso: string | undefined | null): Temporal.PlainDate | null {\n if (!iso) return null;\n\n try {\n return parsePlainDate(iso);\n } catch {\n return null;\n }\n}\n\n/**\n * Serialises a `Temporal.PlainDate` (or `null`) to an ISO 8601 string (`yyyy-MM-dd`).\n * Returns `null` when the input is `null`.\n */\nexport function toIsoString(date: Temporal.PlainDate | null): string | null {\n return date ? date.toString() : null;\n}\n\n/**\n * Formats a `Temporal.PlainDate` for display in a given locale.\n * e.g. \"15 Jun 2025\" (default medium pattern).\n */\nexport function formatDisplayDate(date: Temporal.PlainDate, locale: string): string {\n return format(date, { intl: { day: 'numeric', month: 'short', year: 'numeric' }, locale, tz: 'UTC' });\n}\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type DatePickerView = 'day' | 'month' | 'year';\n\nexport type DateCell = {\n /** Day-of-month number (1–31) */\n day: number;\n isDisabled: boolean;\n /** ISO date string yyyy-MM-dd — stable key for rendering */\n iso: string;\n isOutsideMonth: boolean;\n isSelected: boolean;\n isToday: boolean;\n plain: Temporal.PlainDate;\n};\n\nexport type MonthCell = {\n isDisabled: boolean;\n isSelected: boolean;\n label: string;\n /** 1-based month number (Temporal convention: 1 = January … 12 = December) */\n month: number;\n shortLabel: string;\n};\n\nexport type YearCell = {\n isDisabled: boolean;\n isSelected: boolean;\n year: number;\n};\n\nexport type DatePickerControlOptions = {\n /** Locale for day/month names, defaults to `navigator.language` or `'en'` */\n locale?: string;\n /** Maximum selectable date (inclusive) */\n max?: Temporal.PlainDate | null;\n /** Minimum selectable date (inclusive) */\n min?: Temporal.PlainDate | null;\n /** Called when a date is committed by the user */\n onChange: (date: Temporal.PlainDate | null) => void;\n /** Currently selected date */\n value?: Temporal.PlainDate | null;\n /**\n * Which days of the week to disable (0 = Sunday … 6 = Saturday).\n * @example [0, 6] disables weekends\n */\n weekendDays?: number[];\n};\n\nexport type DatePickerControl = {\n /** Ordered day cells for the visible month grid (includes leading/trailing days) */\n dayCells(): DateCell[];\n /** Month currently shown in the header (1-indexed, Temporal convention) */\n displayMonth(): number;\n /** Year currently shown in the header */\n displayYear(): number;\n /** Jump to a specific display month/year without selecting */\n goTo(year: number, month: number): void;\n /** All 12 month cells */\n monthCells(): MonthCell[];\n /** Move display month forward by one */\n nextMonth(): void;\n /** Move display year forward by one */\n nextYear(): void;\n /** Move display month backward by one */\n prevMonth(): void;\n /** Move display year backward by one */\n prevYear(): void;\n /** Select a date. Pass null to clear. */\n select(date: Temporal.PlainDate | null): void;\n /** The currently selected date, or null */\n selected(): Temporal.PlainDate | null;\n /** Switch the calendar view */\n setView(view: DatePickerView): void;\n /** Currently rendered calendar view */\n view(): DatePickerView;\n /** Short week-day labels in locale order e.g. [\"Su\",\"Mo\",…] */\n weekdayLabels(): string[];\n /** Year cells for the visible decade window */\n yearCells(): YearCell[];\n};\n\n// ── Internal helpers ──────────────────────────────────────────────────────────\n\n/**\n * Returns today as a `Temporal.PlainDate` in the ISO calendar.\n * @internal\n */\nfunction todayPlain(): Temporal.PlainDate {\n return Temporal.Now.plainDateISO();\n}\n\n/**\n * Converts a `Temporal.PlainDate.dayOfWeek` value (1=Mon…7=Sun) to the\n * 0-based weekday index (0=Sun…6=Sat) used in the `weekendDays` prop.\n * @internal\n */\nfunction temporalDowToIndex(dow: number): number {\n // Temporal: 1=Mon, 2=Tue, …, 7=Sun → 0=Sun, 1=Mon, …, 6=Sat\n return dow % 7;\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Pure, framework-agnostic date-picker state machine backed by `Temporal.PlainDate`.\n *\n * All state is held in plain mutable variables — suitable for wrapping in any\n * reactive layer (ore `signal`, Vue ref, etc.). The factory returns a stable\n * handle object; callers are responsible for reactivity.\n *\n * Options with getter-based live bindings (e.g. `get min() { ... }`) are read\n * on every call so the control always reflects the latest reactive state.\n *\n * @example\n * ```ts\n * const ctrl = createDatePickerControl({\n * value: Temporal.PlainDate.from('2025-06-15'),\n * locale: 'en-US',\n * onChange: (date) => console.log(date?.toString()),\n * });\n *\n * ctrl.nextMonth();\n * ctrl.select(Temporal.PlainDate.from('2025-07-04'));\n * ```\n */\nexport function createDatePickerControl(options: DatePickerControlOptions): DatePickerControl {\n // ── Mutable state ─────────────────────────────────────────────────────────\n\n let _selected: Temporal.PlainDate | null = options.value ?? null;\n let _view: DatePickerView = 'day';\n\n const initial = _selected ?? todayPlain();\n\n let _displayYear = initial.year;\n let _displayMonth = initial.month; // 1-indexed (Temporal convention)\n\n // ── Live option accessors (always read from options for reactive compat) ───\n\n function locale(): string {\n return options.locale ?? (typeof navigator !== 'undefined' ? navigator.language : 'en');\n }\n\n function weekendDays(): number[] {\n return options.weekendDays ?? [];\n }\n\n // ── Range / disabled helpers ───────────────────────────────────────────────\n\n function isOutOfRange(date: Temporal.PlainDate): boolean {\n if (options.min && Temporal.PlainDate.compare(date, options.min) < 0) return true;\n\n if (options.max && Temporal.PlainDate.compare(date, options.max) > 0) return true;\n\n return false;\n }\n\n function isDayDisabled(date: Temporal.PlainDate): boolean {\n if (isOutOfRange(date)) return true;\n\n if (weekendDays().includes(temporalDowToIndex(date.dayOfWeek))) return true;\n\n return false;\n }\n\n // ── Grid builders ─────────────────────────────────────────────────────────\n\n function buildDayCells(): DateCell[] {\n const todayDate = todayPlain();\n const firstOfMonth = Temporal.PlainDate.from({ day: 1, month: _displayMonth, year: _displayYear });\n\n // Sunday-start grid: Temporal dayOfWeek is 1=Mon…7=Sun, convert to 0=Sun offset\n const startOffset = temporalDowToIndex(firstOfMonth.dayOfWeek);\n const cells: DateCell[] = [];\n\n // Leading days from previous month\n for (let i = startOffset - 1; i >= 0; i--) {\n const d = firstOfMonth.subtract({ days: i + 1 });\n\n cells.push({\n day: d.day,\n isDisabled: isDayDisabled(d),\n iso: d.toString(),\n isOutsideMonth: true,\n isSelected: _selected !== null && Temporal.PlainDate.compare(d, _selected) === 0,\n isToday: Temporal.PlainDate.compare(d, todayDate) === 0,\n plain: d,\n });\n }\n\n // Days in current month\n const daysInMonth = firstOfMonth.daysInMonth;\n\n for (let day = 1; day <= daysInMonth; day++) {\n const d = Temporal.PlainDate.from({ day, month: _displayMonth, year: _displayYear });\n\n cells.push({\n day,\n isDisabled: isDayDisabled(d),\n iso: d.toString(),\n isOutsideMonth: false,\n isSelected: _selected !== null && Temporal.PlainDate.compare(d, _selected) === 0,\n isToday: Temporal.PlainDate.compare(d, todayDate) === 0,\n plain: d,\n });\n }\n\n // Trailing days to fill last row (always end at multiple of 7)\n const remaining = (7 - (cells.length % 7)) % 7;\n const firstOfNext = firstOfMonth.add({ months: 1 });\n\n for (let i = 0; i < remaining; i++) {\n const d = firstOfNext.add({ days: i });\n\n cells.push({\n day: d.day,\n isDisabled: isDayDisabled(d),\n iso: d.toString(),\n isOutsideMonth: true,\n isSelected: _selected !== null && Temporal.PlainDate.compare(d, _selected) === 0,\n isToday: Temporal.PlainDate.compare(d, todayDate) === 0,\n plain: d,\n });\n }\n\n return cells;\n }\n\n function buildMonthCells(): MonthCell[] {\n const cells: MonthCell[] = [];\n const loc = locale();\n\n for (let m = 1; m <= 12; m++) {\n const plain = Temporal.PlainDate.from({ day: 1, month: m, year: _displayYear });\n const lastOfMonth = plain.with({ day: plain.daysInMonth });\n\n const isDisabled =\n (options.min !== null && options.min !== undefined\n ? Temporal.PlainDate.compare(lastOfMonth, options.min) < 0\n : false) ||\n (options.max !== null && options.max !== undefined\n ? Temporal.PlainDate.compare(plain, options.max) > 0\n : false);\n\n cells.push({\n isDisabled,\n isSelected: _selected !== null && _selected.year === _displayYear && _selected.month === m,\n label: format(plain, { intl: { month: 'long' }, locale: loc, tz: 'UTC' }),\n month: m,\n shortLabel: format(plain, { intl: { month: 'short' }, locale: loc, tz: 'UTC' }),\n });\n }\n\n return cells;\n }\n\n function buildYearCells(): YearCell[] {\n const decadeStart = Math.floor(_displayYear / 10) * 10;\n const cells: YearCell[] = [];\n\n for (let y = decadeStart; y < decadeStart + 12; y++) {\n const firstOfYear = Temporal.PlainDate.from({ day: 1, month: 1, year: y });\n const lastOfYear = Temporal.PlainDate.from({ day: 31, month: 12, year: y });\n\n const isDisabled =\n (options.min !== null && options.min !== undefined\n ? Temporal.PlainDate.compare(lastOfYear, options.min) < 0\n : false) ||\n (options.max !== null && options.max !== undefined\n ? Temporal.PlainDate.compare(firstOfYear, options.max) > 0\n : false);\n\n cells.push({\n isDisabled,\n isSelected: _selected !== null && _selected.year === y,\n year: y,\n });\n }\n\n return cells;\n }\n\n function buildWeekdayLabels(): string[] {\n // 2024-01-07 is a Sunday — use it as the Sunday anchor\n const sunday = parsePlainDate('2024-01-07');\n const loc = locale();\n\n return Array.from({ length: 7 }, (_, i) =>\n format(sunday.add({ days: i }), { intl: { weekday: 'short' }, locale: loc, tz: 'UTC' }),\n );\n }\n\n // ── Handle ────────────────────────────────────────────────────────────────\n\n return {\n dayCells: buildDayCells,\n displayMonth: () => _displayMonth,\n displayYear: () => _displayYear,\n\n goTo(year: number, month: number): void {\n if (!Number.isFinite(year)) return;\n\n _displayYear = year;\n _displayMonth = Math.max(1, Math.min(12, month));\n },\n\n monthCells: buildMonthCells,\n\n nextMonth(): void {\n if (_displayMonth === 12) {\n _displayMonth = 1;\n _displayYear++;\n } else {\n _displayMonth++;\n }\n },\n\n nextYear(): void {\n _displayYear++;\n },\n\n prevMonth(): void {\n if (_displayMonth === 1) {\n _displayMonth = 12;\n _displayYear--;\n } else {\n _displayMonth--;\n }\n },\n\n prevYear(): void {\n _displayYear--;\n },\n\n select(date: Temporal.PlainDate | null): void {\n if (date !== null && isDayDisabled(date)) return;\n\n _selected = date;\n\n if (_selected) {\n _displayYear = _selected.year;\n _displayMonth = _selected.month;\n }\n\n options.onChange(_selected);\n },\n\n selected: () => _selected,\n\n setView(view: DatePickerView): void {\n _view = view;\n },\n\n view: () => _view,\n\n weekdayLabels: buildWeekdayLabels,\n\n yearCells: buildYearCells,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"direction.cjs","names":[],"sources":["../../src/core/direction.ts"],"sourcesContent":["/**\n * Resolves the effective text direction for an element.\n *\n * The closest ancestor with an explicit `dir=\"ltr\"` or `dir=\"rtl\"` attribute wins — this\n * supports locally-scoped RTL sections (e.g. a single `dir=\"rtl\"` panel inside an otherwise\n * LTR page). When no explicit `dir` attribute is found up the tree, falls back to the\n * computed `direction` CSS property (e.g. set via a `direction: rtl` style rule with no\n * `dir` attribute). Defaults to `'ltr'` when `el` is `null`.\n */\nexport function elementDirection(el: Element | null): 'ltr' | 'rtl' {\n let node: Element | null = el;\n\n while (node) {\n const dir = node.getAttribute('dir');\n\n if (dir === 'rtl' || dir === 'ltr') return dir;\n\n node = node.parentElement;\n }\n\n return el !== null && getComputedStyle(el).direction === 'rtl' ? 'rtl' : 'ltr';\n}\n"],"mappings":"AASA,SAAgB,EAAiB,EAAmC,CAClE,IAAI,EAAuB,EAE3B,KAAO,GAAM,CACX,IAAM,EAAM,EAAK,aAAa,KAAK,EAEnC,GAAI,IAAQ,OAAS,IAAQ,MAAO,OAAO,EAE3C,EAAO,EAAK,aACd,CAEA,OAAO,IAAO,MAAQ,iBAAiB,CAAE,CAAC,CAAC,YAAc,MAAQ,MAAQ,KAC3E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"direction.d.ts","sourceRoot":"","sources":["../../src/core/direction.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,CAYlE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"direction.js","names":[],"sources":["../../src/core/direction.ts"],"sourcesContent":["/**\n * Resolves the effective text direction for an element.\n *\n * The closest ancestor with an explicit `dir=\"ltr\"` or `dir=\"rtl\"` attribute wins — this\n * supports locally-scoped RTL sections (e.g. a single `dir=\"rtl\"` panel inside an otherwise\n * LTR page). When no explicit `dir` attribute is found up the tree, falls back to the\n * computed `direction` CSS property (e.g. set via a `direction: rtl` style rule with no\n * `dir` attribute). Defaults to `'ltr'` when `el` is `null`.\n */\nexport function elementDirection(el: Element | null): 'ltr' | 'rtl' {\n let node: Element | null = el;\n\n while (node) {\n const dir = node.getAttribute('dir');\n\n if (dir === 'rtl' || dir === 'ltr') return dir;\n\n node = node.parentElement;\n }\n\n return el !== null && getComputedStyle(el).direction === 'rtl' ? 'rtl' : 'ltr';\n}\n"],"mappings":";AASA,SAAgB,EAAiB,GAAmC;CAClE,IAAI,IAAuB;CAE3B,OAAO,IAAM;EACX,IAAM,IAAM,EAAK,aAAa,KAAK;EAEnC,IAAI,MAAQ,SAAS,MAAQ,OAAO,OAAO;EAE3C,IAAO,EAAK;CACd;CAEA,OAAO,MAAO,QAAQ,iBAAiB,CAAE,CAAC,CAAC,cAAc,QAAQ,QAAQ;AAC3E"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
require("../_dev.cjs");let e=require("@vielzeug/ore"),t=require("@vielzeug/ripple");var n=e=>(0,t.computed)(()=>({errorText:e.error?.value??``,helperText:e.helper?.value??``})),r=e=>(0,t.computed)(()=>{let t=e.value?.value??``,n=e.maxLength?.value,r=Number(n),i=Number.isFinite(r)&&r>0?r:null,a=i!==null,o=a?`${t.length} / ${i}`:``,s=a?t.length/i:0;return{counterAtLimit:a?s>=1:!1,counterNearLimit:a?s>=.9&&s<1:!1,counterText:o}}),i=(e,t=`counter`)=>e?e.counterAtLimit?`${t} at-limit`:e.counterNearLimit?`${t} near-limit`:t:t;function a(){let e=!1;return{clear:()=>{e=!1},get isDirty(){return e},markDirty:()=>{e=!0}}}var o=r=>{let i=(0,t.computed)(()=>!!r.disabled?.value),a=(0,e.createStableId)(`helper`),o=(0,e.createStableId)(`error`),s=n({error:r.error,helper:r.helper}),c=(0,t.computed)(()=>s.value.errorText||s.value.helperText?a:null),l=(0,t.computed)(()=>s.value.errorText?o:null),u=(0,t.computed)(()=>s.value.errorText?`true`:null),d=null;return{ariaDescribedBy:c,ariaErrorMessage:l,ariaInvalid:u,assistiveId:a,attachFormField:e=>{d=e},disabled:i,errorId:o,errorText:(0,t.computed)(()=>s.value.errorText),helperText:(0,t.computed)(()=>s.value.helperText),triggerValidation:e=>{r.validateOn?.value===e&&d&&d.reportValidity()}}},s=n=>{let r=n.id??(0,e.createStableId)(n.prefix??`field`),i=(0,e.createStableId)(`label`),a=n.label??(0,t.computed)(()=>void 0),o=n.hasLabel??(0,t.computed)(()=>!!a.value);return{ariaLabelledBy:(0,t.computed)(()=>o.value?i:null),fieldId:r,labelId:i,labelVisible:o}},c=e=>({...o(e),...s(e)});exports.counterClassName=i,exports.createAssistiveState=o,exports.createCounterState=r,exports.createDirtyTracker=a,exports.createErrorHelperState=n,exports.createField=c,exports.createLabelState=s;
|
|
2
|
+
//# sourceMappingURL=field-base.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-base.cjs","names":[],"sources":["../../src/core/field-base.ts"],"sourcesContent":["import { createStableId } from '@vielzeug/ore';\nimport { computed, type Readable } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\n\n// ── Validation / context types ────────────────────────────────────────────────\n\nexport type ValidationTrigger = 'blur' | 'change' | 'input' | 'submit';\nexport type ControlValidationMode = ValidationTrigger | undefined;\n\n// ── Error/helper assistive state ──────────────────────────────────────────────\n\nexport type ErrorHelperState = {\n errorText: string;\n helperText: string;\n};\n\nexport type ErrorHelperOptions = {\n error?: Readable<string | undefined>;\n helper?: Readable<string | undefined>;\n};\n\nexport const createErrorHelperState = (options: ErrorHelperOptions): Readable<ErrorHelperState> =>\n computed(() => ({\n errorText: options.error?.value ?? '',\n helperText: options.helper?.value ?? '',\n }));\n\n// ── Counter state (opt-in) ─────────────────────────────────────────────────────\n\n/** Counter state for text fields with `maxLength`. */\nexport type CounterState = {\n counterAtLimit: boolean;\n counterNearLimit: boolean;\n counterText: string;\n};\n\nexport type CounterOptions = {\n maxLength?: Readable<number | undefined>;\n value: Readable<string | undefined>;\n};\n\n/**\n * Creates a reactive counter state signal for text fields with `maxLength`.\n * Only call this when `maxLength` may be set — the state is genuinely opt-in.\n *\n * @example\n * ```ts\n * const counter = createCounterState({ value: tf.value, maxLength: props.maxlength });\n * ```\n */\nexport const createCounterState = (options: CounterOptions): Readable<CounterState> =>\n computed<CounterState>(() => {\n const value = options.value?.value ?? '';\n const maxLength = options.maxLength?.value;\n const parsedMaxLength = Number(maxLength);\n const validMaxLength = Number.isFinite(parsedMaxLength) && parsedMaxLength > 0 ? parsedMaxLength : null;\n const hasCounter = validMaxLength !== null;\n const counterText = hasCounter ? `${value.length} / ${validMaxLength}` : '';\n const ratio = hasCounter ? value.length / validMaxLength : 0;\n\n return {\n counterAtLimit: hasCounter ? ratio >= 1 : false,\n counterNearLimit: hasCounter ? ratio >= 0.9 && ratio < 1 : false,\n counterText,\n };\n });\n\n/**\n * The single implementation of \"which modifier class does this counter state map to\" —\n * every field with a character counter (`ore-input`, `ore-textarea`, `ore-message-composer`)\n * renders the identical near-limit/at-limit styling, so this is the one place that decides it.\n *\n * @example\n * ```ts\n * html`<span class=\"${() => counterClassName(counter?.value)}\">...</span>`\n * ```\n */\nexport const counterClassName = (counter: CounterState | undefined, base = 'counter'): string => {\n if (!counter) return base;\n\n if (counter.counterAtLimit) return `${base} at-limit`;\n\n if (counter.counterNearLimit) return `${base} near-limit`;\n\n return base;\n};\n\n// ── Dirty tracking (two-state reset) ───────────────────────────────────────────\n\n/**\n * Tracks whether a control's value has ever been changed by user interaction.\n *\n * Backs the \"two-state reset()\" pattern used by `createCheckable` (`checked`/\n * `indeterminate`) and `createChoiceField` (`selectedValues`): before the first\n * interaction, the live prop is still a reliable \"current default\" to resync from\n * (e.g. an async-loaded value arriving after mount); after it, the prop is\n * contaminated by interaction-driven attribute reflection, so only a snapshot taken\n * at creation still represents \"the default\" in the native sense. `reset()` callers\n * check `isDirty` to decide which source to revert to, then call `clear()`.\n */\nexport type DirtyTracker = {\n clear: () => void;\n readonly isDirty: boolean;\n markDirty: () => void;\n};\n\nexport function createDirtyTracker(): DirtyTracker {\n let dirty = false;\n\n return {\n clear: () => {\n dirty = false;\n },\n get isDirty() {\n return dirty;\n },\n markDirty: () => {\n dirty = true;\n },\n };\n}\n\n// ── Label placement ───────────────────────────────────────────────────────────\n\nexport type LabelPlacement = 'inset' | 'outside' | undefined;\n\n// ── Assistive state (error/helper text, describedby/errormessage/invalid) ─────\n\n/**\n * A field's error/helper/disabled/validation-trigger state — everything an input\n * needs *except* a visible `<label>`. Split out from label state (below) because\n * not every field has one: `ore-message-composer` names itself via `aria-label`\n * (a chat composer's accessible name conventionally comes from context/placeholder,\n * not a floating `<label>`), so it composes this directly instead of pulling in\n * label plumbing it can't use. `createField()` composes both for fields that do\n * render a `<label>`.\n */\nexport type AssistiveStateHandle = {\n /** `aria-describedby` value. Non-null when helper or error text is present. */\n ariaDescribedBy: Readable<string | null>;\n /** `aria-errormessage` value. Non-null when error text is present. */\n ariaErrorMessage: Readable<string | null>;\n /** `aria-invalid` value. `'true'` when error text is present, otherwise `null`. */\n ariaInvalid: Readable<'true' | null>;\n /**\n * The stable `id` used for `aria-describedby` on the input. Points at the\n * assistive-text region (covers both helper text and error text per WAI-ARIA).\n */\n assistiveId: string;\n /**\n * Registers the real form field handle (the return value of `useField()`) so that\n * `triggerValidation()` can call its `reportValidity()`. See `TextFieldHandle.attachFormField`\n * for why this is a post-hoc call instead of a constructor option — the same forward\n * reference applies here for choice fields.\n */\n attachFormField: (formField: { reportValidity(): void }) => void;\n disabled: Readable<boolean>;\n /** Stable `id` for the inline error message element (`aria-errormessage`). */\n errorId: string;\n /** Reactive error text. Empty string when no error is set. */\n errorText: Readable<string>;\n /** Reactive helper text. Empty string when no helper is set. */\n helperText: Readable<string>;\n triggerValidation: (on: Extract<ValidationTrigger, 'blur' | 'change'>) => void;\n};\n\nexport type AssistiveStateOptions = {\n disabled?: Readable<boolean | undefined>;\n error?: Readable<string | undefined>;\n helper?: Readable<string | undefined>;\n validateOn?: Readable<ControlValidationMode>;\n};\n\nexport const createAssistiveState = (options: AssistiveStateOptions): AssistiveStateHandle => {\n const disabled = computed(() => Boolean(options.disabled?.value));\n const assistiveId = createStableId('helper');\n const errorId = createStableId('error');\n const resolvedAssistive = createErrorHelperState({ error: options.error, helper: options.helper });\n\n const ariaDescribedBy = computed(() =>\n resolvedAssistive.value.errorText || resolvedAssistive.value.helperText ? assistiveId : null,\n );\n const ariaErrorMessage = computed(() => (resolvedAssistive.value.errorText ? errorId : null));\n const ariaInvalid = computed<'true' | null>(() => (resolvedAssistive.value.errorText ? 'true' : null));\n\n let formField: { reportValidity(): void } | null = null;\n const attachFormField = (nextFormField: { reportValidity(): void }): void => {\n formField = nextFormField;\n };\n\n const triggerValidation = (on: Extract<ValidationTrigger, 'blur' | 'change'>): void => {\n if (options.validateOn?.value !== on) return;\n\n if (!formField) {\n // Not user-facing — this only fires when a component author wired `validateOn` but\n // forgot the matching `attachFormField(useField(...))` call, so validation silently\n // never runs instead of erroring where the mistake actually is.\n warn(\n \"triggerValidation() called before attachFormField() — validation will not run. See createTextField()/createChoiceField()/createCheckable()'s attachFormField doc comment.\",\n );\n\n return;\n }\n\n formField.reportValidity();\n };\n\n return {\n ariaDescribedBy,\n ariaErrorMessage,\n ariaInvalid,\n assistiveId,\n attachFormField,\n disabled,\n errorId,\n errorText: computed(() => resolvedAssistive.value.errorText),\n helperText: computed(() => resolvedAssistive.value.helperText),\n triggerValidation,\n };\n};\n\n// ── Label state (visible `<label>` + `aria-labelledby`) ────────────────────────\n\nexport type LabelStateHandle = {\n /** `aria-labelledby` value. Non-null when a label is visible. */\n ariaLabelledBy: Readable<string | null>;\n /** Stable `id` for the underlying form control — the `<label for=\"...\">` target. */\n fieldId: string;\n /** Stable `id` for the label element. Stamp this on your `<label id=\"...\">`. */\n labelId: string;\n /** Whether the label should be visible. Use to toggle `hidden` on the `<label>`. */\n labelVisible: Readable<boolean>;\n};\n\nexport type LabelStateOptions = {\n /**\n * Override for label visibility that takes precedence over deriving it from\n * the `label` text signal. Pass a computed signal that checks both prop and\n * slot presence to enable slot-first composition.\n *\n * @example\n * ```ts\n * const hasLabel = computed(() => !!props.label.value || slots.has('label').value);\n * const label = createLabelState({ ...options, hasLabel });\n * ```\n */\n hasLabel?: Readable<boolean>;\n /**\n * When provided, used directly as `fieldId` instead of generating one via\n * `createStableId`. Useful in tests for deterministic ID assertions.\n */\n id?: string;\n /**\n * Label text signal. When provided, `labelVisible` and `ariaLabelledBy` are\n * computed reactively from this value (see `hasLabel` for the slot-first override).\n */\n label?: Readable<string | undefined>;\n /**\n * Label placement signal. Defaults to `'inset'`.\n *\n * Not consumed by `createLabelState()` itself — visibility is driven solely by\n * `hasLabel`/`label`. Threaded through so component authors can apply\n * placement-specific styling (`label-placement` attribute) without a second\n * option; if placement-dependent visibility is ever needed, wire it into\n * `labelVisible` here instead of adding a new option.\n */\n labelPlacement?: Readable<LabelPlacement>;\n prefix?: string;\n};\n\nexport const createLabelState = (options: LabelStateOptions): LabelStateHandle => {\n const fieldId = options.id ?? createStableId(options.prefix ?? 'field');\n const labelId = createStableId('label');\n const label$ = options.label ?? computed(() => undefined);\n\n // Slot-first composition: if the caller provides `hasLabel`, use it instead\n // of deriving visibility purely from the label text signal. This allows\n // components with a `<slot name=\"label\">` to stay visible even when the\n // `label` prop is empty but a slotted label element is present.\n const labelVisible = options.hasLabel ?? computed(() => Boolean(label$.value));\n const ariaLabelledBy = computed(() => (labelVisible.value ? labelId : null));\n\n return { ariaLabelledBy, fieldId, labelId, labelVisible };\n};\n\n// ── Field handle (assistive + label, composed) ──────────────────────────────\n\n/**\n * Common handle returned by `createField` — the base for both `TextFieldHandle`\n * and `ChoiceFieldHandle`. Composes `createAssistiveState` + `createLabelState`;\n * call those directly instead if a field has no visible `<label>` (see\n * `createAssistiveState`'s doc comment).\n *\n * ARIA signals and label state are flat on the handle — no nested `aria` or\n * `label` sub-objects. `labelId`/`labelVisible`/`ariaLabelledBy` are always\n * present but only meaningful when `label`/`hasLabel` was passed — omit both\n * and they resolve to an always-hidden, unreferenced label.\n *\n * @example\n * ```html\n * <label id=\"${labelId}\" ?hidden=\"${() => !labelVisible.value}\">...</label>\n * <input\n * aria-labelledby=\"${ariaLabelledBy}\"\n * aria-describedby=\"${ariaDescribedBy}\"\n * aria-invalid=\"${ariaInvalid}\" />\n * ```\n */\nexport type FieldHandle = AssistiveStateHandle & LabelStateHandle;\n\n/** Options shared by both `createTextField` and `createChoiceField`. */\nexport type FieldOptions = AssistiveStateOptions & LabelStateOptions;\n\nexport const createField = (options: FieldOptions): FieldHandle => ({\n ...createAssistiveState(options),\n ...createLabelState(options),\n});\n"],"mappings":"oFAsBA,IAAa,EAA0B,IAAA,EACrC,EAAA,SAAA,MAAgB,CACd,UAAW,EAAQ,OAAO,OAAS,GACnC,WAAY,EAAQ,QAAQ,OAAS,EACvC,EAAE,EAyBS,EAAsB,IAAA,EACjC,EAAA,SAAA,KAA6B,CAC3B,IAAM,EAAQ,EAAQ,OAAO,OAAS,GAChC,EAAY,EAAQ,WAAW,MAC/B,EAAkB,OAAO,CAAS,EAClC,EAAiB,OAAO,SAAS,CAAe,GAAK,EAAkB,EAAI,EAAkB,KAC7F,EAAa,IAAmB,KAChC,EAAc,EAAa,GAAG,EAAM,OAAO,KAAK,IAAmB,GACnE,EAAQ,EAAa,EAAM,OAAS,EAAiB,EAE3D,MAAO,CACL,eAAgB,EAAa,GAAS,EAAI,GAC1C,iBAAkB,EAAa,GAAS,IAAO,EAAQ,EAAI,GAC3D,aACF,CACF,CAAC,EAYU,GAAoB,EAAmC,EAAO,YACpE,EAED,EAAQ,eAAuB,GAAG,EAAK,WAEvC,EAAQ,iBAAyB,GAAG,EAAK,aAEtC,EANc,EA4BvB,SAAgB,GAAmC,CACjD,IAAI,EAAQ,GAEZ,MAAO,CACL,UAAa,CACX,EAAQ,EACV,EACA,IAAI,SAAU,CACZ,OAAO,CACT,EACA,cAAiB,CACf,EAAQ,EACV,CACF,CACF,CAqDA,IAAa,EAAwB,GAAyD,CAC5F,IAAM,GAAA,EAAW,EAAA,SAAA,KAAe,EAAQ,EAAQ,UAAU,KAAM,EAC1D,GAAA,EAAc,EAAA,eAAA,CAAe,QAAQ,EACrC,GAAA,EAAU,EAAA,eAAA,CAAe,OAAO,EAChC,EAAoB,EAAuB,CAAE,MAAO,EAAQ,MAAO,OAAQ,EAAQ,MAAO,CAAC,EAE3F,GAAA,EAAkB,EAAA,SAAA,KACtB,EAAkB,MAAM,WAAa,EAAkB,MAAM,WAAa,EAAc,IAC1F,EACM,GAAA,EAAmB,EAAA,SAAA,KAAgB,EAAkB,MAAM,UAAY,EAAU,IAAK,EACtF,GAAA,EAAc,EAAA,SAAA,KAA+B,EAAkB,MAAM,UAAY,OAAS,IAAK,EAEjG,EAA+C,KAsBnD,MAAO,CACL,kBACA,mBACA,cACA,cACA,gBA1BuB,GAAoD,CAC3E,EAAY,CACd,EAyBE,WACA,UACA,WAAA,EAAW,EAAA,SAAA,KAAe,EAAkB,MAAM,SAAS,EAC3D,YAAA,EAAY,EAAA,SAAA,KAAe,EAAkB,MAAM,UAAU,EAC7D,kBA3ByB,GAA4D,CACjF,EAAQ,YAAY,QAAU,GAE7B,GAWL,EAAU,eAAe,CAC3B,CAaA,CACF,EAmDa,EAAoB,GAAiD,CAChF,IAAM,EAAU,EAAQ,KAAA,EAAM,EAAA,eAAA,CAAe,EAAQ,QAAU,OAAO,EAChE,GAAA,EAAU,EAAA,eAAA,CAAe,OAAO,EAChC,EAAS,EAAQ,QAAA,EAAS,EAAA,SAAA,KAAe,IAAA,EAAS,EAMlD,EAAe,EAAQ,WAAA,EAAY,EAAA,SAAA,KAAe,EAAQ,EAAO,KAAM,EAG7E,MAAO,CAAE,gBAAA,EAFc,EAAA,SAAA,KAAgB,EAAa,MAAQ,EAAU,IAE7D,EAAgB,UAAS,UAAS,cAAa,CAC1D,EA6Ba,EAAe,IAAwC,CAClE,GAAG,EAAqB,CAAO,EAC/B,GAAG,EAAiB,CAAO,CAC7B"}
|
|
@@ -166,9 +166,9 @@ export declare const createLabelState: (options: LabelStateOptions) => LabelStat
|
|
|
166
166
|
* ```html
|
|
167
167
|
* <label id="${labelId}" ?hidden="${() => !labelVisible.value}">...</label>
|
|
168
168
|
* <input
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
169
|
+
* aria-labelledby="${ariaLabelledBy}"
|
|
170
|
+
* aria-describedby="${ariaDescribedBy}"
|
|
171
|
+
* aria-invalid="${ariaInvalid}" />
|
|
172
172
|
* ```
|
|
173
173
|
*/
|
|
174
174
|
export type FieldHandle = AssistiveStateHandle & LabelStateHandle;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-base.d.ts","sourceRoot":"","sources":["../../src/core/field-base.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import "../_dev.js";
|
|
2
|
+
import { createStableId as e } from "@vielzeug/ore";
|
|
3
|
+
import { computed as t } from "@vielzeug/ripple";
|
|
4
|
+
//#region src/core/field-base.ts
|
|
5
|
+
var n = (e) => t(() => ({
|
|
6
|
+
errorText: e.error?.value ?? "",
|
|
7
|
+
helperText: e.helper?.value ?? ""
|
|
8
|
+
})), r = (e) => t(() => {
|
|
9
|
+
let t = e.value?.value ?? "", n = e.maxLength?.value, r = Number(n), i = Number.isFinite(r) && r > 0 ? r : null, a = i !== null, o = a ? `${t.length} / ${i}` : "", s = a ? t.length / i : 0;
|
|
10
|
+
return {
|
|
11
|
+
counterAtLimit: a ? s >= 1 : !1,
|
|
12
|
+
counterNearLimit: a ? s >= .9 && s < 1 : !1,
|
|
13
|
+
counterText: o
|
|
14
|
+
};
|
|
15
|
+
}), i = (e, t = "counter") => e ? e.counterAtLimit ? `${t} at-limit` : e.counterNearLimit ? `${t} near-limit` : t : t;
|
|
16
|
+
function a() {
|
|
17
|
+
let e = !1;
|
|
18
|
+
return {
|
|
19
|
+
clear: () => {
|
|
20
|
+
e = !1;
|
|
21
|
+
},
|
|
22
|
+
get isDirty() {
|
|
23
|
+
return e;
|
|
24
|
+
},
|
|
25
|
+
markDirty: () => {
|
|
26
|
+
e = !0;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
var o = (r) => {
|
|
31
|
+
let i = t(() => !!r.disabled?.value), a = e("helper"), o = e("error"), s = n({
|
|
32
|
+
error: r.error,
|
|
33
|
+
helper: r.helper
|
|
34
|
+
}), c = t(() => s.value.errorText || s.value.helperText ? a : null), l = t(() => s.value.errorText ? o : null), u = t(() => s.value.errorText ? "true" : null), d = null;
|
|
35
|
+
return {
|
|
36
|
+
ariaDescribedBy: c,
|
|
37
|
+
ariaErrorMessage: l,
|
|
38
|
+
ariaInvalid: u,
|
|
39
|
+
assistiveId: a,
|
|
40
|
+
attachFormField: (e) => {
|
|
41
|
+
d = e;
|
|
42
|
+
},
|
|
43
|
+
disabled: i,
|
|
44
|
+
errorId: o,
|
|
45
|
+
errorText: t(() => s.value.errorText),
|
|
46
|
+
helperText: t(() => s.value.helperText),
|
|
47
|
+
triggerValidation: (e) => {
|
|
48
|
+
r.validateOn?.value === e && d && d.reportValidity();
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}, s = (n) => {
|
|
52
|
+
let r = n.id ?? e(n.prefix ?? "field"), i = e("label"), a = n.label ?? t(() => void 0), o = n.hasLabel ?? t(() => !!a.value);
|
|
53
|
+
return {
|
|
54
|
+
ariaLabelledBy: t(() => o.value ? i : null),
|
|
55
|
+
fieldId: r,
|
|
56
|
+
labelId: i,
|
|
57
|
+
labelVisible: o
|
|
58
|
+
};
|
|
59
|
+
}, c = (e) => ({
|
|
60
|
+
...o(e),
|
|
61
|
+
...s(e)
|
|
62
|
+
});
|
|
63
|
+
//#endregion
|
|
64
|
+
export { i as counterClassName, o as createAssistiveState, r as createCounterState, a as createDirtyTracker, n as createErrorHelperState, c as createField, s as createLabelState };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=field-base.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-base.js","names":[],"sources":["../../src/core/field-base.ts"],"sourcesContent":["import { createStableId } from '@vielzeug/ore';\nimport { computed, type Readable } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\n\n// ── Validation / context types ────────────────────────────────────────────────\n\nexport type ValidationTrigger = 'blur' | 'change' | 'input' | 'submit';\nexport type ControlValidationMode = ValidationTrigger | undefined;\n\n// ── Error/helper assistive state ──────────────────────────────────────────────\n\nexport type ErrorHelperState = {\n errorText: string;\n helperText: string;\n};\n\nexport type ErrorHelperOptions = {\n error?: Readable<string | undefined>;\n helper?: Readable<string | undefined>;\n};\n\nexport const createErrorHelperState = (options: ErrorHelperOptions): Readable<ErrorHelperState> =>\n computed(() => ({\n errorText: options.error?.value ?? '',\n helperText: options.helper?.value ?? '',\n }));\n\n// ── Counter state (opt-in) ─────────────────────────────────────────────────────\n\n/** Counter state for text fields with `maxLength`. */\nexport type CounterState = {\n counterAtLimit: boolean;\n counterNearLimit: boolean;\n counterText: string;\n};\n\nexport type CounterOptions = {\n maxLength?: Readable<number | undefined>;\n value: Readable<string | undefined>;\n};\n\n/**\n * Creates a reactive counter state signal for text fields with `maxLength`.\n * Only call this when `maxLength` may be set — the state is genuinely opt-in.\n *\n * @example\n * ```ts\n * const counter = createCounterState({ value: tf.value, maxLength: props.maxlength });\n * ```\n */\nexport const createCounterState = (options: CounterOptions): Readable<CounterState> =>\n computed<CounterState>(() => {\n const value = options.value?.value ?? '';\n const maxLength = options.maxLength?.value;\n const parsedMaxLength = Number(maxLength);\n const validMaxLength = Number.isFinite(parsedMaxLength) && parsedMaxLength > 0 ? parsedMaxLength : null;\n const hasCounter = validMaxLength !== null;\n const counterText = hasCounter ? `${value.length} / ${validMaxLength}` : '';\n const ratio = hasCounter ? value.length / validMaxLength : 0;\n\n return {\n counterAtLimit: hasCounter ? ratio >= 1 : false,\n counterNearLimit: hasCounter ? ratio >= 0.9 && ratio < 1 : false,\n counterText,\n };\n });\n\n/**\n * The single implementation of \"which modifier class does this counter state map to\" —\n * every field with a character counter (`ore-input`, `ore-textarea`, `ore-message-composer`)\n * renders the identical near-limit/at-limit styling, so this is the one place that decides it.\n *\n * @example\n * ```ts\n * html`<span class=\"${() => counterClassName(counter?.value)}\">...</span>`\n * ```\n */\nexport const counterClassName = (counter: CounterState | undefined, base = 'counter'): string => {\n if (!counter) return base;\n\n if (counter.counterAtLimit) return `${base} at-limit`;\n\n if (counter.counterNearLimit) return `${base} near-limit`;\n\n return base;\n};\n\n// ── Dirty tracking (two-state reset) ───────────────────────────────────────────\n\n/**\n * Tracks whether a control's value has ever been changed by user interaction.\n *\n * Backs the \"two-state reset()\" pattern used by `createCheckable` (`checked`/\n * `indeterminate`) and `createChoiceField` (`selectedValues`): before the first\n * interaction, the live prop is still a reliable \"current default\" to resync from\n * (e.g. an async-loaded value arriving after mount); after it, the prop is\n * contaminated by interaction-driven attribute reflection, so only a snapshot taken\n * at creation still represents \"the default\" in the native sense. `reset()` callers\n * check `isDirty` to decide which source to revert to, then call `clear()`.\n */\nexport type DirtyTracker = {\n clear: () => void;\n readonly isDirty: boolean;\n markDirty: () => void;\n};\n\nexport function createDirtyTracker(): DirtyTracker {\n let dirty = false;\n\n return {\n clear: () => {\n dirty = false;\n },\n get isDirty() {\n return dirty;\n },\n markDirty: () => {\n dirty = true;\n },\n };\n}\n\n// ── Label placement ───────────────────────────────────────────────────────────\n\nexport type LabelPlacement = 'inset' | 'outside' | undefined;\n\n// ── Assistive state (error/helper text, describedby/errormessage/invalid) ─────\n\n/**\n * A field's error/helper/disabled/validation-trigger state — everything an input\n * needs *except* a visible `<label>`. Split out from label state (below) because\n * not every field has one: `ore-message-composer` names itself via `aria-label`\n * (a chat composer's accessible name conventionally comes from context/placeholder,\n * not a floating `<label>`), so it composes this directly instead of pulling in\n * label plumbing it can't use. `createField()` composes both for fields that do\n * render a `<label>`.\n */\nexport type AssistiveStateHandle = {\n /** `aria-describedby` value. Non-null when helper or error text is present. */\n ariaDescribedBy: Readable<string | null>;\n /** `aria-errormessage` value. Non-null when error text is present. */\n ariaErrorMessage: Readable<string | null>;\n /** `aria-invalid` value. `'true'` when error text is present, otherwise `null`. */\n ariaInvalid: Readable<'true' | null>;\n /**\n * The stable `id` used for `aria-describedby` on the input. Points at the\n * assistive-text region (covers both helper text and error text per WAI-ARIA).\n */\n assistiveId: string;\n /**\n * Registers the real form field handle (the return value of `useField()`) so that\n * `triggerValidation()` can call its `reportValidity()`. See `TextFieldHandle.attachFormField`\n * for why this is a post-hoc call instead of a constructor option — the same forward\n * reference applies here for choice fields.\n */\n attachFormField: (formField: { reportValidity(): void }) => void;\n disabled: Readable<boolean>;\n /** Stable `id` for the inline error message element (`aria-errormessage`). */\n errorId: string;\n /** Reactive error text. Empty string when no error is set. */\n errorText: Readable<string>;\n /** Reactive helper text. Empty string when no helper is set. */\n helperText: Readable<string>;\n triggerValidation: (on: Extract<ValidationTrigger, 'blur' | 'change'>) => void;\n};\n\nexport type AssistiveStateOptions = {\n disabled?: Readable<boolean | undefined>;\n error?: Readable<string | undefined>;\n helper?: Readable<string | undefined>;\n validateOn?: Readable<ControlValidationMode>;\n};\n\nexport const createAssistiveState = (options: AssistiveStateOptions): AssistiveStateHandle => {\n const disabled = computed(() => Boolean(options.disabled?.value));\n const assistiveId = createStableId('helper');\n const errorId = createStableId('error');\n const resolvedAssistive = createErrorHelperState({ error: options.error, helper: options.helper });\n\n const ariaDescribedBy = computed(() =>\n resolvedAssistive.value.errorText || resolvedAssistive.value.helperText ? assistiveId : null,\n );\n const ariaErrorMessage = computed(() => (resolvedAssistive.value.errorText ? errorId : null));\n const ariaInvalid = computed<'true' | null>(() => (resolvedAssistive.value.errorText ? 'true' : null));\n\n let formField: { reportValidity(): void } | null = null;\n const attachFormField = (nextFormField: { reportValidity(): void }): void => {\n formField = nextFormField;\n };\n\n const triggerValidation = (on: Extract<ValidationTrigger, 'blur' | 'change'>): void => {\n if (options.validateOn?.value !== on) return;\n\n if (!formField) {\n // Not user-facing — this only fires when a component author wired `validateOn` but\n // forgot the matching `attachFormField(useField(...))` call, so validation silently\n // never runs instead of erroring where the mistake actually is.\n warn(\n \"triggerValidation() called before attachFormField() — validation will not run. See createTextField()/createChoiceField()/createCheckable()'s attachFormField doc comment.\",\n );\n\n return;\n }\n\n formField.reportValidity();\n };\n\n return {\n ariaDescribedBy,\n ariaErrorMessage,\n ariaInvalid,\n assistiveId,\n attachFormField,\n disabled,\n errorId,\n errorText: computed(() => resolvedAssistive.value.errorText),\n helperText: computed(() => resolvedAssistive.value.helperText),\n triggerValidation,\n };\n};\n\n// ── Label state (visible `<label>` + `aria-labelledby`) ────────────────────────\n\nexport type LabelStateHandle = {\n /** `aria-labelledby` value. Non-null when a label is visible. */\n ariaLabelledBy: Readable<string | null>;\n /** Stable `id` for the underlying form control — the `<label for=\"...\">` target. */\n fieldId: string;\n /** Stable `id` for the label element. Stamp this on your `<label id=\"...\">`. */\n labelId: string;\n /** Whether the label should be visible. Use to toggle `hidden` on the `<label>`. */\n labelVisible: Readable<boolean>;\n};\n\nexport type LabelStateOptions = {\n /**\n * Override for label visibility that takes precedence over deriving it from\n * the `label` text signal. Pass a computed signal that checks both prop and\n * slot presence to enable slot-first composition.\n *\n * @example\n * ```ts\n * const hasLabel = computed(() => !!props.label.value || slots.has('label').value);\n * const label = createLabelState({ ...options, hasLabel });\n * ```\n */\n hasLabel?: Readable<boolean>;\n /**\n * When provided, used directly as `fieldId` instead of generating one via\n * `createStableId`. Useful in tests for deterministic ID assertions.\n */\n id?: string;\n /**\n * Label text signal. When provided, `labelVisible` and `ariaLabelledBy` are\n * computed reactively from this value (see `hasLabel` for the slot-first override).\n */\n label?: Readable<string | undefined>;\n /**\n * Label placement signal. Defaults to `'inset'`.\n *\n * Not consumed by `createLabelState()` itself — visibility is driven solely by\n * `hasLabel`/`label`. Threaded through so component authors can apply\n * placement-specific styling (`label-placement` attribute) without a second\n * option; if placement-dependent visibility is ever needed, wire it into\n * `labelVisible` here instead of adding a new option.\n */\n labelPlacement?: Readable<LabelPlacement>;\n prefix?: string;\n};\n\nexport const createLabelState = (options: LabelStateOptions): LabelStateHandle => {\n const fieldId = options.id ?? createStableId(options.prefix ?? 'field');\n const labelId = createStableId('label');\n const label$ = options.label ?? computed(() => undefined);\n\n // Slot-first composition: if the caller provides `hasLabel`, use it instead\n // of deriving visibility purely from the label text signal. This allows\n // components with a `<slot name=\"label\">` to stay visible even when the\n // `label` prop is empty but a slotted label element is present.\n const labelVisible = options.hasLabel ?? computed(() => Boolean(label$.value));\n const ariaLabelledBy = computed(() => (labelVisible.value ? labelId : null));\n\n return { ariaLabelledBy, fieldId, labelId, labelVisible };\n};\n\n// ── Field handle (assistive + label, composed) ──────────────────────────────\n\n/**\n * Common handle returned by `createField` — the base for both `TextFieldHandle`\n * and `ChoiceFieldHandle`. Composes `createAssistiveState` + `createLabelState`;\n * call those directly instead if a field has no visible `<label>` (see\n * `createAssistiveState`'s doc comment).\n *\n * ARIA signals and label state are flat on the handle — no nested `aria` or\n * `label` sub-objects. `labelId`/`labelVisible`/`ariaLabelledBy` are always\n * present but only meaningful when `label`/`hasLabel` was passed — omit both\n * and they resolve to an always-hidden, unreferenced label.\n *\n * @example\n * ```html\n * <label id=\"${labelId}\" ?hidden=\"${() => !labelVisible.value}\">...</label>\n * <input\n * aria-labelledby=\"${ariaLabelledBy}\"\n * aria-describedby=\"${ariaDescribedBy}\"\n * aria-invalid=\"${ariaInvalid}\" />\n * ```\n */\nexport type FieldHandle = AssistiveStateHandle & LabelStateHandle;\n\n/** Options shared by both `createTextField` and `createChoiceField`. */\nexport type FieldOptions = AssistiveStateOptions & LabelStateOptions;\n\nexport const createField = (options: FieldOptions): FieldHandle => ({\n ...createAssistiveState(options),\n ...createLabelState(options),\n});\n"],"mappings":";;;;AAsBA,IAAa,KAA0B,MACrC,SAAgB;CACd,WAAW,EAAQ,OAAO,SAAS;CACnC,YAAY,EAAQ,QAAQ,SAAS;AACvC,EAAE,GAyBS,KAAsB,MACjC,QAA6B;CAC3B,IAAM,IAAQ,EAAQ,OAAO,SAAS,IAChC,IAAY,EAAQ,WAAW,OAC/B,IAAkB,OAAO,CAAS,GAClC,IAAiB,OAAO,SAAS,CAAe,KAAK,IAAkB,IAAI,IAAkB,MAC7F,IAAa,MAAmB,MAChC,IAAc,IAAa,GAAG,EAAM,OAAO,KAAK,MAAmB,IACnE,IAAQ,IAAa,EAAM,SAAS,IAAiB;CAE3D,OAAO;EACL,gBAAgB,IAAa,KAAS,IAAI;EAC1C,kBAAkB,IAAa,KAAS,MAAO,IAAQ,IAAI;EAC3D;CACF;AACF,CAAC,GAYU,KAAoB,GAAmC,IAAO,cACpE,IAED,EAAQ,iBAAuB,GAAG,EAAK,aAEvC,EAAQ,mBAAyB,GAAG,EAAK,eAEtC,IANc;AA4BvB,SAAgB,IAAmC;CACjD,IAAI,IAAQ;CAEZ,OAAO;EACL,aAAa;GACX,IAAQ;EACV;EACA,IAAI,UAAU;GACZ,OAAO;EACT;EACA,iBAAiB;GACf,IAAQ;EACV;CACF;AACF;AAqDA,IAAa,KAAwB,MAAyD;CAC5F,IAAM,IAAW,QAAe,EAAQ,EAAQ,UAAU,KAAM,GAC1D,IAAc,EAAe,QAAQ,GACrC,IAAU,EAAe,OAAO,GAChC,IAAoB,EAAuB;EAAE,OAAO,EAAQ;EAAO,QAAQ,EAAQ;CAAO,CAAC,GAE3F,IAAkB,QACtB,EAAkB,MAAM,aAAa,EAAkB,MAAM,aAAa,IAAc,IAC1F,GACM,IAAmB,QAAgB,EAAkB,MAAM,YAAY,IAAU,IAAK,GACtF,IAAc,QAA+B,EAAkB,MAAM,YAAY,SAAS,IAAK,GAEjG,IAA+C;CAsBnD,OAAO;EACL;EACA;EACA;EACA;EACA,kBA1BuB,MAAoD;GAC3E,IAAY;EACd;EAyBE;EACA;EACA,WAAW,QAAe,EAAkB,MAAM,SAAS;EAC3D,YAAY,QAAe,EAAkB,MAAM,UAAU;EAC7D,oBA3ByB,MAA4D;GACjF,EAAQ,YAAY,UAAU,KAE7B,KAWL,EAAU,eAAe;EAC3B;CAaA;AACF,GAmDa,KAAoB,MAAiD;CAChF,IAAM,IAAU,EAAQ,MAAM,EAAe,EAAQ,UAAU,OAAO,GAChE,IAAU,EAAe,OAAO,GAChC,IAAS,EAAQ,SAAS,QAAe,KAAA,CAAS,GAMlD,IAAe,EAAQ,YAAY,QAAe,EAAQ,EAAO,KAAM;CAG7E,OAAO;EAAE,gBAFc,QAAgB,EAAa,QAAQ,IAAU,IAE7D;EAAgB;EAAS;EAAS;CAAa;AAC1D,GA6Ba,KAAe,OAAwC;CAClE,GAAG,EAAqB,CAAO;CAC/B,GAAG,EAAiB,CAAO;AAC7B"}
|