@vielzeug/refine 26.10.0 → 26.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/accordion-item.cjs.map +1 -1
- package/dist/accordion-item.js.map +1 -1
- package/dist/accordion.cjs.map +1 -1
- package/dist/accordion.js.map +1 -1
- package/dist/async.cjs.map +1 -1
- package/dist/async.js.map +1 -1
- package/dist/badge.cjs.map +1 -1
- package/dist/badge.js.map +1 -1
- package/dist/box.cjs.map +1 -1
- package/dist/box.js.map +1 -1
- package/dist/button.cjs +3 -3
- package/dist/button.cjs.map +1 -1
- package/dist/button.js +3 -3
- package/dist/button.js.map +1 -1
- package/dist/calendar.cjs.map +1 -1
- package/dist/calendar.js.map +1 -1
- package/dist/carousel.cjs.map +1 -1
- package/dist/carousel.js.map +1 -1
- package/dist/chat-message.cjs.map +1 -1
- package/dist/chat-message.js.map +1 -1
- package/dist/chat-panel.cjs.map +1 -1
- package/dist/chat-panel.js.map +1 -1
- package/dist/checkbox-group.cjs.map +1 -1
- package/dist/checkbox-group.js.map +1 -1
- package/dist/chip.cjs.map +1 -1
- package/dist/chip.js.map +1 -1
- package/dist/code-window.cjs.map +1 -1
- package/dist/code-window.js.map +1 -1
- package/dist/combobox.cjs.map +1 -1
- package/dist/combobox.js.map +1 -1
- package/dist/command-palette.cjs.map +1 -1
- package/dist/command-palette.js.map +1 -1
- package/dist/content/card/card.css.cjs.map +1 -1
- package/dist/content/card/card.css.js.map +1 -1
- package/dist/content/carousel/carousel-slide.css.cjs.map +1 -1
- package/dist/content/carousel/carousel-slide.css.js.map +1 -1
- package/dist/content/carousel/carousel.css.cjs.map +1 -1
- package/dist/content/carousel/carousel.css.js.map +1 -1
- package/dist/content/carousel/carousel.d.ts +5 -5
- package/dist/content/chat-message/chat-message.css.cjs +1 -1
- package/dist/content/chat-message/chat-message.css.cjs.map +1 -1
- package/dist/content/chat-message/chat-message.css.js +1 -1
- package/dist/content/chat-message/chat-message.css.js.map +1 -1
- package/dist/content/chat-message/chat-message.d.ts +4 -4
- package/dist/content/chat-message/chat-message.d.ts.map +1 -1
- package/dist/content/chat-panel/chat-panel.d.ts +1 -1
- package/dist/content/code-window/code-window.css.cjs.map +1 -1
- package/dist/content/code-window/code-window.css.js.map +1 -1
- package/dist/content/code-window/code-window.d.ts +4 -4
- package/dist/content/index.d.ts +4 -0
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/list/_list-item-reveal.cjs.map +1 -1
- package/dist/content/list/_list-item-reveal.d.ts +4 -4
- package/dist/content/list/_list-item-reveal.d.ts.map +1 -1
- package/dist/content/list/_list-item-reveal.js.map +1 -1
- package/dist/content/list/list-item.css.cjs.map +1 -1
- package/dist/content/list/list-item.css.js.map +1 -1
- package/dist/content/list/list-item.d.ts +8 -8
- package/dist/content/list/list-item.d.ts.map +1 -1
- package/dist/content/list/list.d.ts +8 -8
- package/dist/content/list/list.d.ts.map +1 -1
- package/dist/content/qr-code/qr-code.d.ts +1 -1
- package/dist/content/rank-list/rank-item.css.cjs +2 -0
- package/dist/content/rank-list/rank-item.css.cjs.map +1 -0
- package/dist/content/rank-list/rank-item.css.js +2 -0
- package/dist/content/rank-list/rank-item.css.js.map +1 -0
- package/dist/content/rank-list/rank-item.d.ts +68 -0
- package/dist/content/rank-list/rank-item.d.ts.map +1 -0
- package/dist/content/rank-list/rank-list.css.cjs +2 -0
- package/dist/content/rank-list/rank-list.css.cjs.map +1 -0
- package/dist/content/rank-list/rank-list.css.js +2 -0
- package/dist/content/rank-list/rank-list.css.js.map +1 -0
- package/dist/content/rank-list/rank-list.d.ts +44 -0
- package/dist/content/rank-list/rank-list.d.ts.map +1 -0
- package/dist/content/separator/separator.d.ts +1 -1
- package/dist/content/separator/separator.d.ts.map +1 -1
- package/dist/content/speech-player/speech-player.css.cjs.map +1 -1
- package/dist/content/speech-player/speech-player.css.js.map +1 -1
- package/dist/content/speech-player/speech-player.d.ts +12 -12
- package/dist/content/speech-player/speech-player.d.ts.map +1 -1
- package/dist/content/stepper/step.css.cjs.map +1 -1
- package/dist/content/stepper/step.css.js.map +1 -1
- package/dist/content/stepper/step.d.ts +3 -3
- package/dist/content/stepper/step.d.ts.map +1 -1
- package/dist/content/stepper/stepper.css.cjs.map +1 -1
- package/dist/content/stepper/stepper.css.js.map +1 -1
- package/dist/content/stepper/stepper.d.ts +3 -3
- package/dist/content/stepper/stepper.d.ts.map +1 -1
- package/dist/content/table/table.css.cjs.map +1 -1
- package/dist/content/table/table.css.js.map +1 -1
- package/dist/content/table/table.d.ts +3 -3
- package/dist/content/table/table.d.ts.map +1 -1
- package/dist/content/text/text.css.cjs.map +1 -1
- package/dist/content/text/text.css.js.map +1 -1
- package/dist/content/text/text.d.ts +2 -2
- package/dist/content/text/text.d.ts.map +1 -1
- package/dist/cookie-banner.cjs.map +1 -1
- package/dist/cookie-banner.js.map +1 -1
- package/dist/copy-command.cjs +1 -1
- package/dist/copy-command.cjs.map +1 -1
- package/dist/copy-command.js +1 -1
- package/dist/copy-command.js.map +1 -1
- package/dist/core/announcer.cjs.map +1 -1
- package/dist/core/announcer.d.ts +2 -2
- package/dist/core/announcer.js.map +1 -1
- package/dist/core/aria-reflection.cjs.map +1 -1
- package/dist/core/aria-reflection.d.ts +3 -3
- package/dist/core/aria-reflection.js.map +1 -1
- package/dist/core/auto-resize.cjs.map +1 -1
- package/dist/core/auto-resize.d.ts +1 -1
- package/dist/core/auto-resize.js.map +1 -1
- package/dist/core/checkable.cjs.map +1 -1
- package/dist/core/checkable.d.ts +2 -2
- package/dist/core/checkable.d.ts.map +1 -1
- package/dist/core/checkable.js.map +1 -1
- package/dist/core/choice-field.cjs.map +1 -1
- package/dist/core/choice-field.d.ts +3 -3
- package/dist/core/choice-field.d.ts.map +1 -1
- package/dist/core/choice-field.js.map +1 -1
- package/dist/core/composer.cjs.map +1 -1
- package/dist/core/composer.d.ts +4 -4
- package/dist/core/composer.d.ts.map +1 -1
- package/dist/core/composer.js.map +1 -1
- package/dist/core/date-picker.cjs.map +1 -1
- package/dist/core/date-picker.d.ts +5 -5
- package/dist/core/date-picker.d.ts.map +1 -1
- package/dist/core/date-picker.js.map +1 -1
- package/dist/core/direction.cjs.map +1 -1
- package/dist/core/direction.d.ts +1 -1
- package/dist/core/direction.js.map +1 -1
- package/dist/core/field-base.cjs.map +1 -1
- package/dist/core/field-base.d.ts +9 -9
- package/dist/core/field-base.d.ts.map +1 -1
- package/dist/core/field-base.js.map +1 -1
- package/dist/core/focus.cjs.map +1 -1
- package/dist/core/focus.d.ts +1 -1
- package/dist/core/focus.js.map +1 -1
- package/dist/core/icon-registry.cjs.map +1 -1
- package/dist/core/icon-registry.d.ts +1 -1
- package/dist/core/icon-registry.js.map +1 -1
- package/dist/core/icons.cjs.map +1 -1
- package/dist/core/icons.js.map +1 -1
- package/dist/core/keyboard.cjs.map +1 -1
- package/dist/core/keyboard.d.ts +1 -1
- package/dist/core/keyboard.js.map +1 -1
- package/dist/core/numbers.cjs.map +1 -1
- package/dist/core/numbers.d.ts +1 -1
- package/dist/core/numbers.js.map +1 -1
- package/dist/core/overlay.cjs.map +1 -1
- package/dist/core/overlay.d.ts +6 -6
- package/dist/core/overlay.js.map +1 -1
- package/dist/core/parse.cjs.map +1 -1
- package/dist/core/parse.d.ts +1 -1
- package/dist/core/parse.js.map +1 -1
- package/dist/core/positioner.cjs.map +1 -1
- package/dist/core/positioner.d.ts +3 -3
- package/dist/core/positioner.js.map +1 -1
- package/dist/core/ref-callback.cjs.map +1 -1
- package/dist/core/ref-callback.d.ts +2 -2
- package/dist/core/ref-callback.d.ts.map +1 -1
- package/dist/core/ref-callback.js.map +1 -1
- package/dist/core/text-field.cjs.map +1 -1
- package/dist/core/text-field.d.ts +6 -6
- package/dist/core/text-field.d.ts.map +1 -1
- package/dist/core/text-field.js.map +1 -1
- package/dist/counter.cjs +1 -0
- package/dist/counter.cjs.map +1 -1
- package/dist/counter.js +1 -0
- package/dist/counter.js.map +1 -1
- package/dist/custom-elements.json +315 -87
- package/dist/datagrid.cjs.map +1 -1
- package/dist/datagrid.js.map +1 -1
- package/dist/date-picker.cjs +1 -1
- package/dist/date-picker.cjs.map +1 -1
- package/dist/date-picker.js +1 -1
- package/dist/date-picker.js.map +1 -1
- package/dist/dialog.cjs.map +1 -1
- package/dist/dialog.js.map +1 -1
- package/dist/disclosure/accordion-item/accordion-item.css.cjs +1 -1
- package/dist/disclosure/accordion-item/accordion-item.css.cjs.map +1 -1
- package/dist/disclosure/accordion-item/accordion-item.css.js +1 -1
- package/dist/disclosure/accordion-item/accordion-item.css.js.map +1 -1
- package/dist/disclosure/tab-item/tab-item.d.ts +1 -1
- package/dist/disclosure/tab-item/tab-item.d.ts.map +1 -1
- package/dist/drawer.cjs.map +1 -1
- package/dist/drawer.js.map +1 -1
- package/dist/feedback/alert/alert.css.cjs.map +1 -1
- package/dist/feedback/alert/alert.css.js.map +1 -1
- package/dist/feedback/badge/badge.css.cjs.map +1 -1
- package/dist/feedback/badge/badge.css.js.map +1 -1
- package/dist/feedback/badge/badge.d.ts +1 -1
- package/dist/feedback/badge/badge.d.ts.map +1 -1
- package/dist/feedback/chip/chip.css.cjs.map +1 -1
- package/dist/feedback/chip/chip.css.js.map +1 -1
- package/dist/feedback/chip/chip.d.ts +3 -3
- package/dist/feedback/chip/chip.d.ts.map +1 -1
- package/dist/feedback/cookie-banner/cookie-banner.css.cjs.map +1 -1
- package/dist/feedback/cookie-banner/cookie-banner.css.js.map +1 -1
- package/dist/feedback/cookie-banner/cookie-banner.d.ts +8 -8
- package/dist/feedback/cookie-banner/cookie-banner.d.ts.map +1 -1
- package/dist/feedback/progress/progress.css.cjs.map +1 -1
- package/dist/feedback/progress/progress.css.js.map +1 -1
- package/dist/feedback/progress/progress.d.ts +1 -1
- package/dist/feedback/progress/progress.d.ts.map +1 -1
- package/dist/feedback/skeleton/skeleton.css.cjs.map +1 -1
- package/dist/feedback/skeleton/skeleton.css.js.map +1 -1
- package/dist/feedback/skeleton/skeleton.d.ts +1 -1
- package/dist/feedback/skeleton/skeleton.d.ts.map +1 -1
- package/dist/feedback/toast/toast.css.cjs +1 -1
- package/dist/feedback/toast/toast.css.cjs.map +1 -1
- package/dist/feedback/toast/toast.css.js +1 -1
- package/dist/feedback/toast/toast.css.js.map +1 -1
- package/dist/feedback/toast/toast.d.ts +1 -1
- package/dist/feedback/toast/toast.d.ts.map +1 -1
- package/dist/feedback/typing-indicator/typing-indicator.d.ts +2 -2
- package/dist/file-input.cjs +1 -1
- package/dist/file-input.cjs.map +1 -1
- package/dist/file-input.js +1 -1
- package/dist/file-input.js.map +1 -1
- package/dist/frameworks/elements.d.ts +1 -1
- package/dist/grid-item.cjs.map +1 -1
- package/dist/grid-item.js.map +1 -1
- package/dist/grid.cjs.map +1 -1
- package/dist/grid.js.map +1 -1
- package/dist/icon.cjs.map +1 -1
- package/dist/icon.js.map +1 -1
- package/dist/input.cjs.map +1 -1
- package/dist/input.js.map +1 -1
- package/dist/inputs/button/button.css.cjs.map +1 -1
- package/dist/inputs/button/button.css.js.map +1 -1
- package/dist/inputs/button/button.d.ts +3 -3
- package/dist/inputs/button/button.d.ts.map +1 -1
- package/dist/inputs/calendar/calendar.css.cjs.map +1 -1
- package/dist/inputs/calendar/calendar.css.js.map +1 -1
- package/dist/inputs/calendar/calendar.d.ts +2 -2
- package/dist/inputs/checkbox-group/checkbox-group.d.ts +2 -2
- package/dist/inputs/checkbox-group/checkbox-group.d.ts.map +1 -1
- package/dist/inputs/combobox/combobox.css.cjs.map +1 -1
- package/dist/inputs/combobox/combobox.css.js.map +1 -1
- package/dist/inputs/counter/counter.css.cjs +1 -1
- package/dist/inputs/counter/counter.css.cjs.map +1 -1
- package/dist/inputs/counter/counter.css.js +1 -1
- package/dist/inputs/counter/counter.css.js.map +1 -1
- package/dist/inputs/counter/counter.d.ts +2 -1
- package/dist/inputs/counter/counter.d.ts.map +1 -1
- package/dist/inputs/datagrid/datagrid.css.cjs.map +1 -1
- package/dist/inputs/datagrid/datagrid.css.js.map +1 -1
- package/dist/inputs/datagrid/datagrid.d.ts +5 -5
- package/dist/inputs/date-picker/date-picker.css.cjs.map +1 -1
- package/dist/inputs/date-picker/date-picker.css.js.map +1 -1
- package/dist/inputs/date-picker/date-picker.d.ts +1 -1
- package/dist/inputs/file-input/file-input-upload.cjs.map +1 -1
- package/dist/inputs/file-input/file-input-upload.d.ts +6 -6
- package/dist/inputs/file-input/file-input-upload.js.map +1 -1
- package/dist/inputs/file-input/file-input.css.cjs.map +1 -1
- package/dist/inputs/file-input/file-input.css.js.map +1 -1
- package/dist/inputs/file-input/file-input.d.ts +6 -6
- package/dist/inputs/input/input.css.cjs +1 -1
- package/dist/inputs/input/input.css.cjs.map +1 -1
- package/dist/inputs/input/input.css.js +1 -1
- package/dist/inputs/input/input.css.js.map +1 -1
- package/dist/inputs/input/input.d.ts +6 -6
- package/dist/inputs/input/input.d.ts.map +1 -1
- package/dist/inputs/message-composer/message-composer.css.cjs.map +1 -1
- package/dist/inputs/message-composer/message-composer.css.js.map +1 -1
- package/dist/inputs/message-composer/message-composer.d.ts +7 -7
- package/dist/inputs/message-composer/message-composer.d.ts.map +1 -1
- package/dist/inputs/number-input/number-input.css.cjs.map +1 -1
- package/dist/inputs/number-input/number-input.css.js.map +1 -1
- package/dist/inputs/number-input/number-input.d.ts +2 -2
- package/dist/inputs/otp-input/otp-input.css.cjs.map +1 -1
- package/dist/inputs/otp-input/otp-input.css.js.map +1 -1
- package/dist/inputs/qr-scanner/qr-scanner.d.ts +1 -1
- package/dist/inputs/qr-scanner/qr-scanner.d.ts.map +1 -1
- package/dist/inputs/rating/rating.d.ts +1 -1
- package/dist/inputs/rating/rating.d.ts.map +1 -1
- package/dist/inputs/select/select.css.cjs +1 -1
- package/dist/inputs/select/select.css.cjs.map +1 -1
- package/dist/inputs/select/select.css.js +1 -1
- package/dist/inputs/select/select.css.js.map +1 -1
- package/dist/inputs/select/select.d.ts +1 -1
- 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 +2 -2
- package/dist/inputs/shared/field-binding.d.ts.map +1 -1
- package/dist/inputs/shared/field-binding.js.map +1 -1
- package/dist/inputs/shared/native-field-event.cjs.map +1 -1
- package/dist/inputs/shared/native-field-event.d.ts +1 -1
- package/dist/inputs/shared/native-field-event.js.map +1 -1
- package/dist/inputs/shared/templates.cjs.map +1 -1
- package/dist/inputs/shared/templates.d.ts +3 -3
- package/dist/inputs/shared/templates.js.map +1 -1
- package/dist/inputs/slider/slider.css.cjs.map +1 -1
- package/dist/inputs/slider/slider.css.js.map +1 -1
- package/dist/inputs/slider/slider.d.ts +1 -1
- package/dist/inputs/slider/slider.d.ts.map +1 -1
- package/dist/inputs/textarea/textarea.css.cjs.map +1 -1
- package/dist/inputs/textarea/textarea.css.js.map +1 -1
- package/dist/inputs/textarea/textarea.d.ts +1 -1
- package/dist/inputs/time-picker/time-picker.css.cjs.map +1 -1
- package/dist/inputs/time-picker/time-picker.css.js.map +1 -1
- package/dist/inputs/time-picker/time-picker.d.ts +1 -1
- package/dist/layout/box/box.css.cjs.map +1 -1
- package/dist/layout/box/box.css.js.map +1 -1
- package/dist/layout/box/box.d.ts +1 -1
- package/dist/layout/grid/grid.d.ts +1 -1
- package/dist/layout/grid-item/grid-item.d.ts +3 -3
- package/dist/layout/grid-item/grid-item.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 +3 -3
- package/dist/layout/sidebar/sidebar-item.css.cjs.map +1 -1
- package/dist/layout/sidebar/sidebar-item.css.js.map +1 -1
- package/dist/layout/sidebar/sidebar.d.ts +5 -5
- package/dist/layout/sidebar/sidebar.d.ts.map +1 -1
- package/dist/list-item.cjs.map +1 -1
- package/dist/list-item.js.map +1 -1
- package/dist/list.cjs.map +1 -1
- package/dist/list.js.map +1 -1
- package/dist/menu.cjs +6 -5
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.js +6 -5
- package/dist/menu.js.map +1 -1
- package/dist/message-composer.cjs.map +1 -1
- package/dist/message-composer.js.map +1 -1
- package/dist/navbar.cjs.map +1 -1
- package/dist/navbar.js.map +1 -1
- package/dist/number-input.cjs.map +1 -1
- package/dist/number-input.js.map +1 -1
- package/dist/otp-input.cjs.map +1 -1
- package/dist/otp-input.js.map +1 -1
- package/dist/overlay/command-palette/command-palette-items.cjs.map +1 -1
- package/dist/overlay/command-palette/command-palette-items.d.ts +1 -1
- package/dist/overlay/command-palette/command-palette-items.js.map +1 -1
- package/dist/overlay/command-palette/command-palette.css.cjs.map +1 -1
- package/dist/overlay/command-palette/command-palette.css.js.map +1 -1
- package/dist/overlay/command-palette/command-palette.d.ts +3 -3
- package/dist/overlay/command-palette/command-palette.types.d.ts +6 -6
- package/dist/overlay/command-palette/command-palette.types.d.ts.map +1 -1
- package/dist/overlay/dialog/dialog.css.cjs +1 -1
- package/dist/overlay/dialog/dialog.css.cjs.map +1 -1
- package/dist/overlay/dialog/dialog.css.js +1 -1
- package/dist/overlay/dialog/dialog.css.js.map +1 -1
- package/dist/overlay/dialog/dialog.d.ts +2 -2
- package/dist/overlay/dialog/dialog.d.ts.map +1 -1
- package/dist/overlay/drawer/drawer.css.cjs.map +1 -1
- package/dist/overlay/drawer/drawer.css.js.map +1 -1
- package/dist/overlay/drawer/drawer.d.ts +1 -1
- package/dist/overlay/drawer/drawer.d.ts.map +1 -1
- package/dist/overlay/menu/menu-item.css.cjs.map +1 -1
- package/dist/overlay/menu/menu-item.css.js.map +1 -1
- package/dist/overlay/menu/menu.d.ts.map +1 -1
- package/dist/overlay/shared/use-dialog.cjs.map +1 -1
- package/dist/overlay/shared/use-dialog.d.ts +6 -6
- package/dist/overlay/shared/use-dialog.d.ts.map +1 -1
- package/dist/overlay/shared/use-dialog.js.map +1 -1
- package/dist/overlay/shared/use-floating-trigger.cjs.map +1 -1
- package/dist/overlay/shared/use-floating-trigger.d.ts +1 -1
- package/dist/overlay/shared/use-floating-trigger.d.ts.map +1 -1
- package/dist/overlay/shared/use-floating-trigger.js.map +1 -1
- package/dist/overlay/tooltip/tooltip.css.cjs.map +1 -1
- package/dist/overlay/tooltip/tooltip.css.js.map +1 -1
- package/dist/progress.cjs.map +1 -1
- package/dist/progress.js.map +1 -1
- package/dist/qr-code.cjs.map +1 -1
- package/dist/qr-code.js.map +1 -1
- package/dist/qr-scanner.cjs.map +1 -1
- package/dist/qr-scanner.js.map +1 -1
- package/dist/radio-group.cjs.map +1 -1
- package/dist/radio-group.js.map +1 -1
- package/dist/radio.cjs.map +1 -1
- package/dist/radio.js.map +1 -1
- package/dist/rank-item.cjs +21 -0
- package/dist/rank-item.cjs.map +1 -0
- package/dist/rank-item.js +21 -0
- package/dist/rank-item.js.map +1 -0
- package/dist/rank-list.cjs +4 -0
- package/dist/rank-list.cjs.map +1 -0
- package/dist/rank-list.js +4 -0
- package/dist/rank-list.js.map +1 -0
- package/dist/rating.cjs.map +1 -1
- package/dist/rating.js.map +1 -1
- package/dist/refine.cjs +103 -80
- package/dist/refine.cjs.map +1 -1
- package/dist/refine.iife.js +103 -80
- package/dist/refine.iife.js.map +1 -1
- package/dist/refine.js +104 -81
- package/dist/refine.js.map +1 -1
- package/dist/select.cjs.map +1 -1
- package/dist/select.js.map +1 -1
- package/dist/separator.cjs.map +1 -1
- package/dist/separator.js.map +1 -1
- package/dist/shared/index.d.ts +3 -3
- package/dist/shared/size-presets.cjs.map +1 -1
- package/dist/shared/size-presets.d.ts +4 -4
- package/dist/shared/size-presets.js.map +1 -1
- package/dist/shared/types.d.ts +1 -1
- package/dist/sidebar.cjs.map +1 -1
- package/dist/sidebar.js.map +1 -1
- package/dist/skeleton.cjs.map +1 -1
- package/dist/skeleton.js.map +1 -1
- package/dist/slider.cjs.map +1 -1
- package/dist/slider.js.map +1 -1
- package/dist/speech-player.cjs.map +1 -1
- package/dist/speech-player.js.map +1 -1
- package/dist/step.cjs.map +1 -1
- package/dist/step.js.map +1 -1
- package/dist/stepper.cjs.map +1 -1
- package/dist/stepper.js.map +1 -1
- package/dist/styles/fouc.css +2 -2
- package/dist/styles/mixins/accessibility.css.cjs.map +1 -1
- package/dist/styles/mixins/accessibility.css.d.ts +4 -4
- package/dist/styles/mixins/accessibility.css.js.map +1 -1
- package/dist/styles/mixins/animation.css.cjs +2 -2
- package/dist/styles/mixins/animation.css.cjs.map +1 -1
- package/dist/styles/mixins/animation.css.d.ts +4 -4
- package/dist/styles/mixins/animation.css.js +2 -2
- package/dist/styles/mixins/animation.css.js.map +1 -1
- package/dist/styles/mixins/field-variants.css.cjs +2 -2
- package/dist/styles/mixins/field-variants.css.cjs.map +1 -1
- package/dist/styles/mixins/field-variants.css.d.ts +6 -6
- package/dist/styles/mixins/field-variants.css.d.ts.map +1 -1
- package/dist/styles/mixins/field-variants.css.js +2 -2
- package/dist/styles/mixins/field-variants.css.js.map +1 -1
- package/dist/styles/mixins/shape.css.cjs.map +1 -1
- package/dist/styles/mixins/shape.css.d.ts +9 -9
- package/dist/styles/mixins/shape.css.d.ts.map +1 -1
- package/dist/styles/mixins/shape.css.js.map +1 -1
- package/dist/styles/mixins/variants.css.cjs.map +1 -1
- package/dist/styles/mixins/variants.css.d.ts +1 -1
- package/dist/styles/mixins/variants.css.js.map +1 -1
- package/dist/styles/theme.css +36 -28
- package/dist/tab-item.cjs.map +1 -1
- package/dist/tab-item.js.map +1 -1
- package/dist/tab-panel.cjs.map +1 -1
- package/dist/tab-panel.js.map +1 -1
- package/dist/table.cjs.map +1 -1
- package/dist/table.js.map +1 -1
- package/dist/text.cjs.map +1 -1
- package/dist/text.js.map +1 -1
- package/dist/textarea.cjs.map +1 -1
- package/dist/textarea.js.map +1 -1
- package/dist/theme.cjs.map +1 -1
- package/dist/theme.d.ts +2 -2
- package/dist/theme.js.map +1 -1
- package/dist/time-picker.cjs +1 -1
- package/dist/time-picker.cjs.map +1 -1
- package/dist/time-picker.js +1 -1
- package/dist/time-picker.js.map +1 -1
- package/dist/toast.cjs.map +1 -1
- package/dist/toast.js.map +1 -1
- package/dist/types/elements.d.ts +6 -2
- package/dist/typing-indicator.cjs.map +1 -1
- package/dist/typing-indicator.js.map +1 -1
- package/dist/utils/css.cjs.map +1 -1
- package/dist/utils/css.js.map +1 -1
- package/dist/utils/link.cjs.map +1 -1
- package/dist/utils/link.d.ts +2 -2
- package/dist/utils/link.js.map +1 -1
- package/package.json +31 -14
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-dialog.cjs","names":[],"sources":["../../../src/overlay/shared/use-dialog.ts"],"sourcesContent":["import { type Readable, type Signal, signal, watch } from '@vielzeug/ripple';\n\nimport { createFocusManager, type DialogCloseReason, lifecycleSignal, type OverlayOpenReason } from '../../core';\nimport { awaitExit } from './await-exit';\nimport { createBackgroundLock } from './background-lock';\n\n// Invoker Commands API — not yet in lib.dom.d.ts\ninterface CommandEvent extends Event {\n command: string;\n}\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type UseDialogOptions = {\n /** Called after showModal() but before isOpen = true. Use to clear drag styles etc. */\n beforeOpen?: (dialog: HTMLDialogElement) => void;\n /** Extra properties merged into the `close-request` custom event detail. */\n closeRequestDetail?: (reason: Exclude<DialogCloseReason, 'programmatic'>) => Record<string, unknown>;\n /** Initial uncontrolled open state. Ignored when `openProp` is defined. */\n defaultOpen: Readable<boolean | undefined>;\n dialogRef: { value: HTMLDialogElement | null | undefined };\n getPanelEl: () => HTMLElement | null | undefined;\n host: HTMLElement;\n initialFocus: Readable<string | undefined>;\n isPersistent: () => boolean;\n /** Cleanup registrar from the component setup ctx. Automatically called on disconnect. */\n onCleanup: (fn: () => void) => void;\n /**\n * Scoped event listener registrar from the component setup ctx.\n * Automatically removed on component disconnect.\n */\n onEvent: <K extends keyof HTMLElementEventMap>(\n target: EventTarget | null | undefined,\n event: K,\n listener: (e: HTMLElementEventMap[K]) => void,\n options?: AddEventListenerOptions,\n ) => void;\n /**\n * Called after the native `close` event fires — after background unlock,\n * isOpen reset, and focus restore. Use to emit component events.\n */\n onNativeClose?: (reason: DialogCloseReason) => void;\n /** Called by the overlay on open. Use to emit events. */\n onOpen?: (reason: OverlayOpenReason) => void;\n /** Open prop from the component — drives programmatic open/close via watch. */\n openProp: Readable<boolean | undefined>;\n /**\n * Called by the overlay when closing. Orchestrates the close animation.\n * Default: `dialog.close()`. Override in dialog.ts to add the CSS exit animation.\n */\n performClose?: (dialog: HTMLDialogElement, reason: DialogCloseReason) => void;\n returnFocus: Readable<boolean | undefined>;\n};\n\nexport type UseDialogHandle = {\n closeWithAnimation: () => void;\n /**\n * Standard backdrop-click-to-close handler. Callers wire it themselves\n * (`onEvent(dialog, 'click', handleBackdropClick)`) because some overlays — e.g. drawer's\n * swipe-to-dismiss — need a backdrop handler with extra guards, so it is deliberately not\n * registered by `mount()`.\n */\n handleBackdropClick: (e: MouseEvent) => void;\n isOpen: Signal<boolean>;\n /**\n * Registers the open-prop watcher and the standard native `close`/`cancel` listeners on the\n * dialog element (Escape closes via the native `cancel` event). Call inside `onMounted()`.\n * Backdrop-click-to-close is intentionally left to the caller — see `handleBackdropClick`.\n */\n mount: () => void;\n /** The modal dialog controller — open/close/toggle/dispose. */\n overlay: ModalDialogController;\n /**\n * Dispatch `close-request`; if allowed, call `overlay.close()`.\n */\n requestClose: (reason: Exclude<DialogCloseReason, 'programmatic'>) => void;\n};\n\ntype ModalDialogController = {\n [Symbol.dispose](): void;\n close(reason?: DialogCloseReason): void;\n dispose(): void;\n open(reason?: OverlayOpenReason): void;\n toggle(openReason?: OverlayOpenReason, closeReason?: DialogCloseReason): void;\n};\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Shared dialog-control composable for `ore-dialog` and `ore-drawer`.\n *\n * Owns: `isOpen`, close-reason tracking, focus management, `overlay`,\n * `requestClose`, background lock/unlock, and the `close` event handler.\n * Components supply only their unique behavior via callbacks and `onNativeClose`.\n */\nexport function useDialogControl(options: UseDialogOptions): UseDialogHandle {\n const abortSignal = lifecycleSignal(options.onCleanup);\n const isOpen = signal(false);\n // Internal close-reason: set atomically before dialog.close() fires.\n let pendingCloseReason: DialogCloseReason = 'programmatic';\n const bgLock = createBackgroundLock();\n let isClosing = false;\n\n // ── Focus management ──────────────────────────────────────────────────────\n const focus = createFocusManager({\n getInitialFocusSelector: () => options.initialFocus.value,\n getReturnFocus: () => options.returnFocus.value,\n host: options.host,\n signal: abortSignal,\n });\n // ─────────────────────────────────────────────────────────────────────────\n\n /** Cancels a pending exit animation so a reopen can keep the dialog open. No-op otherwise. */\n let cancelExit = (): void => {};\n\n const closeWithAnimation = (): void => {\n const dialog = options.dialogRef.value;\n\n if (!dialog?.open || isClosing) return;\n\n isClosing = true;\n dialog.classList.add('closing');\n\n let settled = false;\n\n cancelExit = (): void => {\n if (settled) return;\n\n settled = true;\n dialog.classList.remove('closing');\n isClosing = false;\n };\n\n const finish = () => {\n if (settled) return;\n\n settled = true;\n dialog.close();\n // Do NOT remove 'closing' here — removing it while the native ::backdrop is\n // still held in the top layer by the 'overlay allow-discrete' transition\n // would revert its opacity to 1 and cause a visible flash. The class is\n // cleaned up at the start of the next open cycle (see setOpen below).\n isClosing = false;\n };\n\n const panel = options.getPanelEl();\n\n if (panel) {\n awaitExit(panel, finish, 'transition');\n } else {\n finish();\n }\n };\n\n const dispatchCloseRequest = (reason: Exclude<DialogCloseReason, 'programmatic'>): boolean => {\n return options.host.dispatchEvent(\n new CustomEvent('close-request', {\n bubbles: true,\n cancelable: true,\n composed: true,\n detail: { reason, ...options.closeRequestDetail?.(reason) },\n }),\n );\n };\n\n const overlay: ModalDialogController = {\n close(reason: DialogCloseReason = 'programmatic'): void {\n if (!isOpen.value) return;\n\n const dialog = options.dialogRef.value;\n\n if (dialog?.open) {\n pendingCloseReason = reason;\n (options.performClose ?? ((element, _reason) => element.close()))(dialog, reason);\n } else {\n isOpen.value = false;\n }\n },\n dispose(): void {\n overlay.close('programmatic');\n },\n open(reason: OverlayOpenReason = 'programmatic'): void {\n const dialog = options.dialogRef.value;\n\n if (dialog?.open) {\n // A reopen that arrives while the exit animation is still running cancels\n // the pending close and keeps the dialog open — the open prop is\n // authoritative, so a quick close→reopen cycle must not end with the\n // native dialog closed while the prop still says open.\n cancelExit();\n\n return;\n }\n\n if (!dialog || isOpen.value) return;\n\n // Clean up any leftover 'closing' class from the previous close animation\n // before opening so the entry @starting-style and transition work correctly.\n dialog.classList.remove('closing');\n focus.captureReturnFocus();\n options.beforeOpen?.(dialog);\n dialog.showModal();\n bgLock.lock(options.host);\n focus.applyInitialFocus();\n isOpen.value = true;\n options.onOpen?.(reason);\n },\n [Symbol.dispose](): void {\n overlay.dispose();\n },\n toggle(openReason: OverlayOpenReason = 'click', closeReason: DialogCloseReason = 'trigger'): void {\n if (isOpen.value) overlay.close(closeReason);\n else overlay.open(openReason);\n },\n };\n\n abortSignal.addEventListener('abort', overlay.dispose, { once: true });\n\n const requestClose = (reason: Exclude<DialogCloseReason, 'programmatic'>): void => {\n const allowed = dispatchCloseRequest(reason);\n\n if (!allowed) return;\n\n overlay.close(reason);\n };\n\n // Invoker Commands API — allows declarative open/close from outside the component:\n // <button commandfor=\"ore-dialog-id\" command=\"show-modal\">Open</button>\n // <button commandfor=\"ore-dialog-id\" command=\"close\">Close</button>\n options.host.addEventListener(\n 'command',\n (e: Event) => {\n const cmd = (e as CommandEvent).command;\n\n if (cmd === 'show-modal') overlay.open('trigger');\n else if (cmd === 'close' || cmd === 'request-close') requestClose('trigger');\n },\n { signal: abortSignal },\n );\n\n // The native `<dialog>` element auto-closes on Escape via its own close-watcher, which\n // dispatches a cancelable `cancel` event before `close`. Suppressing/redirecting that is the\n // spec-correct hook — relying on `keydown`'s `preventDefault()` to stop the close-watcher is\n // implementation-specific, not guaranteed across browsers.\n const handleCancel = (e: Event): void => {\n // See `handleNativeClose`'s comment: guard against a same-named bubbling event from a\n // descendant field being misread as this dialog's own native event.\n if (e.target !== options.dialogRef.value) return;\n\n e.preventDefault();\n\n if (options.isPersistent()) return;\n\n requestClose('escape');\n };\n\n const handleBackdropClick = (e: MouseEvent): void => {\n if (options.isPersistent()) return;\n\n if (e.target === options.dialogRef.value) requestClose('outsideClick');\n };\n\n // ── Internal native close handler ─────────────────────────────────────────\n // `e.target` must be checked against the native `<dialog>` itself, the same way\n // `handleBackdropClick` already does: a bubbling `close`-named event fired by *any* descendant\n // component reaches this listener too — regardless of that event's own `composed` flag, since\n // slot-assignment-based event-path computation (a slotted descendant's \"parent\" is the `<slot>`\n // projecting it) isn't gated by `composed` the way crossing back out of a shadow root is. Some\n // fields nested here (`ore-select`, `ore-combobox`, …) emit their own public `close` event when\n // *their* dropdown closes — without this check, selecting an option in one of those closes this\n // dialog too, misreading the field's own event as this dialog's native close.\n const handleNativeClose = (e?: Event): void => {\n if (e && e.target !== options.dialogRef.value) return;\n\n bgLock.unlock();\n options.host.removeAttribute('open');\n isOpen.value = false;\n focus.restoreFocus();\n\n const reason = pendingCloseReason;\n\n pendingCloseReason = 'programmatic';\n options.onNativeClose?.(reason);\n };\n\n const mount = (): void => {\n const dialog = options.dialogRef.value;\n\n if (!dialog) return;\n\n options.onEvent(dialog, 'close', handleNativeClose);\n options.onEvent(dialog, 'cancel', handleCancel);\n\n let initialized = false;\n\n watch(\n options.openProp,\n (open) => {\n if (open === undefined) {\n if (!initialized) {\n if (options.defaultOpen.value) overlay.open('programmatic');\n\n initialized = true;\n\n return;\n }\n\n overlay.close('programmatic');\n\n return;\n }\n\n initialized = true;\n\n if (open) {\n overlay.open('programmatic');\n } else {\n overlay.close('programmatic');\n }\n },\n { immediate: true },\n );\n };\n\n return { closeWithAnimation, handleBackdropClick, isOpen, mount, overlay, requestClose };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-dialog.cjs","names":[],"sources":["../../../src/overlay/shared/use-dialog.ts"],"sourcesContent":["import { type Readable, type Signal, signal, watch } from '@vielzeug/ripple';\n\nimport { createFocusManager, type DialogCloseReason, lifecycleSignal, type OverlayOpenReason } from '../../core';\nimport { awaitExit } from './await-exit';\nimport { createBackgroundLock } from './background-lock';\n\n// Invoker Commands API: not yet in lib.dom.d.ts\ninterface CommandEvent extends Event {\n command: string;\n}\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type UseDialogOptions = {\n /** Called after showModal() but before isOpen = true. Use to clear drag styles etc. */\n beforeOpen?: (dialog: HTMLDialogElement) => void;\n /** Extra properties merged into the `close-request` custom event detail. */\n closeRequestDetail?: (reason: Exclude<DialogCloseReason, 'programmatic'>) => Record<string, unknown>;\n /** Initial uncontrolled open state. Ignored when `openProp` is defined. */\n defaultOpen: Readable<boolean | undefined>;\n dialogRef: { value: HTMLDialogElement | null | undefined };\n getPanelEl: () => HTMLElement | null | undefined;\n host: HTMLElement;\n initialFocus: Readable<string | undefined>;\n isPersistent: () => boolean;\n /** Cleanup registrar from the component setup ctx. Automatically called on disconnect. */\n onCleanup: (fn: () => void) => void;\n /**\n * Scoped event listener registrar from the component setup ctx.\n * Automatically removed on component disconnect.\n */\n onEvent: <K extends keyof HTMLElementEventMap>(\n target: EventTarget | null | undefined,\n event: K,\n listener: (e: HTMLElementEventMap[K]) => void,\n options?: AddEventListenerOptions,\n ) => void;\n /**\n * Called after the native `close` event fires: after background unlock,\n * isOpen reset, and focus restore. Use to emit component events.\n */\n onNativeClose?: (reason: DialogCloseReason) => void;\n /** Called by the overlay on open. Use to emit events. */\n onOpen?: (reason: OverlayOpenReason) => void;\n /** Open prop from the component: drives programmatic open/close via watch. */\n openProp: Readable<boolean | undefined>;\n /**\n * Called by the overlay when closing. Orchestrates the close animation.\n * Default: `dialog.close()`. Override in dialog.ts to add the CSS exit animation.\n */\n performClose?: (dialog: HTMLDialogElement, reason: DialogCloseReason) => void;\n returnFocus: Readable<boolean | undefined>;\n};\n\nexport type UseDialogHandle = {\n closeWithAnimation: () => void;\n /**\n * Standard backdrop-click-to-close handler. Callers wire it themselves\n * (`onEvent(dialog, 'click', handleBackdropClick)`) because some overlays: e.g. drawer's\n * swipe-to-dismiss: need a backdrop handler with extra guards, so it is deliberately not\n * registered by `mount()`.\n */\n handleBackdropClick: (e: MouseEvent) => void;\n isOpen: Signal<boolean>;\n /**\n * Registers the open-prop watcher and the standard native `close`/`cancel` listeners on the\n * dialog element (Escape closes via the native `cancel` event). Call inside `onMounted()`.\n * Backdrop-click-to-close is intentionally left to the caller: see `handleBackdropClick`.\n */\n mount: () => void;\n /** The modal dialog controller: open/close/toggle/dispose. */\n overlay: ModalDialogController;\n /**\n * Dispatch `close-request`; if allowed, call `overlay.close()`.\n */\n requestClose: (reason: Exclude<DialogCloseReason, 'programmatic'>) => void;\n};\n\ntype ModalDialogController = {\n [Symbol.dispose](): void;\n close(reason?: DialogCloseReason): void;\n dispose(): void;\n open(reason?: OverlayOpenReason): void;\n toggle(openReason?: OverlayOpenReason, closeReason?: DialogCloseReason): void;\n};\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Shared dialog-control composable for `ore-dialog` and `ore-drawer`.\n *\n * Owns: `isOpen`, close-reason tracking, focus management, `overlay`,\n * `requestClose`, background lock/unlock, and the `close` event handler.\n * Components supply only their unique behavior via callbacks and `onNativeClose`.\n */\nexport function useDialogControl(options: UseDialogOptions): UseDialogHandle {\n const abortSignal = lifecycleSignal(options.onCleanup);\n const isOpen = signal(false);\n // Internal close-reason: set atomically before dialog.close() fires.\n let pendingCloseReason: DialogCloseReason = 'programmatic';\n const bgLock = createBackgroundLock();\n let isClosing = false;\n\n // ── Focus management ──────────────────────────────────────────────────────\n const focus = createFocusManager({\n getInitialFocusSelector: () => options.initialFocus.value,\n getReturnFocus: () => options.returnFocus.value,\n host: options.host,\n signal: abortSignal,\n });\n // ─────────────────────────────────────────────────────────────────────────\n\n /** Cancels a pending exit animation so a reopen can keep the dialog open. No-op otherwise. */\n let cancelExit = (): void => {};\n\n const closeWithAnimation = (): void => {\n const dialog = options.dialogRef.value;\n\n if (!dialog?.open || isClosing) return;\n\n isClosing = true;\n dialog.classList.add('closing');\n\n let settled = false;\n\n cancelExit = (): void => {\n if (settled) return;\n\n settled = true;\n dialog.classList.remove('closing');\n isClosing = false;\n };\n\n const finish = () => {\n if (settled) return;\n\n settled = true;\n dialog.close();\n // Do NOT remove 'closing' here: removing it while the native ::backdrop is\n // still held in the top layer by the 'overlay allow-discrete' transition\n // would revert its opacity to 1 and cause a visible flash. The class is\n // cleaned up at the start of the next open cycle (see setOpen below).\n isClosing = false;\n };\n\n const panel = options.getPanelEl();\n\n if (panel) {\n awaitExit(panel, finish, 'transition');\n } else {\n finish();\n }\n };\n\n const dispatchCloseRequest = (reason: Exclude<DialogCloseReason, 'programmatic'>): boolean => {\n return options.host.dispatchEvent(\n new CustomEvent('close-request', {\n bubbles: true,\n cancelable: true,\n composed: true,\n detail: { reason, ...options.closeRequestDetail?.(reason) },\n }),\n );\n };\n\n const overlay: ModalDialogController = {\n close(reason: DialogCloseReason = 'programmatic'): void {\n if (!isOpen.value) return;\n\n const dialog = options.dialogRef.value;\n\n if (dialog?.open) {\n pendingCloseReason = reason;\n (options.performClose ?? ((element, _reason) => element.close()))(dialog, reason);\n } else {\n isOpen.value = false;\n }\n },\n dispose(): void {\n overlay.close('programmatic');\n },\n open(reason: OverlayOpenReason = 'programmatic'): void {\n const dialog = options.dialogRef.value;\n\n if (dialog?.open) {\n // A reopen that arrives while the exit animation is still running cancels\n // the pending close and keeps the dialog open: the open prop is\n // authoritative, so a quick close→reopen cycle must not end with the\n // native dialog closed while the prop still says open.\n cancelExit();\n\n return;\n }\n\n if (!dialog || isOpen.value) return;\n\n // Clean up any leftover 'closing' class from the previous close animation\n // before opening so the entry @starting-style and transition work correctly.\n dialog.classList.remove('closing');\n focus.captureReturnFocus();\n options.beforeOpen?.(dialog);\n dialog.showModal();\n bgLock.lock(options.host);\n focus.applyInitialFocus();\n isOpen.value = true;\n options.onOpen?.(reason);\n },\n [Symbol.dispose](): void {\n overlay.dispose();\n },\n toggle(openReason: OverlayOpenReason = 'click', closeReason: DialogCloseReason = 'trigger'): void {\n if (isOpen.value) overlay.close(closeReason);\n else overlay.open(openReason);\n },\n };\n\n abortSignal.addEventListener('abort', overlay.dispose, { once: true });\n\n const requestClose = (reason: Exclude<DialogCloseReason, 'programmatic'>): void => {\n const allowed = dispatchCloseRequest(reason);\n\n if (!allowed) return;\n\n overlay.close(reason);\n };\n\n // Invoker Commands API: allows declarative open/close from outside the component:\n // <button commandfor=\"ore-dialog-id\" command=\"show-modal\">Open</button>\n // <button commandfor=\"ore-dialog-id\" command=\"close\">Close</button>\n options.host.addEventListener(\n 'command',\n (e: Event) => {\n const cmd = (e as CommandEvent).command;\n\n if (cmd === 'show-modal') overlay.open('trigger');\n else if (cmd === 'close' || cmd === 'request-close') requestClose('trigger');\n },\n { signal: abortSignal },\n );\n\n // The native `<dialog>` element auto-closes on Escape via its own close-watcher, which\n // dispatches a cancelable `cancel` event before `close`. Suppressing/redirecting that is the\n // spec-correct hook: relying on `keydown`'s `preventDefault()` to stop the close-watcher is\n // implementation-specific, not guaranteed across browsers.\n const handleCancel = (e: Event): void => {\n // See `handleNativeClose`'s comment: guard against a same-named bubbling event from a\n // descendant field being misread as this dialog's own native event.\n if (e.target !== options.dialogRef.value) return;\n\n e.preventDefault();\n\n if (options.isPersistent()) return;\n\n requestClose('escape');\n };\n\n const handleBackdropClick = (e: MouseEvent): void => {\n if (options.isPersistent()) return;\n\n if (e.target === options.dialogRef.value) requestClose('outsideClick');\n };\n\n // ── Internal native close handler ─────────────────────────────────────────\n // `e.target` must be checked against the native `<dialog>` itself, the same way\n // `handleBackdropClick` already does: a bubbling `close`-named event fired by *any* descendant\n // component reaches this listener too: regardless of that event's own `composed` flag, since\n // slot-assignment-based event-path computation (a slotted descendant's \"parent\" is the `<slot>`\n // projecting it) isn't gated by `composed` the way crossing back out of a shadow root is. Some\n // fields nested here (`ore-select`, `ore-combobox`, …) emit their own public `close` event when\n // *their* dropdown closes: without this check, selecting an option in one of those closes this\n // dialog too, misreading the field's own event as this dialog's native close.\n const handleNativeClose = (e?: Event): void => {\n if (e && e.target !== options.dialogRef.value) return;\n\n bgLock.unlock();\n options.host.removeAttribute('open');\n isOpen.value = false;\n focus.restoreFocus();\n\n const reason = pendingCloseReason;\n\n pendingCloseReason = 'programmatic';\n options.onNativeClose?.(reason);\n };\n\n const mount = (): void => {\n const dialog = options.dialogRef.value;\n\n if (!dialog) return;\n\n options.onEvent(dialog, 'close', handleNativeClose);\n options.onEvent(dialog, 'cancel', handleCancel);\n\n let initialized = false;\n\n watch(\n options.openProp,\n (open) => {\n if (open === undefined) {\n if (!initialized) {\n if (options.defaultOpen.value) overlay.open('programmatic');\n\n initialized = true;\n\n return;\n }\n\n overlay.close('programmatic');\n\n return;\n }\n\n initialized = true;\n\n if (open) {\n overlay.open('programmatic');\n } else {\n overlay.close('programmatic');\n }\n },\n { immediate: true },\n );\n };\n\n return { closeWithAnimation, handleBackdropClick, isOpen, mount, overlay, requestClose };\n}\n"],"mappings":"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"}
|
|
@@ -22,13 +22,13 @@ export type UseDialogOptions = {
|
|
|
22
22
|
*/
|
|
23
23
|
onEvent: <K extends keyof HTMLElementEventMap>(target: EventTarget | null | undefined, event: K, listener: (e: HTMLElementEventMap[K]) => void, options?: AddEventListenerOptions) => void;
|
|
24
24
|
/**
|
|
25
|
-
* Called after the native `close` event fires
|
|
25
|
+
* Called after the native `close` event fires: after background unlock,
|
|
26
26
|
* isOpen reset, and focus restore. Use to emit component events.
|
|
27
27
|
*/
|
|
28
28
|
onNativeClose?: (reason: DialogCloseReason) => void;
|
|
29
29
|
/** Called by the overlay on open. Use to emit events. */
|
|
30
30
|
onOpen?: (reason: OverlayOpenReason) => void;
|
|
31
|
-
/** Open prop from the component
|
|
31
|
+
/** Open prop from the component: drives programmatic open/close via watch. */
|
|
32
32
|
openProp: Readable<boolean | undefined>;
|
|
33
33
|
/**
|
|
34
34
|
* Called by the overlay when closing. Orchestrates the close animation.
|
|
@@ -41,8 +41,8 @@ export type UseDialogHandle = {
|
|
|
41
41
|
closeWithAnimation: () => void;
|
|
42
42
|
/**
|
|
43
43
|
* Standard backdrop-click-to-close handler. Callers wire it themselves
|
|
44
|
-
* (`onEvent(dialog, 'click', handleBackdropClick)`) because some overlays
|
|
45
|
-
* swipe-to-dismiss
|
|
44
|
+
* (`onEvent(dialog, 'click', handleBackdropClick)`) because some overlays: e.g. drawer's
|
|
45
|
+
* swipe-to-dismiss: need a backdrop handler with extra guards, so it is deliberately not
|
|
46
46
|
* registered by `mount()`.
|
|
47
47
|
*/
|
|
48
48
|
handleBackdropClick: (e: MouseEvent) => void;
|
|
@@ -50,10 +50,10 @@ export type UseDialogHandle = {
|
|
|
50
50
|
/**
|
|
51
51
|
* Registers the open-prop watcher and the standard native `close`/`cancel` listeners on the
|
|
52
52
|
* dialog element (Escape closes via the native `cancel` event). Call inside `onMounted()`.
|
|
53
|
-
* Backdrop-click-to-close is intentionally left to the caller
|
|
53
|
+
* Backdrop-click-to-close is intentionally left to the caller: see `handleBackdropClick`.
|
|
54
54
|
*/
|
|
55
55
|
mount: () => void;
|
|
56
|
-
/** The modal dialog controller
|
|
56
|
+
/** The modal dialog controller: open/close/toggle/dispose. */
|
|
57
57
|
overlay: ModalDialogController;
|
|
58
58
|
/**
|
|
59
59
|
* Dispatch `close-request`; if allowed, call `overlay.close()`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-dialog.d.ts","sourceRoot":"","sources":["../../../src/overlay/shared/use-dialog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAiB,MAAM,kBAAkB,CAAC;AAE7E,OAAO,EAAsB,KAAK,iBAAiB,EAAmB,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAWjH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,uFAAuF;IACvF,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACjD,4EAA4E;IAC5E,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,iBAAiB,EAAE,cAAc,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrG,2EAA2E;IAC3E,WAAW,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IAC3C,SAAS,EAAE;QAAE,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,SAAS,CAAA;KAAE,CAAC;IAC3D,UAAU,EAAE,MAAM,WAAW,GAAG,IAAI,GAAG,SAAS,CAAC;IACjD,IAAI,EAAE,WAAW,CAAC;IAClB,YAAY,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC3C,YAAY,EAAE,MAAM,OAAO,CAAC;IAC5B,0FAA0F;IAC1F,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IACpC;;;OAGG;IACH,OAAO,EAAE,CAAC,CAAC,SAAS,MAAM,mBAAmB,EAC3C,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,EACtC,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC,KAAK,IAAI,EAC7C,OAAO,CAAC,EAAE,uBAAuB,KAC9B,IAAI,CAAC;IACV;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACpD,yDAAyD;IACzD,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC7C
|
|
1
|
+
{"version":3,"file":"use-dialog.d.ts","sourceRoot":"","sources":["../../../src/overlay/shared/use-dialog.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-dialog.js","names":[],"sources":["../../../src/overlay/shared/use-dialog.ts"],"sourcesContent":["import { type Readable, type Signal, signal, watch } from '@vielzeug/ripple';\n\nimport { createFocusManager, type DialogCloseReason, lifecycleSignal, type OverlayOpenReason } from '../../core';\nimport { awaitExit } from './await-exit';\nimport { createBackgroundLock } from './background-lock';\n\n// Invoker Commands API — not yet in lib.dom.d.ts\ninterface CommandEvent extends Event {\n command: string;\n}\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type UseDialogOptions = {\n /** Called after showModal() but before isOpen = true. Use to clear drag styles etc. */\n beforeOpen?: (dialog: HTMLDialogElement) => void;\n /** Extra properties merged into the `close-request` custom event detail. */\n closeRequestDetail?: (reason: Exclude<DialogCloseReason, 'programmatic'>) => Record<string, unknown>;\n /** Initial uncontrolled open state. Ignored when `openProp` is defined. */\n defaultOpen: Readable<boolean | undefined>;\n dialogRef: { value: HTMLDialogElement | null | undefined };\n getPanelEl: () => HTMLElement | null | undefined;\n host: HTMLElement;\n initialFocus: Readable<string | undefined>;\n isPersistent: () => boolean;\n /** Cleanup registrar from the component setup ctx. Automatically called on disconnect. */\n onCleanup: (fn: () => void) => void;\n /**\n * Scoped event listener registrar from the component setup ctx.\n * Automatically removed on component disconnect.\n */\n onEvent: <K extends keyof HTMLElementEventMap>(\n target: EventTarget | null | undefined,\n event: K,\n listener: (e: HTMLElementEventMap[K]) => void,\n options?: AddEventListenerOptions,\n ) => void;\n /**\n * Called after the native `close` event fires — after background unlock,\n * isOpen reset, and focus restore. Use to emit component events.\n */\n onNativeClose?: (reason: DialogCloseReason) => void;\n /** Called by the overlay on open. Use to emit events. */\n onOpen?: (reason: OverlayOpenReason) => void;\n /** Open prop from the component — drives programmatic open/close via watch. */\n openProp: Readable<boolean | undefined>;\n /**\n * Called by the overlay when closing. Orchestrates the close animation.\n * Default: `dialog.close()`. Override in dialog.ts to add the CSS exit animation.\n */\n performClose?: (dialog: HTMLDialogElement, reason: DialogCloseReason) => void;\n returnFocus: Readable<boolean | undefined>;\n};\n\nexport type UseDialogHandle = {\n closeWithAnimation: () => void;\n /**\n * Standard backdrop-click-to-close handler. Callers wire it themselves\n * (`onEvent(dialog, 'click', handleBackdropClick)`) because some overlays — e.g. drawer's\n * swipe-to-dismiss — need a backdrop handler with extra guards, so it is deliberately not\n * registered by `mount()`.\n */\n handleBackdropClick: (e: MouseEvent) => void;\n isOpen: Signal<boolean>;\n /**\n * Registers the open-prop watcher and the standard native `close`/`cancel` listeners on the\n * dialog element (Escape closes via the native `cancel` event). Call inside `onMounted()`.\n * Backdrop-click-to-close is intentionally left to the caller — see `handleBackdropClick`.\n */\n mount: () => void;\n /** The modal dialog controller — open/close/toggle/dispose. */\n overlay: ModalDialogController;\n /**\n * Dispatch `close-request`; if allowed, call `overlay.close()`.\n */\n requestClose: (reason: Exclude<DialogCloseReason, 'programmatic'>) => void;\n};\n\ntype ModalDialogController = {\n [Symbol.dispose](): void;\n close(reason?: DialogCloseReason): void;\n dispose(): void;\n open(reason?: OverlayOpenReason): void;\n toggle(openReason?: OverlayOpenReason, closeReason?: DialogCloseReason): void;\n};\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Shared dialog-control composable for `ore-dialog` and `ore-drawer`.\n *\n * Owns: `isOpen`, close-reason tracking, focus management, `overlay`,\n * `requestClose`, background lock/unlock, and the `close` event handler.\n * Components supply only their unique behavior via callbacks and `onNativeClose`.\n */\nexport function useDialogControl(options: UseDialogOptions): UseDialogHandle {\n const abortSignal = lifecycleSignal(options.onCleanup);\n const isOpen = signal(false);\n // Internal close-reason: set atomically before dialog.close() fires.\n let pendingCloseReason: DialogCloseReason = 'programmatic';\n const bgLock = createBackgroundLock();\n let isClosing = false;\n\n // ── Focus management ──────────────────────────────────────────────────────\n const focus = createFocusManager({\n getInitialFocusSelector: () => options.initialFocus.value,\n getReturnFocus: () => options.returnFocus.value,\n host: options.host,\n signal: abortSignal,\n });\n // ─────────────────────────────────────────────────────────────────────────\n\n /** Cancels a pending exit animation so a reopen can keep the dialog open. No-op otherwise. */\n let cancelExit = (): void => {};\n\n const closeWithAnimation = (): void => {\n const dialog = options.dialogRef.value;\n\n if (!dialog?.open || isClosing) return;\n\n isClosing = true;\n dialog.classList.add('closing');\n\n let settled = false;\n\n cancelExit = (): void => {\n if (settled) return;\n\n settled = true;\n dialog.classList.remove('closing');\n isClosing = false;\n };\n\n const finish = () => {\n if (settled) return;\n\n settled = true;\n dialog.close();\n // Do NOT remove 'closing' here — removing it while the native ::backdrop is\n // still held in the top layer by the 'overlay allow-discrete' transition\n // would revert its opacity to 1 and cause a visible flash. The class is\n // cleaned up at the start of the next open cycle (see setOpen below).\n isClosing = false;\n };\n\n const panel = options.getPanelEl();\n\n if (panel) {\n awaitExit(panel, finish, 'transition');\n } else {\n finish();\n }\n };\n\n const dispatchCloseRequest = (reason: Exclude<DialogCloseReason, 'programmatic'>): boolean => {\n return options.host.dispatchEvent(\n new CustomEvent('close-request', {\n bubbles: true,\n cancelable: true,\n composed: true,\n detail: { reason, ...options.closeRequestDetail?.(reason) },\n }),\n );\n };\n\n const overlay: ModalDialogController = {\n close(reason: DialogCloseReason = 'programmatic'): void {\n if (!isOpen.value) return;\n\n const dialog = options.dialogRef.value;\n\n if (dialog?.open) {\n pendingCloseReason = reason;\n (options.performClose ?? ((element, _reason) => element.close()))(dialog, reason);\n } else {\n isOpen.value = false;\n }\n },\n dispose(): void {\n overlay.close('programmatic');\n },\n open(reason: OverlayOpenReason = 'programmatic'): void {\n const dialog = options.dialogRef.value;\n\n if (dialog?.open) {\n // A reopen that arrives while the exit animation is still running cancels\n // the pending close and keeps the dialog open — the open prop is\n // authoritative, so a quick close→reopen cycle must not end with the\n // native dialog closed while the prop still says open.\n cancelExit();\n\n return;\n }\n\n if (!dialog || isOpen.value) return;\n\n // Clean up any leftover 'closing' class from the previous close animation\n // before opening so the entry @starting-style and transition work correctly.\n dialog.classList.remove('closing');\n focus.captureReturnFocus();\n options.beforeOpen?.(dialog);\n dialog.showModal();\n bgLock.lock(options.host);\n focus.applyInitialFocus();\n isOpen.value = true;\n options.onOpen?.(reason);\n },\n [Symbol.dispose](): void {\n overlay.dispose();\n },\n toggle(openReason: OverlayOpenReason = 'click', closeReason: DialogCloseReason = 'trigger'): void {\n if (isOpen.value) overlay.close(closeReason);\n else overlay.open(openReason);\n },\n };\n\n abortSignal.addEventListener('abort', overlay.dispose, { once: true });\n\n const requestClose = (reason: Exclude<DialogCloseReason, 'programmatic'>): void => {\n const allowed = dispatchCloseRequest(reason);\n\n if (!allowed) return;\n\n overlay.close(reason);\n };\n\n // Invoker Commands API — allows declarative open/close from outside the component:\n // <button commandfor=\"ore-dialog-id\" command=\"show-modal\">Open</button>\n // <button commandfor=\"ore-dialog-id\" command=\"close\">Close</button>\n options.host.addEventListener(\n 'command',\n (e: Event) => {\n const cmd = (e as CommandEvent).command;\n\n if (cmd === 'show-modal') overlay.open('trigger');\n else if (cmd === 'close' || cmd === 'request-close') requestClose('trigger');\n },\n { signal: abortSignal },\n );\n\n // The native `<dialog>` element auto-closes on Escape via its own close-watcher, which\n // dispatches a cancelable `cancel` event before `close`. Suppressing/redirecting that is the\n // spec-correct hook — relying on `keydown`'s `preventDefault()` to stop the close-watcher is\n // implementation-specific, not guaranteed across browsers.\n const handleCancel = (e: Event): void => {\n // See `handleNativeClose`'s comment: guard against a same-named bubbling event from a\n // descendant field being misread as this dialog's own native event.\n if (e.target !== options.dialogRef.value) return;\n\n e.preventDefault();\n\n if (options.isPersistent()) return;\n\n requestClose('escape');\n };\n\n const handleBackdropClick = (e: MouseEvent): void => {\n if (options.isPersistent()) return;\n\n if (e.target === options.dialogRef.value) requestClose('outsideClick');\n };\n\n // ── Internal native close handler ─────────────────────────────────────────\n // `e.target` must be checked against the native `<dialog>` itself, the same way\n // `handleBackdropClick` already does: a bubbling `close`-named event fired by *any* descendant\n // component reaches this listener too — regardless of that event's own `composed` flag, since\n // slot-assignment-based event-path computation (a slotted descendant's \"parent\" is the `<slot>`\n // projecting it) isn't gated by `composed` the way crossing back out of a shadow root is. Some\n // fields nested here (`ore-select`, `ore-combobox`, …) emit their own public `close` event when\n // *their* dropdown closes — without this check, selecting an option in one of those closes this\n // dialog too, misreading the field's own event as this dialog's native close.\n const handleNativeClose = (e?: Event): void => {\n if (e && e.target !== options.dialogRef.value) return;\n\n bgLock.unlock();\n options.host.removeAttribute('open');\n isOpen.value = false;\n focus.restoreFocus();\n\n const reason = pendingCloseReason;\n\n pendingCloseReason = 'programmatic';\n options.onNativeClose?.(reason);\n };\n\n const mount = (): void => {\n const dialog = options.dialogRef.value;\n\n if (!dialog) return;\n\n options.onEvent(dialog, 'close', handleNativeClose);\n options.onEvent(dialog, 'cancel', handleCancel);\n\n let initialized = false;\n\n watch(\n options.openProp,\n (open) => {\n if (open === undefined) {\n if (!initialized) {\n if (options.defaultOpen.value) overlay.open('programmatic');\n\n initialized = true;\n\n return;\n }\n\n overlay.close('programmatic');\n\n return;\n }\n\n initialized = true;\n\n if (open) {\n overlay.open('programmatic');\n } else {\n overlay.close('programmatic');\n }\n },\n { immediate: true },\n );\n };\n\n return { closeWithAnimation, handleBackdropClick, isOpen, mount, overlay, requestClose };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-dialog.js","names":[],"sources":["../../../src/overlay/shared/use-dialog.ts"],"sourcesContent":["import { type Readable, type Signal, signal, watch } from '@vielzeug/ripple';\n\nimport { createFocusManager, type DialogCloseReason, lifecycleSignal, type OverlayOpenReason } from '../../core';\nimport { awaitExit } from './await-exit';\nimport { createBackgroundLock } from './background-lock';\n\n// Invoker Commands API: not yet in lib.dom.d.ts\ninterface CommandEvent extends Event {\n command: string;\n}\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type UseDialogOptions = {\n /** Called after showModal() but before isOpen = true. Use to clear drag styles etc. */\n beforeOpen?: (dialog: HTMLDialogElement) => void;\n /** Extra properties merged into the `close-request` custom event detail. */\n closeRequestDetail?: (reason: Exclude<DialogCloseReason, 'programmatic'>) => Record<string, unknown>;\n /** Initial uncontrolled open state. Ignored when `openProp` is defined. */\n defaultOpen: Readable<boolean | undefined>;\n dialogRef: { value: HTMLDialogElement | null | undefined };\n getPanelEl: () => HTMLElement | null | undefined;\n host: HTMLElement;\n initialFocus: Readable<string | undefined>;\n isPersistent: () => boolean;\n /** Cleanup registrar from the component setup ctx. Automatically called on disconnect. */\n onCleanup: (fn: () => void) => void;\n /**\n * Scoped event listener registrar from the component setup ctx.\n * Automatically removed on component disconnect.\n */\n onEvent: <K extends keyof HTMLElementEventMap>(\n target: EventTarget | null | undefined,\n event: K,\n listener: (e: HTMLElementEventMap[K]) => void,\n options?: AddEventListenerOptions,\n ) => void;\n /**\n * Called after the native `close` event fires: after background unlock,\n * isOpen reset, and focus restore. Use to emit component events.\n */\n onNativeClose?: (reason: DialogCloseReason) => void;\n /** Called by the overlay on open. Use to emit events. */\n onOpen?: (reason: OverlayOpenReason) => void;\n /** Open prop from the component: drives programmatic open/close via watch. */\n openProp: Readable<boolean | undefined>;\n /**\n * Called by the overlay when closing. Orchestrates the close animation.\n * Default: `dialog.close()`. Override in dialog.ts to add the CSS exit animation.\n */\n performClose?: (dialog: HTMLDialogElement, reason: DialogCloseReason) => void;\n returnFocus: Readable<boolean | undefined>;\n};\n\nexport type UseDialogHandle = {\n closeWithAnimation: () => void;\n /**\n * Standard backdrop-click-to-close handler. Callers wire it themselves\n * (`onEvent(dialog, 'click', handleBackdropClick)`) because some overlays: e.g. drawer's\n * swipe-to-dismiss: need a backdrop handler with extra guards, so it is deliberately not\n * registered by `mount()`.\n */\n handleBackdropClick: (e: MouseEvent) => void;\n isOpen: Signal<boolean>;\n /**\n * Registers the open-prop watcher and the standard native `close`/`cancel` listeners on the\n * dialog element (Escape closes via the native `cancel` event). Call inside `onMounted()`.\n * Backdrop-click-to-close is intentionally left to the caller: see `handleBackdropClick`.\n */\n mount: () => void;\n /** The modal dialog controller: open/close/toggle/dispose. */\n overlay: ModalDialogController;\n /**\n * Dispatch `close-request`; if allowed, call `overlay.close()`.\n */\n requestClose: (reason: Exclude<DialogCloseReason, 'programmatic'>) => void;\n};\n\ntype ModalDialogController = {\n [Symbol.dispose](): void;\n close(reason?: DialogCloseReason): void;\n dispose(): void;\n open(reason?: OverlayOpenReason): void;\n toggle(openReason?: OverlayOpenReason, closeReason?: DialogCloseReason): void;\n};\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Shared dialog-control composable for `ore-dialog` and `ore-drawer`.\n *\n * Owns: `isOpen`, close-reason tracking, focus management, `overlay`,\n * `requestClose`, background lock/unlock, and the `close` event handler.\n * Components supply only their unique behavior via callbacks and `onNativeClose`.\n */\nexport function useDialogControl(options: UseDialogOptions): UseDialogHandle {\n const abortSignal = lifecycleSignal(options.onCleanup);\n const isOpen = signal(false);\n // Internal close-reason: set atomically before dialog.close() fires.\n let pendingCloseReason: DialogCloseReason = 'programmatic';\n const bgLock = createBackgroundLock();\n let isClosing = false;\n\n // ── Focus management ──────────────────────────────────────────────────────\n const focus = createFocusManager({\n getInitialFocusSelector: () => options.initialFocus.value,\n getReturnFocus: () => options.returnFocus.value,\n host: options.host,\n signal: abortSignal,\n });\n // ─────────────────────────────────────────────────────────────────────────\n\n /** Cancels a pending exit animation so a reopen can keep the dialog open. No-op otherwise. */\n let cancelExit = (): void => {};\n\n const closeWithAnimation = (): void => {\n const dialog = options.dialogRef.value;\n\n if (!dialog?.open || isClosing) return;\n\n isClosing = true;\n dialog.classList.add('closing');\n\n let settled = false;\n\n cancelExit = (): void => {\n if (settled) return;\n\n settled = true;\n dialog.classList.remove('closing');\n isClosing = false;\n };\n\n const finish = () => {\n if (settled) return;\n\n settled = true;\n dialog.close();\n // Do NOT remove 'closing' here: removing it while the native ::backdrop is\n // still held in the top layer by the 'overlay allow-discrete' transition\n // would revert its opacity to 1 and cause a visible flash. The class is\n // cleaned up at the start of the next open cycle (see setOpen below).\n isClosing = false;\n };\n\n const panel = options.getPanelEl();\n\n if (panel) {\n awaitExit(panel, finish, 'transition');\n } else {\n finish();\n }\n };\n\n const dispatchCloseRequest = (reason: Exclude<DialogCloseReason, 'programmatic'>): boolean => {\n return options.host.dispatchEvent(\n new CustomEvent('close-request', {\n bubbles: true,\n cancelable: true,\n composed: true,\n detail: { reason, ...options.closeRequestDetail?.(reason) },\n }),\n );\n };\n\n const overlay: ModalDialogController = {\n close(reason: DialogCloseReason = 'programmatic'): void {\n if (!isOpen.value) return;\n\n const dialog = options.dialogRef.value;\n\n if (dialog?.open) {\n pendingCloseReason = reason;\n (options.performClose ?? ((element, _reason) => element.close()))(dialog, reason);\n } else {\n isOpen.value = false;\n }\n },\n dispose(): void {\n overlay.close('programmatic');\n },\n open(reason: OverlayOpenReason = 'programmatic'): void {\n const dialog = options.dialogRef.value;\n\n if (dialog?.open) {\n // A reopen that arrives while the exit animation is still running cancels\n // the pending close and keeps the dialog open: the open prop is\n // authoritative, so a quick close→reopen cycle must not end with the\n // native dialog closed while the prop still says open.\n cancelExit();\n\n return;\n }\n\n if (!dialog || isOpen.value) return;\n\n // Clean up any leftover 'closing' class from the previous close animation\n // before opening so the entry @starting-style and transition work correctly.\n dialog.classList.remove('closing');\n focus.captureReturnFocus();\n options.beforeOpen?.(dialog);\n dialog.showModal();\n bgLock.lock(options.host);\n focus.applyInitialFocus();\n isOpen.value = true;\n options.onOpen?.(reason);\n },\n [Symbol.dispose](): void {\n overlay.dispose();\n },\n toggle(openReason: OverlayOpenReason = 'click', closeReason: DialogCloseReason = 'trigger'): void {\n if (isOpen.value) overlay.close(closeReason);\n else overlay.open(openReason);\n },\n };\n\n abortSignal.addEventListener('abort', overlay.dispose, { once: true });\n\n const requestClose = (reason: Exclude<DialogCloseReason, 'programmatic'>): void => {\n const allowed = dispatchCloseRequest(reason);\n\n if (!allowed) return;\n\n overlay.close(reason);\n };\n\n // Invoker Commands API: allows declarative open/close from outside the component:\n // <button commandfor=\"ore-dialog-id\" command=\"show-modal\">Open</button>\n // <button commandfor=\"ore-dialog-id\" command=\"close\">Close</button>\n options.host.addEventListener(\n 'command',\n (e: Event) => {\n const cmd = (e as CommandEvent).command;\n\n if (cmd === 'show-modal') overlay.open('trigger');\n else if (cmd === 'close' || cmd === 'request-close') requestClose('trigger');\n },\n { signal: abortSignal },\n );\n\n // The native `<dialog>` element auto-closes on Escape via its own close-watcher, which\n // dispatches a cancelable `cancel` event before `close`. Suppressing/redirecting that is the\n // spec-correct hook: relying on `keydown`'s `preventDefault()` to stop the close-watcher is\n // implementation-specific, not guaranteed across browsers.\n const handleCancel = (e: Event): void => {\n // See `handleNativeClose`'s comment: guard against a same-named bubbling event from a\n // descendant field being misread as this dialog's own native event.\n if (e.target !== options.dialogRef.value) return;\n\n e.preventDefault();\n\n if (options.isPersistent()) return;\n\n requestClose('escape');\n };\n\n const handleBackdropClick = (e: MouseEvent): void => {\n if (options.isPersistent()) return;\n\n if (e.target === options.dialogRef.value) requestClose('outsideClick');\n };\n\n // ── Internal native close handler ─────────────────────────────────────────\n // `e.target` must be checked against the native `<dialog>` itself, the same way\n // `handleBackdropClick` already does: a bubbling `close`-named event fired by *any* descendant\n // component reaches this listener too: regardless of that event's own `composed` flag, since\n // slot-assignment-based event-path computation (a slotted descendant's \"parent\" is the `<slot>`\n // projecting it) isn't gated by `composed` the way crossing back out of a shadow root is. Some\n // fields nested here (`ore-select`, `ore-combobox`, …) emit their own public `close` event when\n // *their* dropdown closes: without this check, selecting an option in one of those closes this\n // dialog too, misreading the field's own event as this dialog's native close.\n const handleNativeClose = (e?: Event): void => {\n if (e && e.target !== options.dialogRef.value) return;\n\n bgLock.unlock();\n options.host.removeAttribute('open');\n isOpen.value = false;\n focus.restoreFocus();\n\n const reason = pendingCloseReason;\n\n pendingCloseReason = 'programmatic';\n options.onNativeClose?.(reason);\n };\n\n const mount = (): void => {\n const dialog = options.dialogRef.value;\n\n if (!dialog) return;\n\n options.onEvent(dialog, 'close', handleNativeClose);\n options.onEvent(dialog, 'cancel', handleCancel);\n\n let initialized = false;\n\n watch(\n options.openProp,\n (open) => {\n if (open === undefined) {\n if (!initialized) {\n if (options.defaultOpen.value) overlay.open('programmatic');\n\n initialized = true;\n\n return;\n }\n\n overlay.close('programmatic');\n\n return;\n }\n\n initialized = true;\n\n if (open) {\n overlay.open('programmatic');\n } else {\n overlay.close('programmatic');\n }\n },\n { immediate: true },\n );\n };\n\n return { closeWithAnimation, handleBackdropClick, isOpen, mount, overlay, requestClose };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-floating-trigger.cjs","names":[],"sources":["../../../src/overlay/shared/use-floating-trigger.ts"],"sourcesContent":["import type { Placement } from '@vielzeug/orbit';\n\nimport { type Readable, type Signal, signal, watch } from '@vielzeug/ripple';\n\nimport {\n createDropdownPositioner,\n createOutsidePointerDismissal,\n type DialogCloseReason,\n lifecycleSignal,\n type OverlayOpenReason,\n} from '../../core';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type FloatingTriggerType = 'click' | 'focus' | 'hover';\n\nexport type AriaBindFn = (triggerEl: HTMLElement) => () => void;\n\nexport type FloatingTriggerOptions = {\n /** ARIA binding factory: called with the trigger element, returns a cleanup. */\n bindTriggerAria: AriaBindFn;\n /** Initial uncontrolled visibility. Ignored when `openProp` is defined. */\n defaultOpen: Readable<boolean | undefined>;\n /** If true, disable all trigger interactions and close if open. */\n disabled: Readable<boolean>;\n /** Returns the host element (e.g. ore-popover) so slotted content clicks aren't treated as outside clicks. */\n getHost?: () => Element | null;\n /** Returns the panel element, if mounted. */\n getPanel: () => HTMLElement | null;\n /** Gap from reference to floating panel in px, read on every position update. Default: 8. */\n offset?: () => number | undefined;\n /** Cleanup registrar from the component setup ctx. Automatically called on disconnect. */\n onCleanup: (fn: () => void) => void;\n /** Called when the popover closes. */\n onClose?: (reason: DialogCloseReason) => void;\n /** Called when the popover opens. */\n onOpen?: (reason: OverlayOpenReason) => void;\n /** Callback when resolved placement changes (useful for CSS attribute). */\n onPlacementChange?: (placement: Placement) => void;\n /** Controlled open prop. When defined, disables uncontrolled logic. */\n openProp: Readable<boolean | undefined>;\n /** Preferred placement. Default: 'bottom'. */\n placement: Readable<Placement>;\n /** Resolves the trigger slot element. Called each time events are (re)bound. */\n slot: () => HTMLSlotElement | null;\n /** Slot elements signal — used to rebind when slotted elements change. */\n slotElements: Readable<Element[]>;\n /** Which triggers are active. Set to empty array or omit if handling events manually. */\n triggers: Readable<FloatingTriggerType[]>;\n};\n\nexport type FloatingTriggerHandle = {\n /** Closes the panel or requests that a controlled consumer close it. */\n close: (reason?: DialogCloseReason) => void;\n /**\n * Call inside `onMounted`. Sets up slot + trigger event watchers\n * and returns a cleanup function to pass back to the framework.\n */\n mount: () => () => void;\n /** Opens the panel or requests that a controlled consumer open it. */\n open: (reason?: OverlayOpenReason) => void;\n /** Toggles the panel or requests the matching controlled-state change. */\n toggle: () => void;\n /** Manually trigger a position recalculation. */\n updatePosition: () => void;\n /** Whether the panel is currently visible. */\n visible: Signal<boolean>;\n};\n\n// ── Implementation ─────────────────────────────────────────────────────────────\n\n/**\n * Shared logic for floating-panel triggers (tooltip, popover).\n *\n * Handles:\n * - Slot-based trigger element detection and rebinding\n * - Event binding per trigger type (hover / focus / click)\n * - Floating-element positioning via `@vielzeug/orbit`\n * - Controlled mode via `openProp` watcher\n * - Keyboard escape dismissal\n *\n * ARIA binding is intentionally delegated to the caller via `bindTriggerAria`\n * so that tooltip (`describedby`) and popover (`expanded`/`controls`/…) can diverge.\n */\nexport const useFloatingTrigger = (options: FloatingTriggerOptions): FloatingTriggerHandle => {\n const {\n bindTriggerAria,\n defaultOpen,\n disabled,\n getPanel,\n onClose,\n onOpen,\n onPlacementChange,\n openProp,\n placement,\n slot,\n slotElements,\n triggers,\n } = options;\n\n const getOffset = (): number => options.offset?.() ?? 8;\n const abortSignal = lifecycleSignal(options.onCleanup);\n const visible = signal(false);\n const isControlled = () => openProp.value !== undefined;\n let currentTrigger: HTMLElement | null = null;\n let triggerBinding: (() => void) | null = null;\n\n const resolveTrigger = (): HTMLElement | null =>\n currentTrigger ?? (slot()?.assignedElements({ flatten: true })[0] as HTMLElement | undefined) ?? null;\n\n // The same Orbit positioner the dropdown overlays use — RTL placement mirroring, the\n // containing-block self-correction, and flip/shift, so popover/tooltip no longer carry a second,\n // divergent copy of the positioning logic. Panels render in the Popover API top layer (no\n // clipping ancestor) and size themselves (no width matching), matching `ore-menu`'s config.\n const positioner = createDropdownPositioner({\n getFloating: getPanel,\n getOffsetPx: getOffset,\n getPlacement: () => placement.value,\n getReference: resolveTrigger,\n matchWidth: false,\n onPlacementChange: (resolved) => {\n const panel = getPanel();\n\n if (panel) panel.dataset.placement = resolved;\n\n onPlacementChange?.(resolved);\n },\n padding: 8,\n useClippingAncestor: false,\n });\n\n function updatePosition(): void {\n positioner.update();\n }\n\n function showFloat(): void {\n visible.value = true;\n\n const panel = getPanel();\n\n if (panel) {\n panel.dataset.open = '';\n\n if ('showPopover' in panel) {\n try {\n if (!panel.matches(':popover-open')) panel.showPopover();\n } catch {\n // Popover API restricted (e.g. sandboxed iframe); data-open CSS fallback handles visibility.\n }\n }\n }\n\n updatePosition();\n }\n\n function hideFloat(): void {\n visible.value = false;\n\n const panel = getPanel();\n\n if (panel) {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // Popover API restricted; data-open removal handles visibility.\n }\n }\n }\n }\n\n function open(reason: OverlayOpenReason = 'programmatic'): void {\n if (disabled.value || visible.value) return;\n if (isControlled()) {\n onOpen?.(reason);\n return;\n }\n\n showFloat();\n onOpen?.(reason);\n }\n\n function close(reason: DialogCloseReason = 'trigger'): void {\n if (!visible.value) return;\n if (isControlled()) {\n onClose?.(reason);\n return;\n }\n\n hideFloat();\n onClose?.(reason);\n }\n\n function toggle(): void {\n if (visible.value) close();\n else open('click');\n }\n\n function handleKeydown(e: KeyboardEvent): void {\n if (e.key === 'Escape') close('escape');\n }\n\n function handleFocusOut(e: FocusEvent): void {\n const next = e.relatedTarget as Element | null;\n const panel = getPanel();\n\n if (next && panel?.contains(next)) return;\n\n if (next && currentTrigger?.contains(next)) return;\n\n close('trigger');\n }\n\n const bindEvents = (): void => {\n triggerBinding?.();\n triggerBinding = null;\n\n const triggerEl = slot()?.assignedElements({ flatten: true })[0] as HTMLElement | undefined;\n\n if (!triggerEl) {\n currentTrigger = null;\n\n return;\n }\n\n currentTrigger = triggerEl;\n\n const removeAria = bindTriggerAria(triggerEl);\n const cleanups: Array<() => void> = [];\n\n const add = (\n target: EventTarget,\n event: string,\n listener: EventListener,\n eventOptions?: AddEventListenerOptions,\n ): void => {\n target.addEventListener(event, listener, eventOptions);\n cleanups.push(() => target.removeEventListener(event, listener, eventOptions));\n };\n\n const t = triggers.value;\n\n if (t.includes('click')) {\n add(triggerEl, 'click', toggle as EventListener);\n }\n\n if (t.includes('hover')) {\n add(triggerEl, 'pointerenter', () => open('hover'));\n add(triggerEl, 'pointerleave', () => close('trigger'));\n\n const panelEl = getPanel();\n\n if (panelEl) {\n add(panelEl, 'pointerenter', () => open('hover'));\n add(panelEl, 'pointerleave', () => close('trigger'));\n }\n }\n\n if (t.includes('focus')) {\n add(triggerEl, 'focusin', () => open('focus'));\n add(triggerEl, 'focusout', handleFocusOut as EventListener);\n\n const panelEl = getPanel();\n\n if (panelEl) add(panelEl, 'focusout', handleFocusOut as EventListener);\n }\n\n add(document, 'keydown', handleKeydown as EventListener);\n\n triggerBinding = () => {\n removeAria();\n\n for (const cleanup of cleanups) cleanup();\n\n currentTrigger = null;\n };\n };\n\n const stopOutsidePointerDismissal = createOutsidePointerDismissal({\n getTargets: () => [currentTrigger, getPanel(), options.getHost?.()],\n isActive: () => visible.value && triggers.value.includes('click'),\n onDismiss: () => close('outsideClick'),\n signal: abortSignal,\n });\n\n const mount = (): (() => void) => {\n let initializedOpenProp = false;\n const triggerSlot = slot();\n const rebind = (): void => {\n queueMicrotask(() => {\n if (!abortSignal.aborted) bindEvents();\n });\n };\n\n triggerSlot?.addEventListener('slotchange', rebind);\n rebind();\n watch(slotElements, bindEvents, { immediate: true });\n\n watch(\n openProp,\n (openVal) => {\n if (openVal === undefined || openVal === null) {\n if (initializedOpenProp && visible.value) {\n hideFloat();\n onClose?.('programmatic');\n }\n\n initializedOpenProp = true;\n\n return;\n }\n\n initializedOpenProp = true;\n\n if (openVal) showFloat();\n else if (visible.value) hideFloat();\n },\n { immediate: true },\n );\n\n if (openProp.value === undefined && defaultOpen.value) {\n showFloat();\n onOpen?.('programmatic');\n }\n\n watch(disabled, (isNowDisabled) => {\n if (isNowDisabled) close('programmatic');\n });\n\n return () => {\n triggerSlot?.removeEventListener('slotchange', rebind);\n triggerBinding?.();\n triggerBinding = null;\n\n const panel = getPanel();\n\n if (panel) {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // Popover API restricted; data-open removal handles visibility.\n }\n }\n }\n\n stopOutsidePointerDismissal();\n };\n };\n\n return { close, mount, open, toggle, updatePosition, visible };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-floating-trigger.cjs","names":[],"sources":["../../../src/overlay/shared/use-floating-trigger.ts"],"sourcesContent":["import type { Placement } from '@vielzeug/orbit';\n\nimport { type Readable, type Signal, signal, watch } from '@vielzeug/ripple';\n\nimport {\n createDropdownPositioner,\n createOutsidePointerDismissal,\n type DialogCloseReason,\n lifecycleSignal,\n type OverlayOpenReason,\n} from '../../core';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type FloatingTriggerType = 'click' | 'focus' | 'hover';\n\nexport type AriaBindFn = (triggerEl: HTMLElement) => () => void;\n\nexport type FloatingTriggerOptions = {\n /** ARIA binding factory: called with the trigger element, returns a cleanup. */\n bindTriggerAria: AriaBindFn;\n /** Initial uncontrolled visibility. Ignored when `openProp` is defined. */\n defaultOpen: Readable<boolean | undefined>;\n /** If true, disable all trigger interactions and close if open. */\n disabled: Readable<boolean>;\n /** Returns the host element (e.g. ore-popover) so slotted content clicks aren't treated as outside clicks. */\n getHost?: () => Element | null;\n /** Returns the panel element, if mounted. */\n getPanel: () => HTMLElement | null;\n /** Gap from reference to floating panel in px, read on every position update. Default: 8. */\n offset?: () => number | undefined;\n /** Cleanup registrar from the component setup ctx. Automatically called on disconnect. */\n onCleanup: (fn: () => void) => void;\n /** Called when the popover closes. */\n onClose?: (reason: DialogCloseReason) => void;\n /** Called when the popover opens. */\n onOpen?: (reason: OverlayOpenReason) => void;\n /** Callback when resolved placement changes (useful for CSS attribute). */\n onPlacementChange?: (placement: Placement) => void;\n /** Controlled open prop. When defined, disables uncontrolled logic. */\n openProp: Readable<boolean | undefined>;\n /** Preferred placement. Default: 'bottom'. */\n placement: Readable<Placement>;\n /** Resolves the trigger slot element. Called each time events are (re)bound. */\n slot: () => HTMLSlotElement | null;\n /** Slot elements signal: used to rebind when slotted elements change. */\n slotElements: Readable<Element[]>;\n /** Which triggers are active. Set to empty array or omit if handling events manually. */\n triggers: Readable<FloatingTriggerType[]>;\n};\n\nexport type FloatingTriggerHandle = {\n /** Closes the panel or requests that a controlled consumer close it. */\n close: (reason?: DialogCloseReason) => void;\n /**\n * Call inside `onMounted`. Sets up slot + trigger event watchers\n * and returns a cleanup function to pass back to the framework.\n */\n mount: () => () => void;\n /** Opens the panel or requests that a controlled consumer open it. */\n open: (reason?: OverlayOpenReason) => void;\n /** Toggles the panel or requests the matching controlled-state change. */\n toggle: () => void;\n /** Manually trigger a position recalculation. */\n updatePosition: () => void;\n /** Whether the panel is currently visible. */\n visible: Signal<boolean>;\n};\n\n// ── Implementation ─────────────────────────────────────────────────────────────\n\n/**\n * Shared logic for floating-panel triggers (tooltip, popover).\n *\n * Handles:\n * - Slot-based trigger element detection and rebinding\n * - Event binding per trigger type (hover / focus / click)\n * - Floating-element positioning via `@vielzeug/orbit`\n * - Controlled mode via `openProp` watcher\n * - Keyboard escape dismissal\n *\n * ARIA binding is intentionally delegated to the caller via `bindTriggerAria`\n * so that tooltip (`describedby`) and popover (`expanded`/`controls`/…) can diverge.\n */\nexport const useFloatingTrigger = (options: FloatingTriggerOptions): FloatingTriggerHandle => {\n const {\n bindTriggerAria,\n defaultOpen,\n disabled,\n getPanel,\n onClose,\n onOpen,\n onPlacementChange,\n openProp,\n placement,\n slot,\n slotElements,\n triggers,\n } = options;\n\n const getOffset = (): number => options.offset?.() ?? 8;\n const abortSignal = lifecycleSignal(options.onCleanup);\n const visible = signal(false);\n const isControlled = () => openProp.value !== undefined;\n let currentTrigger: HTMLElement | null = null;\n let triggerBinding: (() => void) | null = null;\n\n const resolveTrigger = (): HTMLElement | null =>\n currentTrigger ?? (slot()?.assignedElements({ flatten: true })[0] as HTMLElement | undefined) ?? null;\n\n // The same Orbit positioner the dropdown overlays use: RTL placement mirroring, the\n // containing-block self-correction, and flip/shift, so popover/tooltip no longer carry a second,\n // divergent copy of the positioning logic. Panels render in the Popover API top layer (no\n // clipping ancestor) and size themselves (no width matching), matching `ore-menu`'s config.\n const positioner = createDropdownPositioner({\n getFloating: getPanel,\n getOffsetPx: getOffset,\n getPlacement: () => placement.value,\n getReference: resolveTrigger,\n matchWidth: false,\n onPlacementChange: (resolved) => {\n const panel = getPanel();\n\n if (panel) panel.dataset.placement = resolved;\n\n onPlacementChange?.(resolved);\n },\n padding: 8,\n useClippingAncestor: false,\n });\n\n function updatePosition(): void {\n positioner.update();\n }\n\n function showFloat(): void {\n visible.value = true;\n\n const panel = getPanel();\n\n if (panel) {\n panel.dataset.open = '';\n\n if ('showPopover' in panel) {\n try {\n if (!panel.matches(':popover-open')) panel.showPopover();\n } catch {\n // Popover API restricted (e.g. sandboxed iframe); data-open CSS fallback handles visibility.\n }\n }\n }\n\n updatePosition();\n }\n\n function hideFloat(): void {\n visible.value = false;\n\n const panel = getPanel();\n\n if (panel) {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // Popover API restricted; data-open removal handles visibility.\n }\n }\n }\n }\n\n function open(reason: OverlayOpenReason = 'programmatic'): void {\n if (disabled.value || visible.value) return;\n if (isControlled()) {\n onOpen?.(reason);\n return;\n }\n\n showFloat();\n onOpen?.(reason);\n }\n\n function close(reason: DialogCloseReason = 'trigger'): void {\n if (!visible.value) return;\n if (isControlled()) {\n onClose?.(reason);\n return;\n }\n\n hideFloat();\n onClose?.(reason);\n }\n\n function toggle(): void {\n if (visible.value) close();\n else open('click');\n }\n\n function handleKeydown(e: KeyboardEvent): void {\n if (e.key === 'Escape') close('escape');\n }\n\n function handleFocusOut(e: FocusEvent): void {\n const next = e.relatedTarget as Element | null;\n const panel = getPanel();\n\n if (next && panel?.contains(next)) return;\n\n if (next && currentTrigger?.contains(next)) return;\n\n close('trigger');\n }\n\n const bindEvents = (): void => {\n triggerBinding?.();\n triggerBinding = null;\n\n const triggerEl = slot()?.assignedElements({ flatten: true })[0] as HTMLElement | undefined;\n\n if (!triggerEl) {\n currentTrigger = null;\n\n return;\n }\n\n currentTrigger = triggerEl;\n\n const removeAria = bindTriggerAria(triggerEl);\n const cleanups: Array<() => void> = [];\n\n const add = (\n target: EventTarget,\n event: string,\n listener: EventListener,\n eventOptions?: AddEventListenerOptions,\n ): void => {\n target.addEventListener(event, listener, eventOptions);\n cleanups.push(() => target.removeEventListener(event, listener, eventOptions));\n };\n\n const t = triggers.value;\n\n if (t.includes('click')) {\n add(triggerEl, 'click', toggle as EventListener);\n }\n\n if (t.includes('hover')) {\n add(triggerEl, 'pointerenter', () => open('hover'));\n add(triggerEl, 'pointerleave', () => close('trigger'));\n\n const panelEl = getPanel();\n\n if (panelEl) {\n add(panelEl, 'pointerenter', () => open('hover'));\n add(panelEl, 'pointerleave', () => close('trigger'));\n }\n }\n\n if (t.includes('focus')) {\n add(triggerEl, 'focusin', () => open('focus'));\n add(triggerEl, 'focusout', handleFocusOut as EventListener);\n\n const panelEl = getPanel();\n\n if (panelEl) add(panelEl, 'focusout', handleFocusOut as EventListener);\n }\n\n add(document, 'keydown', handleKeydown as EventListener);\n\n triggerBinding = () => {\n removeAria();\n\n for (const cleanup of cleanups) cleanup();\n\n currentTrigger = null;\n };\n };\n\n const stopOutsidePointerDismissal = createOutsidePointerDismissal({\n getTargets: () => [currentTrigger, getPanel(), options.getHost?.()],\n isActive: () => visible.value && triggers.value.includes('click'),\n onDismiss: () => close('outsideClick'),\n signal: abortSignal,\n });\n\n const mount = (): (() => void) => {\n let initializedOpenProp = false;\n const triggerSlot = slot();\n const rebind = (): void => {\n queueMicrotask(() => {\n if (!abortSignal.aborted) bindEvents();\n });\n };\n\n triggerSlot?.addEventListener('slotchange', rebind);\n rebind();\n watch(slotElements, bindEvents, { immediate: true });\n\n watch(\n openProp,\n (openVal) => {\n if (openVal === undefined || openVal === null) {\n if (initializedOpenProp && visible.value) {\n hideFloat();\n onClose?.('programmatic');\n }\n\n initializedOpenProp = true;\n\n return;\n }\n\n initializedOpenProp = true;\n\n if (openVal) showFloat();\n else if (visible.value) hideFloat();\n },\n { immediate: true },\n );\n\n if (openProp.value === undefined && defaultOpen.value) {\n showFloat();\n onOpen?.('programmatic');\n }\n\n watch(disabled, (isNowDisabled) => {\n if (isNowDisabled) close('programmatic');\n });\n\n return () => {\n triggerSlot?.removeEventListener('slotchange', rebind);\n triggerBinding?.();\n triggerBinding = null;\n\n const panel = getPanel();\n\n if (panel) {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // Popover API restricted; data-open removal handles visibility.\n }\n }\n }\n\n stopOutsidePointerDismissal();\n };\n };\n\n return { close, mount, open, toggle, updatePosition, visible };\n};\n"],"mappings":"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"}
|
|
@@ -30,7 +30,7 @@ export type FloatingTriggerOptions = {
|
|
|
30
30
|
placement: Readable<Placement>;
|
|
31
31
|
/** Resolves the trigger slot element. Called each time events are (re)bound. */
|
|
32
32
|
slot: () => HTMLSlotElement | null;
|
|
33
|
-
/** Slot elements signal
|
|
33
|
+
/** Slot elements signal: used to rebind when slotted elements change. */
|
|
34
34
|
slotElements: Readable<Element[]>;
|
|
35
35
|
/** Which triggers are active. Set to empty array or omit if handling events manually. */
|
|
36
36
|
triggers: Readable<FloatingTriggerType[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-floating-trigger.d.ts","sourceRoot":"","sources":["../../../src/overlay/shared/use-floating-trigger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAiB,MAAM,kBAAkB,CAAC;AAE7E,OAAO,EAGL,KAAK,iBAAiB,EAEtB,KAAK,iBAAiB,EACvB,MAAM,YAAY,CAAC;AAIpB,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAE9D,MAAM,MAAM,UAAU,GAAG,CAAC,SAAS,EAAE,WAAW,KAAK,MAAM,IAAI,CAAC;AAEhE,MAAM,MAAM,sBAAsB,GAAG;IACnC,gFAAgF;IAChF,eAAe,EAAE,UAAU,CAAC;IAC5B,2EAA2E;IAC3E,WAAW,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IAC3C,mEAAmE;IACnE,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC5B,8GAA8G;IAC9G,OAAO,CAAC,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC;IAC/B,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACnC,6FAA6F;IAC7F,MAAM,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAClC,0FAA0F;IAC1F,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IACpC,sCAAsC;IACtC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC9C,qCAAqC;IACrC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC7C,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC;IACnD,uEAAuE;IACvE,QAAQ,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACxC,8CAA8C;IAC9C,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC/B,gFAAgF;IAChF,IAAI,EAAE,MAAM,eAAe,GAAG,IAAI,CAAC;IACnC,
|
|
1
|
+
{"version":3,"file":"use-floating-trigger.d.ts","sourceRoot":"","sources":["../../../src/overlay/shared/use-floating-trigger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAiB,MAAM,kBAAkB,CAAC;AAE7E,OAAO,EAGL,KAAK,iBAAiB,EAEtB,KAAK,iBAAiB,EACvB,MAAM,YAAY,CAAC;AAIpB,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAE9D,MAAM,MAAM,UAAU,GAAG,CAAC,SAAS,EAAE,WAAW,KAAK,MAAM,IAAI,CAAC;AAEhE,MAAM,MAAM,sBAAsB,GAAG;IACnC,gFAAgF;IAChF,eAAe,EAAE,UAAU,CAAC;IAC5B,2EAA2E;IAC3E,WAAW,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IAC3C,mEAAmE;IACnE,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC5B,8GAA8G;IAC9G,OAAO,CAAC,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC;IAC/B,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACnC,6FAA6F;IAC7F,MAAM,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAClC,0FAA0F;IAC1F,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IACpC,sCAAsC;IACtC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC9C,qCAAqC;IACrC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC7C,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC;IACnD,uEAAuE;IACvE,QAAQ,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACxC,8CAA8C;IAC9C,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC/B,gFAAgF;IAChF,IAAI,EAAE,MAAM,eAAe,GAAG,IAAI,CAAC;IACnC,yEAAyE;IACzE,YAAY,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IAClC,yFAAyF;IACzF,QAAQ,EAAE,QAAQ,CAAC,mBAAmB,EAAE,CAAC,CAAC;CAC3C,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,wEAAwE;IACxE,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC5C;;;OAGG;IACH,KAAK,EAAE,MAAM,MAAM,IAAI,CAAC;IACxB,sEAAsE;IACtE,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC3C,0EAA0E;IAC1E,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,iDAAiD;IACjD,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;CAC1B,CAAC;AAIF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,sBAAsB,KAAG,qBA+QpE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-floating-trigger.js","names":[],"sources":["../../../src/overlay/shared/use-floating-trigger.ts"],"sourcesContent":["import type { Placement } from '@vielzeug/orbit';\n\nimport { type Readable, type Signal, signal, watch } from '@vielzeug/ripple';\n\nimport {\n createDropdownPositioner,\n createOutsidePointerDismissal,\n type DialogCloseReason,\n lifecycleSignal,\n type OverlayOpenReason,\n} from '../../core';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type FloatingTriggerType = 'click' | 'focus' | 'hover';\n\nexport type AriaBindFn = (triggerEl: HTMLElement) => () => void;\n\nexport type FloatingTriggerOptions = {\n /** ARIA binding factory: called with the trigger element, returns a cleanup. */\n bindTriggerAria: AriaBindFn;\n /** Initial uncontrolled visibility. Ignored when `openProp` is defined. */\n defaultOpen: Readable<boolean | undefined>;\n /** If true, disable all trigger interactions and close if open. */\n disabled: Readable<boolean>;\n /** Returns the host element (e.g. ore-popover) so slotted content clicks aren't treated as outside clicks. */\n getHost?: () => Element | null;\n /** Returns the panel element, if mounted. */\n getPanel: () => HTMLElement | null;\n /** Gap from reference to floating panel in px, read on every position update. Default: 8. */\n offset?: () => number | undefined;\n /** Cleanup registrar from the component setup ctx. Automatically called on disconnect. */\n onCleanup: (fn: () => void) => void;\n /** Called when the popover closes. */\n onClose?: (reason: DialogCloseReason) => void;\n /** Called when the popover opens. */\n onOpen?: (reason: OverlayOpenReason) => void;\n /** Callback when resolved placement changes (useful for CSS attribute). */\n onPlacementChange?: (placement: Placement) => void;\n /** Controlled open prop. When defined, disables uncontrolled logic. */\n openProp: Readable<boolean | undefined>;\n /** Preferred placement. Default: 'bottom'. */\n placement: Readable<Placement>;\n /** Resolves the trigger slot element. Called each time events are (re)bound. */\n slot: () => HTMLSlotElement | null;\n /** Slot elements signal — used to rebind when slotted elements change. */\n slotElements: Readable<Element[]>;\n /** Which triggers are active. Set to empty array or omit if handling events manually. */\n triggers: Readable<FloatingTriggerType[]>;\n};\n\nexport type FloatingTriggerHandle = {\n /** Closes the panel or requests that a controlled consumer close it. */\n close: (reason?: DialogCloseReason) => void;\n /**\n * Call inside `onMounted`. Sets up slot + trigger event watchers\n * and returns a cleanup function to pass back to the framework.\n */\n mount: () => () => void;\n /** Opens the panel or requests that a controlled consumer open it. */\n open: (reason?: OverlayOpenReason) => void;\n /** Toggles the panel or requests the matching controlled-state change. */\n toggle: () => void;\n /** Manually trigger a position recalculation. */\n updatePosition: () => void;\n /** Whether the panel is currently visible. */\n visible: Signal<boolean>;\n};\n\n// ── Implementation ─────────────────────────────────────────────────────────────\n\n/**\n * Shared logic for floating-panel triggers (tooltip, popover).\n *\n * Handles:\n * - Slot-based trigger element detection and rebinding\n * - Event binding per trigger type (hover / focus / click)\n * - Floating-element positioning via `@vielzeug/orbit`\n * - Controlled mode via `openProp` watcher\n * - Keyboard escape dismissal\n *\n * ARIA binding is intentionally delegated to the caller via `bindTriggerAria`\n * so that tooltip (`describedby`) and popover (`expanded`/`controls`/…) can diverge.\n */\nexport const useFloatingTrigger = (options: FloatingTriggerOptions): FloatingTriggerHandle => {\n const {\n bindTriggerAria,\n defaultOpen,\n disabled,\n getPanel,\n onClose,\n onOpen,\n onPlacementChange,\n openProp,\n placement,\n slot,\n slotElements,\n triggers,\n } = options;\n\n const getOffset = (): number => options.offset?.() ?? 8;\n const abortSignal = lifecycleSignal(options.onCleanup);\n const visible = signal(false);\n const isControlled = () => openProp.value !== undefined;\n let currentTrigger: HTMLElement | null = null;\n let triggerBinding: (() => void) | null = null;\n\n const resolveTrigger = (): HTMLElement | null =>\n currentTrigger ?? (slot()?.assignedElements({ flatten: true })[0] as HTMLElement | undefined) ?? null;\n\n // The same Orbit positioner the dropdown overlays use — RTL placement mirroring, the\n // containing-block self-correction, and flip/shift, so popover/tooltip no longer carry a second,\n // divergent copy of the positioning logic. Panels render in the Popover API top layer (no\n // clipping ancestor) and size themselves (no width matching), matching `ore-menu`'s config.\n const positioner = createDropdownPositioner({\n getFloating: getPanel,\n getOffsetPx: getOffset,\n getPlacement: () => placement.value,\n getReference: resolveTrigger,\n matchWidth: false,\n onPlacementChange: (resolved) => {\n const panel = getPanel();\n\n if (panel) panel.dataset.placement = resolved;\n\n onPlacementChange?.(resolved);\n },\n padding: 8,\n useClippingAncestor: false,\n });\n\n function updatePosition(): void {\n positioner.update();\n }\n\n function showFloat(): void {\n visible.value = true;\n\n const panel = getPanel();\n\n if (panel) {\n panel.dataset.open = '';\n\n if ('showPopover' in panel) {\n try {\n if (!panel.matches(':popover-open')) panel.showPopover();\n } catch {\n // Popover API restricted (e.g. sandboxed iframe); data-open CSS fallback handles visibility.\n }\n }\n }\n\n updatePosition();\n }\n\n function hideFloat(): void {\n visible.value = false;\n\n const panel = getPanel();\n\n if (panel) {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // Popover API restricted; data-open removal handles visibility.\n }\n }\n }\n }\n\n function open(reason: OverlayOpenReason = 'programmatic'): void {\n if (disabled.value || visible.value) return;\n if (isControlled()) {\n onOpen?.(reason);\n return;\n }\n\n showFloat();\n onOpen?.(reason);\n }\n\n function close(reason: DialogCloseReason = 'trigger'): void {\n if (!visible.value) return;\n if (isControlled()) {\n onClose?.(reason);\n return;\n }\n\n hideFloat();\n onClose?.(reason);\n }\n\n function toggle(): void {\n if (visible.value) close();\n else open('click');\n }\n\n function handleKeydown(e: KeyboardEvent): void {\n if (e.key === 'Escape') close('escape');\n }\n\n function handleFocusOut(e: FocusEvent): void {\n const next = e.relatedTarget as Element | null;\n const panel = getPanel();\n\n if (next && panel?.contains(next)) return;\n\n if (next && currentTrigger?.contains(next)) return;\n\n close('trigger');\n }\n\n const bindEvents = (): void => {\n triggerBinding?.();\n triggerBinding = null;\n\n const triggerEl = slot()?.assignedElements({ flatten: true })[0] as HTMLElement | undefined;\n\n if (!triggerEl) {\n currentTrigger = null;\n\n return;\n }\n\n currentTrigger = triggerEl;\n\n const removeAria = bindTriggerAria(triggerEl);\n const cleanups: Array<() => void> = [];\n\n const add = (\n target: EventTarget,\n event: string,\n listener: EventListener,\n eventOptions?: AddEventListenerOptions,\n ): void => {\n target.addEventListener(event, listener, eventOptions);\n cleanups.push(() => target.removeEventListener(event, listener, eventOptions));\n };\n\n const t = triggers.value;\n\n if (t.includes('click')) {\n add(triggerEl, 'click', toggle as EventListener);\n }\n\n if (t.includes('hover')) {\n add(triggerEl, 'pointerenter', () => open('hover'));\n add(triggerEl, 'pointerleave', () => close('trigger'));\n\n const panelEl = getPanel();\n\n if (panelEl) {\n add(panelEl, 'pointerenter', () => open('hover'));\n add(panelEl, 'pointerleave', () => close('trigger'));\n }\n }\n\n if (t.includes('focus')) {\n add(triggerEl, 'focusin', () => open('focus'));\n add(triggerEl, 'focusout', handleFocusOut as EventListener);\n\n const panelEl = getPanel();\n\n if (panelEl) add(panelEl, 'focusout', handleFocusOut as EventListener);\n }\n\n add(document, 'keydown', handleKeydown as EventListener);\n\n triggerBinding = () => {\n removeAria();\n\n for (const cleanup of cleanups) cleanup();\n\n currentTrigger = null;\n };\n };\n\n const stopOutsidePointerDismissal = createOutsidePointerDismissal({\n getTargets: () => [currentTrigger, getPanel(), options.getHost?.()],\n isActive: () => visible.value && triggers.value.includes('click'),\n onDismiss: () => close('outsideClick'),\n signal: abortSignal,\n });\n\n const mount = (): (() => void) => {\n let initializedOpenProp = false;\n const triggerSlot = slot();\n const rebind = (): void => {\n queueMicrotask(() => {\n if (!abortSignal.aborted) bindEvents();\n });\n };\n\n triggerSlot?.addEventListener('slotchange', rebind);\n rebind();\n watch(slotElements, bindEvents, { immediate: true });\n\n watch(\n openProp,\n (openVal) => {\n if (openVal === undefined || openVal === null) {\n if (initializedOpenProp && visible.value) {\n hideFloat();\n onClose?.('programmatic');\n }\n\n initializedOpenProp = true;\n\n return;\n }\n\n initializedOpenProp = true;\n\n if (openVal) showFloat();\n else if (visible.value) hideFloat();\n },\n { immediate: true },\n );\n\n if (openProp.value === undefined && defaultOpen.value) {\n showFloat();\n onOpen?.('programmatic');\n }\n\n watch(disabled, (isNowDisabled) => {\n if (isNowDisabled) close('programmatic');\n });\n\n return () => {\n triggerSlot?.removeEventListener('slotchange', rebind);\n triggerBinding?.();\n triggerBinding = null;\n\n const panel = getPanel();\n\n if (panel) {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // Popover API restricted; data-open removal handles visibility.\n }\n }\n }\n\n stopOutsidePointerDismissal();\n };\n };\n\n return { close, mount, open, toggle, updatePosition, visible };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-floating-trigger.js","names":[],"sources":["../../../src/overlay/shared/use-floating-trigger.ts"],"sourcesContent":["import type { Placement } from '@vielzeug/orbit';\n\nimport { type Readable, type Signal, signal, watch } from '@vielzeug/ripple';\n\nimport {\n createDropdownPositioner,\n createOutsidePointerDismissal,\n type DialogCloseReason,\n lifecycleSignal,\n type OverlayOpenReason,\n} from '../../core';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type FloatingTriggerType = 'click' | 'focus' | 'hover';\n\nexport type AriaBindFn = (triggerEl: HTMLElement) => () => void;\n\nexport type FloatingTriggerOptions = {\n /** ARIA binding factory: called with the trigger element, returns a cleanup. */\n bindTriggerAria: AriaBindFn;\n /** Initial uncontrolled visibility. Ignored when `openProp` is defined. */\n defaultOpen: Readable<boolean | undefined>;\n /** If true, disable all trigger interactions and close if open. */\n disabled: Readable<boolean>;\n /** Returns the host element (e.g. ore-popover) so slotted content clicks aren't treated as outside clicks. */\n getHost?: () => Element | null;\n /** Returns the panel element, if mounted. */\n getPanel: () => HTMLElement | null;\n /** Gap from reference to floating panel in px, read on every position update. Default: 8. */\n offset?: () => number | undefined;\n /** Cleanup registrar from the component setup ctx. Automatically called on disconnect. */\n onCleanup: (fn: () => void) => void;\n /** Called when the popover closes. */\n onClose?: (reason: DialogCloseReason) => void;\n /** Called when the popover opens. */\n onOpen?: (reason: OverlayOpenReason) => void;\n /** Callback when resolved placement changes (useful for CSS attribute). */\n onPlacementChange?: (placement: Placement) => void;\n /** Controlled open prop. When defined, disables uncontrolled logic. */\n openProp: Readable<boolean | undefined>;\n /** Preferred placement. Default: 'bottom'. */\n placement: Readable<Placement>;\n /** Resolves the trigger slot element. Called each time events are (re)bound. */\n slot: () => HTMLSlotElement | null;\n /** Slot elements signal: used to rebind when slotted elements change. */\n slotElements: Readable<Element[]>;\n /** Which triggers are active. Set to empty array or omit if handling events manually. */\n triggers: Readable<FloatingTriggerType[]>;\n};\n\nexport type FloatingTriggerHandle = {\n /** Closes the panel or requests that a controlled consumer close it. */\n close: (reason?: DialogCloseReason) => void;\n /**\n * Call inside `onMounted`. Sets up slot + trigger event watchers\n * and returns a cleanup function to pass back to the framework.\n */\n mount: () => () => void;\n /** Opens the panel or requests that a controlled consumer open it. */\n open: (reason?: OverlayOpenReason) => void;\n /** Toggles the panel or requests the matching controlled-state change. */\n toggle: () => void;\n /** Manually trigger a position recalculation. */\n updatePosition: () => void;\n /** Whether the panel is currently visible. */\n visible: Signal<boolean>;\n};\n\n// ── Implementation ─────────────────────────────────────────────────────────────\n\n/**\n * Shared logic for floating-panel triggers (tooltip, popover).\n *\n * Handles:\n * - Slot-based trigger element detection and rebinding\n * - Event binding per trigger type (hover / focus / click)\n * - Floating-element positioning via `@vielzeug/orbit`\n * - Controlled mode via `openProp` watcher\n * - Keyboard escape dismissal\n *\n * ARIA binding is intentionally delegated to the caller via `bindTriggerAria`\n * so that tooltip (`describedby`) and popover (`expanded`/`controls`/…) can diverge.\n */\nexport const useFloatingTrigger = (options: FloatingTriggerOptions): FloatingTriggerHandle => {\n const {\n bindTriggerAria,\n defaultOpen,\n disabled,\n getPanel,\n onClose,\n onOpen,\n onPlacementChange,\n openProp,\n placement,\n slot,\n slotElements,\n triggers,\n } = options;\n\n const getOffset = (): number => options.offset?.() ?? 8;\n const abortSignal = lifecycleSignal(options.onCleanup);\n const visible = signal(false);\n const isControlled = () => openProp.value !== undefined;\n let currentTrigger: HTMLElement | null = null;\n let triggerBinding: (() => void) | null = null;\n\n const resolveTrigger = (): HTMLElement | null =>\n currentTrigger ?? (slot()?.assignedElements({ flatten: true })[0] as HTMLElement | undefined) ?? null;\n\n // The same Orbit positioner the dropdown overlays use: RTL placement mirroring, the\n // containing-block self-correction, and flip/shift, so popover/tooltip no longer carry a second,\n // divergent copy of the positioning logic. Panels render in the Popover API top layer (no\n // clipping ancestor) and size themselves (no width matching), matching `ore-menu`'s config.\n const positioner = createDropdownPositioner({\n getFloating: getPanel,\n getOffsetPx: getOffset,\n getPlacement: () => placement.value,\n getReference: resolveTrigger,\n matchWidth: false,\n onPlacementChange: (resolved) => {\n const panel = getPanel();\n\n if (panel) panel.dataset.placement = resolved;\n\n onPlacementChange?.(resolved);\n },\n padding: 8,\n useClippingAncestor: false,\n });\n\n function updatePosition(): void {\n positioner.update();\n }\n\n function showFloat(): void {\n visible.value = true;\n\n const panel = getPanel();\n\n if (panel) {\n panel.dataset.open = '';\n\n if ('showPopover' in panel) {\n try {\n if (!panel.matches(':popover-open')) panel.showPopover();\n } catch {\n // Popover API restricted (e.g. sandboxed iframe); data-open CSS fallback handles visibility.\n }\n }\n }\n\n updatePosition();\n }\n\n function hideFloat(): void {\n visible.value = false;\n\n const panel = getPanel();\n\n if (panel) {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // Popover API restricted; data-open removal handles visibility.\n }\n }\n }\n }\n\n function open(reason: OverlayOpenReason = 'programmatic'): void {\n if (disabled.value || visible.value) return;\n if (isControlled()) {\n onOpen?.(reason);\n return;\n }\n\n showFloat();\n onOpen?.(reason);\n }\n\n function close(reason: DialogCloseReason = 'trigger'): void {\n if (!visible.value) return;\n if (isControlled()) {\n onClose?.(reason);\n return;\n }\n\n hideFloat();\n onClose?.(reason);\n }\n\n function toggle(): void {\n if (visible.value) close();\n else open('click');\n }\n\n function handleKeydown(e: KeyboardEvent): void {\n if (e.key === 'Escape') close('escape');\n }\n\n function handleFocusOut(e: FocusEvent): void {\n const next = e.relatedTarget as Element | null;\n const panel = getPanel();\n\n if (next && panel?.contains(next)) return;\n\n if (next && currentTrigger?.contains(next)) return;\n\n close('trigger');\n }\n\n const bindEvents = (): void => {\n triggerBinding?.();\n triggerBinding = null;\n\n const triggerEl = slot()?.assignedElements({ flatten: true })[0] as HTMLElement | undefined;\n\n if (!triggerEl) {\n currentTrigger = null;\n\n return;\n }\n\n currentTrigger = triggerEl;\n\n const removeAria = bindTriggerAria(triggerEl);\n const cleanups: Array<() => void> = [];\n\n const add = (\n target: EventTarget,\n event: string,\n listener: EventListener,\n eventOptions?: AddEventListenerOptions,\n ): void => {\n target.addEventListener(event, listener, eventOptions);\n cleanups.push(() => target.removeEventListener(event, listener, eventOptions));\n };\n\n const t = triggers.value;\n\n if (t.includes('click')) {\n add(triggerEl, 'click', toggle as EventListener);\n }\n\n if (t.includes('hover')) {\n add(triggerEl, 'pointerenter', () => open('hover'));\n add(triggerEl, 'pointerleave', () => close('trigger'));\n\n const panelEl = getPanel();\n\n if (panelEl) {\n add(panelEl, 'pointerenter', () => open('hover'));\n add(panelEl, 'pointerleave', () => close('trigger'));\n }\n }\n\n if (t.includes('focus')) {\n add(triggerEl, 'focusin', () => open('focus'));\n add(triggerEl, 'focusout', handleFocusOut as EventListener);\n\n const panelEl = getPanel();\n\n if (panelEl) add(panelEl, 'focusout', handleFocusOut as EventListener);\n }\n\n add(document, 'keydown', handleKeydown as EventListener);\n\n triggerBinding = () => {\n removeAria();\n\n for (const cleanup of cleanups) cleanup();\n\n currentTrigger = null;\n };\n };\n\n const stopOutsidePointerDismissal = createOutsidePointerDismissal({\n getTargets: () => [currentTrigger, getPanel(), options.getHost?.()],\n isActive: () => visible.value && triggers.value.includes('click'),\n onDismiss: () => close('outsideClick'),\n signal: abortSignal,\n });\n\n const mount = (): (() => void) => {\n let initializedOpenProp = false;\n const triggerSlot = slot();\n const rebind = (): void => {\n queueMicrotask(() => {\n if (!abortSignal.aborted) bindEvents();\n });\n };\n\n triggerSlot?.addEventListener('slotchange', rebind);\n rebind();\n watch(slotElements, bindEvents, { immediate: true });\n\n watch(\n openProp,\n (openVal) => {\n if (openVal === undefined || openVal === null) {\n if (initializedOpenProp && visible.value) {\n hideFloat();\n onClose?.('programmatic');\n }\n\n initializedOpenProp = true;\n\n return;\n }\n\n initializedOpenProp = true;\n\n if (openVal) showFloat();\n else if (visible.value) hideFloat();\n },\n { immediate: true },\n );\n\n if (openProp.value === undefined && defaultOpen.value) {\n showFloat();\n onOpen?.('programmatic');\n }\n\n watch(disabled, (isNowDisabled) => {\n if (isNowDisabled) close('programmatic');\n });\n\n return () => {\n triggerSlot?.removeEventListener('slotchange', rebind);\n triggerBinding?.();\n triggerBinding = null;\n\n const panel = getPanel();\n\n if (panel) {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // Popover API restricted; data-open removal handles visibility.\n }\n }\n }\n\n stopOutsidePointerDismissal();\n };\n };\n\n return { close, mount, open, toggle, updatePosition, visible };\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tooltip.css.cjs","names":[],"sources":["../../../src/overlay/tooltip/tooltip.css?inline"],"sourcesContent":["/* ========================================\n Popover UA resets
|
|
1
|
+
{"version":3,"file":"tooltip.css.cjs","names":[],"sources":["../../../src/overlay/tooltip/tooltip.css?inline"],"sourcesContent":["/* ========================================\n Popover UA resets: unlayered so they unconditionally beat\n the browser's [popover] UA stylesheet (padding / inset / border / margin).\n Must NOT be inside @layer; unlayered author rules win over every layer and\n every UA default.\n ======================================== */\n.tooltip {\n inset: unset;\n padding: calc(var(--size-0-5) / 2) var(--size-2) var(--size-0-5);\n margin: 0;\n overflow: visible;\n border: var(--_tooltip-border-width, var(--border-0)) solid var(--_tooltip-border-color, transparent);\n transform-origin: center;\n}\n\n@layer refine.base {\n :host {\n position: relative;\n display: inline-block;\n }\n\n .tooltip {\n --_tooltip-fg: var(--color-contrast-100);\n --_tooltip-bg: color-mix(in oklch, var(--color-contrast-900) 80%, transparent);\n --_tooltip-border-width: var(--border);\n --_tooltip-border-color: color-mix(in oklch, var(--color-contrast-100) 12%, transparent);\n --_tooltip-shadow: var(--shadow-md);\n --_tooltip-closed-transform: translateY(4px) scale(0.98);\n\n /* Positioning: JS sets top/left inline */\n position: fixed;\n max-width: var(--tooltip-max-width, var(--size-72));\n font-size: var(--text-xs);\n font-weight: var(--font-medium);\n line-height: var(--leading-normal);\n color: var(--_tooltip-fg);\n word-break: auto-phrase;\n white-space: normal;\n pointer-events: none;\n background: var(--_tooltip-bg);\n border-radius: var(--tooltip-radius, var(--rounded-lg));\n box-shadow: var(--_tooltip-shadow);\n\n /* Hidden by default (also serves as exit state) */\n opacity: 0;\n backdrop-filter: blur(var(--blur-sm));\n transform: var(--_tooltip-closed-transform);\n transition:\n opacity var(--transition-fast),\n transform var(--transition-fast),\n display var(--transition-fast) allow-discrete,\n overlay var(--transition-fast) allow-discrete;\n }\n\n @media (prefers-color-scheme: dark) {\n .tooltip {\n --_tooltip-fg: var(--color-contrast-900);\n --_tooltip-bg: color-mix(in oklch, var(--color-contrast-100) 80%, transparent);\n --_tooltip-border-color: color-mix(in oklch, var(--color-contrast-900) 12%, transparent);\n }\n }\n\n .tooltip-text {\n display: block;\n white-space: pre-line;\n }\n\n .tooltip[data-placement='top'] {\n --_tooltip-closed-transform: translateY(4px) scale(var(--overlay-enter-scale-dialog));\n }\n\n .tooltip[data-placement='bottom'] {\n --_tooltip-closed-transform: translateY(-4px) scale(var(--overlay-enter-scale-dialog));\n }\n\n .tooltip[data-placement='left'] {\n --_tooltip-closed-transform: translateX(4px) scale(var(--overlay-enter-scale-dialog));\n }\n\n .tooltip[data-placement='right'] {\n --_tooltip-closed-transform: translateX(-4px) scale(var(--overlay-enter-scale-dialog));\n }\n\n .tooltip[data-open] {\n display: block;\n }\n\n /* Entry animation via @starting-style */\n .tooltip:popover-open,\n .tooltip[data-open] {\n opacity: 1;\n transform: translate(0, 0) scale(1);\n transition:\n opacity var(--transition-normal),\n transform var(--transition-spring),\n display var(--transition-normal) allow-discrete,\n overlay var(--transition-normal) allow-discrete;\n will-change: opacity, transform;\n\n @starting-style {\n opacity: 0;\n transform: var(--_tooltip-closed-transform);\n }\n }\n}\n\n@layer refine.variants {\n /* Light tooltip */\n :host([variant='light']) .tooltip {\n --_tooltip-fg: var(--color-contrast-900);\n --_tooltip-bg: color-mix(in oklch, var(--color-contrast-100) 80%, transparent);\n --_tooltip-border-color: color-mix(in oklch, var(--color-contrast-900) 12%, transparent);\n --_tooltip-shadow: var(--shadow-md);\n }\n\n @media (prefers-color-scheme: dark) {\n :host([variant='light']) .tooltip {\n --_tooltip-fg: var(--color-contrast-100);\n --_tooltip-bg: color-mix(in oklch, var(--color-contrast-900) 80%, transparent);\n --_tooltip-border-color: color-mix(in oklch, var(--color-contrast-100) 12%, transparent);\n }\n }\n}\n\n@layer refine.utilities {\n :host([size='sm']) .tooltip {\n font-size: var(--text-xs);\n }\n\n :host([size='lg']) .tooltip {\n font-size: var(--text-base);\n }\n}\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tooltip.css.js","names":[],"sources":["../../../src/overlay/tooltip/tooltip.css?inline"],"sourcesContent":["/* ========================================\n Popover UA resets
|
|
1
|
+
{"version":3,"file":"tooltip.css.js","names":[],"sources":["../../../src/overlay/tooltip/tooltip.css?inline"],"sourcesContent":["/* ========================================\n Popover UA resets: unlayered so they unconditionally beat\n the browser's [popover] UA stylesheet (padding / inset / border / margin).\n Must NOT be inside @layer; unlayered author rules win over every layer and\n every UA default.\n ======================================== */\n.tooltip {\n inset: unset;\n padding: calc(var(--size-0-5) / 2) var(--size-2) var(--size-0-5);\n margin: 0;\n overflow: visible;\n border: var(--_tooltip-border-width, var(--border-0)) solid var(--_tooltip-border-color, transparent);\n transform-origin: center;\n}\n\n@layer refine.base {\n :host {\n position: relative;\n display: inline-block;\n }\n\n .tooltip {\n --_tooltip-fg: var(--color-contrast-100);\n --_tooltip-bg: color-mix(in oklch, var(--color-contrast-900) 80%, transparent);\n --_tooltip-border-width: var(--border);\n --_tooltip-border-color: color-mix(in oklch, var(--color-contrast-100) 12%, transparent);\n --_tooltip-shadow: var(--shadow-md);\n --_tooltip-closed-transform: translateY(4px) scale(0.98);\n\n /* Positioning: JS sets top/left inline */\n position: fixed;\n max-width: var(--tooltip-max-width, var(--size-72));\n font-size: var(--text-xs);\n font-weight: var(--font-medium);\n line-height: var(--leading-normal);\n color: var(--_tooltip-fg);\n word-break: auto-phrase;\n white-space: normal;\n pointer-events: none;\n background: var(--_tooltip-bg);\n border-radius: var(--tooltip-radius, var(--rounded-lg));\n box-shadow: var(--_tooltip-shadow);\n\n /* Hidden by default (also serves as exit state) */\n opacity: 0;\n backdrop-filter: blur(var(--blur-sm));\n transform: var(--_tooltip-closed-transform);\n transition:\n opacity var(--transition-fast),\n transform var(--transition-fast),\n display var(--transition-fast) allow-discrete,\n overlay var(--transition-fast) allow-discrete;\n }\n\n @media (prefers-color-scheme: dark) {\n .tooltip {\n --_tooltip-fg: var(--color-contrast-900);\n --_tooltip-bg: color-mix(in oklch, var(--color-contrast-100) 80%, transparent);\n --_tooltip-border-color: color-mix(in oklch, var(--color-contrast-900) 12%, transparent);\n }\n }\n\n .tooltip-text {\n display: block;\n white-space: pre-line;\n }\n\n .tooltip[data-placement='top'] {\n --_tooltip-closed-transform: translateY(4px) scale(var(--overlay-enter-scale-dialog));\n }\n\n .tooltip[data-placement='bottom'] {\n --_tooltip-closed-transform: translateY(-4px) scale(var(--overlay-enter-scale-dialog));\n }\n\n .tooltip[data-placement='left'] {\n --_tooltip-closed-transform: translateX(4px) scale(var(--overlay-enter-scale-dialog));\n }\n\n .tooltip[data-placement='right'] {\n --_tooltip-closed-transform: translateX(-4px) scale(var(--overlay-enter-scale-dialog));\n }\n\n .tooltip[data-open] {\n display: block;\n }\n\n /* Entry animation via @starting-style */\n .tooltip:popover-open,\n .tooltip[data-open] {\n opacity: 1;\n transform: translate(0, 0) scale(1);\n transition:\n opacity var(--transition-normal),\n transform var(--transition-spring),\n display var(--transition-normal) allow-discrete,\n overlay var(--transition-normal) allow-discrete;\n will-change: opacity, transform;\n\n @starting-style {\n opacity: 0;\n transform: var(--_tooltip-closed-transform);\n }\n }\n}\n\n@layer refine.variants {\n /* Light tooltip */\n :host([variant='light']) .tooltip {\n --_tooltip-fg: var(--color-contrast-900);\n --_tooltip-bg: color-mix(in oklch, var(--color-contrast-100) 80%, transparent);\n --_tooltip-border-color: color-mix(in oklch, var(--color-contrast-900) 12%, transparent);\n --_tooltip-shadow: var(--shadow-md);\n }\n\n @media (prefers-color-scheme: dark) {\n :host([variant='light']) .tooltip {\n --_tooltip-fg: var(--color-contrast-100);\n --_tooltip-bg: color-mix(in oklch, var(--color-contrast-900) 80%, transparent);\n --_tooltip-border-color: color-mix(in oklch, var(--color-contrast-100) 12%, transparent);\n }\n }\n}\n\n@layer refine.utilities {\n :host([size='sm']) .tooltip {\n font-size: var(--text-xs);\n }\n\n :host([size='lg']) .tooltip {\n font-size: var(--text-base);\n }\n}\n"],"mappings":""}
|
package/dist/progress.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.cjs","names":[],"sources":["../src/feedback/progress/progress.ts"],"sourcesContent":["import { define, getHost, html, prop, styleMap, watchEffect } from '@vielzeug/ore';\nimport { computed } from '@vielzeug/ripple';\nimport { sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, forcedColorsMixin, reducedMotionMixin } from '../../styles';\nimport type { ComponentSize, ThemeColor } from '../../types';\nimport componentStyles from './progress.css?inline';\n\n/** Progress bar component properties */\nexport type OreProgressProps = {\n /** Theme color for the fill bar */\n color?: ThemeColor;\n /** Floating chip centered above the fill endpoint (linear only). Hidden in indeterminate mode. Position formula: left = fill% − half chip width (CSS: left:X%; transform:translateX(−50%)). */\n 'floating-label'?: string;\n /** When true, shows an infinite animation — use when progress is unknown. */\n indeterminate?: boolean;\n /** Accessible name AND visible text label.\n * - Linear without `title`: rendered at the end of the bar.\n * - Linear with `title`: moved into the header row above the bar.\n * - Circular: large text centered inside the ring. */\n label?: string;\n /** Maximum value. Defaults to 100. */\n max?: number;\n /** Split the linear bar into this many discrete blocks; blocks up to `value / max` are filled. Ignored when `indeterminate` or circular. */\n segments?: number;\n /** Size variant controlling bar height */\n size?: ComponentSize;\n /** Title text.\n * - Linear: displayed as a header above the bar; moves `label` into the header row.\n * - Circular: smaller text displayed below the `label` inside the ring. */\n title?: string;\n /** 'linear' (default), 'circular', or 'vertical' */\n type?: 'linear' | 'circular' | 'vertical';\n /** Current progress value (0 to `max`). Ignored when `indeterminate`. */\n value?: number;\n /** Human-readable value text for screen readers (e.g. \"Step 2 of 5\", \"75%\"). Overrides the raw aria-valuenow when set. */\n 'value-text'?: string;\n};\n\n/**\n * A linear progress bar for conveying operation progress.\n * Supports determinate (known value) and indeterminate (unknown duration) modes.\n *\n * @element ore-progress\n *\n * @attr {number} value - Current value (0–max). Defaults to 0.\n * @attr {number} max - Maximum value. Defaults to 100.\n * @attr {boolean} indeterminate - Show infinite animation (ignores value/max).\n * @attr {string} color - Theme color: 'primary' | 'success' | 'warning' | 'error' | …\n * @attr {string} size - Bar height/width: 'sm' | 'md' | 'lg'\n * @attr {string} type - 'linear' (default) | 'circular' | 'vertical'\n * @attr {number} segments - Split the linear bar into discrete blocks (tracks, steps, stages)\n *\n * @cssprop --progress-width - Bar width override (vertical only)\n * @attr {string} label - Visible text label + accessible name. Linear: at bar end (or header row with title). Circular: large text centered inside the ring.\n * @attr {string} title - Title text. Linear: header above the bar (moves label to header row). Circular: smaller text below the label inside the ring.\n * @attr {string} floating-label - Floating chip centered above the fill endpoint (linear only); hidden when indeterminate.\n *\n * @cssprop --progress-height - Bar height override\n * @cssprop --progress-track-bg - Track background color\n * @cssprop --progress-fill - Fill bar color\n * @cssprop --progress-radius - Border radius\n * @cssprop --progress-label-gap - Gap between header/bar row and between bar and trailing label (default 0.25 rem)\n * @cssprop --progress-title-color - Title text color (defaults to currentColor)\n * @cssprop --progress-label-color - Label text color (defaults to currentColor)\n * @cssprop --progress-circle-size - Circular indicator diameter (default 6rem)\n * @cssprop --progress-circular-label-size - Font size of the label inside the ring (default --text-xl)\n * @cssprop --progress-circular-title-size - Font size of the title inside the ring (default --text-xs)\n * @cssprop --progress-segment-gap - Gap between blocks when `segments` is set (default --size-1)\n *\n * @part fill - Progress fill element.\n * @part segment - One block when `segments` is set; carries `data-filled` once reached.\n * @example\n * ```html\n * <ore-progress value=\"45\"></ore-progress>\n * <ore-progress value=\"75\" max=\"100\" color=\"success\" size=\"lg\"></ore-progress>\n * <ore-progress indeterminate color=\"primary\" label=\"Loading…\"></ore-progress>\n * <ore-progress value=\"2\" max=\"6\" segments=\"6\" label=\"Struggle\"></ore-progress>\n * ```\n */\nexport const PROGRESS_TAG = 'ore-progress' as const;\ndefine<OreProgressProps>(PROGRESS_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n 'floating-label': prop.string(),\n indeterminate: prop.bool(false),\n label: prop.string(),\n max: prop.number(100),\n segments: prop.number(0),\n title: prop.string(),\n type: prop.oneOf(['linear', 'circular', 'vertical'] as const, 'linear'),\n value: prop.number(0),\n 'value-text': prop.string(),\n },\n\n setup(props) {\n const el = getHost();\n const watch = watchEffect;\n\n // The SVG circle circumference for a radius of 45 (viewBox 0 0 100 100)\n const RADIUS = 45;\n const CIRC = 2 * Math.PI * RADIUS; // ~282.7\n\n const percent = computed(() => {\n const v = Math.max(0, Math.min(Number(props.value.value), Number(props.max.value)));\n const m = Math.max(1, Number(props.max.value));\n\n return `${(v / m) * 100}%`;\n });\n\n const dashoffset = computed(() => {\n const v = Math.max(0, Math.min(Number(props.value.value), Number(props.max.value)));\n const m = Math.max(1, Number(props.max.value));\n\n return CIRC - (v / m) * CIRC;\n });\n\n const ariaValueNow = () => (props.indeterminate.value ? null : String(props.value.value));\n const ariaLabel = () => props.label.value ?? props.title.value ?? 'Progress';\n const circularStyle = styleMap({ '--_circ': `${CIRC}px` });\n const strokeDasharray = () => (props.indeterminate.value ? undefined : `${CIRC}px`);\n const strokeDashoffset = () => (props.indeterminate.value ? undefined : `${dashoffset.value}px`);\n const isVertical = computed(() => props.type.value === 'vertical');\n const segmentCount = computed(() => Math.max(0, Math.floor(Number(props.segments.value) || 0)));\n const isSegmented = computed(() => segmentCount.value > 0 && !props.indeterminate.value && !isVertical.value);\n const filledSegments = computed(() => {\n const v = Math.max(0, Math.min(Number(props.value.value), Number(props.max.value)));\n const m = Math.max(1, Number(props.max.value));\n\n return Math.round((v / m) * segmentCount.value);\n });\n const segmentStyle = styleMap({ '--_segments': () => String(segmentCount.value) });\n const linearFillStyle = styleMap({\n height: () => (!props.indeterminate.value && isVertical.value ? percent.value : null),\n width: () => (!props.indeterminate.value && !isVertical.value ? percent.value : null),\n });\n\n watch(() => {\n el.style.setProperty('--_percent', props.indeterminate.value ? '0%' : percent.value);\n });\n\n const circularTemplate = () => html`\n <div\n class=\"circular-track\"\n role=\"progressbar\"\n aria-valuenow=\"${ariaValueNow}\"\n aria-valuemin=\"0\"\n aria-valuemax=\"${props.max}\"\n aria-label=\"${ariaLabel}\"\n aria-valuetext=\"${props['value-text']}\"\n style=\"${circularStyle}\">\n <svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle class=\"circle-bg\" cx=\"50\" cy=\"50\" r=\"${RADIUS}\"></circle>\n <circle\n class=\"circle-fill\"\n cx=\"50\"\n cy=\"50\"\n r=\"${RADIUS}\"\n stroke-dasharray=\"${strokeDasharray}\"\n stroke-dashoffset=\"${strokeDashoffset}\"></circle>\n </svg>\n <div class=\"circular-inner\">\n <span class=\"circular-label\">${() => props.label.value ?? ''}</span>\n <span class=\"circular-title\">${() => props.title.value ?? ''}</span>\n </div>\n </div>\n `;\n\n const barTemplate = () => html`\n <div class=\"wrapper\">\n <div class=\"header\">\n <span class=\"progress-title\">${() => props.title.value ?? ''}</span>\n <span class=\"end-label header-label\">${() => props.label.value ?? ''}</span>\n </div>\n <div class=${() => `bar-row${isVertical.value ? ' bar-row-vertical' : ''}`}>\n <div class=\"track-outer\">\n <div\n class=${() => `track${isSegmented.value ? ' track-segmented' : ''}`}\n role=\"progressbar\"\n aria-valuenow=\"${ariaValueNow}\"\n aria-valuemin=\"0\"\n aria-valuemax=\"${props.max}\"\n aria-label=\"${ariaLabel}\"\n aria-valuetext=\"${props['value-text']}\"\n style=\"${segmentStyle}\">\n ${() =>\n isSegmented.value\n ? Array.from(\n { length: segmentCount.value },\n (_, i) => html`\n <span\n class=\"segment\"\n part=\"segment\"\n data-filled=\"${() => (i < filledSegments.value ? '' : null)}\"></span>\n `,\n )\n : html`\n <div class=\"fill\" part=\"fill\" style=\"${linearFillStyle}\"></div>\n `}\n </div>\n ${() =>\n !isVertical.value\n ? html`\n <span class=\"floating-label\">${props['floating-label']}</span>\n `\n : null}\n </div>\n <span class=\"end-label row-label\">${() => props.label.value ?? ''}</span>\n </div>\n </div>\n `;\n\n return html`\n ${() => (props.type.value === 'circular' ? circularTemplate() : barTemplate())}\n `;\n },\n\n styles: [colorThemeMixin, forcedColorsMixin, reducedMotionMixin, componentStyles],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"progress.cjs","names":[],"sources":["../src/feedback/progress/progress.ts"],"sourcesContent":["import { define, getHost, html, prop, styleMap, watchEffect } from '@vielzeug/ore';\nimport { computed } from '@vielzeug/ripple';\nimport { sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, forcedColorsMixin, reducedMotionMixin } from '../../styles';\nimport type { ComponentSize, ThemeColor } from '../../types';\nimport componentStyles from './progress.css?inline';\n\n/** Progress bar component properties */\nexport type OreProgressProps = {\n /** Theme color for the fill bar */\n color?: ThemeColor;\n /** Floating chip centered above the fill endpoint (linear only). Hidden in indeterminate mode. Position formula: left = fill% − half chip width (CSS: left:X%; transform:translateX(−50%)). */\n 'floating-label'?: string;\n /** When true, shows an infinite animation: use when progress is unknown. */\n indeterminate?: boolean;\n /** Accessible name AND visible text label.\n * - Linear without `title`: rendered at the end of the bar.\n * - Linear with `title`: moved into the header row above the bar.\n * - Circular: large text centered inside the ring. */\n label?: string;\n /** Maximum value. Defaults to 100. */\n max?: number;\n /** Split the linear bar into this many discrete blocks; blocks up to `value / max` are filled. Ignored when `indeterminate` or circular. */\n segments?: number;\n /** Size variant controlling bar height */\n size?: ComponentSize;\n /** Title text.\n * - Linear: displayed as a header above the bar; moves `label` into the header row.\n * - Circular: smaller text displayed below the `label` inside the ring. */\n title?: string;\n /** 'linear' (default), 'circular', or 'vertical' */\n type?: 'linear' | 'circular' | 'vertical';\n /** Current progress value (0 to `max`). Ignored when `indeterminate`. */\n value?: number;\n /** Human-readable value text for screen readers (e.g. \"Step 2 of 5\", \"75%\"). Overrides the raw aria-valuenow when set. */\n 'value-text'?: string;\n};\n\n/**\n * A linear progress bar for conveying operation progress.\n * Supports determinate (known value) and indeterminate (unknown duration) modes.\n *\n * @element ore-progress\n *\n * @attr {number} value - Current value (0–max). Defaults to 0.\n * @attr {number} max - Maximum value. Defaults to 100.\n * @attr {boolean} indeterminate - Show infinite animation (ignores value/max).\n * @attr {string} color - Theme color: 'primary' | 'success' | 'warning' | 'error' | …\n * @attr {string} size - Bar height/width: 'sm' | 'md' | 'lg'\n * @attr {string} type - 'linear' (default) | 'circular' | 'vertical'\n * @attr {number} segments - Split the linear bar into discrete blocks (tracks, steps, stages)\n *\n * @cssprop --progress-width - Bar width override (vertical only)\n * @attr {string} label - Visible text label + accessible name. Linear: at bar end (or header row with title). Circular: large text centered inside the ring.\n * @attr {string} title - Title text. Linear: header above the bar (moves label to header row). Circular: smaller text below the label inside the ring.\n * @attr {string} floating-label - Floating chip centered above the fill endpoint (linear only); hidden when indeterminate.\n *\n * @cssprop --progress-height - Bar height override\n * @cssprop --progress-track-bg - Track background color\n * @cssprop --progress-fill - Fill bar color\n * @cssprop --progress-radius - Border radius\n * @cssprop --progress-label-gap - Gap between header/bar row and between bar and trailing label (default 0.25 rem)\n * @cssprop --progress-title-color - Title text color (defaults to currentColor)\n * @cssprop --progress-label-color - Label text color (defaults to currentColor)\n * @cssprop --progress-circle-size - Circular indicator diameter (default 6rem)\n * @cssprop --progress-circular-label-size - Font size of the label inside the ring (default --text-xl)\n * @cssprop --progress-circular-title-size - Font size of the title inside the ring (default --text-xs)\n * @cssprop --progress-segment-gap - Gap between blocks when `segments` is set (default --size-1)\n *\n * @part fill - Progress fill element.\n * @part segment - One block when `segments` is set; carries `data-filled` once reached.\n * @example\n * ```html\n * <ore-progress value=\"45\"></ore-progress>\n * <ore-progress value=\"75\" max=\"100\" color=\"success\" size=\"lg\"></ore-progress>\n * <ore-progress indeterminate color=\"primary\" label=\"Loading…\"></ore-progress>\n * <ore-progress value=\"2\" max=\"6\" segments=\"6\" label=\"Struggle\"></ore-progress>\n * ```\n */\nexport const PROGRESS_TAG = 'ore-progress' as const;\ndefine<OreProgressProps>(PROGRESS_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n 'floating-label': prop.string(),\n indeterminate: prop.bool(false),\n label: prop.string(),\n max: prop.number(100),\n segments: prop.number(0),\n title: prop.string(),\n type: prop.oneOf(['linear', 'circular', 'vertical'] as const, 'linear'),\n value: prop.number(0),\n 'value-text': prop.string(),\n },\n\n setup(props) {\n const el = getHost();\n const watch = watchEffect;\n\n // The SVG circle circumference for a radius of 45 (viewBox 0 0 100 100)\n const RADIUS = 45;\n const CIRC = 2 * Math.PI * RADIUS; // ~282.7\n\n const percent = computed(() => {\n const v = Math.max(0, Math.min(Number(props.value.value), Number(props.max.value)));\n const m = Math.max(1, Number(props.max.value));\n\n return `${(v / m) * 100}%`;\n });\n\n const dashoffset = computed(() => {\n const v = Math.max(0, Math.min(Number(props.value.value), Number(props.max.value)));\n const m = Math.max(1, Number(props.max.value));\n\n return CIRC - (v / m) * CIRC;\n });\n\n const ariaValueNow = () => (props.indeterminate.value ? null : String(props.value.value));\n const ariaLabel = () => props.label.value ?? props.title.value ?? 'Progress';\n const circularStyle = styleMap({ '--_circ': `${CIRC}px` });\n const strokeDasharray = () => (props.indeterminate.value ? undefined : `${CIRC}px`);\n const strokeDashoffset = () => (props.indeterminate.value ? undefined : `${dashoffset.value}px`);\n const isVertical = computed(() => props.type.value === 'vertical');\n const segmentCount = computed(() => Math.max(0, Math.floor(Number(props.segments.value) || 0)));\n const isSegmented = computed(() => segmentCount.value > 0 && !props.indeterminate.value && !isVertical.value);\n const filledSegments = computed(() => {\n const v = Math.max(0, Math.min(Number(props.value.value), Number(props.max.value)));\n const m = Math.max(1, Number(props.max.value));\n\n return Math.round((v / m) * segmentCount.value);\n });\n const segmentStyle = styleMap({ '--_segments': () => String(segmentCount.value) });\n const linearFillStyle = styleMap({\n height: () => (!props.indeterminate.value && isVertical.value ? percent.value : null),\n width: () => (!props.indeterminate.value && !isVertical.value ? percent.value : null),\n });\n\n watch(() => {\n el.style.setProperty('--_percent', props.indeterminate.value ? '0%' : percent.value);\n });\n\n const circularTemplate = () => html`\n <div\n class=\"circular-track\"\n role=\"progressbar\"\n aria-valuenow=\"${ariaValueNow}\"\n aria-valuemin=\"0\"\n aria-valuemax=\"${props.max}\"\n aria-label=\"${ariaLabel}\"\n aria-valuetext=\"${props['value-text']}\"\n style=\"${circularStyle}\">\n <svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle class=\"circle-bg\" cx=\"50\" cy=\"50\" r=\"${RADIUS}\"></circle>\n <circle\n class=\"circle-fill\"\n cx=\"50\"\n cy=\"50\"\n r=\"${RADIUS}\"\n stroke-dasharray=\"${strokeDasharray}\"\n stroke-dashoffset=\"${strokeDashoffset}\"></circle>\n </svg>\n <div class=\"circular-inner\">\n <span class=\"circular-label\">${() => props.label.value ?? ''}</span>\n <span class=\"circular-title\">${() => props.title.value ?? ''}</span>\n </div>\n </div>\n `;\n\n const barTemplate = () => html`\n <div class=\"wrapper\">\n <div class=\"header\">\n <span class=\"progress-title\">${() => props.title.value ?? ''}</span>\n <span class=\"end-label header-label\">${() => props.label.value ?? ''}</span>\n </div>\n <div class=${() => `bar-row${isVertical.value ? ' bar-row-vertical' : ''}`}>\n <div class=\"track-outer\">\n <div\n class=${() => `track${isSegmented.value ? ' track-segmented' : ''}`}\n role=\"progressbar\"\n aria-valuenow=\"${ariaValueNow}\"\n aria-valuemin=\"0\"\n aria-valuemax=\"${props.max}\"\n aria-label=\"${ariaLabel}\"\n aria-valuetext=\"${props['value-text']}\"\n style=\"${segmentStyle}\">\n ${() =>\n isSegmented.value\n ? Array.from(\n { length: segmentCount.value },\n (_, i) => html`\n <span\n class=\"segment\"\n part=\"segment\"\n data-filled=\"${() => (i < filledSegments.value ? '' : null)}\"></span>\n `,\n )\n : html`\n <div class=\"fill\" part=\"fill\" style=\"${linearFillStyle}\"></div>\n `}\n </div>\n ${() =>\n !isVertical.value\n ? html`\n <span class=\"floating-label\">${props['floating-label']}</span>\n `\n : null}\n </div>\n <span class=\"end-label row-label\">${() => props.label.value ?? ''}</span>\n </div>\n </div>\n `;\n\n return html`\n ${() => (props.type.value === 'circular' ? circularTemplate() : barTemplate())}\n `;\n },\n\n styles: [colorThemeMixin, forcedColorsMixin, reducedMotionMixin, componentStyles],\n});\n"],"mappings":"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"}
|
package/dist/progress.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.js","names":[],"sources":["../src/feedback/progress/progress.ts"],"sourcesContent":["import { define, getHost, html, prop, styleMap, watchEffect } from '@vielzeug/ore';\nimport { computed } from '@vielzeug/ripple';\nimport { sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, forcedColorsMixin, reducedMotionMixin } from '../../styles';\nimport type { ComponentSize, ThemeColor } from '../../types';\nimport componentStyles from './progress.css?inline';\n\n/** Progress bar component properties */\nexport type OreProgressProps = {\n /** Theme color for the fill bar */\n color?: ThemeColor;\n /** Floating chip centered above the fill endpoint (linear only). Hidden in indeterminate mode. Position formula: left = fill% − half chip width (CSS: left:X%; transform:translateX(−50%)). */\n 'floating-label'?: string;\n /** When true, shows an infinite animation — use when progress is unknown. */\n indeterminate?: boolean;\n /** Accessible name AND visible text label.\n * - Linear without `title`: rendered at the end of the bar.\n * - Linear with `title`: moved into the header row above the bar.\n * - Circular: large text centered inside the ring. */\n label?: string;\n /** Maximum value. Defaults to 100. */\n max?: number;\n /** Split the linear bar into this many discrete blocks; blocks up to `value / max` are filled. Ignored when `indeterminate` or circular. */\n segments?: number;\n /** Size variant controlling bar height */\n size?: ComponentSize;\n /** Title text.\n * - Linear: displayed as a header above the bar; moves `label` into the header row.\n * - Circular: smaller text displayed below the `label` inside the ring. */\n title?: string;\n /** 'linear' (default), 'circular', or 'vertical' */\n type?: 'linear' | 'circular' | 'vertical';\n /** Current progress value (0 to `max`). Ignored when `indeterminate`. */\n value?: number;\n /** Human-readable value text for screen readers (e.g. \"Step 2 of 5\", \"75%\"). Overrides the raw aria-valuenow when set. */\n 'value-text'?: string;\n};\n\n/**\n * A linear progress bar for conveying operation progress.\n * Supports determinate (known value) and indeterminate (unknown duration) modes.\n *\n * @element ore-progress\n *\n * @attr {number} value - Current value (0–max). Defaults to 0.\n * @attr {number} max - Maximum value. Defaults to 100.\n * @attr {boolean} indeterminate - Show infinite animation (ignores value/max).\n * @attr {string} color - Theme color: 'primary' | 'success' | 'warning' | 'error' | …\n * @attr {string} size - Bar height/width: 'sm' | 'md' | 'lg'\n * @attr {string} type - 'linear' (default) | 'circular' | 'vertical'\n * @attr {number} segments - Split the linear bar into discrete blocks (tracks, steps, stages)\n *\n * @cssprop --progress-width - Bar width override (vertical only)\n * @attr {string} label - Visible text label + accessible name. Linear: at bar end (or header row with title). Circular: large text centered inside the ring.\n * @attr {string} title - Title text. Linear: header above the bar (moves label to header row). Circular: smaller text below the label inside the ring.\n * @attr {string} floating-label - Floating chip centered above the fill endpoint (linear only); hidden when indeterminate.\n *\n * @cssprop --progress-height - Bar height override\n * @cssprop --progress-track-bg - Track background color\n * @cssprop --progress-fill - Fill bar color\n * @cssprop --progress-radius - Border radius\n * @cssprop --progress-label-gap - Gap between header/bar row and between bar and trailing label (default 0.25 rem)\n * @cssprop --progress-title-color - Title text color (defaults to currentColor)\n * @cssprop --progress-label-color - Label text color (defaults to currentColor)\n * @cssprop --progress-circle-size - Circular indicator diameter (default 6rem)\n * @cssprop --progress-circular-label-size - Font size of the label inside the ring (default --text-xl)\n * @cssprop --progress-circular-title-size - Font size of the title inside the ring (default --text-xs)\n * @cssprop --progress-segment-gap - Gap between blocks when `segments` is set (default --size-1)\n *\n * @part fill - Progress fill element.\n * @part segment - One block when `segments` is set; carries `data-filled` once reached.\n * @example\n * ```html\n * <ore-progress value=\"45\"></ore-progress>\n * <ore-progress value=\"75\" max=\"100\" color=\"success\" size=\"lg\"></ore-progress>\n * <ore-progress indeterminate color=\"primary\" label=\"Loading…\"></ore-progress>\n * <ore-progress value=\"2\" max=\"6\" segments=\"6\" label=\"Struggle\"></ore-progress>\n * ```\n */\nexport const PROGRESS_TAG = 'ore-progress' as const;\ndefine<OreProgressProps>(PROGRESS_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n 'floating-label': prop.string(),\n indeterminate: prop.bool(false),\n label: prop.string(),\n max: prop.number(100),\n segments: prop.number(0),\n title: prop.string(),\n type: prop.oneOf(['linear', 'circular', 'vertical'] as const, 'linear'),\n value: prop.number(0),\n 'value-text': prop.string(),\n },\n\n setup(props) {\n const el = getHost();\n const watch = watchEffect;\n\n // The SVG circle circumference for a radius of 45 (viewBox 0 0 100 100)\n const RADIUS = 45;\n const CIRC = 2 * Math.PI * RADIUS; // ~282.7\n\n const percent = computed(() => {\n const v = Math.max(0, Math.min(Number(props.value.value), Number(props.max.value)));\n const m = Math.max(1, Number(props.max.value));\n\n return `${(v / m) * 100}%`;\n });\n\n const dashoffset = computed(() => {\n const v = Math.max(0, Math.min(Number(props.value.value), Number(props.max.value)));\n const m = Math.max(1, Number(props.max.value));\n\n return CIRC - (v / m) * CIRC;\n });\n\n const ariaValueNow = () => (props.indeterminate.value ? null : String(props.value.value));\n const ariaLabel = () => props.label.value ?? props.title.value ?? 'Progress';\n const circularStyle = styleMap({ '--_circ': `${CIRC}px` });\n const strokeDasharray = () => (props.indeterminate.value ? undefined : `${CIRC}px`);\n const strokeDashoffset = () => (props.indeterminate.value ? undefined : `${dashoffset.value}px`);\n const isVertical = computed(() => props.type.value === 'vertical');\n const segmentCount = computed(() => Math.max(0, Math.floor(Number(props.segments.value) || 0)));\n const isSegmented = computed(() => segmentCount.value > 0 && !props.indeterminate.value && !isVertical.value);\n const filledSegments = computed(() => {\n const v = Math.max(0, Math.min(Number(props.value.value), Number(props.max.value)));\n const m = Math.max(1, Number(props.max.value));\n\n return Math.round((v / m) * segmentCount.value);\n });\n const segmentStyle = styleMap({ '--_segments': () => String(segmentCount.value) });\n const linearFillStyle = styleMap({\n height: () => (!props.indeterminate.value && isVertical.value ? percent.value : null),\n width: () => (!props.indeterminate.value && !isVertical.value ? percent.value : null),\n });\n\n watch(() => {\n el.style.setProperty('--_percent', props.indeterminate.value ? '0%' : percent.value);\n });\n\n const circularTemplate = () => html`\n <div\n class=\"circular-track\"\n role=\"progressbar\"\n aria-valuenow=\"${ariaValueNow}\"\n aria-valuemin=\"0\"\n aria-valuemax=\"${props.max}\"\n aria-label=\"${ariaLabel}\"\n aria-valuetext=\"${props['value-text']}\"\n style=\"${circularStyle}\">\n <svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle class=\"circle-bg\" cx=\"50\" cy=\"50\" r=\"${RADIUS}\"></circle>\n <circle\n class=\"circle-fill\"\n cx=\"50\"\n cy=\"50\"\n r=\"${RADIUS}\"\n stroke-dasharray=\"${strokeDasharray}\"\n stroke-dashoffset=\"${strokeDashoffset}\"></circle>\n </svg>\n <div class=\"circular-inner\">\n <span class=\"circular-label\">${() => props.label.value ?? ''}</span>\n <span class=\"circular-title\">${() => props.title.value ?? ''}</span>\n </div>\n </div>\n `;\n\n const barTemplate = () => html`\n <div class=\"wrapper\">\n <div class=\"header\">\n <span class=\"progress-title\">${() => props.title.value ?? ''}</span>\n <span class=\"end-label header-label\">${() => props.label.value ?? ''}</span>\n </div>\n <div class=${() => `bar-row${isVertical.value ? ' bar-row-vertical' : ''}`}>\n <div class=\"track-outer\">\n <div\n class=${() => `track${isSegmented.value ? ' track-segmented' : ''}`}\n role=\"progressbar\"\n aria-valuenow=\"${ariaValueNow}\"\n aria-valuemin=\"0\"\n aria-valuemax=\"${props.max}\"\n aria-label=\"${ariaLabel}\"\n aria-valuetext=\"${props['value-text']}\"\n style=\"${segmentStyle}\">\n ${() =>\n isSegmented.value\n ? Array.from(\n { length: segmentCount.value },\n (_, i) => html`\n <span\n class=\"segment\"\n part=\"segment\"\n data-filled=\"${() => (i < filledSegments.value ? '' : null)}\"></span>\n `,\n )\n : html`\n <div class=\"fill\" part=\"fill\" style=\"${linearFillStyle}\"></div>\n `}\n </div>\n ${() =>\n !isVertical.value\n ? html`\n <span class=\"floating-label\">${props['floating-label']}</span>\n `\n : null}\n </div>\n <span class=\"end-label row-label\">${() => props.label.value ?? ''}</span>\n </div>\n </div>\n `;\n\n return html`\n ${() => (props.type.value === 'circular' ? circularTemplate() : barTemplate())}\n `;\n },\n\n styles: [colorThemeMixin, forcedColorsMixin, reducedMotionMixin, componentStyles],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"progress.js","names":[],"sources":["../src/feedback/progress/progress.ts"],"sourcesContent":["import { define, getHost, html, prop, styleMap, watchEffect } from '@vielzeug/ore';\nimport { computed } from '@vielzeug/ripple';\nimport { sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, forcedColorsMixin, reducedMotionMixin } from '../../styles';\nimport type { ComponentSize, ThemeColor } from '../../types';\nimport componentStyles from './progress.css?inline';\n\n/** Progress bar component properties */\nexport type OreProgressProps = {\n /** Theme color for the fill bar */\n color?: ThemeColor;\n /** Floating chip centered above the fill endpoint (linear only). Hidden in indeterminate mode. Position formula: left = fill% − half chip width (CSS: left:X%; transform:translateX(−50%)). */\n 'floating-label'?: string;\n /** When true, shows an infinite animation: use when progress is unknown. */\n indeterminate?: boolean;\n /** Accessible name AND visible text label.\n * - Linear without `title`: rendered at the end of the bar.\n * - Linear with `title`: moved into the header row above the bar.\n * - Circular: large text centered inside the ring. */\n label?: string;\n /** Maximum value. Defaults to 100. */\n max?: number;\n /** Split the linear bar into this many discrete blocks; blocks up to `value / max` are filled. Ignored when `indeterminate` or circular. */\n segments?: number;\n /** Size variant controlling bar height */\n size?: ComponentSize;\n /** Title text.\n * - Linear: displayed as a header above the bar; moves `label` into the header row.\n * - Circular: smaller text displayed below the `label` inside the ring. */\n title?: string;\n /** 'linear' (default), 'circular', or 'vertical' */\n type?: 'linear' | 'circular' | 'vertical';\n /** Current progress value (0 to `max`). Ignored when `indeterminate`. */\n value?: number;\n /** Human-readable value text for screen readers (e.g. \"Step 2 of 5\", \"75%\"). Overrides the raw aria-valuenow when set. */\n 'value-text'?: string;\n};\n\n/**\n * A linear progress bar for conveying operation progress.\n * Supports determinate (known value) and indeterminate (unknown duration) modes.\n *\n * @element ore-progress\n *\n * @attr {number} value - Current value (0–max). Defaults to 0.\n * @attr {number} max - Maximum value. Defaults to 100.\n * @attr {boolean} indeterminate - Show infinite animation (ignores value/max).\n * @attr {string} color - Theme color: 'primary' | 'success' | 'warning' | 'error' | …\n * @attr {string} size - Bar height/width: 'sm' | 'md' | 'lg'\n * @attr {string} type - 'linear' (default) | 'circular' | 'vertical'\n * @attr {number} segments - Split the linear bar into discrete blocks (tracks, steps, stages)\n *\n * @cssprop --progress-width - Bar width override (vertical only)\n * @attr {string} label - Visible text label + accessible name. Linear: at bar end (or header row with title). Circular: large text centered inside the ring.\n * @attr {string} title - Title text. Linear: header above the bar (moves label to header row). Circular: smaller text below the label inside the ring.\n * @attr {string} floating-label - Floating chip centered above the fill endpoint (linear only); hidden when indeterminate.\n *\n * @cssprop --progress-height - Bar height override\n * @cssprop --progress-track-bg - Track background color\n * @cssprop --progress-fill - Fill bar color\n * @cssprop --progress-radius - Border radius\n * @cssprop --progress-label-gap - Gap between header/bar row and between bar and trailing label (default 0.25 rem)\n * @cssprop --progress-title-color - Title text color (defaults to currentColor)\n * @cssprop --progress-label-color - Label text color (defaults to currentColor)\n * @cssprop --progress-circle-size - Circular indicator diameter (default 6rem)\n * @cssprop --progress-circular-label-size - Font size of the label inside the ring (default --text-xl)\n * @cssprop --progress-circular-title-size - Font size of the title inside the ring (default --text-xs)\n * @cssprop --progress-segment-gap - Gap between blocks when `segments` is set (default --size-1)\n *\n * @part fill - Progress fill element.\n * @part segment - One block when `segments` is set; carries `data-filled` once reached.\n * @example\n * ```html\n * <ore-progress value=\"45\"></ore-progress>\n * <ore-progress value=\"75\" max=\"100\" color=\"success\" size=\"lg\"></ore-progress>\n * <ore-progress indeterminate color=\"primary\" label=\"Loading…\"></ore-progress>\n * <ore-progress value=\"2\" max=\"6\" segments=\"6\" label=\"Struggle\"></ore-progress>\n * ```\n */\nexport const PROGRESS_TAG = 'ore-progress' as const;\ndefine<OreProgressProps>(PROGRESS_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n 'floating-label': prop.string(),\n indeterminate: prop.bool(false),\n label: prop.string(),\n max: prop.number(100),\n segments: prop.number(0),\n title: prop.string(),\n type: prop.oneOf(['linear', 'circular', 'vertical'] as const, 'linear'),\n value: prop.number(0),\n 'value-text': prop.string(),\n },\n\n setup(props) {\n const el = getHost();\n const watch = watchEffect;\n\n // The SVG circle circumference for a radius of 45 (viewBox 0 0 100 100)\n const RADIUS = 45;\n const CIRC = 2 * Math.PI * RADIUS; // ~282.7\n\n const percent = computed(() => {\n const v = Math.max(0, Math.min(Number(props.value.value), Number(props.max.value)));\n const m = Math.max(1, Number(props.max.value));\n\n return `${(v / m) * 100}%`;\n });\n\n const dashoffset = computed(() => {\n const v = Math.max(0, Math.min(Number(props.value.value), Number(props.max.value)));\n const m = Math.max(1, Number(props.max.value));\n\n return CIRC - (v / m) * CIRC;\n });\n\n const ariaValueNow = () => (props.indeterminate.value ? null : String(props.value.value));\n const ariaLabel = () => props.label.value ?? props.title.value ?? 'Progress';\n const circularStyle = styleMap({ '--_circ': `${CIRC}px` });\n const strokeDasharray = () => (props.indeterminate.value ? undefined : `${CIRC}px`);\n const strokeDashoffset = () => (props.indeterminate.value ? undefined : `${dashoffset.value}px`);\n const isVertical = computed(() => props.type.value === 'vertical');\n const segmentCount = computed(() => Math.max(0, Math.floor(Number(props.segments.value) || 0)));\n const isSegmented = computed(() => segmentCount.value > 0 && !props.indeterminate.value && !isVertical.value);\n const filledSegments = computed(() => {\n const v = Math.max(0, Math.min(Number(props.value.value), Number(props.max.value)));\n const m = Math.max(1, Number(props.max.value));\n\n return Math.round((v / m) * segmentCount.value);\n });\n const segmentStyle = styleMap({ '--_segments': () => String(segmentCount.value) });\n const linearFillStyle = styleMap({\n height: () => (!props.indeterminate.value && isVertical.value ? percent.value : null),\n width: () => (!props.indeterminate.value && !isVertical.value ? percent.value : null),\n });\n\n watch(() => {\n el.style.setProperty('--_percent', props.indeterminate.value ? '0%' : percent.value);\n });\n\n const circularTemplate = () => html`\n <div\n class=\"circular-track\"\n role=\"progressbar\"\n aria-valuenow=\"${ariaValueNow}\"\n aria-valuemin=\"0\"\n aria-valuemax=\"${props.max}\"\n aria-label=\"${ariaLabel}\"\n aria-valuetext=\"${props['value-text']}\"\n style=\"${circularStyle}\">\n <svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle class=\"circle-bg\" cx=\"50\" cy=\"50\" r=\"${RADIUS}\"></circle>\n <circle\n class=\"circle-fill\"\n cx=\"50\"\n cy=\"50\"\n r=\"${RADIUS}\"\n stroke-dasharray=\"${strokeDasharray}\"\n stroke-dashoffset=\"${strokeDashoffset}\"></circle>\n </svg>\n <div class=\"circular-inner\">\n <span class=\"circular-label\">${() => props.label.value ?? ''}</span>\n <span class=\"circular-title\">${() => props.title.value ?? ''}</span>\n </div>\n </div>\n `;\n\n const barTemplate = () => html`\n <div class=\"wrapper\">\n <div class=\"header\">\n <span class=\"progress-title\">${() => props.title.value ?? ''}</span>\n <span class=\"end-label header-label\">${() => props.label.value ?? ''}</span>\n </div>\n <div class=${() => `bar-row${isVertical.value ? ' bar-row-vertical' : ''}`}>\n <div class=\"track-outer\">\n <div\n class=${() => `track${isSegmented.value ? ' track-segmented' : ''}`}\n role=\"progressbar\"\n aria-valuenow=\"${ariaValueNow}\"\n aria-valuemin=\"0\"\n aria-valuemax=\"${props.max}\"\n aria-label=\"${ariaLabel}\"\n aria-valuetext=\"${props['value-text']}\"\n style=\"${segmentStyle}\">\n ${() =>\n isSegmented.value\n ? Array.from(\n { length: segmentCount.value },\n (_, i) => html`\n <span\n class=\"segment\"\n part=\"segment\"\n data-filled=\"${() => (i < filledSegments.value ? '' : null)}\"></span>\n `,\n )\n : html`\n <div class=\"fill\" part=\"fill\" style=\"${linearFillStyle}\"></div>\n `}\n </div>\n ${() =>\n !isVertical.value\n ? html`\n <span class=\"floating-label\">${props['floating-label']}</span>\n `\n : null}\n </div>\n <span class=\"end-label row-label\">${() => props.label.value ?? ''}</span>\n </div>\n </div>\n `;\n\n return html`\n ${() => (props.type.value === 'circular' ? circularTemplate() : barTemplate())}\n `;\n },\n\n styles: [colorThemeMixin, forcedColorsMixin, reducedMotionMixin, componentStyles],\n});\n"],"mappings":"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"}
|