@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
package/dist/sidebar.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidebar.cjs","names":[],"sources":["../src/layout/sidebar/sidebar.ts"],"sourcesContent":["import { captureFocus, type FocusRestorer } from '@vielzeug/focus';\nimport {\n bind,\n createContext,\n define,\n getHost,\n html,\n inject,\n onCleanup,\n onMounted,\n prop,\n provide,\n useEmit,\n useSlots,\n} from '@vielzeug/ore';\nimport { computed, fromSubscribable, type Readable, signal, watch } from '@vielzeug/ripple';\nimport { createElementSize, createMediaQuery, SentinelUnavailableError } from '@vielzeug/sentinel';\n\nimport { createBackgroundLock } from '../../overlay/shared/background-lock';\nimport { coarsePointerMixin, reducedMotionMixin } from '../../styles';\nimport { computeSafeRel } from '../../utils';\nimport { parseMaxWidthPx, readContainerWidth, resolveContainerElement } from '../shared';\nimport sidebarStyles from './sidebar.css?inline';\nimport groupStyles from './sidebar-group.css?inline';\nimport itemStyles from './sidebar-item.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\n\n// ─── Types ────────────────────────────────────────────────────────────────\n\ntype SidebarVariant = 'floating' | 'inset';\ntype SidebarCollapseSource = 'api' | 'responsive' | 'toggle';\ntype DrawerSource = 'api' | 'responsive' | 'toggle';\ntype SidebarMode = 'bottom-nav' | 'collapsed' | 'default';\n\ntype BottomNavItem = {\n active: boolean;\n disabled: boolean;\n href?: string;\n iconName?: string;\n label: string;\n source: HTMLElement;\n};\n\n/** Context provided by `ore-sidebar` to its `ore-sidebar-group` and `ore-sidebar-item` children. */\nexport type SidebarContext = {\n closeDrawer: () => void;\n closeOnSelect: Readable<boolean>;\n collapsed: Readable<boolean>;\n drawerOpen: Readable<boolean>;\n mode: Readable<SidebarMode>;\n variant: Readable<SidebarVariant | undefined>;\n};\n\n/** Injection key for the sidebar context. */\nexport const SIDEBAR_CTX = createContext<SidebarContext>('SidebarContext');\n\n// ─── ore-sidebar styles ──────────────────────────────────────────────────────\n\n/** ore-sidebar element interface */\nexport type SidebarElement = HTMLElement &\n OreSidebarProps & {\n /** Close the drawer. */\n closeDrawer(): void;\n /** Open the drawer. */\n openDrawer(): void;\n /** Set collapsed state imperatively. */\n setCollapsed(next: boolean): void;\n /** Toggle between collapsed and expanded. */\n toggle(): void;\n /** Toggle the drawer. */\n toggleDrawer(): void;\n };\n\n/** Sidebar component properties */\n\nexport type OreSidebarEvents = {\n 'collapsed-change': { collapsed: boolean; source: SidebarCollapseSource };\n 'drawer-change': { open: boolean; source: DrawerSource };\n};\n\nexport type OreSidebarGroupEvents = {\n 'open-change': { open: boolean };\n};\n\nexport type OreSidebarProps = {\n /** CSS media query that switches the sidebar to bottom navigation + drawer mode */\n 'bottom-nav-at'?: string;\n 'close-on-select'?: boolean;\n /** Controlled collapsed state */\n collapsed?: boolean;\n /** Whether the sidebar supports collapsing */\n collapsible?: boolean;\n 'collapse-label'?: string;\n /** Evaluate responsive and bottom-nav breakpoints against container width only. */\n 'container-breakpoints'?: boolean;\n /** Initial collapsed state in uncontrolled mode */\n 'default-collapsed'?: boolean;\n /** Drawer-only mode: no inline panel and no bottom bar, just the navbar/API-driven drawer. */\n drawer?: boolean;\n /** Accessible label for the backdrop close control. */\n 'drawer-close-label'?: string;\n 'expand-label'?: string;\n /**\n * Accessible label for the navigation landmark.\n * Use to distinguish multiple navigation regions on a page.\n * @default 'Sidebar navigation'\n */\n label?: string;\n /**\n * CSS media query that, when it matches, automatically collapses the sidebar.\n * Unset by default — no automatic collapse.\n * @example 'responsive=\"(max-width: 768px)\"'\n */\n responsive?: string;\n /** Visual style variant */\n variant?: SidebarVariant;\n};\n\n/**\n * `ore-sidebar` — A collapsible navigation sidebar with group and item support.\n *\n * @element ore-sidebar\n * @element ore-sidebar-group - Labelled group of navigation items\n * @element ore-sidebar-item - Individual navigation link or button\n *\n * @attr {boolean} collapsed - Controlled collapsed state\n * @attr {boolean} default-collapsed - Initial collapsed state for uncontrolled sidebars\n * @attr {boolean} collapsible - Show the collapse toggle button\n * @attr {boolean} drawer - Drawer-only mode: no inline panel and no bottom bar, just the navbar/API-driven drawer\n * @attr {string} variant - Visual variant: 'floating' | 'inset'\n * @attr {string} label - Accessible aria-label for the nav landmark\n *\n * @fires collapsed-change - Fired when collapsed state changes. detail: { collapsed: boolean; source: string }\n * @fires drawer-change - Fired when the drawer open state changes. detail: { open: boolean; source: string }\n *\n * @slot header - Branding or logo content above the nav\n * @slot - Navigation content (ore-sidebar-group / ore-sidebar-item)\n * @slot footer - Footer content below the nav (user info, settings, etc.)\n *\n * @cssprop --sidebar-width - Expanded sidebar width (default: 16rem)\n * @cssprop --sidebar-collapsed-width - Collapsed sidebar width (default: 3.5rem)\n * @cssprop --sidebar-drawer-width - Drawer panel width (default: min(--sidebar-width, 85vw))\n * @cssprop --sidebar-bg - Sidebar background color\n * @cssprop --sidebar-border-color - Border color\n *\n * @part drawer-backdrop - Backdrop shown behind the open drawer.\n * @part nav - Navigation container.\n * @part header - Header container.\n * @part toggle-btn - Shadow part for the `toggle-btn` element.\n * @part content - Content container.\n * @part footer - Footer container.\n * @part bottom-bar - Bottom bar container.\n * @part group - Group container.\n * @part group-header - Group header container.\n * @part group-icon - Group icon container.\n * @part group-label - Group label container.\n * @part group-items - Group items container.\n * @part item-icon - Leading item icon container.\n * @part item-label - Item label container.\n * @part item-end - Trailing item content container.\n * @part item - Item root element.\n * @example\n * ```html\n * <ore-sidebar collapsible label=\"App navigation\">\n * <span slot=\"header\">My App</span>\n * <ore-sidebar-group label=\"Main\">\n * <ore-sidebar-item href=\"/dashboard\" active>Dashboard</ore-sidebar-item>\n * <ore-sidebar-item href=\"/settings\">Settings</ore-sidebar-item>\n * </ore-sidebar-group>\n * </ore-sidebar>\n *\n * <!-- Auto-collapse on mobile -->\n * <ore-sidebar collapsible responsive=\"(max-width: 768px)\">...</ore-sidebar>\n * ```\n */\nexport const SIDEBAR_TAG = 'ore-sidebar' as const;\ndefine<OreSidebarProps>(SIDEBAR_TAG, {\n props: {\n 'bottom-nav-at': prop.string(),\n 'close-on-select': prop.bool(false),\n 'collapse-label': prop.string('Collapse sidebar'),\n collapsed: prop.bool(false),\n collapsible: prop.bool(false),\n 'container-breakpoints': prop.bool(false),\n 'default-collapsed': prop.bool(false),\n drawer: prop.bool(false),\n 'drawer-close-label': prop.string('Close sidebar'),\n 'expand-label': prop.string('Expand sidebar'),\n label: prop.string('Sidebar navigation'),\n responsive: prop.string(),\n variant: prop.string<SidebarVariant>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreSidebarEvents>();\n const slots = useSlots();\n\n const hasHeader = () => slots.has('header').value;\n const hasFooter = () => slots.has('footer').value;\n const hasLogo = () => slots.has('logo').value;\n\n const isControlled = signal(el.hasAttribute('collapsed'));\n const collapsedState = signal(isControlled.value ? el.hasAttribute('collapsed') : props['default-collapsed'].value);\n const isBottomNav = signal(false);\n const isDrawerOpen = signal(false);\n const bottomNavItems = signal<BottomNavItem[]>([]);\n const responsiveMediaMatches = signal(false);\n const responsiveSizeMatches = signal(false);\n const responsiveMaxWidthPx = signal<number | undefined>(parseMaxWidthPx(props.responsive.value));\n const hasResponsiveQuery = signal(Boolean(String(props.responsive.value ?? '').trim()));\n const bottomNavMediaMatches = signal(false);\n const bottomNavSizeMatches = signal(false);\n const bottomNavMaxWidthPx = signal<number | undefined>(parseMaxWidthPx(props['bottom-nav-at'].value));\n const isPreviewMode = signal(false);\n const bgLock = createBackgroundLock();\n let restoreDrawerFocus: FocusRestorer | null = null;\n\n const isCollapsed = () => collapsedState.value;\n const mode = computed<SidebarMode>(() => {\n if (isBottomNav.value) return 'bottom-nav';\n\n return collapsedState.value ? 'collapsed' : 'default';\n });\n\n const applyResponsiveState = () => {\n const useContainerBreakpoints = props['container-breakpoints'].value;\n const responsiveMatched = useContainerBreakpoints\n ? responsiveSizeMatches.value\n : responsiveMediaMatches.value || responsiveSizeMatches.value;\n const bottomMatched = useContainerBreakpoints\n ? bottomNavSizeMatches.value\n : bottomNavMediaMatches.value || bottomNavSizeMatches.value;\n\n // If the drawer is currently open, stay in bottom-nav mode\n // regardless of the responsive match. This prevents the drawer from\n // disappearing mid-animation or if forced open via the API on desktop.\n if (!isDrawerOpen.value) {\n isBottomNav.value = bottomMatched;\n }\n\n if (!bottomMatched && !isDrawerOpen.value) {\n setDrawerOpen(false, 'responsive');\n }\n\n if (hasResponsiveQuery.value) {\n setCollapsed(responsiveMatched, 'responsive');\n }\n };\n\n const readBottomNavItems = () => {\n if (props.drawer.value) {\n bottomNavItems.value = [];\n\n return;\n }\n\n const directItems = slots\n .elements()\n .value.filter(\n (item): item is HTMLElement =>\n item instanceof HTMLElement && item.tagName.toLowerCase() === 'ore-sidebar-item',\n );\n const promotedItems = [...el.querySelectorAll<HTMLElement>('ore-sidebar-item[bottom-nav]')];\n const sourceItems = promotedItems.length > 0 ? promotedItems : directItems;\n const next = sourceItems.map((item, index) => {\n const el = item;\n const iconSlotEl =\n (el.querySelector(':scope > [slot=\"icon\"]') as HTMLElement | null) ??\n (el.querySelector('[slot=\"icon\"]') as HTMLElement | null);\n const directIconName =\n iconSlotEl?.tagName.toLowerCase() === 'ore-icon' ? iconSlotEl.getAttribute('name') : null;\n const nestedIconName = iconSlotEl?.querySelector('ore-icon')?.getAttribute('name') ?? null;\n const rawLabel = (el.textContent ?? '').trim();\n\n return {\n active: el.hasAttribute('active'),\n disabled: el.hasAttribute('disabled'),\n href: el.getAttribute('href') ?? undefined,\n iconName: directIconName ?? nestedIconName ?? undefined,\n label: el.getAttribute('bottom-nav-label')?.trim() || rawLabel || `Item ${index + 1}`,\n source: el,\n } satisfies BottomNavItem;\n });\n\n bottomNavItems.value = next;\n };\n\n provide(SIDEBAR_CTX, {\n closeDrawer: () => setDrawerOpen(false, 'toggle'),\n closeOnSelect: props['close-on-select'],\n collapsed: computed(() => !isBottomNav.value && collapsedState.value) as Readable<boolean>,\n drawerOpen: computed(() => isDrawerOpen.value) as Readable<boolean>,\n mode: mode as Readable<SidebarMode>,\n variant: props.variant,\n });\n\n const setCollapsed = (next: boolean, source: SidebarCollapseSource) => {\n if (isCollapsed() === next) return;\n\n if (!isControlled.value) {\n collapsedState.value = next;\n }\n\n emit('collapsed-change', { collapsed: next, source });\n };\n\n const setDrawerOpen = (next: boolean, source: DrawerSource) => {\n const open = Boolean(next);\n\n // Outside drawer-only mode, opening the drawer forces the sidebar out of its inline\n // panel into the overlay presentation until the drawer closes again.\n if (open && !isBottomNav.value && !props.drawer.value) {\n isBottomNav.value = true;\n }\n\n if (isDrawerOpen.value === open) return;\n\n if (open) {\n restoreDrawerFocus = captureFocus();\n // Inert the background where the DOM structure allows (see createBackgroundLock) and\n // refocus the panel whenever focus escapes the sidebar — together these keep Tab and\n // Escape working on the open drawer even in apps where the page shares one wrapper\n // element and the background cannot be inerted.\n bgLock.lock(el);\n }\n\n isDrawerOpen.value = open;\n\n // If closing, re-evaluate responsive state to potentially exit forced bottom-nav mode\n if (!open) {\n bgLock.unlock();\n applyResponsiveState();\n queueMicrotask(() => {\n restoreDrawerFocus?.();\n restoreDrawerFocus = null;\n });\n } else {\n queueMicrotask(() => el.shadowRoot?.querySelector<HTMLElement>('nav')?.focus());\n }\n\n emit('drawer-change', { open, source });\n };\n\n const doToggle = () => {\n setCollapsed(!isCollapsed(), 'toggle');\n };\n\n const sidebarEl = el as SidebarElement;\n\n sidebarEl.setCollapsed = (next) => setCollapsed(Boolean(next), 'api');\n sidebarEl.toggle = doToggle;\n sidebarEl.openDrawer = () => setDrawerOpen(true, 'api');\n sidebarEl.closeDrawer = () => setDrawerOpen(false, 'api');\n sidebarEl.toggleDrawer = () => setDrawerOpen(!isDrawerOpen.value, 'toggle');\n\n bind({\n attr: {\n 'data-bottom-nav': () => (isBottomNav.value ? true : undefined),\n 'data-collapsed': () => (isCollapsed() && !isBottomNav.value ? true : undefined),\n 'data-drawer-open': () => (isDrawerOpen.value ? true : undefined),\n 'data-has-logo': () => (hasLogo() ? true : undefined),\n 'data-preview-mode': () => (isPreviewMode.value ? true : undefined),\n },\n });\n\n onMounted(() => {\n // Suppress transitions during initial layout so the sidebar doesn't\n // animate from a collapsed/0 state before the first ResizeObserver fires.\n el.setAttribute('data-no-transition', '');\n\n let transitionUnlocked = false;\n const unlockTransition = () => {\n if (transitionUnlocked) return;\n\n transitionUnlocked = true;\n el.removeAttribute('data-no-transition');\n };\n\n // Fallback: unlock after two frames in case ResizeObserver doesn't fire.\n requestAnimationFrame(() => requestAnimationFrame(unlockTransition));\n\n let mediaCleanup: (() => void) | undefined;\n let bottomNavCleanup: (() => void) | undefined;\n const itemObservers = new Map<HTMLElement, MutationObserver>();\n const observer = new MutationObserver(() => {\n if (!el.hasAttribute('collapsed') && !isControlled.value) return;\n\n isControlled.value = true;\n collapsedState.value = el.hasAttribute('collapsed');\n });\n\n observer.observe(el, {\n attributeFilter: ['collapsed'],\n attributes: true,\n });\n const closeOnEscape = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || !isDrawerOpen.value) return;\n\n event.preventDefault();\n setDrawerOpen(false, 'toggle');\n };\n el.addEventListener('keydown', closeOnEscape);\n\n // Focus containment: createBackgroundLock inertes background siblings, but apps that wrap\n // the whole page in one root element (so nothing can be inerted) still let Tab walk out of\n // the open drawer — and once focus is outside the host, its Escape handler is unreachable.\n // Pull focus back to the panel whenever it lands outside the sidebar. focusin is composed,\n // so its target is retargeted to this host for focus inside the shadow root.\n const keepFocusInDrawer = (event: FocusEvent) => {\n if (!isDrawerOpen.value) return;\n\n const target = event.target;\n\n if (target instanceof Node && (el === target || el.contains(target))) return;\n\n el.shadowRoot?.querySelector<HTMLElement>('nav')?.focus();\n };\n document.addEventListener('focusin', keepFocusInDrawer);\n\n watch(\n props.responsive,\n (query) => {\n mediaCleanup?.();\n mediaCleanup = undefined;\n\n const mediaQuery = String(query ?? '').trim();\n\n hasResponsiveQuery.value = Boolean(mediaQuery);\n responsiveMaxWidthPx.value = parseMaxWidthPx(mediaQuery);\n responsiveMediaMatches.value = false;\n\n const width = readContainerWidth(el);\n\n responsiveSizeMatches.value =\n width > 0 && responsiveMaxWidthPx.value != null ? width <= responsiveMaxWidthPx.value : false;\n applyResponsiveState();\n\n // For parseable max-width queries, keep behavior container-driven.\n // This avoids preview viewport controls being overridden by window width.\n if (props['container-breakpoints'].value && responsiveMaxWidthPx.value != null) {\n return;\n }\n\n if (!mediaQuery) {\n return;\n }\n\n try {\n const mediaHandle = createMediaQuery(mediaQuery);\n const syncMedia = (state: ReturnType<typeof mediaHandle.getSnapshot>) => {\n if (state) {\n responsiveMediaMatches.value = state.matches;\n applyResponsiveState();\n }\n };\n\n syncMedia(mediaHandle.getSnapshot());\n\n const mediaCleanupFn = watch(\n fromSubscribable(mediaHandle, { signal: mediaHandle.disposalSignal }),\n syncMedia,\n );\n mediaCleanup = () => {\n mediaCleanupFn.dispose();\n mediaHandle.dispose();\n };\n } catch (error) {\n if (!(error instanceof SentinelUnavailableError)) throw error;\n }\n },\n { immediate: true },\n );\n\n watch(\n props['bottom-nav-at'],\n (query) => {\n bottomNavCleanup?.();\n bottomNavCleanup = undefined;\n\n const mediaQuery = String(query ?? '').trim();\n\n bottomNavMaxWidthPx.value = parseMaxWidthPx(mediaQuery);\n bottomNavMediaMatches.value = false;\n\n const width = readContainerWidth(el);\n\n bottomNavSizeMatches.value =\n width > 0 && bottomNavMaxWidthPx.value != null ? width <= bottomNavMaxWidthPx.value : false;\n applyResponsiveState();\n\n // For parseable max-width queries, keep behavior container-driven.\n // This avoids preview viewport controls being overridden by window width.\n if (props['container-breakpoints'].value && bottomNavMaxWidthPx.value != null) {\n return;\n }\n\n if (!mediaQuery) {\n return;\n }\n\n try {\n const mediaHandle = createMediaQuery(mediaQuery);\n const syncMedia = (state: ReturnType<typeof mediaHandle.getSnapshot>) => {\n if (state) {\n bottomNavMediaMatches.value = state.matches;\n applyResponsiveState();\n }\n };\n\n syncMedia(mediaHandle.getSnapshot());\n\n const mediaCleanupFn = watch(\n fromSubscribable(mediaHandle, { signal: mediaHandle.disposalSignal }),\n syncMedia,\n );\n bottomNavCleanup = () => {\n mediaCleanupFn.dispose();\n mediaHandle.dispose();\n };\n } catch (error) {\n if (!(error instanceof SentinelUnavailableError)) throw error;\n }\n },\n { immediate: true },\n );\n\n watch(props.drawer, () => {\n applyResponsiveState();\n readBottomNavItems();\n });\n\n const stopResizeEffect =\n typeof ResizeObserver === 'function'\n ? (() => {\n const hostSize = createElementSize(el);\n onCleanup(() => hostSize.dispose());\n const hostSizeSignal = fromSubscribable(hostSize, { signal: hostSize.disposalSignal });\n\n const wrapperEl = el.parentElement;\n const containerEl = resolveContainerElement(el);\n const wrapperSize = wrapperEl ? createElementSize(wrapperEl) : undefined;\n const parentSize = containerEl && containerEl !== wrapperEl ? createElementSize(containerEl) : undefined;\n\n if (wrapperSize) {\n onCleanup(() => wrapperSize.dispose());\n }\n if (parentSize) {\n onCleanup(() => parentSize.dispose());\n }\n\n const wrapperSizeSignal = wrapperSize\n ? fromSubscribable(wrapperSize, { signal: wrapperSize.disposalSignal })\n : undefined;\n const parentSizeSignal = parentSize\n ? fromSubscribable(parentSize, { signal: parentSize.disposalSignal })\n : undefined;\n\n let rafId: number | undefined;\n\n const onResize = () => {\n if (rafId !== undefined) cancelAnimationFrame(rafId);\n\n rafId = requestAnimationFrame(() => {\n const resolvedContainer = resolveContainerElement(el);\n const width = readContainerWidth(el);\n const responsiveWasMatched = responsiveSizeMatches.value;\n const bottomNavWasMatched = bottomNavSizeMatches.value;\n const parentWidth = resolvedContainer?.clientWidth ?? 0;\n\n isPreviewMode.value = parentWidth > 0 && parentWidth < window.innerWidth;\n\n responsiveSizeMatches.value =\n width > 0 && responsiveMaxWidthPx.value != null ? width <= responsiveMaxWidthPx.value : false;\n bottomNavSizeMatches.value =\n width > 0 && bottomNavMaxWidthPx.value != null ? width <= bottomNavMaxWidthPx.value : false;\n\n if (\n responsiveWasMatched !== responsiveSizeMatches.value ||\n bottomNavWasMatched !== bottomNavSizeMatches.value\n ) {\n applyResponsiveState();\n }\n\n unlockTransition();\n });\n };\n\n return watch(\n computed(() => [\n hostSizeSignal.value?.width,\n hostSizeSignal.value?.height,\n wrapperSizeSignal?.value?.width,\n wrapperSizeSignal?.value?.height,\n parentSizeSignal?.value?.width,\n parentSizeSignal?.value?.height,\n ]),\n onResize,\n );\n })()\n : undefined;\n\n const bindItemObservers = (items: HTMLElement[]) => {\n const set = new Set(items);\n\n for (const [item, cleanup] of itemObservers) {\n if (set.has(item)) continue;\n\n cleanup.disconnect();\n itemObservers.delete(item);\n }\n\n for (const item of items) {\n if (itemObservers.has(item)) continue;\n\n const itemObserver = new MutationObserver(() => {\n // Only update if in bottom-nav mode to minimize re-renders\n if (isBottomNav.value) {\n readBottomNavItems();\n }\n });\n\n // Watch attributes and content that affect bottom-nav rendering.\n itemObserver.observe(item, {\n attributeFilter: ['active', 'bottom-nav', 'bottom-nav-label', 'disabled', 'href'],\n attributes: true,\n characterData: true,\n childList: true,\n subtree: true,\n });\n itemObservers.set(item, itemObserver);\n }\n };\n\n const syncBottomNavItems = () => {\n bindItemObservers([...el.querySelectorAll<HTMLElement>('ore-sidebar-item')]);\n readBottomNavItems();\n };\n watch(slots.elements(), syncBottomNavItems, { immediate: true });\n const nestedItemsObserver = new MutationObserver(syncBottomNavItems);\n nestedItemsObserver.observe(el, { childList: true, subtree: true });\n\n return () => {\n observer.disconnect();\n nestedItemsObserver.disconnect();\n el.removeEventListener('keydown', closeOnEscape);\n document.removeEventListener('focusin', keepFocusInDrawer);\n bgLock.unlock();\n mediaCleanup?.();\n bottomNavCleanup?.();\n stopResizeEffect?.dispose();\n\n for (const itemObserver of itemObservers.values()) {\n itemObserver.disconnect();\n }\n\n itemObservers.clear();\n };\n });\n\n return html`\n <button\n class=\"drawer-backdrop\"\n part=\"drawer-backdrop\"\n type=\"button\"\n aria-label=\"${props['drawer-close-label']}\"\n ?hidden=${() => !isDrawerOpen.value}\n @click=${() => setDrawerOpen(false, 'toggle')}></button>\n <nav\n aria-label=\"${props.label}\"\n aria-hidden=${() => ((isBottomNav.value || props.drawer.value) && !isDrawerOpen.value ? 'true' : null)}\n part=\"nav\"\n tabindex=\"-1\"\n ?inert=${() => (isBottomNav.value || props.drawer.value) && !isDrawerOpen.value}>\n <div class=\"sidebar-header\" part=\"header\" ?hidden=${() => !hasHeader() && !props.collapsible.value}>\n <span class=\"sidebar-logo\" ?hidden=${() => !hasLogo()}>\n <slot name=\"logo\"></slot>\n </span>\n <span class=\"sidebar-header-content\">\n <slot name=\"header\"></slot>\n </span>\n <button\n class=\"toggle-btn\"\n part=\"toggle-btn\"\n type=\"button\"\n ?hidden=${() => !props.collapsible.value}\n aria-label=\"${() => (isCollapsed() ? props['expand-label'].value : props['collapse-label'].value)}\"\n aria-expanded=\"${() => !isCollapsed()}\"\n @click=\"${doToggle}\">\n <ore-icon\n class=\"toggle-icon\"\n name=${() => (isCollapsed() ? 'panel-left-open' : 'panel-left-close')}\n size=\"18\"\n stroke-width=\"2\"\n aria-hidden=\"true\"></ore-icon>\n </button>\n </div>\n <div class=\"sidebar-content\" part=\"content\">\n <slot></slot>\n </div>\n <div class=\"sidebar-footer\" part=\"footer\" ?hidden=${() => !hasFooter()}>\n <slot name=\"footer\"></slot>\n </div>\n </nav>\n\n <div class=\"bottom-bar\" part=\"bottom-bar\" ?hidden=${() => !isBottomNav.value}>\n ${() =>\n bottomNavItems.value.map((item) => {\n const className = `bottom-tab${item.active ? ' bottom-tab-active' : ''}`;\n\n if (item.href && !item.disabled) {\n return html`\n <a\n class=\"${className}\"\n href=\"${item.href}\"\n aria-current=\"${item.active ? 'page' : null}\"\n data-active=\"${item.active ? 'true' : null}\">\n <span class=\"bottom-tab-icon\" aria-hidden=\"true\" ?hidden=${() => !item.iconName}>\n <ore-icon name=\"${item.iconName}\" size=\"18\" stroke-width=\"2\"></ore-icon>\n </span>\n <span class=\"bottom-tab-label\">${item.label}</span>\n </a>\n `;\n }\n\n return html`\n <button\n class=\"${className}\"\n type=\"button\"\n ?disabled=${item.disabled}\n aria-current=\"${item.active ? 'page' : null}\"\n data-active=\"${item.active ? 'true' : null}\"\n @click=${() => item.source.click()}>\n <span class=\"bottom-tab-icon\" aria-hidden=\"true\" ?hidden=${() => !item.iconName}>\n <ore-icon name=\"${item.iconName}\" size=\"18\" stroke-width=\"2\"></ore-icon>\n </span>\n <span class=\"bottom-tab-label\">${item.label}</span>\n </button>\n `;\n })}\n </div>\n `;\n },\n styles: [coarsePointerMixin, reducedMotionMixin, sidebarStyles],\n});\n\n// ─── ore-sidebar-group styles ────────────────────────────────────────────────\n\n/** Sidebar group properties */\nexport type OreSidebarGroupProps = {\n /** Whether this group can be collapsed */\n collapsible?: boolean;\n /** Initial open state in uncontrolled mode */\n 'default-open'?: boolean;\n /** Accessible label for the group */\n label?: string;\n /** Controlled open state */\n open?: boolean;\n};\n\n/**\n * `ore-sidebar-group` — A labelled section within `ore-sidebar`.\n *\n * @element ore-sidebar-group\n *\n * @attr {string} label - Group label text\n * @attr {boolean} collapsible - Whether this group can be toggled open/closed\n * @attr {boolean} open - Controlled expanded state\n * @attr {boolean} default-open - Initial expanded state in uncontrolled mode\n *\n * @fires open-change - Fired when the group open state changes (collapsible groups only). detail: { open: boolean }\n *\n * @slot - Navigation items (`ore-sidebar-item`)\n * @slot icon - Icon displayed before the label\n *\n * @example\n * ```html\n * <ore-sidebar-group label=\"Main\" collapsible open>\n * <ore-sidebar-item href=\"/home\">Home</ore-sidebar-item>\n * </ore-sidebar-group>\n * ```\n */\nexport const SIDEBAR_GROUP_TAG = 'ore-sidebar-group' as const;\ndefine<OreSidebarGroupProps>(SIDEBAR_GROUP_TAG, {\n props: {\n collapsible: prop.bool(false),\n 'default-open': prop.bool(true),\n label: prop.string(),\n open: {\n default: undefined as boolean | undefined,\n parse: (value: string | null) => (value == null ? undefined : value === '' || value === 'true'),\n reflect: false,\n },\n },\n setup(props) {\n const slots = useSlots();\n\n const hasIcon = () => slots.has('icon').value;\n const sidebarCtx = inject(SIDEBAR_CTX);\n\n bind({\n attr: {\n 'sidebar-bottom-nav': () =>\n sidebarCtx?.mode.value === 'bottom-nav' && !sidebarCtx?.drawerOpen.value ? true : undefined,\n 'sidebar-collapsed': () => (sidebarCtx?.collapsed.value ? true : undefined),\n },\n });\n\n const isControlled = () => props.open.value !== undefined;\n const openState = signal(props['default-open'].value);\n const isOpen = computed(() => {\n if (!props.collapsible.value) return true;\n\n if (isControlled()) return props.open.value ?? false;\n\n return openState.value;\n });\n\n watch(props.open, (value) => {\n if (value === undefined) return;\n\n openState.value = value;\n });\n\n bind({\n attr: {\n open: () => (isOpen.value ? true : undefined),\n },\n });\n\n return html`\n <details class=\"group\" part=\"group\" ?open=${isOpen}>\n <summary\n class=\"group-header\"\n part=\"group-header\"\n aria-expanded=\"${() => (props.collapsible.value ? String(props.open.value) : null)}\"\n @click=${(e: MouseEvent) => {\n if (!props.collapsible.value) {\n e.preventDefault();\n }\n }}>\n <span class=\"group-icon\" part=\"group-icon\" ?hidden=${() => !hasIcon()} aria-hidden=\"true\">\n <slot name=\"icon\"></slot>\n </span>\n <span class=\"group-label\" part=\"group-label\">${props.label}</span>\n <span class=\"chevron\" ?hidden=${() => !props.collapsible.value} aria-hidden=\"true\">\n <ore-icon name=\"chevron-right\" size=\"12\" stroke-width=\"2\" aria-hidden=\"true\"></ore-icon>\n </span>\n </summary>\n <div class=\"group-items\" part=\"group-items\" role=\"list\">\n <slot></slot>\n </div>\n </details>\n `;\n },\n styles: [reducedMotionMixin, groupStyles],\n});\n\n// ─── ore-sidebar-item styles ─────────────────────────────────────────────────\n\n/** Sidebar item properties */\nexport type OreSidebarItemProps = {\n /** Whether this item represents the current page/section */\n active?: boolean;\n 'bottom-nav'?: boolean;\n 'bottom-nav-label'?: string;\n /** Whether this item is disabled */\n disabled?: boolean;\n /** Navigation href — renders an `<a>` when set, otherwise a `<button>` */\n href?: string;\n label?: string;\n /**\n * Relationship of the linked URL (`rel` attribute on the inner `<a>`).\n * Only applies when `href` is set.\n */\n rel?: string;\n /**\n * Browsing context for the link (`target` attribute on the inner `<a>`).\n * Only applies when `href` is set.\n */\n target?: string;\n};\n\n/**\n * `ore-sidebar-item` — An individual navigation item in a `ore-sidebar`.\n *\n * Renders as an `<a>` when `href` is provided, otherwise as a `<button>`.\n * Marks the active page via `aria-current=\"page\"` when the `active` attribute is set.\n *\n * @element ore-sidebar-item\n *\n * @attr {string} href - Link URL; renders an anchor when set\n * @attr {boolean} active - Marks the item as the current page\n * @attr {boolean} disabled - Disables the item\n * @attr {string} rel - Anchor `rel` attribute (links only)\n * @attr {string} target - Anchor `target` attribute (links only)\n *\n * @slot - Label text\n * @slot icon - Leading icon\n * @slot end - Trailing content (badge, shortcut, arrow, etc.)\n *\n * @cssprop --sidebar-item-color - Default text color\n * @cssprop --sidebar-item-hover-bg - Hover background\n * @cssprop --sidebar-item-hover-color - Hover text color\n * @cssprop --sidebar-item-active-bg - Active background\n * @cssprop --sidebar-item-active-color - Active text color\n * @cssprop --sidebar-item-indicator - Active indicator bar color\n *\n * @part item - The inner anchor or button element\n * @part item-icon - The icon wrapper\n * @part item-label - The label wrapper\n * @part item-end - The trailing content wrapper\n *\n * @example\n * ```html\n * <ore-sidebar-item href=\"/dashboard\" active>\n * <span slot=\"icon\">🏠</span>\n * Dashboard\n * </ore-sidebar-item>\n *\n * <ore-sidebar-item href=\"/users\">\n * <span slot=\"icon\">👤</span>\n * Users\n * <ore-badge slot=\"end\" color=\"primary\">3</ore-badge>\n * </ore-sidebar-item>\n * ```\n */\nexport const SIDEBAR_ITEM_TAG = 'ore-sidebar-item' as const;\ndefine<OreSidebarItemProps>(SIDEBAR_ITEM_TAG, {\n props: {\n active: prop.bool(false),\n 'bottom-nav': prop.bool(false),\n 'bottom-nav-label': prop.string(),\n disabled: prop.bool(false),\n href: prop.string(),\n label: prop.string(),\n rel: prop.string(),\n target: prop.string(),\n },\n setup(props) {\n const slots = useSlots();\n\n const hasIcon = () => slots.has('icon').value;\n const hasEnd = () => slots.has('end').value;\n const sidebarCtx = inject(SIDEBAR_CTX);\n\n bind({\n attr: {\n 'sidebar-bottom-nav': () =>\n sidebarCtx?.mode.value === 'bottom-nav' && !sidebarCtx?.drawerOpen.value ? true : undefined,\n 'sidebar-collapsed': () => (sidebarCtx?.collapsed.value ? true : undefined),\n },\n });\n\n const isLink = () => !!props.href.value && !props.disabled.value;\n\n // Prevent reverse tabnapping: auto-inject noopener + noreferrer for _blank links.\n const effectiveRel = computed(() => computeSafeRel(props.rel.value, props.target.value));\n const closeDrawerOnSelect = (event: MouseEvent) => {\n if (!sidebarCtx?.closeOnSelect.value || !sidebarCtx.drawerOpen.value) return;\n if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;\n\n queueMicrotask(sidebarCtx.closeDrawer);\n };\n\n const renderItemContent = () => html`\n <span class=\"item-icon\" part=\"item-icon\" ?hidden=${() => !hasIcon()} aria-hidden=\"true\">\n <slot name=\"icon\"></slot>\n </span>\n <span class=\"item-label\" part=\"item-label\"><slot></slot></span>\n <span class=\"item-end\" part=\"item-end\" ?hidden=${() => !hasEnd()}>\n <slot name=\"end\"></slot>\n </span>\n `;\n\n return html`\n ${() => {\n if (isLink()) {\n return html`\n <a\n class=\"item\"\n part=\"item\"\n href=\"${props.href}\"\n rel=\"${effectiveRel}\"\n target=\"${props.target}\"\n aria-label=\"${props.label}\"\n aria-current=\"${() => (props.active.value ? 'page' : null)}\"\n @click=${closeDrawerOnSelect}>\n ${renderItemContent()}\n </a>\n `;\n }\n\n if (props.disabled.value) {\n return html`\n <div\n class=\"item\"\n part=\"item\"\n aria-disabled=\"true\"\n tabindex=\"-1\"\n aria-label=\"${props.label}\"\n aria-current=\"${() => (props.active.value ? 'page' : null)}\">\n ${renderItemContent()}\n </div>\n `;\n }\n\n return html`\n <button\n class=\"item\"\n part=\"item\"\n type=\"button\"\n ?disabled=\"${props.disabled}\"\n aria-label=\"${props.label}\"\n aria-current=\"${() => (props.active.value ? 'page' : null)}\"\n @click=${closeDrawerOnSelect}>\n ${renderItemContent()}\n </button>\n `;\n }}\n `;\n },\n styles: [coarsePointerMixin, itemStyles],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sidebar.cjs","names":[],"sources":["../src/layout/sidebar/sidebar.ts"],"sourcesContent":["import { captureFocus, type FocusRestorer } from '@vielzeug/focus';\nimport {\n bind,\n createContext,\n define,\n getHost,\n html,\n inject,\n onCleanup,\n onMounted,\n prop,\n provide,\n useEmit,\n useSlots,\n} from '@vielzeug/ore';\nimport { computed, fromSubscribable, type Readable, signal, watch } from '@vielzeug/ripple';\nimport { createElementSize, createMediaQuery, SentinelUnavailableError } from '@vielzeug/sentinel';\n\nimport { createBackgroundLock } from '../../overlay/shared/background-lock';\nimport { coarsePointerMixin, reducedMotionMixin } from '../../styles';\nimport { computeSafeRel } from '../../utils';\nimport { parseMaxWidthPx, readContainerWidth, resolveContainerElement } from '../shared';\nimport sidebarStyles from './sidebar.css?inline';\nimport groupStyles from './sidebar-group.css?inline';\nimport itemStyles from './sidebar-item.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\n\n// ─── Types ────────────────────────────────────────────────────────────────\n\ntype SidebarVariant = 'floating' | 'inset';\ntype SidebarCollapseSource = 'api' | 'responsive' | 'toggle';\ntype DrawerSource = 'api' | 'responsive' | 'toggle';\ntype SidebarMode = 'bottom-nav' | 'collapsed' | 'default';\n\ntype BottomNavItem = {\n active: boolean;\n disabled: boolean;\n href?: string;\n iconName?: string;\n label: string;\n source: HTMLElement;\n};\n\n/** Context provided by `ore-sidebar` to its `ore-sidebar-group` and `ore-sidebar-item` children. */\nexport type SidebarContext = {\n closeDrawer: () => void;\n closeOnSelect: Readable<boolean>;\n collapsed: Readable<boolean>;\n drawerOpen: Readable<boolean>;\n mode: Readable<SidebarMode>;\n variant: Readable<SidebarVariant | undefined>;\n};\n\n/** Injection key for the sidebar context. */\nexport const SIDEBAR_CTX = createContext<SidebarContext>('SidebarContext');\n\n// ─── ore-sidebar styles ──────────────────────────────────────────────────────\n\n/** ore-sidebar element interface */\nexport type SidebarElement = HTMLElement &\n OreSidebarProps & {\n /** Close the drawer. */\n closeDrawer(): void;\n /** Open the drawer. */\n openDrawer(): void;\n /** Set collapsed state imperatively. */\n setCollapsed(next: boolean): void;\n /** Toggle between collapsed and expanded. */\n toggle(): void;\n /** Toggle the drawer. */\n toggleDrawer(): void;\n };\n\n/** Sidebar component properties */\n\nexport type OreSidebarEvents = {\n 'collapsed-change': { collapsed: boolean; source: SidebarCollapseSource };\n 'drawer-change': { open: boolean; source: DrawerSource };\n};\n\nexport type OreSidebarGroupEvents = {\n 'open-change': { open: boolean };\n};\n\nexport type OreSidebarProps = {\n /** CSS media query that switches the sidebar to bottom navigation + drawer mode */\n 'bottom-nav-at'?: string;\n 'close-on-select'?: boolean;\n /** Controlled collapsed state */\n collapsed?: boolean;\n /** Whether the sidebar supports collapsing */\n collapsible?: boolean;\n 'collapse-label'?: string;\n /** Evaluate responsive and bottom-nav breakpoints against container width only. */\n 'container-breakpoints'?: boolean;\n /** Initial collapsed state in uncontrolled mode */\n 'default-collapsed'?: boolean;\n /** Drawer-only mode: no inline panel and no bottom bar, just the navbar/API-driven drawer. */\n drawer?: boolean;\n /** Accessible label for the backdrop close control. */\n 'drawer-close-label'?: string;\n 'expand-label'?: string;\n /**\n * Accessible label for the navigation landmark.\n * Use to distinguish multiple navigation regions on a page.\n * @default 'Sidebar navigation'\n */\n label?: string;\n /**\n * CSS media query that, when it matches, automatically collapses the sidebar.\n * Unset by default: no automatic collapse.\n * @example 'responsive=\"(max-width: 768px)\"'\n */\n responsive?: string;\n /** Visual style variant */\n variant?: SidebarVariant;\n};\n\n/**\n * `ore-sidebar`: A collapsible navigation sidebar with group and item support.\n *\n * @element ore-sidebar\n * @element ore-sidebar-group - Labelled group of navigation items\n * @element ore-sidebar-item - Individual navigation link or button\n *\n * @attr {boolean} collapsed - Controlled collapsed state\n * @attr {boolean} default-collapsed - Initial collapsed state for uncontrolled sidebars\n * @attr {boolean} collapsible - Show the collapse toggle button\n * @attr {boolean} drawer - Drawer-only mode: no inline panel and no bottom bar, just the navbar/API-driven drawer\n * @attr {string} variant - Visual variant: 'floating' | 'inset'\n * @attr {string} label - Accessible aria-label for the nav landmark\n *\n * @fires collapsed-change - Fired when collapsed state changes. detail: { collapsed: boolean; source: string }\n * @fires drawer-change - Fired when the drawer open state changes. detail: { open: boolean; source: string }\n *\n * @slot header - Branding or logo content above the nav\n * @slot - Navigation content (ore-sidebar-group / ore-sidebar-item)\n * @slot footer - Footer content below the nav (user info, settings, etc.)\n *\n * @cssprop --sidebar-width - Expanded sidebar width (default: 16rem)\n * @cssprop --sidebar-collapsed-width - Collapsed sidebar width (default: 3.5rem)\n * @cssprop --sidebar-drawer-width - Drawer panel width (default: min(--sidebar-width, 85vw))\n * @cssprop --sidebar-bg - Sidebar background color\n * @cssprop --sidebar-border-color - Border color\n *\n * @part drawer-backdrop - Backdrop shown behind the open drawer.\n * @part nav - Navigation container.\n * @part header - Header container.\n * @part toggle-btn - Shadow part for the `toggle-btn` element.\n * @part content - Content container.\n * @part footer - Footer container.\n * @part bottom-bar - Bottom bar container.\n * @part group - Group container.\n * @part group-header - Group header container.\n * @part group-icon - Group icon container.\n * @part group-label - Group label container.\n * @part group-items - Group items container.\n * @part item-icon - Leading item icon container.\n * @part item-label - Item label container.\n * @part item-end - Trailing item content container.\n * @part item - Item root element.\n * @example\n * ```html\n * <ore-sidebar collapsible label=\"App navigation\">\n * <span slot=\"header\">My App</span>\n * <ore-sidebar-group label=\"Main\">\n * <ore-sidebar-item href=\"/dashboard\" active>Dashboard</ore-sidebar-item>\n * <ore-sidebar-item href=\"/settings\">Settings</ore-sidebar-item>\n * </ore-sidebar-group>\n * </ore-sidebar>\n *\n * <!-- Auto-collapse on mobile -->\n * <ore-sidebar collapsible responsive=\"(max-width: 768px)\">...</ore-sidebar>\n * ```\n */\nexport const SIDEBAR_TAG = 'ore-sidebar' as const;\ndefine<OreSidebarProps>(SIDEBAR_TAG, {\n props: {\n 'bottom-nav-at': prop.string(),\n 'close-on-select': prop.bool(false),\n 'collapse-label': prop.string('Collapse sidebar'),\n collapsed: prop.bool(false),\n collapsible: prop.bool(false),\n 'container-breakpoints': prop.bool(false),\n 'default-collapsed': prop.bool(false),\n drawer: prop.bool(false),\n 'drawer-close-label': prop.string('Close sidebar'),\n 'expand-label': prop.string('Expand sidebar'),\n label: prop.string('Sidebar navigation'),\n responsive: prop.string(),\n variant: prop.string<SidebarVariant>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreSidebarEvents>();\n const slots = useSlots();\n\n const hasHeader = () => slots.has('header').value;\n const hasFooter = () => slots.has('footer').value;\n const hasLogo = () => slots.has('logo').value;\n\n const isControlled = signal(el.hasAttribute('collapsed'));\n const collapsedState = signal(isControlled.value ? el.hasAttribute('collapsed') : props['default-collapsed'].value);\n const isBottomNav = signal(false);\n const isDrawerOpen = signal(false);\n const bottomNavItems = signal<BottomNavItem[]>([]);\n const responsiveMediaMatches = signal(false);\n const responsiveSizeMatches = signal(false);\n const responsiveMaxWidthPx = signal<number | undefined>(parseMaxWidthPx(props.responsive.value));\n const hasResponsiveQuery = signal(Boolean(String(props.responsive.value ?? '').trim()));\n const bottomNavMediaMatches = signal(false);\n const bottomNavSizeMatches = signal(false);\n const bottomNavMaxWidthPx = signal<number | undefined>(parseMaxWidthPx(props['bottom-nav-at'].value));\n const isPreviewMode = signal(false);\n const bgLock = createBackgroundLock();\n let restoreDrawerFocus: FocusRestorer | null = null;\n\n const isCollapsed = () => collapsedState.value;\n const mode = computed<SidebarMode>(() => {\n if (isBottomNav.value) return 'bottom-nav';\n\n return collapsedState.value ? 'collapsed' : 'default';\n });\n\n const applyResponsiveState = () => {\n const useContainerBreakpoints = props['container-breakpoints'].value;\n const responsiveMatched = useContainerBreakpoints\n ? responsiveSizeMatches.value\n : responsiveMediaMatches.value || responsiveSizeMatches.value;\n const bottomMatched = useContainerBreakpoints\n ? bottomNavSizeMatches.value\n : bottomNavMediaMatches.value || bottomNavSizeMatches.value;\n\n // If the drawer is currently open, stay in bottom-nav mode\n // regardless of the responsive match. This prevents the drawer from\n // disappearing mid-animation or if forced open via the API on desktop.\n if (!isDrawerOpen.value) {\n isBottomNav.value = bottomMatched;\n }\n\n if (!bottomMatched && !isDrawerOpen.value) {\n setDrawerOpen(false, 'responsive');\n }\n\n if (hasResponsiveQuery.value) {\n setCollapsed(responsiveMatched, 'responsive');\n }\n };\n\n const readBottomNavItems = () => {\n if (props.drawer.value) {\n bottomNavItems.value = [];\n\n return;\n }\n\n const directItems = slots\n .elements()\n .value.filter(\n (item): item is HTMLElement =>\n item instanceof HTMLElement && item.tagName.toLowerCase() === 'ore-sidebar-item',\n );\n const promotedItems = [...el.querySelectorAll<HTMLElement>('ore-sidebar-item[bottom-nav]')];\n const sourceItems = promotedItems.length > 0 ? promotedItems : directItems;\n const next = sourceItems.map((item, index) => {\n const el = item;\n const iconSlotEl =\n (el.querySelector(':scope > [slot=\"icon\"]') as HTMLElement | null) ??\n (el.querySelector('[slot=\"icon\"]') as HTMLElement | null);\n const directIconName =\n iconSlotEl?.tagName.toLowerCase() === 'ore-icon' ? iconSlotEl.getAttribute('name') : null;\n const nestedIconName = iconSlotEl?.querySelector('ore-icon')?.getAttribute('name') ?? null;\n const rawLabel = (el.textContent ?? '').trim();\n\n return {\n active: el.hasAttribute('active'),\n disabled: el.hasAttribute('disabled'),\n href: el.getAttribute('href') ?? undefined,\n iconName: directIconName ?? nestedIconName ?? undefined,\n label: el.getAttribute('bottom-nav-label')?.trim() || rawLabel || `Item ${index + 1}`,\n source: el,\n } satisfies BottomNavItem;\n });\n\n bottomNavItems.value = next;\n };\n\n provide(SIDEBAR_CTX, {\n closeDrawer: () => setDrawerOpen(false, 'toggle'),\n closeOnSelect: props['close-on-select'],\n collapsed: computed(() => !isBottomNav.value && collapsedState.value) as Readable<boolean>,\n drawerOpen: computed(() => isDrawerOpen.value) as Readable<boolean>,\n mode: mode as Readable<SidebarMode>,\n variant: props.variant,\n });\n\n const setCollapsed = (next: boolean, source: SidebarCollapseSource) => {\n if (isCollapsed() === next) return;\n\n if (!isControlled.value) {\n collapsedState.value = next;\n }\n\n emit('collapsed-change', { collapsed: next, source });\n };\n\n const setDrawerOpen = (next: boolean, source: DrawerSource) => {\n const open = Boolean(next);\n\n // Outside drawer-only mode, opening the drawer forces the sidebar out of its inline\n // panel into the overlay presentation until the drawer closes again.\n if (open && !isBottomNav.value && !props.drawer.value) {\n isBottomNav.value = true;\n }\n\n if (isDrawerOpen.value === open) return;\n\n if (open) {\n restoreDrawerFocus = captureFocus();\n // Inert the background where the DOM structure allows (see createBackgroundLock) and\n // refocus the panel whenever focus escapes the sidebar: together these keep Tab and\n // Escape working on the open drawer even in apps where the page shares one wrapper\n // element and the background cannot be inerted.\n bgLock.lock(el);\n }\n\n isDrawerOpen.value = open;\n\n // If closing, re-evaluate responsive state to potentially exit forced bottom-nav mode\n if (!open) {\n bgLock.unlock();\n applyResponsiveState();\n queueMicrotask(() => {\n restoreDrawerFocus?.();\n restoreDrawerFocus = null;\n });\n } else {\n queueMicrotask(() => el.shadowRoot?.querySelector<HTMLElement>('nav')?.focus());\n }\n\n emit('drawer-change', { open, source });\n };\n\n const doToggle = () => {\n setCollapsed(!isCollapsed(), 'toggle');\n };\n\n const sidebarEl = el as SidebarElement;\n\n sidebarEl.setCollapsed = (next) => setCollapsed(Boolean(next), 'api');\n sidebarEl.toggle = doToggle;\n sidebarEl.openDrawer = () => setDrawerOpen(true, 'api');\n sidebarEl.closeDrawer = () => setDrawerOpen(false, 'api');\n sidebarEl.toggleDrawer = () => setDrawerOpen(!isDrawerOpen.value, 'toggle');\n\n bind({\n attr: {\n 'data-bottom-nav': () => (isBottomNav.value ? true : undefined),\n 'data-collapsed': () => (isCollapsed() && !isBottomNav.value ? true : undefined),\n 'data-drawer-open': () => (isDrawerOpen.value ? true : undefined),\n 'data-has-logo': () => (hasLogo() ? true : undefined),\n 'data-preview-mode': () => (isPreviewMode.value ? true : undefined),\n },\n });\n\n onMounted(() => {\n // Suppress transitions during initial layout so the sidebar doesn't\n // animate from a collapsed/0 state before the first ResizeObserver fires.\n el.setAttribute('data-no-transition', '');\n\n let transitionUnlocked = false;\n const unlockTransition = () => {\n if (transitionUnlocked) return;\n\n transitionUnlocked = true;\n el.removeAttribute('data-no-transition');\n };\n\n // Fallback: unlock after two frames in case ResizeObserver doesn't fire.\n requestAnimationFrame(() => requestAnimationFrame(unlockTransition));\n\n let mediaCleanup: (() => void) | undefined;\n let bottomNavCleanup: (() => void) | undefined;\n const itemObservers = new Map<HTMLElement, MutationObserver>();\n const observer = new MutationObserver(() => {\n if (!el.hasAttribute('collapsed') && !isControlled.value) return;\n\n isControlled.value = true;\n collapsedState.value = el.hasAttribute('collapsed');\n });\n\n observer.observe(el, {\n attributeFilter: ['collapsed'],\n attributes: true,\n });\n const closeOnEscape = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || !isDrawerOpen.value) return;\n\n event.preventDefault();\n setDrawerOpen(false, 'toggle');\n };\n el.addEventListener('keydown', closeOnEscape);\n\n // Focus containment: createBackgroundLock inertes background siblings, but apps that wrap\n // the whole page in one root element (so nothing can be inerted) still let Tab walk out of\n // the open drawer, and once focus is outside the host, its Escape handler is unreachable.\n // Pull focus back to the panel whenever it lands outside the sidebar. focusin is composed,\n // so its target is retargeted to this host for focus inside the shadow root.\n const keepFocusInDrawer = (event: FocusEvent) => {\n if (!isDrawerOpen.value) return;\n\n const target = event.target;\n\n if (target instanceof Node && (el === target || el.contains(target))) return;\n\n el.shadowRoot?.querySelector<HTMLElement>('nav')?.focus();\n };\n document.addEventListener('focusin', keepFocusInDrawer);\n\n watch(\n props.responsive,\n (query) => {\n mediaCleanup?.();\n mediaCleanup = undefined;\n\n const mediaQuery = String(query ?? '').trim();\n\n hasResponsiveQuery.value = Boolean(mediaQuery);\n responsiveMaxWidthPx.value = parseMaxWidthPx(mediaQuery);\n responsiveMediaMatches.value = false;\n\n const width = readContainerWidth(el);\n\n responsiveSizeMatches.value =\n width > 0 && responsiveMaxWidthPx.value != null ? width <= responsiveMaxWidthPx.value : false;\n applyResponsiveState();\n\n // For parseable max-width queries, keep behavior container-driven.\n // This avoids preview viewport controls being overridden by window width.\n if (props['container-breakpoints'].value && responsiveMaxWidthPx.value != null) {\n return;\n }\n\n if (!mediaQuery) {\n return;\n }\n\n try {\n const mediaHandle = createMediaQuery(mediaQuery);\n const syncMedia = (state: ReturnType<typeof mediaHandle.getSnapshot>) => {\n if (state) {\n responsiveMediaMatches.value = state.matches;\n applyResponsiveState();\n }\n };\n\n syncMedia(mediaHandle.getSnapshot());\n\n const mediaCleanupFn = watch(\n fromSubscribable(mediaHandle, { signal: mediaHandle.disposalSignal }),\n syncMedia,\n );\n mediaCleanup = () => {\n mediaCleanupFn.dispose();\n mediaHandle.dispose();\n };\n } catch (error) {\n if (!(error instanceof SentinelUnavailableError)) throw error;\n }\n },\n { immediate: true },\n );\n\n watch(\n props['bottom-nav-at'],\n (query) => {\n bottomNavCleanup?.();\n bottomNavCleanup = undefined;\n\n const mediaQuery = String(query ?? '').trim();\n\n bottomNavMaxWidthPx.value = parseMaxWidthPx(mediaQuery);\n bottomNavMediaMatches.value = false;\n\n const width = readContainerWidth(el);\n\n bottomNavSizeMatches.value =\n width > 0 && bottomNavMaxWidthPx.value != null ? width <= bottomNavMaxWidthPx.value : false;\n applyResponsiveState();\n\n // For parseable max-width queries, keep behavior container-driven.\n // This avoids preview viewport controls being overridden by window width.\n if (props['container-breakpoints'].value && bottomNavMaxWidthPx.value != null) {\n return;\n }\n\n if (!mediaQuery) {\n return;\n }\n\n try {\n const mediaHandle = createMediaQuery(mediaQuery);\n const syncMedia = (state: ReturnType<typeof mediaHandle.getSnapshot>) => {\n if (state) {\n bottomNavMediaMatches.value = state.matches;\n applyResponsiveState();\n }\n };\n\n syncMedia(mediaHandle.getSnapshot());\n\n const mediaCleanupFn = watch(\n fromSubscribable(mediaHandle, { signal: mediaHandle.disposalSignal }),\n syncMedia,\n );\n bottomNavCleanup = () => {\n mediaCleanupFn.dispose();\n mediaHandle.dispose();\n };\n } catch (error) {\n if (!(error instanceof SentinelUnavailableError)) throw error;\n }\n },\n { immediate: true },\n );\n\n watch(props.drawer, () => {\n applyResponsiveState();\n readBottomNavItems();\n });\n\n const stopResizeEffect =\n typeof ResizeObserver === 'function'\n ? (() => {\n const hostSize = createElementSize(el);\n onCleanup(() => hostSize.dispose());\n const hostSizeSignal = fromSubscribable(hostSize, { signal: hostSize.disposalSignal });\n\n const wrapperEl = el.parentElement;\n const containerEl = resolveContainerElement(el);\n const wrapperSize = wrapperEl ? createElementSize(wrapperEl) : undefined;\n const parentSize = containerEl && containerEl !== wrapperEl ? createElementSize(containerEl) : undefined;\n\n if (wrapperSize) {\n onCleanup(() => wrapperSize.dispose());\n }\n if (parentSize) {\n onCleanup(() => parentSize.dispose());\n }\n\n const wrapperSizeSignal = wrapperSize\n ? fromSubscribable(wrapperSize, { signal: wrapperSize.disposalSignal })\n : undefined;\n const parentSizeSignal = parentSize\n ? fromSubscribable(parentSize, { signal: parentSize.disposalSignal })\n : undefined;\n\n let rafId: number | undefined;\n\n const onResize = () => {\n if (rafId !== undefined) cancelAnimationFrame(rafId);\n\n rafId = requestAnimationFrame(() => {\n const resolvedContainer = resolveContainerElement(el);\n const width = readContainerWidth(el);\n const responsiveWasMatched = responsiveSizeMatches.value;\n const bottomNavWasMatched = bottomNavSizeMatches.value;\n const parentWidth = resolvedContainer?.clientWidth ?? 0;\n\n isPreviewMode.value = parentWidth > 0 && parentWidth < window.innerWidth;\n\n responsiveSizeMatches.value =\n width > 0 && responsiveMaxWidthPx.value != null ? width <= responsiveMaxWidthPx.value : false;\n bottomNavSizeMatches.value =\n width > 0 && bottomNavMaxWidthPx.value != null ? width <= bottomNavMaxWidthPx.value : false;\n\n if (\n responsiveWasMatched !== responsiveSizeMatches.value ||\n bottomNavWasMatched !== bottomNavSizeMatches.value\n ) {\n applyResponsiveState();\n }\n\n unlockTransition();\n });\n };\n\n return watch(\n computed(() => [\n hostSizeSignal.value?.width,\n hostSizeSignal.value?.height,\n wrapperSizeSignal?.value?.width,\n wrapperSizeSignal?.value?.height,\n parentSizeSignal?.value?.width,\n parentSizeSignal?.value?.height,\n ]),\n onResize,\n );\n })()\n : undefined;\n\n const bindItemObservers = (items: HTMLElement[]) => {\n const set = new Set(items);\n\n for (const [item, cleanup] of itemObservers) {\n if (set.has(item)) continue;\n\n cleanup.disconnect();\n itemObservers.delete(item);\n }\n\n for (const item of items) {\n if (itemObservers.has(item)) continue;\n\n const itemObserver = new MutationObserver(() => {\n // Only update if in bottom-nav mode to minimize re-renders\n if (isBottomNav.value) {\n readBottomNavItems();\n }\n });\n\n // Watch attributes and content that affect bottom-nav rendering.\n itemObserver.observe(item, {\n attributeFilter: ['active', 'bottom-nav', 'bottom-nav-label', 'disabled', 'href'],\n attributes: true,\n characterData: true,\n childList: true,\n subtree: true,\n });\n itemObservers.set(item, itemObserver);\n }\n };\n\n const syncBottomNavItems = () => {\n bindItemObservers([...el.querySelectorAll<HTMLElement>('ore-sidebar-item')]);\n readBottomNavItems();\n };\n watch(slots.elements(), syncBottomNavItems, { immediate: true });\n const nestedItemsObserver = new MutationObserver(syncBottomNavItems);\n nestedItemsObserver.observe(el, { childList: true, subtree: true });\n\n return () => {\n observer.disconnect();\n nestedItemsObserver.disconnect();\n el.removeEventListener('keydown', closeOnEscape);\n document.removeEventListener('focusin', keepFocusInDrawer);\n bgLock.unlock();\n mediaCleanup?.();\n bottomNavCleanup?.();\n stopResizeEffect?.dispose();\n\n for (const itemObserver of itemObservers.values()) {\n itemObserver.disconnect();\n }\n\n itemObservers.clear();\n };\n });\n\n return html`\n <button\n class=\"drawer-backdrop\"\n part=\"drawer-backdrop\"\n type=\"button\"\n aria-label=\"${props['drawer-close-label']}\"\n ?hidden=${() => !isDrawerOpen.value}\n @click=${() => setDrawerOpen(false, 'toggle')}></button>\n <nav\n aria-label=\"${props.label}\"\n aria-hidden=${() => ((isBottomNav.value || props.drawer.value) && !isDrawerOpen.value ? 'true' : null)}\n part=\"nav\"\n tabindex=\"-1\"\n ?inert=${() => (isBottomNav.value || props.drawer.value) && !isDrawerOpen.value}>\n <div class=\"sidebar-header\" part=\"header\" ?hidden=${() => !hasHeader() && !props.collapsible.value}>\n <span class=\"sidebar-logo\" ?hidden=${() => !hasLogo()}>\n <slot name=\"logo\"></slot>\n </span>\n <span class=\"sidebar-header-content\">\n <slot name=\"header\"></slot>\n </span>\n <button\n class=\"toggle-btn\"\n part=\"toggle-btn\"\n type=\"button\"\n ?hidden=${() => !props.collapsible.value}\n aria-label=\"${() => (isCollapsed() ? props['expand-label'].value : props['collapse-label'].value)}\"\n aria-expanded=\"${() => !isCollapsed()}\"\n @click=\"${doToggle}\">\n <ore-icon\n class=\"toggle-icon\"\n name=${() => (isCollapsed() ? 'panel-left-open' : 'panel-left-close')}\n size=\"18\"\n stroke-width=\"2\"\n aria-hidden=\"true\"></ore-icon>\n </button>\n </div>\n <div class=\"sidebar-content\" part=\"content\">\n <slot></slot>\n </div>\n <div class=\"sidebar-footer\" part=\"footer\" ?hidden=${() => !hasFooter()}>\n <slot name=\"footer\"></slot>\n </div>\n </nav>\n\n <div class=\"bottom-bar\" part=\"bottom-bar\" ?hidden=${() => !isBottomNav.value}>\n ${() =>\n bottomNavItems.value.map((item) => {\n const className = `bottom-tab${item.active ? ' bottom-tab-active' : ''}`;\n\n if (item.href && !item.disabled) {\n return html`\n <a\n class=\"${className}\"\n href=\"${item.href}\"\n aria-current=\"${item.active ? 'page' : null}\"\n data-active=\"${item.active ? 'true' : null}\">\n <span class=\"bottom-tab-icon\" aria-hidden=\"true\" ?hidden=${() => !item.iconName}>\n <ore-icon name=\"${item.iconName}\" size=\"18\" stroke-width=\"2\"></ore-icon>\n </span>\n <span class=\"bottom-tab-label\">${item.label}</span>\n </a>\n `;\n }\n\n return html`\n <button\n class=\"${className}\"\n type=\"button\"\n ?disabled=${item.disabled}\n aria-current=\"${item.active ? 'page' : null}\"\n data-active=\"${item.active ? 'true' : null}\"\n @click=${() => item.source.click()}>\n <span class=\"bottom-tab-icon\" aria-hidden=\"true\" ?hidden=${() => !item.iconName}>\n <ore-icon name=\"${item.iconName}\" size=\"18\" stroke-width=\"2\"></ore-icon>\n </span>\n <span class=\"bottom-tab-label\">${item.label}</span>\n </button>\n `;\n })}\n </div>\n `;\n },\n styles: [coarsePointerMixin, reducedMotionMixin, sidebarStyles],\n});\n\n// ─── ore-sidebar-group styles ────────────────────────────────────────────────\n\n/** Sidebar group properties */\nexport type OreSidebarGroupProps = {\n /** Whether this group can be collapsed */\n collapsible?: boolean;\n /** Initial open state in uncontrolled mode */\n 'default-open'?: boolean;\n /** Accessible label for the group */\n label?: string;\n /** Controlled open state */\n open?: boolean;\n};\n\n/**\n * `ore-sidebar-group`: A labelled section within `ore-sidebar`.\n *\n * @element ore-sidebar-group\n *\n * @attr {string} label - Group label text\n * @attr {boolean} collapsible - Whether this group can be toggled open/closed\n * @attr {boolean} open - Controlled expanded state\n * @attr {boolean} default-open - Initial expanded state in uncontrolled mode\n *\n * @fires open-change - Fired when the group open state changes (collapsible groups only). detail: { open: boolean }\n *\n * @slot - Navigation items (`ore-sidebar-item`)\n * @slot icon - Icon displayed before the label\n *\n * @example\n * ```html\n * <ore-sidebar-group label=\"Main\" collapsible open>\n * <ore-sidebar-item href=\"/home\">Home</ore-sidebar-item>\n * </ore-sidebar-group>\n * ```\n */\nexport const SIDEBAR_GROUP_TAG = 'ore-sidebar-group' as const;\ndefine<OreSidebarGroupProps>(SIDEBAR_GROUP_TAG, {\n props: {\n collapsible: prop.bool(false),\n 'default-open': prop.bool(true),\n label: prop.string(),\n open: {\n default: undefined as boolean | undefined,\n parse: (value: string | null) => (value == null ? undefined : value === '' || value === 'true'),\n reflect: false,\n },\n },\n setup(props) {\n const slots = useSlots();\n\n const hasIcon = () => slots.has('icon').value;\n const sidebarCtx = inject(SIDEBAR_CTX);\n\n bind({\n attr: {\n 'sidebar-bottom-nav': () =>\n sidebarCtx?.mode.value === 'bottom-nav' && !sidebarCtx?.drawerOpen.value ? true : undefined,\n 'sidebar-collapsed': () => (sidebarCtx?.collapsed.value ? true : undefined),\n },\n });\n\n const isControlled = () => props.open.value !== undefined;\n const openState = signal(props['default-open'].value);\n const isOpen = computed(() => {\n if (!props.collapsible.value) return true;\n\n if (isControlled()) return props.open.value ?? false;\n\n return openState.value;\n });\n\n watch(props.open, (value) => {\n if (value === undefined) return;\n\n openState.value = value;\n });\n\n bind({\n attr: {\n open: () => (isOpen.value ? true : undefined),\n },\n });\n\n return html`\n <details class=\"group\" part=\"group\" ?open=${isOpen}>\n <summary\n class=\"group-header\"\n part=\"group-header\"\n aria-expanded=\"${() => (props.collapsible.value ? String(props.open.value) : null)}\"\n @click=${(e: MouseEvent) => {\n if (!props.collapsible.value) {\n e.preventDefault();\n }\n }}>\n <span class=\"group-icon\" part=\"group-icon\" ?hidden=${() => !hasIcon()} aria-hidden=\"true\">\n <slot name=\"icon\"></slot>\n </span>\n <span class=\"group-label\" part=\"group-label\">${props.label}</span>\n <span class=\"chevron\" ?hidden=${() => !props.collapsible.value} aria-hidden=\"true\">\n <ore-icon name=\"chevron-right\" size=\"12\" stroke-width=\"2\" aria-hidden=\"true\"></ore-icon>\n </span>\n </summary>\n <div class=\"group-items\" part=\"group-items\" role=\"list\">\n <slot></slot>\n </div>\n </details>\n `;\n },\n styles: [reducedMotionMixin, groupStyles],\n});\n\n// ─── ore-sidebar-item styles ─────────────────────────────────────────────────\n\n/** Sidebar item properties */\nexport type OreSidebarItemProps = {\n /** Whether this item represents the current page/section */\n active?: boolean;\n 'bottom-nav'?: boolean;\n 'bottom-nav-label'?: string;\n /** Whether this item is disabled */\n disabled?: boolean;\n /** Navigation href: renders an `<a>` when set, otherwise a `<button>` */\n href?: string;\n label?: string;\n /**\n * Relationship of the linked URL (`rel` attribute on the inner `<a>`).\n * Only applies when `href` is set.\n */\n rel?: string;\n /**\n * Browsing context for the link (`target` attribute on the inner `<a>`).\n * Only applies when `href` is set.\n */\n target?: string;\n};\n\n/**\n * `ore-sidebar-item`: An individual navigation item in a `ore-sidebar`.\n *\n * Renders as an `<a>` when `href` is provided, otherwise as a `<button>`.\n * Marks the active page via `aria-current=\"page\"` when the `active` attribute is set.\n *\n * @element ore-sidebar-item\n *\n * @attr {string} href - Link URL; renders an anchor when set\n * @attr {boolean} active - Marks the item as the current page\n * @attr {boolean} disabled - Disables the item\n * @attr {string} rel - Anchor `rel` attribute (links only)\n * @attr {string} target - Anchor `target` attribute (links only)\n *\n * @slot - Label text\n * @slot icon - Leading icon\n * @slot end - Trailing content (badge, shortcut, arrow, etc.)\n *\n * @cssprop --sidebar-item-color - Default text color\n * @cssprop --sidebar-item-hover-bg - Hover background\n * @cssprop --sidebar-item-hover-color - Hover text color\n * @cssprop --sidebar-item-active-bg - Active background\n * @cssprop --sidebar-item-active-color - Active text color\n * @cssprop --sidebar-item-indicator - Active indicator bar color\n *\n * @part item - The inner anchor or button element\n * @part item-icon - The icon wrapper\n * @part item-label - The label wrapper\n * @part item-end - The trailing content wrapper\n *\n * @example\n * ```html\n * <ore-sidebar-item href=\"/dashboard\" active>\n * <span slot=\"icon\">🏠</span>\n * Dashboard\n * </ore-sidebar-item>\n *\n * <ore-sidebar-item href=\"/users\">\n * <span slot=\"icon\">👤</span>\n * Users\n * <ore-badge slot=\"end\" color=\"primary\">3</ore-badge>\n * </ore-sidebar-item>\n * ```\n */\nexport const SIDEBAR_ITEM_TAG = 'ore-sidebar-item' as const;\ndefine<OreSidebarItemProps>(SIDEBAR_ITEM_TAG, {\n props: {\n active: prop.bool(false),\n 'bottom-nav': prop.bool(false),\n 'bottom-nav-label': prop.string(),\n disabled: prop.bool(false),\n href: prop.string(),\n label: prop.string(),\n rel: prop.string(),\n target: prop.string(),\n },\n setup(props) {\n const slots = useSlots();\n\n const hasIcon = () => slots.has('icon').value;\n const hasEnd = () => slots.has('end').value;\n const sidebarCtx = inject(SIDEBAR_CTX);\n\n bind({\n attr: {\n 'sidebar-bottom-nav': () =>\n sidebarCtx?.mode.value === 'bottom-nav' && !sidebarCtx?.drawerOpen.value ? true : undefined,\n 'sidebar-collapsed': () => (sidebarCtx?.collapsed.value ? true : undefined),\n },\n });\n\n const isLink = () => !!props.href.value && !props.disabled.value;\n\n // Prevent reverse tabnapping: auto-inject noopener + noreferrer for _blank links.\n const effectiveRel = computed(() => computeSafeRel(props.rel.value, props.target.value));\n const closeDrawerOnSelect = (event: MouseEvent) => {\n if (!sidebarCtx?.closeOnSelect.value || !sidebarCtx.drawerOpen.value) return;\n if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;\n\n queueMicrotask(sidebarCtx.closeDrawer);\n };\n\n const renderItemContent = () => html`\n <span class=\"item-icon\" part=\"item-icon\" ?hidden=${() => !hasIcon()} aria-hidden=\"true\">\n <slot name=\"icon\"></slot>\n </span>\n <span class=\"item-label\" part=\"item-label\"><slot></slot></span>\n <span class=\"item-end\" part=\"item-end\" ?hidden=${() => !hasEnd()}>\n <slot name=\"end\"></slot>\n </span>\n `;\n\n return html`\n ${() => {\n if (isLink()) {\n return html`\n <a\n class=\"item\"\n part=\"item\"\n href=\"${props.href}\"\n rel=\"${effectiveRel}\"\n target=\"${props.target}\"\n aria-label=\"${props.label}\"\n aria-current=\"${() => (props.active.value ? 'page' : null)}\"\n @click=${closeDrawerOnSelect}>\n ${renderItemContent()}\n </a>\n `;\n }\n\n if (props.disabled.value) {\n return html`\n <div\n class=\"item\"\n part=\"item\"\n aria-disabled=\"true\"\n tabindex=\"-1\"\n aria-label=\"${props.label}\"\n aria-current=\"${() => (props.active.value ? 'page' : null)}\">\n ${renderItemContent()}\n </div>\n `;\n }\n\n return html`\n <button\n class=\"item\"\n part=\"item\"\n type=\"button\"\n ?disabled=\"${props.disabled}\"\n aria-label=\"${props.label}\"\n aria-current=\"${() => (props.active.value ? 'page' : null)}\"\n @click=${closeDrawerOnSelect}>\n ${renderItemContent()}\n </button>\n `;\n }}\n `;\n },\n styles: [coarsePointerMixin, itemStyles],\n});\n"],"mappings":"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"}
|
package/dist/sidebar.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidebar.js","names":[],"sources":["../src/layout/sidebar/sidebar.ts"],"sourcesContent":["import { captureFocus, type FocusRestorer } from '@vielzeug/focus';\nimport {\n bind,\n createContext,\n define,\n getHost,\n html,\n inject,\n onCleanup,\n onMounted,\n prop,\n provide,\n useEmit,\n useSlots,\n} from '@vielzeug/ore';\nimport { computed, fromSubscribable, type Readable, signal, watch } from '@vielzeug/ripple';\nimport { createElementSize, createMediaQuery, SentinelUnavailableError } from '@vielzeug/sentinel';\n\nimport { createBackgroundLock } from '../../overlay/shared/background-lock';\nimport { coarsePointerMixin, reducedMotionMixin } from '../../styles';\nimport { computeSafeRel } from '../../utils';\nimport { parseMaxWidthPx, readContainerWidth, resolveContainerElement } from '../shared';\nimport sidebarStyles from './sidebar.css?inline';\nimport groupStyles from './sidebar-group.css?inline';\nimport itemStyles from './sidebar-item.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\n\n// ─── Types ────────────────────────────────────────────────────────────────\n\ntype SidebarVariant = 'floating' | 'inset';\ntype SidebarCollapseSource = 'api' | 'responsive' | 'toggle';\ntype DrawerSource = 'api' | 'responsive' | 'toggle';\ntype SidebarMode = 'bottom-nav' | 'collapsed' | 'default';\n\ntype BottomNavItem = {\n active: boolean;\n disabled: boolean;\n href?: string;\n iconName?: string;\n label: string;\n source: HTMLElement;\n};\n\n/** Context provided by `ore-sidebar` to its `ore-sidebar-group` and `ore-sidebar-item` children. */\nexport type SidebarContext = {\n closeDrawer: () => void;\n closeOnSelect: Readable<boolean>;\n collapsed: Readable<boolean>;\n drawerOpen: Readable<boolean>;\n mode: Readable<SidebarMode>;\n variant: Readable<SidebarVariant | undefined>;\n};\n\n/** Injection key for the sidebar context. */\nexport const SIDEBAR_CTX = createContext<SidebarContext>('SidebarContext');\n\n// ─── ore-sidebar styles ──────────────────────────────────────────────────────\n\n/** ore-sidebar element interface */\nexport type SidebarElement = HTMLElement &\n OreSidebarProps & {\n /** Close the drawer. */\n closeDrawer(): void;\n /** Open the drawer. */\n openDrawer(): void;\n /** Set collapsed state imperatively. */\n setCollapsed(next: boolean): void;\n /** Toggle between collapsed and expanded. */\n toggle(): void;\n /** Toggle the drawer. */\n toggleDrawer(): void;\n };\n\n/** Sidebar component properties */\n\nexport type OreSidebarEvents = {\n 'collapsed-change': { collapsed: boolean; source: SidebarCollapseSource };\n 'drawer-change': { open: boolean; source: DrawerSource };\n};\n\nexport type OreSidebarGroupEvents = {\n 'open-change': { open: boolean };\n};\n\nexport type OreSidebarProps = {\n /** CSS media query that switches the sidebar to bottom navigation + drawer mode */\n 'bottom-nav-at'?: string;\n 'close-on-select'?: boolean;\n /** Controlled collapsed state */\n collapsed?: boolean;\n /** Whether the sidebar supports collapsing */\n collapsible?: boolean;\n 'collapse-label'?: string;\n /** Evaluate responsive and bottom-nav breakpoints against container width only. */\n 'container-breakpoints'?: boolean;\n /** Initial collapsed state in uncontrolled mode */\n 'default-collapsed'?: boolean;\n /** Drawer-only mode: no inline panel and no bottom bar, just the navbar/API-driven drawer. */\n drawer?: boolean;\n /** Accessible label for the backdrop close control. */\n 'drawer-close-label'?: string;\n 'expand-label'?: string;\n /**\n * Accessible label for the navigation landmark.\n * Use to distinguish multiple navigation regions on a page.\n * @default 'Sidebar navigation'\n */\n label?: string;\n /**\n * CSS media query that, when it matches, automatically collapses the sidebar.\n * Unset by default — no automatic collapse.\n * @example 'responsive=\"(max-width: 768px)\"'\n */\n responsive?: string;\n /** Visual style variant */\n variant?: SidebarVariant;\n};\n\n/**\n * `ore-sidebar` — A collapsible navigation sidebar with group and item support.\n *\n * @element ore-sidebar\n * @element ore-sidebar-group - Labelled group of navigation items\n * @element ore-sidebar-item - Individual navigation link or button\n *\n * @attr {boolean} collapsed - Controlled collapsed state\n * @attr {boolean} default-collapsed - Initial collapsed state for uncontrolled sidebars\n * @attr {boolean} collapsible - Show the collapse toggle button\n * @attr {boolean} drawer - Drawer-only mode: no inline panel and no bottom bar, just the navbar/API-driven drawer\n * @attr {string} variant - Visual variant: 'floating' | 'inset'\n * @attr {string} label - Accessible aria-label for the nav landmark\n *\n * @fires collapsed-change - Fired when collapsed state changes. detail: { collapsed: boolean; source: string }\n * @fires drawer-change - Fired when the drawer open state changes. detail: { open: boolean; source: string }\n *\n * @slot header - Branding or logo content above the nav\n * @slot - Navigation content (ore-sidebar-group / ore-sidebar-item)\n * @slot footer - Footer content below the nav (user info, settings, etc.)\n *\n * @cssprop --sidebar-width - Expanded sidebar width (default: 16rem)\n * @cssprop --sidebar-collapsed-width - Collapsed sidebar width (default: 3.5rem)\n * @cssprop --sidebar-drawer-width - Drawer panel width (default: min(--sidebar-width, 85vw))\n * @cssprop --sidebar-bg - Sidebar background color\n * @cssprop --sidebar-border-color - Border color\n *\n * @part drawer-backdrop - Backdrop shown behind the open drawer.\n * @part nav - Navigation container.\n * @part header - Header container.\n * @part toggle-btn - Shadow part for the `toggle-btn` element.\n * @part content - Content container.\n * @part footer - Footer container.\n * @part bottom-bar - Bottom bar container.\n * @part group - Group container.\n * @part group-header - Group header container.\n * @part group-icon - Group icon container.\n * @part group-label - Group label container.\n * @part group-items - Group items container.\n * @part item-icon - Leading item icon container.\n * @part item-label - Item label container.\n * @part item-end - Trailing item content container.\n * @part item - Item root element.\n * @example\n * ```html\n * <ore-sidebar collapsible label=\"App navigation\">\n * <span slot=\"header\">My App</span>\n * <ore-sidebar-group label=\"Main\">\n * <ore-sidebar-item href=\"/dashboard\" active>Dashboard</ore-sidebar-item>\n * <ore-sidebar-item href=\"/settings\">Settings</ore-sidebar-item>\n * </ore-sidebar-group>\n * </ore-sidebar>\n *\n * <!-- Auto-collapse on mobile -->\n * <ore-sidebar collapsible responsive=\"(max-width: 768px)\">...</ore-sidebar>\n * ```\n */\nexport const SIDEBAR_TAG = 'ore-sidebar' as const;\ndefine<OreSidebarProps>(SIDEBAR_TAG, {\n props: {\n 'bottom-nav-at': prop.string(),\n 'close-on-select': prop.bool(false),\n 'collapse-label': prop.string('Collapse sidebar'),\n collapsed: prop.bool(false),\n collapsible: prop.bool(false),\n 'container-breakpoints': prop.bool(false),\n 'default-collapsed': prop.bool(false),\n drawer: prop.bool(false),\n 'drawer-close-label': prop.string('Close sidebar'),\n 'expand-label': prop.string('Expand sidebar'),\n label: prop.string('Sidebar navigation'),\n responsive: prop.string(),\n variant: prop.string<SidebarVariant>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreSidebarEvents>();\n const slots = useSlots();\n\n const hasHeader = () => slots.has('header').value;\n const hasFooter = () => slots.has('footer').value;\n const hasLogo = () => slots.has('logo').value;\n\n const isControlled = signal(el.hasAttribute('collapsed'));\n const collapsedState = signal(isControlled.value ? el.hasAttribute('collapsed') : props['default-collapsed'].value);\n const isBottomNav = signal(false);\n const isDrawerOpen = signal(false);\n const bottomNavItems = signal<BottomNavItem[]>([]);\n const responsiveMediaMatches = signal(false);\n const responsiveSizeMatches = signal(false);\n const responsiveMaxWidthPx = signal<number | undefined>(parseMaxWidthPx(props.responsive.value));\n const hasResponsiveQuery = signal(Boolean(String(props.responsive.value ?? '').trim()));\n const bottomNavMediaMatches = signal(false);\n const bottomNavSizeMatches = signal(false);\n const bottomNavMaxWidthPx = signal<number | undefined>(parseMaxWidthPx(props['bottom-nav-at'].value));\n const isPreviewMode = signal(false);\n const bgLock = createBackgroundLock();\n let restoreDrawerFocus: FocusRestorer | null = null;\n\n const isCollapsed = () => collapsedState.value;\n const mode = computed<SidebarMode>(() => {\n if (isBottomNav.value) return 'bottom-nav';\n\n return collapsedState.value ? 'collapsed' : 'default';\n });\n\n const applyResponsiveState = () => {\n const useContainerBreakpoints = props['container-breakpoints'].value;\n const responsiveMatched = useContainerBreakpoints\n ? responsiveSizeMatches.value\n : responsiveMediaMatches.value || responsiveSizeMatches.value;\n const bottomMatched = useContainerBreakpoints\n ? bottomNavSizeMatches.value\n : bottomNavMediaMatches.value || bottomNavSizeMatches.value;\n\n // If the drawer is currently open, stay in bottom-nav mode\n // regardless of the responsive match. This prevents the drawer from\n // disappearing mid-animation or if forced open via the API on desktop.\n if (!isDrawerOpen.value) {\n isBottomNav.value = bottomMatched;\n }\n\n if (!bottomMatched && !isDrawerOpen.value) {\n setDrawerOpen(false, 'responsive');\n }\n\n if (hasResponsiveQuery.value) {\n setCollapsed(responsiveMatched, 'responsive');\n }\n };\n\n const readBottomNavItems = () => {\n if (props.drawer.value) {\n bottomNavItems.value = [];\n\n return;\n }\n\n const directItems = slots\n .elements()\n .value.filter(\n (item): item is HTMLElement =>\n item instanceof HTMLElement && item.tagName.toLowerCase() === 'ore-sidebar-item',\n );\n const promotedItems = [...el.querySelectorAll<HTMLElement>('ore-sidebar-item[bottom-nav]')];\n const sourceItems = promotedItems.length > 0 ? promotedItems : directItems;\n const next = sourceItems.map((item, index) => {\n const el = item;\n const iconSlotEl =\n (el.querySelector(':scope > [slot=\"icon\"]') as HTMLElement | null) ??\n (el.querySelector('[slot=\"icon\"]') as HTMLElement | null);\n const directIconName =\n iconSlotEl?.tagName.toLowerCase() === 'ore-icon' ? iconSlotEl.getAttribute('name') : null;\n const nestedIconName = iconSlotEl?.querySelector('ore-icon')?.getAttribute('name') ?? null;\n const rawLabel = (el.textContent ?? '').trim();\n\n return {\n active: el.hasAttribute('active'),\n disabled: el.hasAttribute('disabled'),\n href: el.getAttribute('href') ?? undefined,\n iconName: directIconName ?? nestedIconName ?? undefined,\n label: el.getAttribute('bottom-nav-label')?.trim() || rawLabel || `Item ${index + 1}`,\n source: el,\n } satisfies BottomNavItem;\n });\n\n bottomNavItems.value = next;\n };\n\n provide(SIDEBAR_CTX, {\n closeDrawer: () => setDrawerOpen(false, 'toggle'),\n closeOnSelect: props['close-on-select'],\n collapsed: computed(() => !isBottomNav.value && collapsedState.value) as Readable<boolean>,\n drawerOpen: computed(() => isDrawerOpen.value) as Readable<boolean>,\n mode: mode as Readable<SidebarMode>,\n variant: props.variant,\n });\n\n const setCollapsed = (next: boolean, source: SidebarCollapseSource) => {\n if (isCollapsed() === next) return;\n\n if (!isControlled.value) {\n collapsedState.value = next;\n }\n\n emit('collapsed-change', { collapsed: next, source });\n };\n\n const setDrawerOpen = (next: boolean, source: DrawerSource) => {\n const open = Boolean(next);\n\n // Outside drawer-only mode, opening the drawer forces the sidebar out of its inline\n // panel into the overlay presentation until the drawer closes again.\n if (open && !isBottomNav.value && !props.drawer.value) {\n isBottomNav.value = true;\n }\n\n if (isDrawerOpen.value === open) return;\n\n if (open) {\n restoreDrawerFocus = captureFocus();\n // Inert the background where the DOM structure allows (see createBackgroundLock) and\n // refocus the panel whenever focus escapes the sidebar — together these keep Tab and\n // Escape working on the open drawer even in apps where the page shares one wrapper\n // element and the background cannot be inerted.\n bgLock.lock(el);\n }\n\n isDrawerOpen.value = open;\n\n // If closing, re-evaluate responsive state to potentially exit forced bottom-nav mode\n if (!open) {\n bgLock.unlock();\n applyResponsiveState();\n queueMicrotask(() => {\n restoreDrawerFocus?.();\n restoreDrawerFocus = null;\n });\n } else {\n queueMicrotask(() => el.shadowRoot?.querySelector<HTMLElement>('nav')?.focus());\n }\n\n emit('drawer-change', { open, source });\n };\n\n const doToggle = () => {\n setCollapsed(!isCollapsed(), 'toggle');\n };\n\n const sidebarEl = el as SidebarElement;\n\n sidebarEl.setCollapsed = (next) => setCollapsed(Boolean(next), 'api');\n sidebarEl.toggle = doToggle;\n sidebarEl.openDrawer = () => setDrawerOpen(true, 'api');\n sidebarEl.closeDrawer = () => setDrawerOpen(false, 'api');\n sidebarEl.toggleDrawer = () => setDrawerOpen(!isDrawerOpen.value, 'toggle');\n\n bind({\n attr: {\n 'data-bottom-nav': () => (isBottomNav.value ? true : undefined),\n 'data-collapsed': () => (isCollapsed() && !isBottomNav.value ? true : undefined),\n 'data-drawer-open': () => (isDrawerOpen.value ? true : undefined),\n 'data-has-logo': () => (hasLogo() ? true : undefined),\n 'data-preview-mode': () => (isPreviewMode.value ? true : undefined),\n },\n });\n\n onMounted(() => {\n // Suppress transitions during initial layout so the sidebar doesn't\n // animate from a collapsed/0 state before the first ResizeObserver fires.\n el.setAttribute('data-no-transition', '');\n\n let transitionUnlocked = false;\n const unlockTransition = () => {\n if (transitionUnlocked) return;\n\n transitionUnlocked = true;\n el.removeAttribute('data-no-transition');\n };\n\n // Fallback: unlock after two frames in case ResizeObserver doesn't fire.\n requestAnimationFrame(() => requestAnimationFrame(unlockTransition));\n\n let mediaCleanup: (() => void) | undefined;\n let bottomNavCleanup: (() => void) | undefined;\n const itemObservers = new Map<HTMLElement, MutationObserver>();\n const observer = new MutationObserver(() => {\n if (!el.hasAttribute('collapsed') && !isControlled.value) return;\n\n isControlled.value = true;\n collapsedState.value = el.hasAttribute('collapsed');\n });\n\n observer.observe(el, {\n attributeFilter: ['collapsed'],\n attributes: true,\n });\n const closeOnEscape = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || !isDrawerOpen.value) return;\n\n event.preventDefault();\n setDrawerOpen(false, 'toggle');\n };\n el.addEventListener('keydown', closeOnEscape);\n\n // Focus containment: createBackgroundLock inertes background siblings, but apps that wrap\n // the whole page in one root element (so nothing can be inerted) still let Tab walk out of\n // the open drawer — and once focus is outside the host, its Escape handler is unreachable.\n // Pull focus back to the panel whenever it lands outside the sidebar. focusin is composed,\n // so its target is retargeted to this host for focus inside the shadow root.\n const keepFocusInDrawer = (event: FocusEvent) => {\n if (!isDrawerOpen.value) return;\n\n const target = event.target;\n\n if (target instanceof Node && (el === target || el.contains(target))) return;\n\n el.shadowRoot?.querySelector<HTMLElement>('nav')?.focus();\n };\n document.addEventListener('focusin', keepFocusInDrawer);\n\n watch(\n props.responsive,\n (query) => {\n mediaCleanup?.();\n mediaCleanup = undefined;\n\n const mediaQuery = String(query ?? '').trim();\n\n hasResponsiveQuery.value = Boolean(mediaQuery);\n responsiveMaxWidthPx.value = parseMaxWidthPx(mediaQuery);\n responsiveMediaMatches.value = false;\n\n const width = readContainerWidth(el);\n\n responsiveSizeMatches.value =\n width > 0 && responsiveMaxWidthPx.value != null ? width <= responsiveMaxWidthPx.value : false;\n applyResponsiveState();\n\n // For parseable max-width queries, keep behavior container-driven.\n // This avoids preview viewport controls being overridden by window width.\n if (props['container-breakpoints'].value && responsiveMaxWidthPx.value != null) {\n return;\n }\n\n if (!mediaQuery) {\n return;\n }\n\n try {\n const mediaHandle = createMediaQuery(mediaQuery);\n const syncMedia = (state: ReturnType<typeof mediaHandle.getSnapshot>) => {\n if (state) {\n responsiveMediaMatches.value = state.matches;\n applyResponsiveState();\n }\n };\n\n syncMedia(mediaHandle.getSnapshot());\n\n const mediaCleanupFn = watch(\n fromSubscribable(mediaHandle, { signal: mediaHandle.disposalSignal }),\n syncMedia,\n );\n mediaCleanup = () => {\n mediaCleanupFn.dispose();\n mediaHandle.dispose();\n };\n } catch (error) {\n if (!(error instanceof SentinelUnavailableError)) throw error;\n }\n },\n { immediate: true },\n );\n\n watch(\n props['bottom-nav-at'],\n (query) => {\n bottomNavCleanup?.();\n bottomNavCleanup = undefined;\n\n const mediaQuery = String(query ?? '').trim();\n\n bottomNavMaxWidthPx.value = parseMaxWidthPx(mediaQuery);\n bottomNavMediaMatches.value = false;\n\n const width = readContainerWidth(el);\n\n bottomNavSizeMatches.value =\n width > 0 && bottomNavMaxWidthPx.value != null ? width <= bottomNavMaxWidthPx.value : false;\n applyResponsiveState();\n\n // For parseable max-width queries, keep behavior container-driven.\n // This avoids preview viewport controls being overridden by window width.\n if (props['container-breakpoints'].value && bottomNavMaxWidthPx.value != null) {\n return;\n }\n\n if (!mediaQuery) {\n return;\n }\n\n try {\n const mediaHandle = createMediaQuery(mediaQuery);\n const syncMedia = (state: ReturnType<typeof mediaHandle.getSnapshot>) => {\n if (state) {\n bottomNavMediaMatches.value = state.matches;\n applyResponsiveState();\n }\n };\n\n syncMedia(mediaHandle.getSnapshot());\n\n const mediaCleanupFn = watch(\n fromSubscribable(mediaHandle, { signal: mediaHandle.disposalSignal }),\n syncMedia,\n );\n bottomNavCleanup = () => {\n mediaCleanupFn.dispose();\n mediaHandle.dispose();\n };\n } catch (error) {\n if (!(error instanceof SentinelUnavailableError)) throw error;\n }\n },\n { immediate: true },\n );\n\n watch(props.drawer, () => {\n applyResponsiveState();\n readBottomNavItems();\n });\n\n const stopResizeEffect =\n typeof ResizeObserver === 'function'\n ? (() => {\n const hostSize = createElementSize(el);\n onCleanup(() => hostSize.dispose());\n const hostSizeSignal = fromSubscribable(hostSize, { signal: hostSize.disposalSignal });\n\n const wrapperEl = el.parentElement;\n const containerEl = resolveContainerElement(el);\n const wrapperSize = wrapperEl ? createElementSize(wrapperEl) : undefined;\n const parentSize = containerEl && containerEl !== wrapperEl ? createElementSize(containerEl) : undefined;\n\n if (wrapperSize) {\n onCleanup(() => wrapperSize.dispose());\n }\n if (parentSize) {\n onCleanup(() => parentSize.dispose());\n }\n\n const wrapperSizeSignal = wrapperSize\n ? fromSubscribable(wrapperSize, { signal: wrapperSize.disposalSignal })\n : undefined;\n const parentSizeSignal = parentSize\n ? fromSubscribable(parentSize, { signal: parentSize.disposalSignal })\n : undefined;\n\n let rafId: number | undefined;\n\n const onResize = () => {\n if (rafId !== undefined) cancelAnimationFrame(rafId);\n\n rafId = requestAnimationFrame(() => {\n const resolvedContainer = resolveContainerElement(el);\n const width = readContainerWidth(el);\n const responsiveWasMatched = responsiveSizeMatches.value;\n const bottomNavWasMatched = bottomNavSizeMatches.value;\n const parentWidth = resolvedContainer?.clientWidth ?? 0;\n\n isPreviewMode.value = parentWidth > 0 && parentWidth < window.innerWidth;\n\n responsiveSizeMatches.value =\n width > 0 && responsiveMaxWidthPx.value != null ? width <= responsiveMaxWidthPx.value : false;\n bottomNavSizeMatches.value =\n width > 0 && bottomNavMaxWidthPx.value != null ? width <= bottomNavMaxWidthPx.value : false;\n\n if (\n responsiveWasMatched !== responsiveSizeMatches.value ||\n bottomNavWasMatched !== bottomNavSizeMatches.value\n ) {\n applyResponsiveState();\n }\n\n unlockTransition();\n });\n };\n\n return watch(\n computed(() => [\n hostSizeSignal.value?.width,\n hostSizeSignal.value?.height,\n wrapperSizeSignal?.value?.width,\n wrapperSizeSignal?.value?.height,\n parentSizeSignal?.value?.width,\n parentSizeSignal?.value?.height,\n ]),\n onResize,\n );\n })()\n : undefined;\n\n const bindItemObservers = (items: HTMLElement[]) => {\n const set = new Set(items);\n\n for (const [item, cleanup] of itemObservers) {\n if (set.has(item)) continue;\n\n cleanup.disconnect();\n itemObservers.delete(item);\n }\n\n for (const item of items) {\n if (itemObservers.has(item)) continue;\n\n const itemObserver = new MutationObserver(() => {\n // Only update if in bottom-nav mode to minimize re-renders\n if (isBottomNav.value) {\n readBottomNavItems();\n }\n });\n\n // Watch attributes and content that affect bottom-nav rendering.\n itemObserver.observe(item, {\n attributeFilter: ['active', 'bottom-nav', 'bottom-nav-label', 'disabled', 'href'],\n attributes: true,\n characterData: true,\n childList: true,\n subtree: true,\n });\n itemObservers.set(item, itemObserver);\n }\n };\n\n const syncBottomNavItems = () => {\n bindItemObservers([...el.querySelectorAll<HTMLElement>('ore-sidebar-item')]);\n readBottomNavItems();\n };\n watch(slots.elements(), syncBottomNavItems, { immediate: true });\n const nestedItemsObserver = new MutationObserver(syncBottomNavItems);\n nestedItemsObserver.observe(el, { childList: true, subtree: true });\n\n return () => {\n observer.disconnect();\n nestedItemsObserver.disconnect();\n el.removeEventListener('keydown', closeOnEscape);\n document.removeEventListener('focusin', keepFocusInDrawer);\n bgLock.unlock();\n mediaCleanup?.();\n bottomNavCleanup?.();\n stopResizeEffect?.dispose();\n\n for (const itemObserver of itemObservers.values()) {\n itemObserver.disconnect();\n }\n\n itemObservers.clear();\n };\n });\n\n return html`\n <button\n class=\"drawer-backdrop\"\n part=\"drawer-backdrop\"\n type=\"button\"\n aria-label=\"${props['drawer-close-label']}\"\n ?hidden=${() => !isDrawerOpen.value}\n @click=${() => setDrawerOpen(false, 'toggle')}></button>\n <nav\n aria-label=\"${props.label}\"\n aria-hidden=${() => ((isBottomNav.value || props.drawer.value) && !isDrawerOpen.value ? 'true' : null)}\n part=\"nav\"\n tabindex=\"-1\"\n ?inert=${() => (isBottomNav.value || props.drawer.value) && !isDrawerOpen.value}>\n <div class=\"sidebar-header\" part=\"header\" ?hidden=${() => !hasHeader() && !props.collapsible.value}>\n <span class=\"sidebar-logo\" ?hidden=${() => !hasLogo()}>\n <slot name=\"logo\"></slot>\n </span>\n <span class=\"sidebar-header-content\">\n <slot name=\"header\"></slot>\n </span>\n <button\n class=\"toggle-btn\"\n part=\"toggle-btn\"\n type=\"button\"\n ?hidden=${() => !props.collapsible.value}\n aria-label=\"${() => (isCollapsed() ? props['expand-label'].value : props['collapse-label'].value)}\"\n aria-expanded=\"${() => !isCollapsed()}\"\n @click=\"${doToggle}\">\n <ore-icon\n class=\"toggle-icon\"\n name=${() => (isCollapsed() ? 'panel-left-open' : 'panel-left-close')}\n size=\"18\"\n stroke-width=\"2\"\n aria-hidden=\"true\"></ore-icon>\n </button>\n </div>\n <div class=\"sidebar-content\" part=\"content\">\n <slot></slot>\n </div>\n <div class=\"sidebar-footer\" part=\"footer\" ?hidden=${() => !hasFooter()}>\n <slot name=\"footer\"></slot>\n </div>\n </nav>\n\n <div class=\"bottom-bar\" part=\"bottom-bar\" ?hidden=${() => !isBottomNav.value}>\n ${() =>\n bottomNavItems.value.map((item) => {\n const className = `bottom-tab${item.active ? ' bottom-tab-active' : ''}`;\n\n if (item.href && !item.disabled) {\n return html`\n <a\n class=\"${className}\"\n href=\"${item.href}\"\n aria-current=\"${item.active ? 'page' : null}\"\n data-active=\"${item.active ? 'true' : null}\">\n <span class=\"bottom-tab-icon\" aria-hidden=\"true\" ?hidden=${() => !item.iconName}>\n <ore-icon name=\"${item.iconName}\" size=\"18\" stroke-width=\"2\"></ore-icon>\n </span>\n <span class=\"bottom-tab-label\">${item.label}</span>\n </a>\n `;\n }\n\n return html`\n <button\n class=\"${className}\"\n type=\"button\"\n ?disabled=${item.disabled}\n aria-current=\"${item.active ? 'page' : null}\"\n data-active=\"${item.active ? 'true' : null}\"\n @click=${() => item.source.click()}>\n <span class=\"bottom-tab-icon\" aria-hidden=\"true\" ?hidden=${() => !item.iconName}>\n <ore-icon name=\"${item.iconName}\" size=\"18\" stroke-width=\"2\"></ore-icon>\n </span>\n <span class=\"bottom-tab-label\">${item.label}</span>\n </button>\n `;\n })}\n </div>\n `;\n },\n styles: [coarsePointerMixin, reducedMotionMixin, sidebarStyles],\n});\n\n// ─── ore-sidebar-group styles ────────────────────────────────────────────────\n\n/** Sidebar group properties */\nexport type OreSidebarGroupProps = {\n /** Whether this group can be collapsed */\n collapsible?: boolean;\n /** Initial open state in uncontrolled mode */\n 'default-open'?: boolean;\n /** Accessible label for the group */\n label?: string;\n /** Controlled open state */\n open?: boolean;\n};\n\n/**\n * `ore-sidebar-group` — A labelled section within `ore-sidebar`.\n *\n * @element ore-sidebar-group\n *\n * @attr {string} label - Group label text\n * @attr {boolean} collapsible - Whether this group can be toggled open/closed\n * @attr {boolean} open - Controlled expanded state\n * @attr {boolean} default-open - Initial expanded state in uncontrolled mode\n *\n * @fires open-change - Fired when the group open state changes (collapsible groups only). detail: { open: boolean }\n *\n * @slot - Navigation items (`ore-sidebar-item`)\n * @slot icon - Icon displayed before the label\n *\n * @example\n * ```html\n * <ore-sidebar-group label=\"Main\" collapsible open>\n * <ore-sidebar-item href=\"/home\">Home</ore-sidebar-item>\n * </ore-sidebar-group>\n * ```\n */\nexport const SIDEBAR_GROUP_TAG = 'ore-sidebar-group' as const;\ndefine<OreSidebarGroupProps>(SIDEBAR_GROUP_TAG, {\n props: {\n collapsible: prop.bool(false),\n 'default-open': prop.bool(true),\n label: prop.string(),\n open: {\n default: undefined as boolean | undefined,\n parse: (value: string | null) => (value == null ? undefined : value === '' || value === 'true'),\n reflect: false,\n },\n },\n setup(props) {\n const slots = useSlots();\n\n const hasIcon = () => slots.has('icon').value;\n const sidebarCtx = inject(SIDEBAR_CTX);\n\n bind({\n attr: {\n 'sidebar-bottom-nav': () =>\n sidebarCtx?.mode.value === 'bottom-nav' && !sidebarCtx?.drawerOpen.value ? true : undefined,\n 'sidebar-collapsed': () => (sidebarCtx?.collapsed.value ? true : undefined),\n },\n });\n\n const isControlled = () => props.open.value !== undefined;\n const openState = signal(props['default-open'].value);\n const isOpen = computed(() => {\n if (!props.collapsible.value) return true;\n\n if (isControlled()) return props.open.value ?? false;\n\n return openState.value;\n });\n\n watch(props.open, (value) => {\n if (value === undefined) return;\n\n openState.value = value;\n });\n\n bind({\n attr: {\n open: () => (isOpen.value ? true : undefined),\n },\n });\n\n return html`\n <details class=\"group\" part=\"group\" ?open=${isOpen}>\n <summary\n class=\"group-header\"\n part=\"group-header\"\n aria-expanded=\"${() => (props.collapsible.value ? String(props.open.value) : null)}\"\n @click=${(e: MouseEvent) => {\n if (!props.collapsible.value) {\n e.preventDefault();\n }\n }}>\n <span class=\"group-icon\" part=\"group-icon\" ?hidden=${() => !hasIcon()} aria-hidden=\"true\">\n <slot name=\"icon\"></slot>\n </span>\n <span class=\"group-label\" part=\"group-label\">${props.label}</span>\n <span class=\"chevron\" ?hidden=${() => !props.collapsible.value} aria-hidden=\"true\">\n <ore-icon name=\"chevron-right\" size=\"12\" stroke-width=\"2\" aria-hidden=\"true\"></ore-icon>\n </span>\n </summary>\n <div class=\"group-items\" part=\"group-items\" role=\"list\">\n <slot></slot>\n </div>\n </details>\n `;\n },\n styles: [reducedMotionMixin, groupStyles],\n});\n\n// ─── ore-sidebar-item styles ─────────────────────────────────────────────────\n\n/** Sidebar item properties */\nexport type OreSidebarItemProps = {\n /** Whether this item represents the current page/section */\n active?: boolean;\n 'bottom-nav'?: boolean;\n 'bottom-nav-label'?: string;\n /** Whether this item is disabled */\n disabled?: boolean;\n /** Navigation href — renders an `<a>` when set, otherwise a `<button>` */\n href?: string;\n label?: string;\n /**\n * Relationship of the linked URL (`rel` attribute on the inner `<a>`).\n * Only applies when `href` is set.\n */\n rel?: string;\n /**\n * Browsing context for the link (`target` attribute on the inner `<a>`).\n * Only applies when `href` is set.\n */\n target?: string;\n};\n\n/**\n * `ore-sidebar-item` — An individual navigation item in a `ore-sidebar`.\n *\n * Renders as an `<a>` when `href` is provided, otherwise as a `<button>`.\n * Marks the active page via `aria-current=\"page\"` when the `active` attribute is set.\n *\n * @element ore-sidebar-item\n *\n * @attr {string} href - Link URL; renders an anchor when set\n * @attr {boolean} active - Marks the item as the current page\n * @attr {boolean} disabled - Disables the item\n * @attr {string} rel - Anchor `rel` attribute (links only)\n * @attr {string} target - Anchor `target` attribute (links only)\n *\n * @slot - Label text\n * @slot icon - Leading icon\n * @slot end - Trailing content (badge, shortcut, arrow, etc.)\n *\n * @cssprop --sidebar-item-color - Default text color\n * @cssprop --sidebar-item-hover-bg - Hover background\n * @cssprop --sidebar-item-hover-color - Hover text color\n * @cssprop --sidebar-item-active-bg - Active background\n * @cssprop --sidebar-item-active-color - Active text color\n * @cssprop --sidebar-item-indicator - Active indicator bar color\n *\n * @part item - The inner anchor or button element\n * @part item-icon - The icon wrapper\n * @part item-label - The label wrapper\n * @part item-end - The trailing content wrapper\n *\n * @example\n * ```html\n * <ore-sidebar-item href=\"/dashboard\" active>\n * <span slot=\"icon\">🏠</span>\n * Dashboard\n * </ore-sidebar-item>\n *\n * <ore-sidebar-item href=\"/users\">\n * <span slot=\"icon\">👤</span>\n * Users\n * <ore-badge slot=\"end\" color=\"primary\">3</ore-badge>\n * </ore-sidebar-item>\n * ```\n */\nexport const SIDEBAR_ITEM_TAG = 'ore-sidebar-item' as const;\ndefine<OreSidebarItemProps>(SIDEBAR_ITEM_TAG, {\n props: {\n active: prop.bool(false),\n 'bottom-nav': prop.bool(false),\n 'bottom-nav-label': prop.string(),\n disabled: prop.bool(false),\n href: prop.string(),\n label: prop.string(),\n rel: prop.string(),\n target: prop.string(),\n },\n setup(props) {\n const slots = useSlots();\n\n const hasIcon = () => slots.has('icon').value;\n const hasEnd = () => slots.has('end').value;\n const sidebarCtx = inject(SIDEBAR_CTX);\n\n bind({\n attr: {\n 'sidebar-bottom-nav': () =>\n sidebarCtx?.mode.value === 'bottom-nav' && !sidebarCtx?.drawerOpen.value ? true : undefined,\n 'sidebar-collapsed': () => (sidebarCtx?.collapsed.value ? true : undefined),\n },\n });\n\n const isLink = () => !!props.href.value && !props.disabled.value;\n\n // Prevent reverse tabnapping: auto-inject noopener + noreferrer for _blank links.\n const effectiveRel = computed(() => computeSafeRel(props.rel.value, props.target.value));\n const closeDrawerOnSelect = (event: MouseEvent) => {\n if (!sidebarCtx?.closeOnSelect.value || !sidebarCtx.drawerOpen.value) return;\n if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;\n\n queueMicrotask(sidebarCtx.closeDrawer);\n };\n\n const renderItemContent = () => html`\n <span class=\"item-icon\" part=\"item-icon\" ?hidden=${() => !hasIcon()} aria-hidden=\"true\">\n <slot name=\"icon\"></slot>\n </span>\n <span class=\"item-label\" part=\"item-label\"><slot></slot></span>\n <span class=\"item-end\" part=\"item-end\" ?hidden=${() => !hasEnd()}>\n <slot name=\"end\"></slot>\n </span>\n `;\n\n return html`\n ${() => {\n if (isLink()) {\n return html`\n <a\n class=\"item\"\n part=\"item\"\n href=\"${props.href}\"\n rel=\"${effectiveRel}\"\n target=\"${props.target}\"\n aria-label=\"${props.label}\"\n aria-current=\"${() => (props.active.value ? 'page' : null)}\"\n @click=${closeDrawerOnSelect}>\n ${renderItemContent()}\n </a>\n `;\n }\n\n if (props.disabled.value) {\n return html`\n <div\n class=\"item\"\n part=\"item\"\n aria-disabled=\"true\"\n tabindex=\"-1\"\n aria-label=\"${props.label}\"\n aria-current=\"${() => (props.active.value ? 'page' : null)}\">\n ${renderItemContent()}\n </div>\n `;\n }\n\n return html`\n <button\n class=\"item\"\n part=\"item\"\n type=\"button\"\n ?disabled=\"${props.disabled}\"\n aria-label=\"${props.label}\"\n aria-current=\"${() => (props.active.value ? 'page' : null)}\"\n @click=${closeDrawerOnSelect}>\n ${renderItemContent()}\n </button>\n `;\n }}\n `;\n },\n styles: [coarsePointerMixin, itemStyles],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sidebar.js","names":[],"sources":["../src/layout/sidebar/sidebar.ts"],"sourcesContent":["import { captureFocus, type FocusRestorer } from '@vielzeug/focus';\nimport {\n bind,\n createContext,\n define,\n getHost,\n html,\n inject,\n onCleanup,\n onMounted,\n prop,\n provide,\n useEmit,\n useSlots,\n} from '@vielzeug/ore';\nimport { computed, fromSubscribable, type Readable, signal, watch } from '@vielzeug/ripple';\nimport { createElementSize, createMediaQuery, SentinelUnavailableError } from '@vielzeug/sentinel';\n\nimport { createBackgroundLock } from '../../overlay/shared/background-lock';\nimport { coarsePointerMixin, reducedMotionMixin } from '../../styles';\nimport { computeSafeRel } from '../../utils';\nimport { parseMaxWidthPx, readContainerWidth, resolveContainerElement } from '../shared';\nimport sidebarStyles from './sidebar.css?inline';\nimport groupStyles from './sidebar-group.css?inline';\nimport itemStyles from './sidebar-item.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\n\n// ─── Types ────────────────────────────────────────────────────────────────\n\ntype SidebarVariant = 'floating' | 'inset';\ntype SidebarCollapseSource = 'api' | 'responsive' | 'toggle';\ntype DrawerSource = 'api' | 'responsive' | 'toggle';\ntype SidebarMode = 'bottom-nav' | 'collapsed' | 'default';\n\ntype BottomNavItem = {\n active: boolean;\n disabled: boolean;\n href?: string;\n iconName?: string;\n label: string;\n source: HTMLElement;\n};\n\n/** Context provided by `ore-sidebar` to its `ore-sidebar-group` and `ore-sidebar-item` children. */\nexport type SidebarContext = {\n closeDrawer: () => void;\n closeOnSelect: Readable<boolean>;\n collapsed: Readable<boolean>;\n drawerOpen: Readable<boolean>;\n mode: Readable<SidebarMode>;\n variant: Readable<SidebarVariant | undefined>;\n};\n\n/** Injection key for the sidebar context. */\nexport const SIDEBAR_CTX = createContext<SidebarContext>('SidebarContext');\n\n// ─── ore-sidebar styles ──────────────────────────────────────────────────────\n\n/** ore-sidebar element interface */\nexport type SidebarElement = HTMLElement &\n OreSidebarProps & {\n /** Close the drawer. */\n closeDrawer(): void;\n /** Open the drawer. */\n openDrawer(): void;\n /** Set collapsed state imperatively. */\n setCollapsed(next: boolean): void;\n /** Toggle between collapsed and expanded. */\n toggle(): void;\n /** Toggle the drawer. */\n toggleDrawer(): void;\n };\n\n/** Sidebar component properties */\n\nexport type OreSidebarEvents = {\n 'collapsed-change': { collapsed: boolean; source: SidebarCollapseSource };\n 'drawer-change': { open: boolean; source: DrawerSource };\n};\n\nexport type OreSidebarGroupEvents = {\n 'open-change': { open: boolean };\n};\n\nexport type OreSidebarProps = {\n /** CSS media query that switches the sidebar to bottom navigation + drawer mode */\n 'bottom-nav-at'?: string;\n 'close-on-select'?: boolean;\n /** Controlled collapsed state */\n collapsed?: boolean;\n /** Whether the sidebar supports collapsing */\n collapsible?: boolean;\n 'collapse-label'?: string;\n /** Evaluate responsive and bottom-nav breakpoints against container width only. */\n 'container-breakpoints'?: boolean;\n /** Initial collapsed state in uncontrolled mode */\n 'default-collapsed'?: boolean;\n /** Drawer-only mode: no inline panel and no bottom bar, just the navbar/API-driven drawer. */\n drawer?: boolean;\n /** Accessible label for the backdrop close control. */\n 'drawer-close-label'?: string;\n 'expand-label'?: string;\n /**\n * Accessible label for the navigation landmark.\n * Use to distinguish multiple navigation regions on a page.\n * @default 'Sidebar navigation'\n */\n label?: string;\n /**\n * CSS media query that, when it matches, automatically collapses the sidebar.\n * Unset by default: no automatic collapse.\n * @example 'responsive=\"(max-width: 768px)\"'\n */\n responsive?: string;\n /** Visual style variant */\n variant?: SidebarVariant;\n};\n\n/**\n * `ore-sidebar`: A collapsible navigation sidebar with group and item support.\n *\n * @element ore-sidebar\n * @element ore-sidebar-group - Labelled group of navigation items\n * @element ore-sidebar-item - Individual navigation link or button\n *\n * @attr {boolean} collapsed - Controlled collapsed state\n * @attr {boolean} default-collapsed - Initial collapsed state for uncontrolled sidebars\n * @attr {boolean} collapsible - Show the collapse toggle button\n * @attr {boolean} drawer - Drawer-only mode: no inline panel and no bottom bar, just the navbar/API-driven drawer\n * @attr {string} variant - Visual variant: 'floating' | 'inset'\n * @attr {string} label - Accessible aria-label for the nav landmark\n *\n * @fires collapsed-change - Fired when collapsed state changes. detail: { collapsed: boolean; source: string }\n * @fires drawer-change - Fired when the drawer open state changes. detail: { open: boolean; source: string }\n *\n * @slot header - Branding or logo content above the nav\n * @slot - Navigation content (ore-sidebar-group / ore-sidebar-item)\n * @slot footer - Footer content below the nav (user info, settings, etc.)\n *\n * @cssprop --sidebar-width - Expanded sidebar width (default: 16rem)\n * @cssprop --sidebar-collapsed-width - Collapsed sidebar width (default: 3.5rem)\n * @cssprop --sidebar-drawer-width - Drawer panel width (default: min(--sidebar-width, 85vw))\n * @cssprop --sidebar-bg - Sidebar background color\n * @cssprop --sidebar-border-color - Border color\n *\n * @part drawer-backdrop - Backdrop shown behind the open drawer.\n * @part nav - Navigation container.\n * @part header - Header container.\n * @part toggle-btn - Shadow part for the `toggle-btn` element.\n * @part content - Content container.\n * @part footer - Footer container.\n * @part bottom-bar - Bottom bar container.\n * @part group - Group container.\n * @part group-header - Group header container.\n * @part group-icon - Group icon container.\n * @part group-label - Group label container.\n * @part group-items - Group items container.\n * @part item-icon - Leading item icon container.\n * @part item-label - Item label container.\n * @part item-end - Trailing item content container.\n * @part item - Item root element.\n * @example\n * ```html\n * <ore-sidebar collapsible label=\"App navigation\">\n * <span slot=\"header\">My App</span>\n * <ore-sidebar-group label=\"Main\">\n * <ore-sidebar-item href=\"/dashboard\" active>Dashboard</ore-sidebar-item>\n * <ore-sidebar-item href=\"/settings\">Settings</ore-sidebar-item>\n * </ore-sidebar-group>\n * </ore-sidebar>\n *\n * <!-- Auto-collapse on mobile -->\n * <ore-sidebar collapsible responsive=\"(max-width: 768px)\">...</ore-sidebar>\n * ```\n */\nexport const SIDEBAR_TAG = 'ore-sidebar' as const;\ndefine<OreSidebarProps>(SIDEBAR_TAG, {\n props: {\n 'bottom-nav-at': prop.string(),\n 'close-on-select': prop.bool(false),\n 'collapse-label': prop.string('Collapse sidebar'),\n collapsed: prop.bool(false),\n collapsible: prop.bool(false),\n 'container-breakpoints': prop.bool(false),\n 'default-collapsed': prop.bool(false),\n drawer: prop.bool(false),\n 'drawer-close-label': prop.string('Close sidebar'),\n 'expand-label': prop.string('Expand sidebar'),\n label: prop.string('Sidebar navigation'),\n responsive: prop.string(),\n variant: prop.string<SidebarVariant>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreSidebarEvents>();\n const slots = useSlots();\n\n const hasHeader = () => slots.has('header').value;\n const hasFooter = () => slots.has('footer').value;\n const hasLogo = () => slots.has('logo').value;\n\n const isControlled = signal(el.hasAttribute('collapsed'));\n const collapsedState = signal(isControlled.value ? el.hasAttribute('collapsed') : props['default-collapsed'].value);\n const isBottomNav = signal(false);\n const isDrawerOpen = signal(false);\n const bottomNavItems = signal<BottomNavItem[]>([]);\n const responsiveMediaMatches = signal(false);\n const responsiveSizeMatches = signal(false);\n const responsiveMaxWidthPx = signal<number | undefined>(parseMaxWidthPx(props.responsive.value));\n const hasResponsiveQuery = signal(Boolean(String(props.responsive.value ?? '').trim()));\n const bottomNavMediaMatches = signal(false);\n const bottomNavSizeMatches = signal(false);\n const bottomNavMaxWidthPx = signal<number | undefined>(parseMaxWidthPx(props['bottom-nav-at'].value));\n const isPreviewMode = signal(false);\n const bgLock = createBackgroundLock();\n let restoreDrawerFocus: FocusRestorer | null = null;\n\n const isCollapsed = () => collapsedState.value;\n const mode = computed<SidebarMode>(() => {\n if (isBottomNav.value) return 'bottom-nav';\n\n return collapsedState.value ? 'collapsed' : 'default';\n });\n\n const applyResponsiveState = () => {\n const useContainerBreakpoints = props['container-breakpoints'].value;\n const responsiveMatched = useContainerBreakpoints\n ? responsiveSizeMatches.value\n : responsiveMediaMatches.value || responsiveSizeMatches.value;\n const bottomMatched = useContainerBreakpoints\n ? bottomNavSizeMatches.value\n : bottomNavMediaMatches.value || bottomNavSizeMatches.value;\n\n // If the drawer is currently open, stay in bottom-nav mode\n // regardless of the responsive match. This prevents the drawer from\n // disappearing mid-animation or if forced open via the API on desktop.\n if (!isDrawerOpen.value) {\n isBottomNav.value = bottomMatched;\n }\n\n if (!bottomMatched && !isDrawerOpen.value) {\n setDrawerOpen(false, 'responsive');\n }\n\n if (hasResponsiveQuery.value) {\n setCollapsed(responsiveMatched, 'responsive');\n }\n };\n\n const readBottomNavItems = () => {\n if (props.drawer.value) {\n bottomNavItems.value = [];\n\n return;\n }\n\n const directItems = slots\n .elements()\n .value.filter(\n (item): item is HTMLElement =>\n item instanceof HTMLElement && item.tagName.toLowerCase() === 'ore-sidebar-item',\n );\n const promotedItems = [...el.querySelectorAll<HTMLElement>('ore-sidebar-item[bottom-nav]')];\n const sourceItems = promotedItems.length > 0 ? promotedItems : directItems;\n const next = sourceItems.map((item, index) => {\n const el = item;\n const iconSlotEl =\n (el.querySelector(':scope > [slot=\"icon\"]') as HTMLElement | null) ??\n (el.querySelector('[slot=\"icon\"]') as HTMLElement | null);\n const directIconName =\n iconSlotEl?.tagName.toLowerCase() === 'ore-icon' ? iconSlotEl.getAttribute('name') : null;\n const nestedIconName = iconSlotEl?.querySelector('ore-icon')?.getAttribute('name') ?? null;\n const rawLabel = (el.textContent ?? '').trim();\n\n return {\n active: el.hasAttribute('active'),\n disabled: el.hasAttribute('disabled'),\n href: el.getAttribute('href') ?? undefined,\n iconName: directIconName ?? nestedIconName ?? undefined,\n label: el.getAttribute('bottom-nav-label')?.trim() || rawLabel || `Item ${index + 1}`,\n source: el,\n } satisfies BottomNavItem;\n });\n\n bottomNavItems.value = next;\n };\n\n provide(SIDEBAR_CTX, {\n closeDrawer: () => setDrawerOpen(false, 'toggle'),\n closeOnSelect: props['close-on-select'],\n collapsed: computed(() => !isBottomNav.value && collapsedState.value) as Readable<boolean>,\n drawerOpen: computed(() => isDrawerOpen.value) as Readable<boolean>,\n mode: mode as Readable<SidebarMode>,\n variant: props.variant,\n });\n\n const setCollapsed = (next: boolean, source: SidebarCollapseSource) => {\n if (isCollapsed() === next) return;\n\n if (!isControlled.value) {\n collapsedState.value = next;\n }\n\n emit('collapsed-change', { collapsed: next, source });\n };\n\n const setDrawerOpen = (next: boolean, source: DrawerSource) => {\n const open = Boolean(next);\n\n // Outside drawer-only mode, opening the drawer forces the sidebar out of its inline\n // panel into the overlay presentation until the drawer closes again.\n if (open && !isBottomNav.value && !props.drawer.value) {\n isBottomNav.value = true;\n }\n\n if (isDrawerOpen.value === open) return;\n\n if (open) {\n restoreDrawerFocus = captureFocus();\n // Inert the background where the DOM structure allows (see createBackgroundLock) and\n // refocus the panel whenever focus escapes the sidebar: together these keep Tab and\n // Escape working on the open drawer even in apps where the page shares one wrapper\n // element and the background cannot be inerted.\n bgLock.lock(el);\n }\n\n isDrawerOpen.value = open;\n\n // If closing, re-evaluate responsive state to potentially exit forced bottom-nav mode\n if (!open) {\n bgLock.unlock();\n applyResponsiveState();\n queueMicrotask(() => {\n restoreDrawerFocus?.();\n restoreDrawerFocus = null;\n });\n } else {\n queueMicrotask(() => el.shadowRoot?.querySelector<HTMLElement>('nav')?.focus());\n }\n\n emit('drawer-change', { open, source });\n };\n\n const doToggle = () => {\n setCollapsed(!isCollapsed(), 'toggle');\n };\n\n const sidebarEl = el as SidebarElement;\n\n sidebarEl.setCollapsed = (next) => setCollapsed(Boolean(next), 'api');\n sidebarEl.toggle = doToggle;\n sidebarEl.openDrawer = () => setDrawerOpen(true, 'api');\n sidebarEl.closeDrawer = () => setDrawerOpen(false, 'api');\n sidebarEl.toggleDrawer = () => setDrawerOpen(!isDrawerOpen.value, 'toggle');\n\n bind({\n attr: {\n 'data-bottom-nav': () => (isBottomNav.value ? true : undefined),\n 'data-collapsed': () => (isCollapsed() && !isBottomNav.value ? true : undefined),\n 'data-drawer-open': () => (isDrawerOpen.value ? true : undefined),\n 'data-has-logo': () => (hasLogo() ? true : undefined),\n 'data-preview-mode': () => (isPreviewMode.value ? true : undefined),\n },\n });\n\n onMounted(() => {\n // Suppress transitions during initial layout so the sidebar doesn't\n // animate from a collapsed/0 state before the first ResizeObserver fires.\n el.setAttribute('data-no-transition', '');\n\n let transitionUnlocked = false;\n const unlockTransition = () => {\n if (transitionUnlocked) return;\n\n transitionUnlocked = true;\n el.removeAttribute('data-no-transition');\n };\n\n // Fallback: unlock after two frames in case ResizeObserver doesn't fire.\n requestAnimationFrame(() => requestAnimationFrame(unlockTransition));\n\n let mediaCleanup: (() => void) | undefined;\n let bottomNavCleanup: (() => void) | undefined;\n const itemObservers = new Map<HTMLElement, MutationObserver>();\n const observer = new MutationObserver(() => {\n if (!el.hasAttribute('collapsed') && !isControlled.value) return;\n\n isControlled.value = true;\n collapsedState.value = el.hasAttribute('collapsed');\n });\n\n observer.observe(el, {\n attributeFilter: ['collapsed'],\n attributes: true,\n });\n const closeOnEscape = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || !isDrawerOpen.value) return;\n\n event.preventDefault();\n setDrawerOpen(false, 'toggle');\n };\n el.addEventListener('keydown', closeOnEscape);\n\n // Focus containment: createBackgroundLock inertes background siblings, but apps that wrap\n // the whole page in one root element (so nothing can be inerted) still let Tab walk out of\n // the open drawer, and once focus is outside the host, its Escape handler is unreachable.\n // Pull focus back to the panel whenever it lands outside the sidebar. focusin is composed,\n // so its target is retargeted to this host for focus inside the shadow root.\n const keepFocusInDrawer = (event: FocusEvent) => {\n if (!isDrawerOpen.value) return;\n\n const target = event.target;\n\n if (target instanceof Node && (el === target || el.contains(target))) return;\n\n el.shadowRoot?.querySelector<HTMLElement>('nav')?.focus();\n };\n document.addEventListener('focusin', keepFocusInDrawer);\n\n watch(\n props.responsive,\n (query) => {\n mediaCleanup?.();\n mediaCleanup = undefined;\n\n const mediaQuery = String(query ?? '').trim();\n\n hasResponsiveQuery.value = Boolean(mediaQuery);\n responsiveMaxWidthPx.value = parseMaxWidthPx(mediaQuery);\n responsiveMediaMatches.value = false;\n\n const width = readContainerWidth(el);\n\n responsiveSizeMatches.value =\n width > 0 && responsiveMaxWidthPx.value != null ? width <= responsiveMaxWidthPx.value : false;\n applyResponsiveState();\n\n // For parseable max-width queries, keep behavior container-driven.\n // This avoids preview viewport controls being overridden by window width.\n if (props['container-breakpoints'].value && responsiveMaxWidthPx.value != null) {\n return;\n }\n\n if (!mediaQuery) {\n return;\n }\n\n try {\n const mediaHandle = createMediaQuery(mediaQuery);\n const syncMedia = (state: ReturnType<typeof mediaHandle.getSnapshot>) => {\n if (state) {\n responsiveMediaMatches.value = state.matches;\n applyResponsiveState();\n }\n };\n\n syncMedia(mediaHandle.getSnapshot());\n\n const mediaCleanupFn = watch(\n fromSubscribable(mediaHandle, { signal: mediaHandle.disposalSignal }),\n syncMedia,\n );\n mediaCleanup = () => {\n mediaCleanupFn.dispose();\n mediaHandle.dispose();\n };\n } catch (error) {\n if (!(error instanceof SentinelUnavailableError)) throw error;\n }\n },\n { immediate: true },\n );\n\n watch(\n props['bottom-nav-at'],\n (query) => {\n bottomNavCleanup?.();\n bottomNavCleanup = undefined;\n\n const mediaQuery = String(query ?? '').trim();\n\n bottomNavMaxWidthPx.value = parseMaxWidthPx(mediaQuery);\n bottomNavMediaMatches.value = false;\n\n const width = readContainerWidth(el);\n\n bottomNavSizeMatches.value =\n width > 0 && bottomNavMaxWidthPx.value != null ? width <= bottomNavMaxWidthPx.value : false;\n applyResponsiveState();\n\n // For parseable max-width queries, keep behavior container-driven.\n // This avoids preview viewport controls being overridden by window width.\n if (props['container-breakpoints'].value && bottomNavMaxWidthPx.value != null) {\n return;\n }\n\n if (!mediaQuery) {\n return;\n }\n\n try {\n const mediaHandle = createMediaQuery(mediaQuery);\n const syncMedia = (state: ReturnType<typeof mediaHandle.getSnapshot>) => {\n if (state) {\n bottomNavMediaMatches.value = state.matches;\n applyResponsiveState();\n }\n };\n\n syncMedia(mediaHandle.getSnapshot());\n\n const mediaCleanupFn = watch(\n fromSubscribable(mediaHandle, { signal: mediaHandle.disposalSignal }),\n syncMedia,\n );\n bottomNavCleanup = () => {\n mediaCleanupFn.dispose();\n mediaHandle.dispose();\n };\n } catch (error) {\n if (!(error instanceof SentinelUnavailableError)) throw error;\n }\n },\n { immediate: true },\n );\n\n watch(props.drawer, () => {\n applyResponsiveState();\n readBottomNavItems();\n });\n\n const stopResizeEffect =\n typeof ResizeObserver === 'function'\n ? (() => {\n const hostSize = createElementSize(el);\n onCleanup(() => hostSize.dispose());\n const hostSizeSignal = fromSubscribable(hostSize, { signal: hostSize.disposalSignal });\n\n const wrapperEl = el.parentElement;\n const containerEl = resolveContainerElement(el);\n const wrapperSize = wrapperEl ? createElementSize(wrapperEl) : undefined;\n const parentSize = containerEl && containerEl !== wrapperEl ? createElementSize(containerEl) : undefined;\n\n if (wrapperSize) {\n onCleanup(() => wrapperSize.dispose());\n }\n if (parentSize) {\n onCleanup(() => parentSize.dispose());\n }\n\n const wrapperSizeSignal = wrapperSize\n ? fromSubscribable(wrapperSize, { signal: wrapperSize.disposalSignal })\n : undefined;\n const parentSizeSignal = parentSize\n ? fromSubscribable(parentSize, { signal: parentSize.disposalSignal })\n : undefined;\n\n let rafId: number | undefined;\n\n const onResize = () => {\n if (rafId !== undefined) cancelAnimationFrame(rafId);\n\n rafId = requestAnimationFrame(() => {\n const resolvedContainer = resolveContainerElement(el);\n const width = readContainerWidth(el);\n const responsiveWasMatched = responsiveSizeMatches.value;\n const bottomNavWasMatched = bottomNavSizeMatches.value;\n const parentWidth = resolvedContainer?.clientWidth ?? 0;\n\n isPreviewMode.value = parentWidth > 0 && parentWidth < window.innerWidth;\n\n responsiveSizeMatches.value =\n width > 0 && responsiveMaxWidthPx.value != null ? width <= responsiveMaxWidthPx.value : false;\n bottomNavSizeMatches.value =\n width > 0 && bottomNavMaxWidthPx.value != null ? width <= bottomNavMaxWidthPx.value : false;\n\n if (\n responsiveWasMatched !== responsiveSizeMatches.value ||\n bottomNavWasMatched !== bottomNavSizeMatches.value\n ) {\n applyResponsiveState();\n }\n\n unlockTransition();\n });\n };\n\n return watch(\n computed(() => [\n hostSizeSignal.value?.width,\n hostSizeSignal.value?.height,\n wrapperSizeSignal?.value?.width,\n wrapperSizeSignal?.value?.height,\n parentSizeSignal?.value?.width,\n parentSizeSignal?.value?.height,\n ]),\n onResize,\n );\n })()\n : undefined;\n\n const bindItemObservers = (items: HTMLElement[]) => {\n const set = new Set(items);\n\n for (const [item, cleanup] of itemObservers) {\n if (set.has(item)) continue;\n\n cleanup.disconnect();\n itemObservers.delete(item);\n }\n\n for (const item of items) {\n if (itemObservers.has(item)) continue;\n\n const itemObserver = new MutationObserver(() => {\n // Only update if in bottom-nav mode to minimize re-renders\n if (isBottomNav.value) {\n readBottomNavItems();\n }\n });\n\n // Watch attributes and content that affect bottom-nav rendering.\n itemObserver.observe(item, {\n attributeFilter: ['active', 'bottom-nav', 'bottom-nav-label', 'disabled', 'href'],\n attributes: true,\n characterData: true,\n childList: true,\n subtree: true,\n });\n itemObservers.set(item, itemObserver);\n }\n };\n\n const syncBottomNavItems = () => {\n bindItemObservers([...el.querySelectorAll<HTMLElement>('ore-sidebar-item')]);\n readBottomNavItems();\n };\n watch(slots.elements(), syncBottomNavItems, { immediate: true });\n const nestedItemsObserver = new MutationObserver(syncBottomNavItems);\n nestedItemsObserver.observe(el, { childList: true, subtree: true });\n\n return () => {\n observer.disconnect();\n nestedItemsObserver.disconnect();\n el.removeEventListener('keydown', closeOnEscape);\n document.removeEventListener('focusin', keepFocusInDrawer);\n bgLock.unlock();\n mediaCleanup?.();\n bottomNavCleanup?.();\n stopResizeEffect?.dispose();\n\n for (const itemObserver of itemObservers.values()) {\n itemObserver.disconnect();\n }\n\n itemObservers.clear();\n };\n });\n\n return html`\n <button\n class=\"drawer-backdrop\"\n part=\"drawer-backdrop\"\n type=\"button\"\n aria-label=\"${props['drawer-close-label']}\"\n ?hidden=${() => !isDrawerOpen.value}\n @click=${() => setDrawerOpen(false, 'toggle')}></button>\n <nav\n aria-label=\"${props.label}\"\n aria-hidden=${() => ((isBottomNav.value || props.drawer.value) && !isDrawerOpen.value ? 'true' : null)}\n part=\"nav\"\n tabindex=\"-1\"\n ?inert=${() => (isBottomNav.value || props.drawer.value) && !isDrawerOpen.value}>\n <div class=\"sidebar-header\" part=\"header\" ?hidden=${() => !hasHeader() && !props.collapsible.value}>\n <span class=\"sidebar-logo\" ?hidden=${() => !hasLogo()}>\n <slot name=\"logo\"></slot>\n </span>\n <span class=\"sidebar-header-content\">\n <slot name=\"header\"></slot>\n </span>\n <button\n class=\"toggle-btn\"\n part=\"toggle-btn\"\n type=\"button\"\n ?hidden=${() => !props.collapsible.value}\n aria-label=\"${() => (isCollapsed() ? props['expand-label'].value : props['collapse-label'].value)}\"\n aria-expanded=\"${() => !isCollapsed()}\"\n @click=\"${doToggle}\">\n <ore-icon\n class=\"toggle-icon\"\n name=${() => (isCollapsed() ? 'panel-left-open' : 'panel-left-close')}\n size=\"18\"\n stroke-width=\"2\"\n aria-hidden=\"true\"></ore-icon>\n </button>\n </div>\n <div class=\"sidebar-content\" part=\"content\">\n <slot></slot>\n </div>\n <div class=\"sidebar-footer\" part=\"footer\" ?hidden=${() => !hasFooter()}>\n <slot name=\"footer\"></slot>\n </div>\n </nav>\n\n <div class=\"bottom-bar\" part=\"bottom-bar\" ?hidden=${() => !isBottomNav.value}>\n ${() =>\n bottomNavItems.value.map((item) => {\n const className = `bottom-tab${item.active ? ' bottom-tab-active' : ''}`;\n\n if (item.href && !item.disabled) {\n return html`\n <a\n class=\"${className}\"\n href=\"${item.href}\"\n aria-current=\"${item.active ? 'page' : null}\"\n data-active=\"${item.active ? 'true' : null}\">\n <span class=\"bottom-tab-icon\" aria-hidden=\"true\" ?hidden=${() => !item.iconName}>\n <ore-icon name=\"${item.iconName}\" size=\"18\" stroke-width=\"2\"></ore-icon>\n </span>\n <span class=\"bottom-tab-label\">${item.label}</span>\n </a>\n `;\n }\n\n return html`\n <button\n class=\"${className}\"\n type=\"button\"\n ?disabled=${item.disabled}\n aria-current=\"${item.active ? 'page' : null}\"\n data-active=\"${item.active ? 'true' : null}\"\n @click=${() => item.source.click()}>\n <span class=\"bottom-tab-icon\" aria-hidden=\"true\" ?hidden=${() => !item.iconName}>\n <ore-icon name=\"${item.iconName}\" size=\"18\" stroke-width=\"2\"></ore-icon>\n </span>\n <span class=\"bottom-tab-label\">${item.label}</span>\n </button>\n `;\n })}\n </div>\n `;\n },\n styles: [coarsePointerMixin, reducedMotionMixin, sidebarStyles],\n});\n\n// ─── ore-sidebar-group styles ────────────────────────────────────────────────\n\n/** Sidebar group properties */\nexport type OreSidebarGroupProps = {\n /** Whether this group can be collapsed */\n collapsible?: boolean;\n /** Initial open state in uncontrolled mode */\n 'default-open'?: boolean;\n /** Accessible label for the group */\n label?: string;\n /** Controlled open state */\n open?: boolean;\n};\n\n/**\n * `ore-sidebar-group`: A labelled section within `ore-sidebar`.\n *\n * @element ore-sidebar-group\n *\n * @attr {string} label - Group label text\n * @attr {boolean} collapsible - Whether this group can be toggled open/closed\n * @attr {boolean} open - Controlled expanded state\n * @attr {boolean} default-open - Initial expanded state in uncontrolled mode\n *\n * @fires open-change - Fired when the group open state changes (collapsible groups only). detail: { open: boolean }\n *\n * @slot - Navigation items (`ore-sidebar-item`)\n * @slot icon - Icon displayed before the label\n *\n * @example\n * ```html\n * <ore-sidebar-group label=\"Main\" collapsible open>\n * <ore-sidebar-item href=\"/home\">Home</ore-sidebar-item>\n * </ore-sidebar-group>\n * ```\n */\nexport const SIDEBAR_GROUP_TAG = 'ore-sidebar-group' as const;\ndefine<OreSidebarGroupProps>(SIDEBAR_GROUP_TAG, {\n props: {\n collapsible: prop.bool(false),\n 'default-open': prop.bool(true),\n label: prop.string(),\n open: {\n default: undefined as boolean | undefined,\n parse: (value: string | null) => (value == null ? undefined : value === '' || value === 'true'),\n reflect: false,\n },\n },\n setup(props) {\n const slots = useSlots();\n\n const hasIcon = () => slots.has('icon').value;\n const sidebarCtx = inject(SIDEBAR_CTX);\n\n bind({\n attr: {\n 'sidebar-bottom-nav': () =>\n sidebarCtx?.mode.value === 'bottom-nav' && !sidebarCtx?.drawerOpen.value ? true : undefined,\n 'sidebar-collapsed': () => (sidebarCtx?.collapsed.value ? true : undefined),\n },\n });\n\n const isControlled = () => props.open.value !== undefined;\n const openState = signal(props['default-open'].value);\n const isOpen = computed(() => {\n if (!props.collapsible.value) return true;\n\n if (isControlled()) return props.open.value ?? false;\n\n return openState.value;\n });\n\n watch(props.open, (value) => {\n if (value === undefined) return;\n\n openState.value = value;\n });\n\n bind({\n attr: {\n open: () => (isOpen.value ? true : undefined),\n },\n });\n\n return html`\n <details class=\"group\" part=\"group\" ?open=${isOpen}>\n <summary\n class=\"group-header\"\n part=\"group-header\"\n aria-expanded=\"${() => (props.collapsible.value ? String(props.open.value) : null)}\"\n @click=${(e: MouseEvent) => {\n if (!props.collapsible.value) {\n e.preventDefault();\n }\n }}>\n <span class=\"group-icon\" part=\"group-icon\" ?hidden=${() => !hasIcon()} aria-hidden=\"true\">\n <slot name=\"icon\"></slot>\n </span>\n <span class=\"group-label\" part=\"group-label\">${props.label}</span>\n <span class=\"chevron\" ?hidden=${() => !props.collapsible.value} aria-hidden=\"true\">\n <ore-icon name=\"chevron-right\" size=\"12\" stroke-width=\"2\" aria-hidden=\"true\"></ore-icon>\n </span>\n </summary>\n <div class=\"group-items\" part=\"group-items\" role=\"list\">\n <slot></slot>\n </div>\n </details>\n `;\n },\n styles: [reducedMotionMixin, groupStyles],\n});\n\n// ─── ore-sidebar-item styles ─────────────────────────────────────────────────\n\n/** Sidebar item properties */\nexport type OreSidebarItemProps = {\n /** Whether this item represents the current page/section */\n active?: boolean;\n 'bottom-nav'?: boolean;\n 'bottom-nav-label'?: string;\n /** Whether this item is disabled */\n disabled?: boolean;\n /** Navigation href: renders an `<a>` when set, otherwise a `<button>` */\n href?: string;\n label?: string;\n /**\n * Relationship of the linked URL (`rel` attribute on the inner `<a>`).\n * Only applies when `href` is set.\n */\n rel?: string;\n /**\n * Browsing context for the link (`target` attribute on the inner `<a>`).\n * Only applies when `href` is set.\n */\n target?: string;\n};\n\n/**\n * `ore-sidebar-item`: An individual navigation item in a `ore-sidebar`.\n *\n * Renders as an `<a>` when `href` is provided, otherwise as a `<button>`.\n * Marks the active page via `aria-current=\"page\"` when the `active` attribute is set.\n *\n * @element ore-sidebar-item\n *\n * @attr {string} href - Link URL; renders an anchor when set\n * @attr {boolean} active - Marks the item as the current page\n * @attr {boolean} disabled - Disables the item\n * @attr {string} rel - Anchor `rel` attribute (links only)\n * @attr {string} target - Anchor `target` attribute (links only)\n *\n * @slot - Label text\n * @slot icon - Leading icon\n * @slot end - Trailing content (badge, shortcut, arrow, etc.)\n *\n * @cssprop --sidebar-item-color - Default text color\n * @cssprop --sidebar-item-hover-bg - Hover background\n * @cssprop --sidebar-item-hover-color - Hover text color\n * @cssprop --sidebar-item-active-bg - Active background\n * @cssprop --sidebar-item-active-color - Active text color\n * @cssprop --sidebar-item-indicator - Active indicator bar color\n *\n * @part item - The inner anchor or button element\n * @part item-icon - The icon wrapper\n * @part item-label - The label wrapper\n * @part item-end - The trailing content wrapper\n *\n * @example\n * ```html\n * <ore-sidebar-item href=\"/dashboard\" active>\n * <span slot=\"icon\">🏠</span>\n * Dashboard\n * </ore-sidebar-item>\n *\n * <ore-sidebar-item href=\"/users\">\n * <span slot=\"icon\">👤</span>\n * Users\n * <ore-badge slot=\"end\" color=\"primary\">3</ore-badge>\n * </ore-sidebar-item>\n * ```\n */\nexport const SIDEBAR_ITEM_TAG = 'ore-sidebar-item' as const;\ndefine<OreSidebarItemProps>(SIDEBAR_ITEM_TAG, {\n props: {\n active: prop.bool(false),\n 'bottom-nav': prop.bool(false),\n 'bottom-nav-label': prop.string(),\n disabled: prop.bool(false),\n href: prop.string(),\n label: prop.string(),\n rel: prop.string(),\n target: prop.string(),\n },\n setup(props) {\n const slots = useSlots();\n\n const hasIcon = () => slots.has('icon').value;\n const hasEnd = () => slots.has('end').value;\n const sidebarCtx = inject(SIDEBAR_CTX);\n\n bind({\n attr: {\n 'sidebar-bottom-nav': () =>\n sidebarCtx?.mode.value === 'bottom-nav' && !sidebarCtx?.drawerOpen.value ? true : undefined,\n 'sidebar-collapsed': () => (sidebarCtx?.collapsed.value ? true : undefined),\n },\n });\n\n const isLink = () => !!props.href.value && !props.disabled.value;\n\n // Prevent reverse tabnapping: auto-inject noopener + noreferrer for _blank links.\n const effectiveRel = computed(() => computeSafeRel(props.rel.value, props.target.value));\n const closeDrawerOnSelect = (event: MouseEvent) => {\n if (!sidebarCtx?.closeOnSelect.value || !sidebarCtx.drawerOpen.value) return;\n if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;\n\n queueMicrotask(sidebarCtx.closeDrawer);\n };\n\n const renderItemContent = () => html`\n <span class=\"item-icon\" part=\"item-icon\" ?hidden=${() => !hasIcon()} aria-hidden=\"true\">\n <slot name=\"icon\"></slot>\n </span>\n <span class=\"item-label\" part=\"item-label\"><slot></slot></span>\n <span class=\"item-end\" part=\"item-end\" ?hidden=${() => !hasEnd()}>\n <slot name=\"end\"></slot>\n </span>\n `;\n\n return html`\n ${() => {\n if (isLink()) {\n return html`\n <a\n class=\"item\"\n part=\"item\"\n href=\"${props.href}\"\n rel=\"${effectiveRel}\"\n target=\"${props.target}\"\n aria-label=\"${props.label}\"\n aria-current=\"${() => (props.active.value ? 'page' : null)}\"\n @click=${closeDrawerOnSelect}>\n ${renderItemContent()}\n </a>\n `;\n }\n\n if (props.disabled.value) {\n return html`\n <div\n class=\"item\"\n part=\"item\"\n aria-disabled=\"true\"\n tabindex=\"-1\"\n aria-label=\"${props.label}\"\n aria-current=\"${() => (props.active.value ? 'page' : null)}\">\n ${renderItemContent()}\n </div>\n `;\n }\n\n return html`\n <button\n class=\"item\"\n part=\"item\"\n type=\"button\"\n ?disabled=\"${props.disabled}\"\n aria-label=\"${props.label}\"\n aria-current=\"${() => (props.active.value ? 'page' : null)}\"\n @click=${closeDrawerOnSelect}>\n ${renderItemContent()}\n </button>\n `;\n }}\n `;\n },\n styles: [coarsePointerMixin, itemStyles],\n});\n"],"mappings":"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"}
|
package/dist/skeleton.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skeleton.cjs","names":[],"sources":["../src/feedback/skeleton/skeleton.ts"],"sourcesContent":["import { bind, define, getHost, html, onCleanup, onMounted, prop } from '@vielzeug/ore';\nimport { computed, fromSubscribable, signal, watch } from '@vielzeug/ripple';\nimport { createIntersection } from '@vielzeug/sentinel';\nimport { sizableBundle } from '../../shared';\nimport { reducedMotionMixin } from '../../styles';\nimport type { ComponentSize } from '../../types';\nimport { safeCSSLength } from '../../utils';\nimport componentStyles from './skeleton.css?inline';\n\n/** Skeleton loader component properties */\nexport type OreSkeletonProps = {\n /** Toggle shimmer animation */\n animated?: boolean;\n /** Height override (e.g. '1rem', '3rem') */\n height?: string;\n /** Number of text lines for `variant='text'` */\n lines?: number;\n /** Radius override (e.g. '9999px', 'var(--rounded-xl)') */\n radius?: string;\n /** Size preset controlling line height and circle size */\n size?: ComponentSize;\n /** Render diagonal stripes instead of the shimmer
|
|
1
|
+
{"version":3,"file":"skeleton.cjs","names":[],"sources":["../src/feedback/skeleton/skeleton.ts"],"sourcesContent":["import { bind, define, getHost, html, onCleanup, onMounted, prop } from '@vielzeug/ore';\nimport { computed, fromSubscribable, signal, watch } from '@vielzeug/ripple';\nimport { createIntersection } from '@vielzeug/sentinel';\nimport { sizableBundle } from '../../shared';\nimport { reducedMotionMixin } from '../../styles';\nimport type { ComponentSize } from '../../types';\nimport { safeCSSLength } from '../../utils';\nimport componentStyles from './skeleton.css?inline';\n\n/** Skeleton loader component properties */\nexport type OreSkeletonProps = {\n /** Toggle shimmer animation */\n animated?: boolean;\n /** Height override (e.g. '1rem', '3rem') */\n height?: string;\n /** Number of text lines for `variant='text'` */\n lines?: number;\n /** Radius override (e.g. '9999px', 'var(--rounded-xl)') */\n radius?: string;\n /** Size preset controlling line height and circle size */\n size?: ComponentSize;\n /** Render diagonal stripes instead of the shimmer: useful as a design-mode placeholder */\n striped?: boolean;\n /** Visual variant: 'rect' (default), 'circle', or 'text' */\n variant?: 'rect' | 'circle' | 'text';\n /** Width override (e.g. '12rem', '70%') */\n width?: string;\n};\n\n/**\n * A shimmer placeholder that represents loading content.\n * Control dimensions via the `--skeleton-width` and `--skeleton-height` CSS custom properties,\n * or via `width` / `height` inline styles. Rectangle and circle bones fill the rendered host box.\n *\n * @element ore-skeleton\n *\n * @attr {string} variant - Shape: 'rect' (default) | 'circle' | 'text'\n * @attr {string} size - Height/circle preset: 'sm' | 'md' | 'lg'\n * @attr {string} width - Width override (CSS length/percentage)\n * @attr {string} height - Height override (CSS length)\n * @attr {string} radius - Radius override\n * @attr {boolean} animated - Disable with `animated=\"false\"`\n * @attr {number} lines - Text line count (only for `variant='text'`)\n * @attr {boolean} striped - Replace shimmer with diagonal stripes\n *\n * @cssprop --skeleton-bg - Base shimmer color\n * @cssprop --skeleton-highlight - Shimmer highlight color\n * @cssprop --skeleton-radius - Border radius\n * @cssprop --skeleton-size - Circle fallback size\n * @cssprop --skeleton-width - Width (default: 100%)\n * @cssprop --skeleton-height - Height (default: var(--size-4))\n * @cssprop --skeleton-line-gap - Vertical gap between text lines\n * @cssprop --skeleton-last-line-width - Width of the final text line\n * @cssprop --skeleton-duration - Shimmer animation duration\n * @cssprop --skeleton-stripe-size - Width of each diagonal stripe (default: 6px)\n *\n * @part stack - Stack container.\n * @part bone - Skeleton bone element.\n * @example\n * ```html\n * <!-- Paragraph lines -->\n * <ore-skeleton variant=\"text\" lines=\"3\" width=\"100%\"></ore-skeleton>\n *\n * <!-- Avatar -->\n * <ore-skeleton variant=\"circle\" size=\"md\"></ore-skeleton>\n *\n * <!-- Card image -->\n * <ore-skeleton width=\"100%\" height=\"10rem\"></ore-skeleton>\n * ```\n */\nexport const SKELETON_TAG = 'ore-skeleton' as const;\ndefine<OreSkeletonProps>(SKELETON_TAG, {\n props: {\n ...sizableBundle,\n animated: prop.bool(true),\n height: prop.string(),\n lines: prop.number(1),\n radius: prop.string(),\n striped: prop.bool(false),\n variant: prop.oneOf(['rect', 'circle', 'text'] as const, 'rect'),\n width: prop.string(),\n },\n setup(props) {\n const el = getHost();\n\n const isPaused = signal(false);\n const lineCount = () => {\n const value = Math.floor(Number(props.lines.value));\n\n return Number.isFinite(value) && value > 0 ? value : 1;\n };\n const renderLineCount = () => (props.variant.value === 'text' ? lineCount() : 1);\n const styleDeps = () =>\n `${props.width.value ?? ''}|${props.height.value ?? ''}|${props.radius.value ?? ''}|${props.animated.value === false ? '0' : '1'}`;\n\n watch(\n computed(styleDeps),\n () => {\n const safeWidth = safeCSSLength(props.width.value);\n const safeHeight = safeCSSLength(props.height.value);\n const safeRadius = safeCSSLength(props.radius.value);\n\n if (safeWidth) el.style.setProperty('--skeleton-width', safeWidth);\n else el.style.removeProperty('--skeleton-width');\n\n if (safeHeight) el.style.setProperty('--skeleton-height', safeHeight);\n else el.style.removeProperty('--skeleton-height');\n\n if (safeRadius) el.style.setProperty('--skeleton-radius', safeRadius);\n else el.style.removeProperty('--skeleton-radius');\n\n const rawAnimated = el.getAttribute('animated');\n const isAnimated = rawAnimated !== 'false' && props.animated.value !== false;\n\n el.setAttribute('data-animated', isAnimated ? 'true' : 'false');\n },\n { immediate: true },\n );\n\n bind({\n attr: {\n 'data-paused': () => (isPaused.value ? true : undefined),\n },\n });\n\n onMounted(() => {\n const intersection = createIntersection(el, { threshold: 0 });\n\n const intersectionState = fromSubscribable(intersection, { signal: intersection.disposalSignal });\n const intersectionWatch = watch(intersectionState, (state) => {\n const paused = state === null ? false : !state.isIntersecting;\n isPaused.value = paused;\n });\n\n onCleanup(() => {\n intersectionWatch.dispose();\n intersection.dispose();\n });\n });\n\n return html`\n <div class=\"stack\" part=\"stack\">\n ${() =>\n Array.from({ length: renderLineCount() }, (_, index) => {\n const isLastLine =\n props.variant.value === 'text' && renderLineCount() > 1 && index === renderLineCount() - 1;\n\n return html`\n <div class=\"bone\" part=\"bone\" aria-hidden=\"true\" data-last=\"${() => (isLastLine ? 'true' : null)}\"></div>\n `;\n })}\n </div>\n `;\n },\n styles: [reducedMotionMixin, componentStyles],\n});\n"],"mappings":"+UAsEA,IAAa,EAAe,gBAC5B,EAAA,EAAA,OAAA,CAAyB,EAAc,CACrC,MAAO,CACL,GAAG,EAAA,cACH,SAAU,EAAA,KAAK,KAAK,EAAI,EACxB,OAAQ,EAAA,KAAK,OAAO,EACpB,MAAO,EAAA,KAAK,OAAO,CAAC,EACpB,OAAQ,EAAA,KAAK,OAAO,EACpB,QAAS,EAAA,KAAK,KAAK,EAAK,EACxB,QAAS,EAAA,KAAK,MAAM,CAAC,OAAQ,SAAU,MAAM,EAAY,MAAM,EAC/D,MAAO,EAAA,KAAK,OAAO,CACrB,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAK,EAAA,QAAA,CAAQ,EAEb,GAAA,EAAW,EAAA,OAAA,CAAO,EAAK,EACvB,cAAkB,CACtB,IAAM,EAAQ,KAAK,MAAM,OAAO,EAAM,MAAM,KAAK,CAAC,EAElD,OAAO,OAAO,SAAS,CAAK,GAAK,EAAQ,EAAI,EAAQ,CACvD,EACM,oBAAyB,EAAM,QAAQ,QAAU,OAAS,UAAU,EAAI,EACxE,cACJ,GAAG,EAAM,MAAM,OAAS,GAAG,GAAG,EAAM,OAAO,OAAS,GAAG,GAAG,EAAM,OAAO,OAAS,GAAG,GAAG,EAAM,SAAS,QAAU,GAAQ,IAAM,MA+C/H,OA7CA,EAAA,EAAA,MAAA,EAAA,EACE,EAAA,SAAA,CAAS,SAAS,MACZ,CACJ,IAAM,EAAY,EAAA,cAAc,EAAM,MAAM,KAAK,EAC3C,EAAa,EAAA,cAAc,EAAM,OAAO,KAAK,EAC7C,EAAa,EAAA,cAAc,EAAM,OAAO,KAAK,EAE/C,EAAW,EAAG,MAAM,YAAY,mBAAoB,CAAS,EAC5D,EAAG,MAAM,eAAe,kBAAkB,EAE3C,EAAY,EAAG,MAAM,YAAY,oBAAqB,CAAU,EAC/D,EAAG,MAAM,eAAe,mBAAmB,EAE5C,EAAY,EAAG,MAAM,YAAY,oBAAqB,CAAU,EAC/D,EAAG,MAAM,eAAe,mBAAmB,EAGhD,IAAM,EADc,EAAG,aAAa,UACjB,IAAgB,SAAW,EAAM,SAAS,QAAU,GAEvE,EAAG,aAAa,gBAAiB,EAAa,OAAS,OAAO,CAChE,EACA,CAAE,UAAW,EAAK,CACpB,GAEA,EAAA,EAAA,KAAA,CAAK,CACH,KAAM,CACJ,kBAAsB,EAAS,MAAQ,GAAO,IAAA,EAChD,CACF,CAAC,GAED,EAAA,EAAA,UAAA,KAAgB,CACd,IAAM,GAAA,EAAe,EAAA,mBAAA,CAAmB,EAAI,CAAE,UAAW,CAAE,CAAC,EAEtD,GAAA,EAAoB,EAAA,iBAAA,CAAiB,EAAc,CAAE,OAAQ,EAAa,cAAe,CAAC,EAC1F,GAAA,EAAoB,EAAA,MAAA,CAAM,EAAoB,GAAU,CAC5D,IAAM,EAAS,IAAU,MAAe,CAAC,EAAM,eAC/C,EAAS,MAAQ,CACnB,CAAC,GAED,EAAA,EAAA,UAAA,KAAgB,CACd,EAAkB,QAAQ,EAC1B,EAAa,QAAQ,CACvB,CAAC,CACH,CAAC,EAEM,EAAA,IAAI;;cAGL,MAAM,KAAK,CAAE,OAAQ,gBAAgB,CAAE,GAAI,EAAG,IAAU,CACtD,IAAM,EACJ,EAAM,QAAQ,QAAU,QAAU,gBAAgB,EAAI,GAAK,IAAU,gBAAgB,EAAI,EAE3F,MAAO,GAAA,IAAI;gFAC4D,EAAa,OAAS,KAAM;aAErG,CAAC,EAAE;;KAGX,EACA,OAAQ,CAAC,EAAA,mBAAoB,EAAA,OAAe,CAC9C,CAAC"}
|