sheer-ui 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +42 -0
- package/package.json +369 -0
- package/src/lib/VENDORED.md +109 -0
- package/src/lib/assets/ui.css +733 -0
- package/src/lib/blocks/Appearance.svelte +104 -0
- package/src/lib/blocks/BoundaryFailed.svelte +33 -0
- package/src/lib/blocks/ErrorPage.svelte +65 -0
- package/src/lib/blocks/Footer.svelte +71 -0
- package/src/lib/blocks/Header.svelte +67 -0
- package/src/lib/blocks/Heading.svelte +86 -0
- package/src/lib/blocks/MobileNav.svelte +169 -0
- package/src/lib/blocks/PhoneInput.svelte +68 -0
- package/src/lib/blocks/Section.svelte +34 -0
- package/src/lib/blocks/alert-modal/AlertModal.svelte +83 -0
- package/src/lib/blocks/alert-modal/alert-modal.svelte.ts +81 -0
- package/src/lib/blocks/alert-modal/index.ts +2 -0
- package/src/lib/blocks/calendar.svelte +155 -0
- package/src/lib/blocks/copy-button/copy-button.svelte +43 -0
- package/src/lib/blocks/copy-button/copy.svelte.ts +52 -0
- package/src/lib/blocks/copy-button/index.ts +2 -0
- package/src/lib/blocks/date-picker/date-picker-with-presets.svelte +109 -0
- package/src/lib/blocks/date-picker/date-picker.svelte +91 -0
- package/src/lib/blocks/date-picker/index.ts +13 -0
- package/src/lib/blocks/date-picker/range-date-picker.svelte +128 -0
- package/src/lib/blocks/index.ts +12 -0
- package/src/lib/blocks/mockups/Browser.svelte +22 -0
- package/src/lib/blocks/mockups/Phone.svelte +44 -0
- package/src/lib/blocks/mockups/index.ts +2 -0
- package/src/lib/blocks/tab-nav/index.ts +1 -0
- package/src/lib/blocks/tab-nav/tab-nav.svelte +46 -0
- package/src/lib/blocks/time-picker/index.ts +16 -0
- package/src/lib/blocks/time-picker/time-input.svelte +118 -0
- package/src/lib/blocks/time-picker/time-picker.svelte +93 -0
- package/src/lib/blocks/time-picker/time-range-input.svelte +34 -0
- package/src/lib/blocks/time-picker/time-range-picker.svelte +180 -0
- package/src/lib/blocks/time-picker/time-slots.ts +61 -0
- package/src/lib/blocks/user-avatar/UserAvatar.svelte +36 -0
- package/src/lib/blocks/user-avatar/index.ts +1 -0
- package/src/lib/blocks/verification-code-field/VerificationCodeField.svelte +57 -0
- package/src/lib/blocks/verification-code-field/index.ts +1 -0
- package/src/lib/components/accordion/accordion.svelte.ts +43 -0
- package/src/lib/components/accordion/components/accordion-content.svelte +17 -0
- package/src/lib/components/accordion/components/accordion-item.svelte +58 -0
- package/src/lib/components/accordion/components/accordion-trigger.svelte +38 -0
- package/src/lib/components/accordion/components/accordion.svelte +24 -0
- package/src/lib/components/accordion/index.ts +11 -0
- package/src/lib/components/accordion/types.ts +19 -0
- package/src/lib/components/alert/alert-action.svelte +11 -0
- package/src/lib/components/alert/alert-description.svelte +18 -0
- package/src/lib/components/alert/alert-title.svelte +18 -0
- package/src/lib/components/alert/alert.svelte +37 -0
- package/src/lib/components/alert/index.ts +17 -0
- package/src/lib/components/avatar/avatar.svelte.ts +154 -0
- package/src/lib/components/avatar/components/avatar-fallback.svelte +38 -0
- package/src/lib/components/avatar/components/avatar-image.svelte +47 -0
- package/src/lib/components/avatar/components/avatar.svelte +57 -0
- package/src/lib/components/avatar/index.ts +9 -0
- package/src/lib/components/avatar/types.ts +46 -0
- package/src/lib/components/badge/badge.svelte +49 -0
- package/src/lib/components/badge/index.ts +1 -0
- package/src/lib/components/breadcrumb/breadcrumb-ellipsis.svelte +23 -0
- package/src/lib/components/breadcrumb/breadcrumb-item.svelte +11 -0
- package/src/lib/components/breadcrumb/breadcrumb-link.svelte +32 -0
- package/src/lib/components/breadcrumb/breadcrumb-list.svelte +15 -0
- package/src/lib/components/breadcrumb/breadcrumb-page.svelte +18 -0
- package/src/lib/components/breadcrumb/breadcrumb-separator.svelte +22 -0
- package/src/lib/components/breadcrumb/breadcrumb.svelte +10 -0
- package/src/lib/components/breadcrumb/index.ts +25 -0
- package/src/lib/components/button/button.svelte +55 -0
- package/src/lib/components/button/index.ts +12 -0
- package/src/lib/components/button/variants.ts +51 -0
- package/src/lib/components/calendar/calendar.svelte.ts +1081 -0
- package/src/lib/components/calendar/components/calendar-cell.svelte +34 -0
- package/src/lib/components/calendar/components/calendar-day.svelte +36 -0
- package/src/lib/components/calendar/components/calendar-grid-body.svelte +29 -0
- package/src/lib/components/calendar/components/calendar-grid-head.svelte +29 -0
- package/src/lib/components/calendar/components/calendar-grid-row.svelte +29 -0
- package/src/lib/components/calendar/components/calendar-grid.svelte +29 -0
- package/src/lib/components/calendar/components/calendar-head-cell.svelte +29 -0
- package/src/lib/components/calendar/components/calendar-header.svelte +29 -0
- package/src/lib/components/calendar/components/calendar-heading.svelte +33 -0
- package/src/lib/components/calendar/components/calendar-month-select.svelte +50 -0
- package/src/lib/components/calendar/components/calendar-next-button.svelte +37 -0
- package/src/lib/components/calendar/components/calendar-prev-button.svelte +37 -0
- package/src/lib/components/calendar/components/calendar-year-select.svelte +50 -0
- package/src/lib/components/calendar/components/calendar.svelte +123 -0
- package/src/lib/components/calendar/exports.ts +31 -0
- package/src/lib/components/calendar/index.ts +2 -0
- package/src/lib/components/calendar/types.ts +417 -0
- package/src/lib/components/card/card-action.svelte +15 -0
- package/src/lib/components/card/card-content.svelte +11 -0
- package/src/lib/components/card/card-description.svelte +11 -0
- package/src/lib/components/card/card-footer.svelte +11 -0
- package/src/lib/components/card/card-header.svelte +18 -0
- package/src/lib/components/card/card-title.svelte +11 -0
- package/src/lib/components/card/card.svelte +15 -0
- package/src/lib/components/card/index.ts +25 -0
- package/src/lib/components/carousel/carousel-arrow.svelte +42 -0
- package/src/lib/components/carousel/carousel-content.svelte +25 -0
- package/src/lib/components/carousel/carousel-dots.svelte +21 -0
- package/src/lib/components/carousel/carousel-item.svelte +21 -0
- package/src/lib/components/carousel/carousel-next.svelte +9 -0
- package/src/lib/components/carousel/carousel-previous.svelte +9 -0
- package/src/lib/components/carousel/carousel.svelte +245 -0
- package/src/lib/components/carousel/carouselState.svelte.ts +30 -0
- package/src/lib/components/carousel/index.ts +24 -0
- package/src/lib/components/chart/chart-container.svelte +52 -0
- package/src/lib/components/chart/chart-style.svelte +29 -0
- package/src/lib/components/chart/chart-tooltip.svelte +163 -0
- package/src/lib/components/chart/chart-utils.ts +50 -0
- package/src/lib/components/chart/index.ts +6 -0
- package/src/lib/components/checkbox/checkbox.svelte.ts +199 -0
- package/src/lib/components/checkbox/components/checkbox-group-label.svelte +29 -0
- package/src/lib/components/checkbox/components/checkbox-group.svelte +48 -0
- package/src/lib/components/checkbox/components/checkbox.svelte +85 -0
- package/src/lib/components/checkbox/index.ts +9 -0
- package/src/lib/components/checkbox/types.ts +131 -0
- package/src/lib/components/checkbox-native/checkbox.svelte +96 -0
- package/src/lib/components/checkbox-native/index.ts +8 -0
- package/src/lib/components/collapsible/components/collapsible-content.svelte +16 -0
- package/src/lib/components/collapsible/components/collapsible-trigger.svelte +14 -0
- package/src/lib/components/collapsible/components/collapsible.svelte +68 -0
- package/src/lib/components/collapsible/index.ts +9 -0
- package/src/lib/components/collapsible/types.ts +25 -0
- package/src/lib/components/combobox/components/combobox-arrow.svelte +8 -0
- package/src/lib/components/combobox/components/combobox-input.svelte +46 -0
- package/src/lib/components/combobox/components/combobox-trigger.svelte +44 -0
- package/src/lib/components/combobox/components/combobox.svelte +83 -0
- package/src/lib/components/combobox/index.ts +30 -0
- package/src/lib/components/combobox/select/components/select-content-static.svelte +56 -0
- package/src/lib/components/combobox/select/components/select-content.svelte +95 -0
- package/src/lib/components/combobox/select/components/select-group-heading.svelte +51 -0
- package/src/lib/components/combobox/select/components/select-group.svelte +51 -0
- package/src/lib/components/combobox/select/components/select-hidden-input.svelte +16 -0
- package/src/lib/components/combobox/select/components/select-item.svelte +86 -0
- package/src/lib/components/combobox/select/components/select-scroll-down-button.svelte +41 -0
- package/src/lib/components/combobox/select/components/select-scroll-up-button.svelte +34 -0
- package/src/lib/components/combobox/select/components/select-trigger.svelte +32 -0
- package/src/lib/components/combobox/select/components/select-value.svelte +40 -0
- package/src/lib/components/combobox/select/components/select-viewport.svelte +29 -0
- package/src/lib/components/combobox/select/components/select.svelte +92 -0
- package/src/lib/components/combobox/select/select.svelte.ts +1596 -0
- package/src/lib/components/combobox/select/types.ts +291 -0
- package/src/lib/components/combobox/types.ts +77 -0
- package/src/lib/components/command/command-dialog.svelte +42 -0
- package/src/lib/components/command/command.svelte.ts +1506 -0
- package/src/lib/components/command/components/_command-label.svelte +31 -0
- package/src/lib/components/command/components/command-empty.svelte +34 -0
- package/src/lib/components/command/components/command-group-heading.svelte +29 -0
- package/src/lib/components/command/components/command-group-items.svelte +31 -0
- package/src/lib/components/command/components/command-group.svelte +76 -0
- package/src/lib/components/command/components/command-input.svelte +55 -0
- package/src/lib/components/command/components/command-item.svelte +61 -0
- package/src/lib/components/command/components/command-link-item.svelte +61 -0
- package/src/lib/components/command/components/command-list.svelte +41 -0
- package/src/lib/components/command/components/command-loading.svelte +30 -0
- package/src/lib/components/command/components/command-separator.svelte +34 -0
- package/src/lib/components/command/components/command-viewport.svelte +29 -0
- package/src/lib/components/command/components/command.svelte +143 -0
- package/src/lib/components/command/compute-command-score.ts +207 -0
- package/src/lib/components/command/index.ts +29 -0
- package/src/lib/components/command/types.ts +222 -0
- package/src/lib/components/command/utils.ts +30 -0
- package/src/lib/components/context-menu/components/context-menu-content-static.svelte +22 -0
- package/src/lib/components/context-menu/components/context-menu-content.svelte +22 -0
- package/src/lib/components/context-menu/components/context-menu-trigger.svelte +46 -0
- package/src/lib/components/context-menu/components/context-menu.svelte +8 -0
- package/src/lib/components/context-menu/index.ts +39 -0
- package/src/lib/components/context-menu/types.ts +56 -0
- package/src/lib/components/data-table/column-helpers.ts +97 -0
- package/src/lib/components/data-table/data-table-checkbox.svelte +25 -0
- package/src/lib/components/data-table/data-table-sort-button.svelte +16 -0
- package/src/lib/components/data-table/flex-render.svelte +42 -0
- package/src/lib/components/data-table/index.ts +29 -0
- package/src/lib/components/data-table/render-helpers.ts +105 -0
- package/src/lib/components/data-table/table-cell.svelte +9 -0
- package/src/lib/components/data-table/table-column-header.svelte +51 -0
- package/src/lib/components/data-table/table-faceted-filter.svelte +109 -0
- package/src/lib/components/data-table/table-pagination.svelte +54 -0
- package/src/lib/components/data-table/table-priority-cell.svelte +22 -0
- package/src/lib/components/data-table/table-row-actions.svelte +29 -0
- package/src/lib/components/data-table/table-status-cell.svelte +34 -0
- package/src/lib/components/data-table/table-title-cell.svelte +24 -0
- package/src/lib/components/data-table/table-toolbar.svelte +55 -0
- package/src/lib/components/data-table/table-view-options.svelte +25 -0
- package/src/lib/components/date-field/components/date-field-hidden-input.svelte +9 -0
- package/src/lib/components/date-field/components/date-field-input.svelte +33 -0
- package/src/lib/components/date-field/components/date-field-label.svelte +29 -0
- package/src/lib/components/date-field/components/date-field-segment.svelte +29 -0
- package/src/lib/components/date-field/components/date-field.svelte +106 -0
- package/src/lib/components/date-field/date-field.svelte.ts +1474 -0
- package/src/lib/components/date-field/index.ts +11 -0
- package/src/lib/components/date-field/types.ts +165 -0
- package/src/lib/components/date-range-field/components/date-range-field-input.svelte +36 -0
- package/src/lib/components/date-range-field/components/date-range-field-label.svelte +29 -0
- package/src/lib/components/date-range-field/components/date-range-field.svelte +106 -0
- package/src/lib/components/date-range-field/date-range-field.svelte.ts +212 -0
- package/src/lib/components/date-range-field/index.ts +11 -0
- package/src/lib/components/date-range-field/types.ts +171 -0
- package/src/lib/components/dialog/components/dialog-close.svelte +31 -0
- package/src/lib/components/dialog/components/dialog-content-headless.svelte +104 -0
- package/src/lib/components/dialog/components/dialog-content.svelte +69 -0
- package/src/lib/components/dialog/components/dialog-description.svelte +29 -0
- package/src/lib/components/dialog/components/dialog-overlay.svelte +31 -0
- package/src/lib/components/dialog/components/dialog-title.svelte +30 -0
- package/src/lib/components/dialog/components/dialog-trigger.svelte +30 -0
- package/src/lib/components/dialog/components/dialog.svelte +21 -0
- package/src/lib/components/dialog/components/modal-surface.svelte +131 -0
- package/src/lib/components/dialog/dialog.svelte.ts +371 -0
- package/src/lib/components/dialog/index.ts +19 -0
- package/src/lib/components/dialog/types.ts +105 -0
- package/src/lib/components/drawer/drawer-close.svelte +7 -0
- package/src/lib/components/drawer/drawer-content.svelte +91 -0
- package/src/lib/components/drawer/drawer-description.svelte +8 -0
- package/src/lib/components/drawer/drawer-footer.svelte +11 -0
- package/src/lib/components/drawer/drawer-header.svelte +11 -0
- package/src/lib/components/drawer/drawer-nested.svelte +44 -0
- package/src/lib/components/drawer/drawer-overlay.svelte +60 -0
- package/src/lib/components/drawer/drawer-portal.svelte +10 -0
- package/src/lib/components/drawer/drawer-title.svelte +8 -0
- package/src/lib/components/drawer/drawer-trigger.svelte +7 -0
- package/src/lib/components/drawer/drawer.svelte +290 -0
- package/src/lib/components/drawer/index.ts +37 -0
- package/src/lib/components/dropdown-menu/components/dropdown-menu-content-static.svelte +22 -0
- package/src/lib/components/dropdown-menu/components/dropdown-menu-content.svelte +22 -0
- package/src/lib/components/dropdown-menu/index.ts +39 -0
- package/src/lib/components/dropdown-menu/types.ts +41 -0
- package/src/lib/components/empty/empty-content.svelte +15 -0
- package/src/lib/components/empty/empty-description.svelte +15 -0
- package/src/lib/components/empty/empty-header.svelte +15 -0
- package/src/lib/components/empty/empty-media.svelte +21 -0
- package/src/lib/components/empty/empty-title.svelte +11 -0
- package/src/lib/components/empty/empty.svelte +18 -0
- package/src/lib/components/empty/index.ts +22 -0
- package/src/lib/components/empty/variants.ts +16 -0
- package/src/lib/components/field/field-content.svelte +14 -0
- package/src/lib/components/field/field-description.svelte +19 -0
- package/src/lib/components/field/field-error.svelte +47 -0
- package/src/lib/components/field/field-group.svelte +17 -0
- package/src/lib/components/field/field-label.svelte +18 -0
- package/src/lib/components/field/field-legend.svelte +23 -0
- package/src/lib/components/field/field-separator.svelte +32 -0
- package/src/lib/components/field/field-set.svelte +14 -0
- package/src/lib/components/field/field-title.svelte +14 -0
- package/src/lib/components/field/field.svelte +36 -0
- package/src/lib/components/field/index.ts +32 -0
- package/src/lib/components/field/variants.ts +19 -0
- package/src/lib/components/input/file-input.svelte +26 -0
- package/src/lib/components/input/index.ts +12 -0
- package/src/lib/components/input/input.svelte +24 -0
- package/src/lib/components/input/variants.ts +28 -0
- package/src/lib/components/item/index.ts +34 -0
- package/src/lib/components/item/item-actions.svelte +11 -0
- package/src/lib/components/item/item-content.svelte +15 -0
- package/src/lib/components/item/item-description.svelte +18 -0
- package/src/lib/components/item/item-footer.svelte +11 -0
- package/src/lib/components/item/item-group.svelte +16 -0
- package/src/lib/components/item/item-header.svelte +11 -0
- package/src/lib/components/item/item-media.svelte +37 -0
- package/src/lib/components/item/item-separator.svelte +9 -0
- package/src/lib/components/item/item-title.svelte +15 -0
- package/src/lib/components/item/item.svelte +61 -0
- package/src/lib/components/kbd/index.ts +10 -0
- package/src/lib/components/kbd/kbd-group.svelte +11 -0
- package/src/lib/components/kbd/kbd.svelte +18 -0
- package/src/lib/components/label/exports.ts +2 -0
- package/src/lib/components/label/index.ts +2 -0
- package/src/lib/components/label/label.svelte +39 -0
- package/src/lib/components/label/label.svelte.ts +39 -0
- package/src/lib/components/label/types.ts +7 -0
- package/src/lib/components/link-preview/components/link-preview-content.svelte +60 -0
- package/src/lib/components/link-preview/components/link-preview-trigger.svelte +29 -0
- package/src/lib/components/link-preview/components/link-preview.svelte +34 -0
- package/src/lib/components/link-preview/exports.ts +11 -0
- package/src/lib/components/link-preview/index.ts +2 -0
- package/src/lib/components/link-preview/link-preview.svelte.ts +270 -0
- package/src/lib/components/link-preview/types.ts +102 -0
- package/src/lib/components/menu/components/menu-arrow.svelte +14 -0
- package/src/lib/components/menu/components/menu-checkbox-group.svelte +42 -0
- package/src/lib/components/menu/components/menu-checkbox-item.svelte +85 -0
- package/src/lib/components/menu/components/menu-content.svelte +98 -0
- package/src/lib/components/menu/components/menu-group-heading.svelte +47 -0
- package/src/lib/components/menu/components/menu-group.svelte +36 -0
- package/src/lib/components/menu/components/menu-item.svelte +58 -0
- package/src/lib/components/menu/components/menu-radio-group.svelte +44 -0
- package/src/lib/components/menu/components/menu-radio-item.svelte +65 -0
- package/src/lib/components/menu/components/menu-separator.svelte +38 -0
- package/src/lib/components/menu/components/menu-sub-content-static.svelte +8 -0
- package/src/lib/components/menu/components/menu-sub-content.svelte +133 -0
- package/src/lib/components/menu/components/menu-sub-trigger.svelte +60 -0
- package/src/lib/components/menu/components/menu-sub.svelte +23 -0
- package/src/lib/components/menu/components/menu-trigger.svelte +50 -0
- package/src/lib/components/menu/components/menu.svelte +58 -0
- package/src/lib/components/menu/exports.ts +38 -0
- package/src/lib/components/menu/menu.svelte.ts +1599 -0
- package/src/lib/components/menu/types.ts +285 -0
- package/src/lib/components/menu/utils.ts +68 -0
- package/src/lib/components/menubar/components/menubar-content-static.svelte +50 -0
- package/src/lib/components/menubar/components/menubar-content.svelte +54 -0
- package/src/lib/components/menubar/components/menubar-menu.svelte +34 -0
- package/src/lib/components/menubar/components/menubar-trigger.svelte +50 -0
- package/src/lib/components/menubar/components/menubar.svelte +57 -0
- package/src/lib/components/menubar/exports.ts +44 -0
- package/src/lib/components/menubar/index.ts +2 -0
- package/src/lib/components/menubar/menubar.svelte.ts +394 -0
- package/src/lib/components/menubar/types.ts +97 -0
- package/src/lib/components/meter/components/meter.svelte +41 -0
- package/src/lib/components/meter/exports.ts +2 -0
- package/src/lib/components/meter/index.ts +2 -0
- package/src/lib/components/meter/meter.svelte.ts +47 -0
- package/src/lib/components/meter/types.ts +28 -0
- package/src/lib/components/navigation-menu/components/navigation-menu-content-impl.svelte +85 -0
- package/src/lib/components/navigation-menu/components/navigation-menu-content.svelte +58 -0
- package/src/lib/components/navigation-menu/components/navigation-menu-indicator-impl.svelte +29 -0
- package/src/lib/components/navigation-menu/components/navigation-menu-indicator.svelte +56 -0
- package/src/lib/components/navigation-menu/components/navigation-menu-item.svelte +56 -0
- package/src/lib/components/navigation-menu/components/navigation-menu-link.svelte +64 -0
- package/src/lib/components/navigation-menu/components/navigation-menu-list.svelte +59 -0
- package/src/lib/components/navigation-menu/components/navigation-menu-sub.svelte +46 -0
- package/src/lib/components/navigation-menu/components/navigation-menu-trigger.svelte +80 -0
- package/src/lib/components/navigation-menu/components/navigation-menu-viewport.svelte +63 -0
- package/src/lib/components/navigation-menu/components/navigation-menu.svelte +80 -0
- package/src/lib/components/navigation-menu/index.ts +28 -0
- package/src/lib/components/navigation-menu/navigation-menu.svelte.ts +1092 -0
- package/src/lib/components/navigation-menu/types.ts +189 -0
- package/src/lib/components/pagination/components/pagination-next-button.svelte +60 -0
- package/src/lib/components/pagination/components/pagination-page.svelte +65 -0
- package/src/lib/components/pagination/components/pagination-prev-button.svelte +60 -0
- package/src/lib/components/pagination/components/pagination.svelte +65 -0
- package/src/lib/components/pagination/index.ts +11 -0
- package/src/lib/components/pagination/pagination.svelte.ts +275 -0
- package/src/lib/components/pagination/types.ts +100 -0
- package/src/lib/components/pin-input/components/pin-input-cell.svelte +49 -0
- package/src/lib/components/pin-input/components/pin-input.svelte +76 -0
- package/src/lib/components/pin-input/exports.ts +4 -0
- package/src/lib/components/pin-input/index.ts +2 -0
- package/src/lib/components/pin-input/pin-input.svelte.ts +541 -0
- package/src/lib/components/pin-input/types.ts +118 -0
- package/src/lib/components/pin-input/usePasswordManager.svelte.ts +122 -0
- package/src/lib/components/popover/components/popover-close.svelte +38 -0
- package/src/lib/components/popover/components/popover-content.svelte +70 -0
- package/src/lib/components/popover/components/popover-header.svelte +11 -0
- package/src/lib/components/popover/components/popover-title.svelte +11 -0
- package/src/lib/components/popover/components/popover-trigger.svelte +44 -0
- package/src/lib/components/popover/components/popover.svelte +26 -0
- package/src/lib/components/popover/exports.ts +13 -0
- package/src/lib/components/popover/index.ts +2 -0
- package/src/lib/components/popover/popover.svelte.ts +392 -0
- package/src/lib/components/popover/types.ts +92 -0
- package/src/lib/components/progress/components/progress.svelte +56 -0
- package/src/lib/components/progress/exports.ts +3 -0
- package/src/lib/components/progress/index.ts +2 -0
- package/src/lib/components/progress/progress.svelte.ts +58 -0
- package/src/lib/components/progress/types.ts +28 -0
- package/src/lib/components/radio-group/context.ts +13 -0
- package/src/lib/components/radio-group/index.ts +10 -0
- package/src/lib/components/radio-group/radio-group-item.svelte +67 -0
- package/src/lib/components/radio-group/radio-group.svelte +87 -0
- package/src/lib/components/range-calendar/components/range-calendar-cell.svelte +31 -0
- package/src/lib/components/range-calendar/components/range-calendar-day.svelte +33 -0
- package/src/lib/components/range-calendar/components/range-calendar.svelte +120 -0
- package/src/lib/components/range-calendar/exports.ts +31 -0
- package/src/lib/components/range-calendar/index.ts +2 -0
- package/src/lib/components/range-calendar/range-calendar.svelte.ts +739 -0
- package/src/lib/components/range-calendar/types.ts +258 -0
- package/src/lib/components/resizable/index.ts +15 -0
- package/src/lib/components/resizable/resizable-handle.svelte +30 -0
- package/src/lib/components/resizable/resizable-pane-group.svelte +19 -0
- package/src/lib/components/scroll-area/components/scroll-area-corner-impl.svelte +34 -0
- package/src/lib/components/scroll-area/components/scroll-area-corner.svelte +18 -0
- package/src/lib/components/scroll-area/components/scroll-area-scrollbar-auto.svelte +15 -0
- package/src/lib/components/scroll-area/components/scroll-area-scrollbar-hover.svelte +22 -0
- package/src/lib/components/scroll-area/components/scroll-area-scrollbar-scroll.svelte +16 -0
- package/src/lib/components/scroll-area/components/scroll-area-scrollbar-shared.svelte +19 -0
- package/src/lib/components/scroll-area/components/scroll-area-scrollbar-visible.svelte +16 -0
- package/src/lib/components/scroll-area/components/scroll-area-scrollbar-x.svelte +23 -0
- package/src/lib/components/scroll-area/components/scroll-area-scrollbar-y.svelte +24 -0
- package/src/lib/components/scroll-area/components/scroll-area-scrollbar.svelte +56 -0
- package/src/lib/components/scroll-area/components/scroll-area-thumb-impl.svelte +49 -0
- package/src/lib/components/scroll-area/components/scroll-area-thumb.svelte +21 -0
- package/src/lib/components/scroll-area/components/scroll-area-viewport.svelte +39 -0
- package/src/lib/components/scroll-area/components/scroll-area.svelte +82 -0
- package/src/lib/components/scroll-area/exports.ts +13 -0
- package/src/lib/components/scroll-area/index.ts +2 -0
- package/src/lib/components/scroll-area/scroll-area.svelte.ts +946 -0
- package/src/lib/components/scroll-area/types.ts +67 -0
- package/src/lib/components/select/index.ts +5 -0
- package/src/lib/components/select/select-opt-group.svelte +10 -0
- package/src/lib/components/select/select-option.svelte +15 -0
- package/src/lib/components/select/select.svelte +281 -0
- package/src/lib/components/separator/components/separator.svelte +49 -0
- package/src/lib/components/separator/index.ts +3 -0
- package/src/lib/components/separator/separator.svelte.ts +43 -0
- package/src/lib/components/separator/types.ts +22 -0
- package/src/lib/components/sheet/components/sheet-close.svelte +8 -0
- package/src/lib/components/sheet/components/sheet-content.svelte +108 -0
- package/src/lib/components/sheet/components/sheet-description.svelte +9 -0
- package/src/lib/components/sheet/components/sheet-footer.svelte +11 -0
- package/src/lib/components/sheet/components/sheet-header.svelte +11 -0
- package/src/lib/components/sheet/components/sheet-overlay.svelte +13 -0
- package/src/lib/components/sheet/components/sheet-title.svelte +9 -0
- package/src/lib/components/sheet/components/sheet-trigger.svelte +8 -0
- package/src/lib/components/sheet/index.ts +47 -0
- package/src/lib/components/sheet/variants.ts +20 -0
- package/src/lib/components/sidebar/constants.ts +6 -0
- package/src/lib/components/sidebar/context.svelte.ts +54 -0
- package/src/lib/components/sidebar/index.ts +77 -0
- package/src/lib/components/sidebar/sidebar-content.svelte +16 -0
- package/src/lib/components/sidebar/sidebar-desktop-surface.svelte +60 -0
- package/src/lib/components/sidebar/sidebar-footer.svelte +11 -0
- package/src/lib/components/sidebar/sidebar-group-action.svelte +34 -0
- package/src/lib/components/sidebar/sidebar-group-content.svelte +16 -0
- package/src/lib/components/sidebar/sidebar-group-label.svelte +34 -0
- package/src/lib/components/sidebar/sidebar-group.svelte +16 -0
- package/src/lib/components/sidebar/sidebar-header.svelte +11 -0
- package/src/lib/components/sidebar/sidebar-input.svelte +14 -0
- package/src/lib/components/sidebar/sidebar-inset.svelte +18 -0
- package/src/lib/components/sidebar/sidebar-menu-action.svelte +38 -0
- package/src/lib/components/sidebar/sidebar-menu-badge.svelte +19 -0
- package/src/lib/components/sidebar/sidebar-menu-button.svelte +99 -0
- package/src/lib/components/sidebar/sidebar-menu-item.svelte +21 -0
- package/src/lib/components/sidebar/sidebar-menu-skeleton.svelte +34 -0
- package/src/lib/components/sidebar/sidebar-menu-sub-button.svelte +40 -0
- package/src/lib/components/sidebar/sidebar-menu-sub-item.svelte +16 -0
- package/src/lib/components/sidebar/sidebar-menu-sub.svelte +19 -0
- package/src/lib/components/sidebar/sidebar-menu.svelte +21 -0
- package/src/lib/components/sidebar/sidebar-mobile-surface.svelte +48 -0
- package/src/lib/components/sidebar/sidebar-provider.svelte +60 -0
- package/src/lib/components/sidebar/sidebar-rail.svelte +36 -0
- package/src/lib/components/sidebar/sidebar-separator.svelte +9 -0
- package/src/lib/components/sidebar/sidebar-trigger.svelte +35 -0
- package/src/lib/components/sidebar/sidebar.svelte +30 -0
- package/src/lib/components/sidebar/types.ts +14 -0
- package/src/lib/components/skeleton/index.ts +7 -0
- package/src/lib/components/skeleton/skeleton.svelte +9 -0
- package/src/lib/components/slider/components/slider-range.svelte +37 -0
- package/src/lib/components/slider/components/slider-thumb-label.svelte +42 -0
- package/src/lib/components/slider/components/slider-thumb.svelte +46 -0
- package/src/lib/components/slider/components/slider-tick-label.svelte +42 -0
- package/src/lib/components/slider/components/slider-tick.svelte +28 -0
- package/src/lib/components/slider/components/slider.svelte +136 -0
- package/src/lib/components/slider/helpers.ts +249 -0
- package/src/lib/components/slider/index.ts +15 -0
- package/src/lib/components/slider/slider.svelte.ts +979 -0
- package/src/lib/components/slider/types.ts +294 -0
- package/src/lib/components/sonner/Toast.svelte +558 -0
- package/src/lib/components/sonner/Toaster.svelte +603 -0
- package/src/lib/components/sonner/index.ts +15 -0
- package/src/lib/components/sonner/toast-state.svelte.ts +217 -0
- package/src/lib/components/sonner/types.ts +384 -0
- package/src/lib/components/switch/index.ts +8 -0
- package/src/lib/components/switch/switch.svelte +78 -0
- package/src/lib/components/table/index.ts +28 -0
- package/src/lib/components/table/table-body.svelte +15 -0
- package/src/lib/components/table/table-caption.svelte +10 -0
- package/src/lib/components/table/table-cell.svelte +14 -0
- package/src/lib/components/table/table-footer.svelte +19 -0
- package/src/lib/components/table/table-head.svelte +17 -0
- package/src/lib/components/table/table-header.svelte +15 -0
- package/src/lib/components/table/table-row.svelte +14 -0
- package/src/lib/components/table/table.svelte +12 -0
- package/src/lib/components/tabs/components/tabs-content.svelte +30 -0
- package/src/lib/components/tabs/components/tabs-list.svelte +38 -0
- package/src/lib/components/tabs/components/tabs-trigger.svelte +51 -0
- package/src/lib/components/tabs/components/tabs.svelte +52 -0
- package/src/lib/components/tabs/index.ts +11 -0
- package/src/lib/components/tabs/tabs.svelte.ts +253 -0
- package/src/lib/components/tabs/types.ts +86 -0
- package/src/lib/components/textarea/index.ts +7 -0
- package/src/lib/components/textarea/textarea.svelte +23 -0
- package/src/lib/components/theme-toggle/index.ts +5 -0
- package/src/lib/components/theme-toggle/theme-toggle.svelte +49 -0
- package/src/lib/components/theme-toggle/theme.svelte.ts +81 -0
- package/src/lib/components/time-field/components/time-field-hidden-input.svelte +9 -0
- package/src/lib/components/time-field/components/time-field-input.svelte +33 -0
- package/src/lib/components/time-field/components/time-field-label.svelte +29 -0
- package/src/lib/components/time-field/components/time-field-segment.svelte +29 -0
- package/src/lib/components/time-field/components/time-field.svelte +99 -0
- package/src/lib/components/time-field/index.ts +11 -0
- package/src/lib/components/time-field/time-field.svelte.ts +1198 -0
- package/src/lib/components/time-field/types.ts +174 -0
- package/src/lib/components/time-range-field/components/time-range-field-input.svelte +36 -0
- package/src/lib/components/time-range-field/components/time-range-field-label.svelte +29 -0
- package/src/lib/components/time-range-field/components/time-range-field.svelte +104 -0
- package/src/lib/components/time-range-field/index.ts +11 -0
- package/src/lib/components/time-range-field/time-range-field.svelte.ts +235 -0
- package/src/lib/components/time-range-field/types.ts +181 -0
- package/src/lib/components/toggle/components/toggle.svelte +55 -0
- package/src/lib/components/toggle/index.ts +3 -0
- package/src/lib/components/toggle/toggle.svelte.ts +57 -0
- package/src/lib/components/toggle/types.ts +33 -0
- package/src/lib/components/toggle/variants.ts +25 -0
- package/src/lib/components/toggle-group/components/toggle-group-item.svelte +67 -0
- package/src/lib/components/toggle-group/components/toggle-group.svelte +109 -0
- package/src/lib/components/toggle-group/index.ts +7 -0
- package/src/lib/components/toggle-group/toggle-group.svelte.ts +235 -0
- package/src/lib/components/toggle-group/types.ts +89 -0
- package/src/lib/components/toolbar/components/toolbar-button.svelte +46 -0
- package/src/lib/components/toolbar/components/toolbar-group-item.svelte +56 -0
- package/src/lib/components/toolbar/components/toolbar-group.svelte +69 -0
- package/src/lib/components/toolbar/components/toolbar-link.svelte +39 -0
- package/src/lib/components/toolbar/components/toolbar.svelte +39 -0
- package/src/lib/components/toolbar/index.ts +13 -0
- package/src/lib/components/toolbar/toolbar.svelte.ts +373 -0
- package/src/lib/components/toolbar/types.ts +60 -0
- package/src/lib/components/tooltip/components/tooltip-content.svelte +81 -0
- package/src/lib/components/tooltip/components/tooltip-provider.svelte +26 -0
- package/src/lib/components/tooltip/components/tooltip-trigger.svelte +48 -0
- package/src/lib/components/tooltip/components/tooltip.svelte +55 -0
- package/src/lib/components/tooltip/index.ts +17 -0
- package/src/lib/components/tooltip/tooltip.svelte.ts +796 -0
- package/src/lib/components/tooltip/types.ts +175 -0
- package/src/lib/components/tooltip/utils.ts +1 -0
- package/src/lib/icons/google-icon.svelte +10 -0
- package/src/lib/icons/google-workspace.svelte +18 -0
- package/src/lib/icons/icon.d.ts +19 -0
- package/src/lib/icons/instagram.svelte +9 -0
- package/src/lib/icons/instantly.svelte +10 -0
- package/src/lib/icons/linkedin.svelte +9 -0
- package/src/lib/icons/mailshake.svelte +10 -0
- package/src/lib/icons/microsoft-365.svelte +7 -0
- package/src/lib/icons/passcode.svelte +12 -0
- package/src/lib/icons/replyio.svelte +10 -0
- package/src/lib/icons/smartlead.svelte +19 -0
- package/src/lib/icons/smtp.svelte +10 -0
- package/src/lib/icons/x-logo.svelte +9 -0
- package/src/lib/internal/animations-settled.svelte.ts +119 -0
- package/src/lib/internal/arrays.ts +253 -0
- package/src/lib/internal/attribute-types.ts +53 -0
- package/src/lib/internal/attrs.ts +64 -0
- package/src/lib/internal/body-scroll-lock.svelte.ts +330 -0
- package/src/lib/internal/create-id.ts +12 -0
- package/src/lib/internal/date-time/announcer.ts +74 -0
- package/src/lib/internal/date-time/calendar-helpers.svelte.ts +399 -0
- package/src/lib/internal/date-time/field/helpers.ts +431 -0
- package/src/lib/internal/date-time/field/parts.ts +15 -0
- package/src/lib/internal/date-time/field/range-field.svelte.ts +45 -0
- package/src/lib/internal/date-time/field/segments.ts +89 -0
- package/src/lib/internal/date-time/field/time-helpers.ts +311 -0
- package/src/lib/internal/date-time/formatter.ts +86 -0
- package/src/lib/internal/date-time/placeholders.ts +131 -0
- package/src/lib/internal/date-time/types.ts +121 -0
- package/src/lib/internal/date-time/utils.ts +243 -0
- package/src/lib/internal/dismissible-layer/index.ts +3 -0
- package/src/lib/internal/dismissible-layer/scheduler.ts +48 -0
- package/src/lib/internal/dismissible-layer/types.ts +57 -0
- package/src/lib/internal/dismissible-layer/use-dismissable-layer.svelte.ts +291 -0
- package/src/lib/internal/escape-layer/index.ts +3 -0
- package/src/lib/internal/escape-layer/types.ts +34 -0
- package/src/lib/internal/escape-layer/use-escape-layer.svelte.ts +91 -0
- package/src/lib/internal/floating-layer/components/floating-layer-arrow.svelte +35 -0
- package/src/lib/internal/floating-layer/components/floating-layer-content-static.svelte +19 -0
- package/src/lib/internal/floating-layer/components/floating-layer-content.svelte +52 -0
- package/src/lib/internal/floating-layer/components/floating-layer.svelte +12 -0
- package/src/lib/internal/floating-layer/components/index.ts +6 -0
- package/src/lib/internal/floating-layer/index.ts +2 -0
- package/src/lib/internal/floating-layer/types.ts +126 -0
- package/src/lib/internal/floating-layer/use-floating-layer.svelte.ts +471 -0
- package/src/lib/internal/focus-scope/focus-scope.svelte.ts +310 -0
- package/src/lib/internal/focus-scope/index.ts +4 -0
- package/src/lib/internal/focus-scope/types.ts +45 -0
- package/src/lib/internal/focus.ts +14 -0
- package/src/lib/internal/global-singleton.ts +10 -0
- package/src/lib/internal/group-value.svelte.ts +36 -0
- package/src/lib/internal/hover-intent-geometry.ts +96 -0
- package/src/lib/internal/index.ts +44 -0
- package/src/lib/internal/kbd.ts +60 -0
- package/src/lib/internal/layer-stack.ts +37 -0
- package/src/lib/internal/math.ts +11 -0
- package/src/lib/internal/merge-props.ts +7 -0
- package/src/lib/internal/native-dialog-controller.svelte.ts +102 -0
- package/src/lib/internal/native-popover.svelte.ts +173 -0
- package/src/lib/internal/open-cell.svelte.ts +41 -0
- package/src/lib/internal/paneforge/index.ts +5 -0
- package/src/lib/internal/paneforge/internal/constants.ts +2 -0
- package/src/lib/internal/paneforge/internal/helpers.ts +354 -0
- package/src/lib/internal/paneforge/internal/types.ts +33 -0
- package/src/lib/internal/paneforge/internal/utils/adjust-layout.ts +122 -0
- package/src/lib/internal/paneforge/internal/utils/aria.ts +36 -0
- package/src/lib/internal/paneforge/internal/utils/compare.ts +15 -0
- package/src/lib/internal/paneforge/internal/utils/is.ts +13 -0
- package/src/lib/internal/paneforge/internal/utils/resize.ts +31 -0
- package/src/lib/internal/paneforge/internal/utils/storage.ts +116 -0
- package/src/lib/internal/paneforge/internal/utils/style.ts +104 -0
- package/src/lib/internal/paneforge/pane-group.svelte +48 -0
- package/src/lib/internal/paneforge/pane-resizer.svelte +40 -0
- package/src/lib/internal/paneforge/pane.svelte +60 -0
- package/src/lib/internal/paneforge/paneforge.svelte.ts +821 -0
- package/src/lib/internal/paneforge/types.ts +179 -0
- package/src/lib/internal/popper-layer/index.ts +2 -0
- package/src/lib/internal/popper-layer/popper-content.svelte +19 -0
- package/src/lib/internal/popper-layer/popper-layer-inner.svelte +122 -0
- package/src/lib/internal/popper-layer/popper-layer.svelte +71 -0
- package/src/lib/internal/popper-layer/types.ts +65 -0
- package/src/lib/internal/portal/index.ts +2 -0
- package/src/lib/internal/portal/portal.svelte +61 -0
- package/src/lib/internal/prop-resolvers.ts +14 -0
- package/src/lib/internal/roving-focus-group.ts +147 -0
- package/src/lib/internal/roving-focus-item.svelte.ts +51 -0
- package/src/lib/internal/safe-polygon.svelte.ts +239 -0
- package/src/lib/internal/shared-state.svelte.ts +39 -0
- package/src/lib/internal/state-machine.ts +32 -0
- package/src/lib/internal/svelte-resize-observer.svelte.ts +37 -0
- package/src/lib/internal/tabbable.ts +398 -0
- package/src/lib/internal/table/core.ts +53 -0
- package/src/lib/internal/table/filter-fns.ts +10 -0
- package/src/lib/internal/table/index.ts +2 -0
- package/src/lib/internal/table/sorting-fns.ts +84 -0
- package/src/lib/internal/table/table.svelte.ts +330 -0
- package/src/lib/internal/table/types.ts +33 -0
- package/src/lib/internal/timeout-fn.svelte.ts +36 -0
- package/src/lib/internal/tools/box.svelte.ts +153 -0
- package/src/lib/internal/tools/index.ts +22 -0
- package/src/lib/internal/tools/types.ts +44 -0
- package/src/lib/internal/tools/utils/attach-ref.ts +74 -0
- package/src/lib/internal/tools/utils/compose-handlers.ts +36 -0
- package/src/lib/internal/tools/utils/dom-context.svelte.ts +63 -0
- package/src/lib/internal/tools/utils/dom.ts +62 -0
- package/src/lib/internal/tools/utils/events.ts +1 -0
- package/src/lib/internal/tools/utils/index.ts +5 -0
- package/src/lib/internal/tools/utils/strings.ts +28 -0
- package/src/lib/internal/typeahead.svelte.ts +48 -0
- package/src/lib/internal/types.ts +119 -0
- package/src/lib/internal/utils.ts +10 -0
- package/src/lib/internal/vaul/browser.ts +8 -0
- package/src/lib/internal/vaul/constants.ts +19 -0
- package/src/lib/internal/vaul/helpers.ts +39 -0
- package/src/lib/internal/vaul/types.ts +142 -0
- package/src/lib/internal/vaul/use-drawer-content.svelte.ts +219 -0
- package/src/lib/internal/vaul/use-drawer-root.svelte.ts +682 -0
- package/src/lib/internal/vaul/use-position-fixed.svelte.ts +142 -0
- package/src/lib/internal/vaul/use-prevent-scroll.svelte.ts +267 -0
- package/src/lib/internal/vaul/use-snap-points.svelte.ts +246 -0
- package/src/lib/time.ts +73 -0
|
@@ -0,0 +1,1506 @@
|
|
|
1
|
+
import { srOnlyStyles, attachRef, type WritableBoxedValues, type ReadableBoxedValues } from '../../internal/tools/index.js';
|
|
2
|
+
import { createContext, tick, untrack } from 'svelte';
|
|
3
|
+
import { findNextSibling, findPreviousSibling } from './utils.js';
|
|
4
|
+
import type { CommandState } from './types.js';
|
|
5
|
+
import type { BitsKeyboardEvent, BitsMouseEvent, BitsPointerEvent, RefAttachment, WithRefOpts } from '../../internal/types.js';
|
|
6
|
+
import { kbd } from '../../internal/kbd.js';
|
|
7
|
+
import { createBitsAttrs, boolToStr, boolToEmptyStrOrUndef } from '../../internal/attrs.js';
|
|
8
|
+
import { computeCommandScore } from './index.js';
|
|
9
|
+
|
|
10
|
+
const COMMAND_VALUE_ATTR = 'data-value';
|
|
11
|
+
|
|
12
|
+
const commandAttrs = createBitsAttrs({
|
|
13
|
+
component: 'command',
|
|
14
|
+
parts: [
|
|
15
|
+
'root',
|
|
16
|
+
'list',
|
|
17
|
+
'input',
|
|
18
|
+
'separator',
|
|
19
|
+
'loading',
|
|
20
|
+
'empty',
|
|
21
|
+
'group',
|
|
22
|
+
'group-items',
|
|
23
|
+
'group-heading',
|
|
24
|
+
'item',
|
|
25
|
+
'viewport',
|
|
26
|
+
'input-label',
|
|
27
|
+
],
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// selectors
|
|
31
|
+
const COMMAND_GROUP_SELECTOR = commandAttrs.selector('group');
|
|
32
|
+
const COMMAND_GROUP_ITEMS_SELECTOR = commandAttrs.selector('group-items');
|
|
33
|
+
const COMMAND_GROUP_HEADING_SELECTOR = commandAttrs.selector('group-heading');
|
|
34
|
+
const COMMAND_ITEM_SELECTOR = commandAttrs.selector('item');
|
|
35
|
+
const COMMAND_VALID_ITEM_SELECTOR = `${commandAttrs.selector('item')}:not([aria-disabled="true"])`;
|
|
36
|
+
|
|
37
|
+
const [getCommandRoot, setCommandRoot] = createContext<CommandRootState>();
|
|
38
|
+
const [getCommandList, setCommandList] = createContext<CommandListState>();
|
|
39
|
+
const [getCommandGroupContainer, setCommandGroupContainer, hasCommandGroupContainer] =
|
|
40
|
+
createContext<CommandGroupContainerState>();
|
|
41
|
+
|
|
42
|
+
interface GridItem {
|
|
43
|
+
index: number;
|
|
44
|
+
firstRowOfGroup: boolean;
|
|
45
|
+
ref: HTMLElement;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type ItemsGrid = GridItem[][];
|
|
49
|
+
|
|
50
|
+
interface CommandRootStateOpts
|
|
51
|
+
extends
|
|
52
|
+
WithRefOpts,
|
|
53
|
+
ReadableBoxedValues<{
|
|
54
|
+
filter: (value: string, search: string, keywords?: string[]) => number;
|
|
55
|
+
shouldFilter: boolean;
|
|
56
|
+
loop: boolean;
|
|
57
|
+
vimBindings: boolean;
|
|
58
|
+
columns: number | null;
|
|
59
|
+
disablePointerSelection: boolean;
|
|
60
|
+
disableInitialScroll: boolean;
|
|
61
|
+
onStateChange?: (state: Readonly<CommandState>) => void;
|
|
62
|
+
}>,
|
|
63
|
+
WritableBoxedValues<{
|
|
64
|
+
value: string;
|
|
65
|
+
}> {}
|
|
66
|
+
|
|
67
|
+
const defaultState = {
|
|
68
|
+
/** Value of the search query */
|
|
69
|
+
search: '',
|
|
70
|
+
/** Currently selected item value */
|
|
71
|
+
value: '',
|
|
72
|
+
filtered: {
|
|
73
|
+
/** The count of all visible items. */
|
|
74
|
+
count: 0,
|
|
75
|
+
/** Map from visible item id to its search store. */
|
|
76
|
+
items: new Map<string, number>(),
|
|
77
|
+
/** Set of groups with at least one visible item. */
|
|
78
|
+
groups: new Set<string>(),
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export class CommandRootState {
|
|
83
|
+
static create(opts: CommandRootStateOpts) {
|
|
84
|
+
return setCommandRoot(new CommandRootState(opts));
|
|
85
|
+
}
|
|
86
|
+
readonly opts: CommandRootStateOpts;
|
|
87
|
+
readonly attachment: RefAttachment;
|
|
88
|
+
#updateScheduled = false;
|
|
89
|
+
#isInitialMount = true;
|
|
90
|
+
sortAfterTick = false;
|
|
91
|
+
sortAndFilterAfterTick = false;
|
|
92
|
+
allItems = new Set<string>();
|
|
93
|
+
allGroups = new Map<string, Set<string>>();
|
|
94
|
+
allIds = new Map<string, { value: string; keywords?: string[] }>();
|
|
95
|
+
// attempt to prevent the harsh delay when user is typing fast
|
|
96
|
+
key = $state(0);
|
|
97
|
+
viewportNode = $state<HTMLElement | null>(null);
|
|
98
|
+
inputNode = $state<HTMLElement | null>(null);
|
|
99
|
+
labelNode = $state<HTMLElement | null>(null);
|
|
100
|
+
// published state that the components and other things can react to
|
|
101
|
+
commandState = $state.raw<CommandState>(defaultState);
|
|
102
|
+
// internal state that we mutate in batches and publish to the `state` at once
|
|
103
|
+
_commandState = $state<CommandState>(defaultState);
|
|
104
|
+
|
|
105
|
+
#scheduleUpdate() {
|
|
106
|
+
if (this.#updateScheduled) return;
|
|
107
|
+
this.#updateScheduled = true;
|
|
108
|
+
|
|
109
|
+
tick().then(() => {
|
|
110
|
+
this.#updateScheduled = false;
|
|
111
|
+
|
|
112
|
+
const currentState = $state.snapshot(this._commandState);
|
|
113
|
+
const hasStateChanged = !Object.is(this.commandState, currentState);
|
|
114
|
+
|
|
115
|
+
if (hasStateChanged) {
|
|
116
|
+
this.commandState = currentState;
|
|
117
|
+
this.opts.onStateChange?.current?.(currentState);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
setState<K extends keyof CommandState>(key: K, value: CommandState[K], preventScroll?: boolean) {
|
|
123
|
+
if (Object.is(this._commandState[key], value)) return;
|
|
124
|
+
|
|
125
|
+
this._commandState[key] = value;
|
|
126
|
+
|
|
127
|
+
if (key === 'search') {
|
|
128
|
+
// Filter synchronously before emitting back to children
|
|
129
|
+
this.#filterItems();
|
|
130
|
+
this.#sort();
|
|
131
|
+
} else if (key === 'value') {
|
|
132
|
+
if (!preventScroll) this.#scrollSelectedIntoView();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.#scheduleUpdate();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
constructor(opts: CommandRootStateOpts) {
|
|
139
|
+
this.opts = opts;
|
|
140
|
+
this.attachment = attachRef(this.opts.ref);
|
|
141
|
+
|
|
142
|
+
const defaults = { ...this._commandState, value: this.opts.value.current ?? '' };
|
|
143
|
+
|
|
144
|
+
this._commandState = defaults;
|
|
145
|
+
this.commandState = defaults;
|
|
146
|
+
|
|
147
|
+
this.onkeydown = this.onkeydown.bind(this);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Calculates score for an item based on search text and keywords.
|
|
152
|
+
* Higher score = better match.
|
|
153
|
+
*
|
|
154
|
+
* @param value - Item's display text
|
|
155
|
+
* @param keywords - Optional keywords to boost scoring
|
|
156
|
+
* @returns Score from 0-1, where 0 = no match
|
|
157
|
+
*/
|
|
158
|
+
#score(value: string, keywords?: string[]) {
|
|
159
|
+
const filter = this.opts.filter.current ?? computeCommandScore;
|
|
160
|
+
return value ? filter(value, this._commandState.search, keywords) : 0;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Sorts items and groups based on search scores.
|
|
165
|
+
* Groups are sorted by their highest scoring item.
|
|
166
|
+
* When no search active, selects first item.
|
|
167
|
+
*/
|
|
168
|
+
#sort(): void {
|
|
169
|
+
if (!this._commandState.search || this.opts.shouldFilter.current === false) {
|
|
170
|
+
// if no search and no initial value set or when clearing search,
|
|
171
|
+
// we select the first item.
|
|
172
|
+
if (!this._commandState.value || !this.#isInitialMount) {
|
|
173
|
+
this.#selectFirstItem();
|
|
174
|
+
} else if (this.#isInitialMount && this._commandState.value) {
|
|
175
|
+
// scroll the initial value into view if it exists
|
|
176
|
+
this.#scrollInitialValue();
|
|
177
|
+
}
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const scores = this._commandState.filtered.items;
|
|
182
|
+
|
|
183
|
+
// sort the groups
|
|
184
|
+
const groups: [string, number][] = [];
|
|
185
|
+
for (const value of this._commandState.filtered.groups) {
|
|
186
|
+
const items = this.allGroups.get(value);
|
|
187
|
+
let max = 0;
|
|
188
|
+
if (!items) {
|
|
189
|
+
groups.push([value, max]);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// get the max score of the group's items
|
|
194
|
+
for (const item of items!) {
|
|
195
|
+
const score = scores.get(item);
|
|
196
|
+
max = Math.max(score ?? 0, max);
|
|
197
|
+
}
|
|
198
|
+
groups.push([value, max]);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Sort items within groups to bottom
|
|
202
|
+
// Sort items outside of groups
|
|
203
|
+
// Sort groups to bottom (pushes all non-grouped items to the top)
|
|
204
|
+
const listInsertionElement = this.viewportNode;
|
|
205
|
+
|
|
206
|
+
const sorted = this.getValidItems().sort((a, b) => {
|
|
207
|
+
const valueA = a.getAttribute('data-value');
|
|
208
|
+
const valueB = b.getAttribute('data-value');
|
|
209
|
+
const scoresA = scores.get(valueA!) ?? 0;
|
|
210
|
+
const scoresB = scores.get(valueB!) ?? 0;
|
|
211
|
+
return scoresB - scoresA;
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
for (const item of sorted) {
|
|
215
|
+
const group = item.closest(COMMAND_GROUP_ITEMS_SELECTOR);
|
|
216
|
+
|
|
217
|
+
if (group) {
|
|
218
|
+
const itemToAppend = item.parentElement === group ? item : item.closest(`${COMMAND_GROUP_ITEMS_SELECTOR} > *`);
|
|
219
|
+
|
|
220
|
+
if (itemToAppend) {
|
|
221
|
+
group.appendChild(itemToAppend);
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
const itemToAppend = item.parentElement === listInsertionElement ? item : item.closest(`${COMMAND_GROUP_ITEMS_SELECTOR} > *`);
|
|
225
|
+
|
|
226
|
+
if (itemToAppend) {
|
|
227
|
+
listInsertionElement?.appendChild(itemToAppend);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const sortedGroups = groups.sort((a, b) => b[1] - a[1]);
|
|
233
|
+
|
|
234
|
+
for (const group of sortedGroups) {
|
|
235
|
+
const element = listInsertionElement?.querySelector(`${COMMAND_GROUP_SELECTOR}[${COMMAND_VALUE_ATTR}="${CSS.escape(group[0])}"]`);
|
|
236
|
+
element?.parentElement?.appendChild(element);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
this.#selectFirstItem();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Sets current value and triggers re-render if cleared.
|
|
244
|
+
*
|
|
245
|
+
* @param value - New value to set
|
|
246
|
+
*/
|
|
247
|
+
setValue(value: string, opts?: boolean) {
|
|
248
|
+
if (value !== this.opts.value.current && value === '') {
|
|
249
|
+
tick().then(() => {
|
|
250
|
+
this.key++;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
this.setState('value', value, opts);
|
|
254
|
+
this.opts.value.current = value;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Selects first non-disabled item on next tick.
|
|
259
|
+
*/
|
|
260
|
+
#selectFirstItem(): void {
|
|
261
|
+
tick().then(() => {
|
|
262
|
+
const item = this.getValidItems().find((item) => item.getAttribute('aria-disabled') !== 'true');
|
|
263
|
+
const value = item?.getAttribute(COMMAND_VALUE_ATTR);
|
|
264
|
+
const shouldPreventScroll = this.#isInitialMount && this.opts.disableInitialScroll.current;
|
|
265
|
+
this.setValue(value ?? '', shouldPreventScroll);
|
|
266
|
+
this.#isInitialMount = false;
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Scrolls the initial value into view if it exists and is not the first item.
|
|
272
|
+
* Called during initial mount when a value is provided.
|
|
273
|
+
*/
|
|
274
|
+
#scrollInitialValue(): void {
|
|
275
|
+
tick().then(() => {
|
|
276
|
+
const shouldPreventScroll = this.opts.disableInitialScroll.current;
|
|
277
|
+
if (!shouldPreventScroll) {
|
|
278
|
+
this.#scrollSelectedIntoView();
|
|
279
|
+
}
|
|
280
|
+
this.#isInitialMount = false;
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Updates filtered items/groups based on search.
|
|
286
|
+
* Recalculates scores and filtered count.
|
|
287
|
+
*/
|
|
288
|
+
#filterItems(): void {
|
|
289
|
+
if (!this._commandState.search || this.opts.shouldFilter.current === false) {
|
|
290
|
+
this._commandState.filtered.count = this.allItems.size;
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// reset the groups
|
|
295
|
+
this._commandState.filtered.groups = new Set();
|
|
296
|
+
let itemCount = 0;
|
|
297
|
+
|
|
298
|
+
// Check which items should be included
|
|
299
|
+
for (const id of this.allItems) {
|
|
300
|
+
const value = this.allIds.get(id)?.value ?? '';
|
|
301
|
+
const keywords = this.allIds.get(id)?.keywords ?? [];
|
|
302
|
+
const rank = this.#score(value, keywords);
|
|
303
|
+
this._commandState.filtered.items.set(id, rank);
|
|
304
|
+
if (rank > 0) itemCount++;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Check which groups have at least 1 item shown
|
|
308
|
+
for (const [groupId, group] of this.allGroups) {
|
|
309
|
+
const hasVisibleItem = group.values().some((itemId) => (this._commandState.filtered.items.get(itemId) ?? 0) > 0);
|
|
310
|
+
if (hasVisibleItem) this._commandState.filtered.groups.add(groupId);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
this._commandState.filtered.count = itemCount;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Gets all non-disabled, visible command items.
|
|
318
|
+
*
|
|
319
|
+
* @returns Array of valid item elements
|
|
320
|
+
* @remarks Exposed for direct item access and bound checking
|
|
321
|
+
*/
|
|
322
|
+
getValidItems(): HTMLElement[] {
|
|
323
|
+
const node = this.opts.ref.current;
|
|
324
|
+
if (!node) return [];
|
|
325
|
+
return [...node.querySelectorAll<HTMLElement>(COMMAND_VALID_ITEM_SELECTOR)];
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Gets all visible command items.
|
|
330
|
+
*
|
|
331
|
+
* @returns Array of valid item elements
|
|
332
|
+
* @remarks Exposed for direct item access and bound checking
|
|
333
|
+
*/
|
|
334
|
+
getVisibleItems(): HTMLElement[] {
|
|
335
|
+
const node = this.opts.ref.current;
|
|
336
|
+
if (!node) return [];
|
|
337
|
+
return [...node.querySelectorAll<HTMLElement>(COMMAND_ITEM_SELECTOR)];
|
|
338
|
+
}
|
|
339
|
+
/** Returns all visible items in a matrix structure
|
|
340
|
+
*
|
|
341
|
+
* @remarks Returns empty if the command isn't configured as a grid
|
|
342
|
+
*
|
|
343
|
+
* @returns
|
|
344
|
+
*/
|
|
345
|
+
get itemsGrid(): ItemsGrid {
|
|
346
|
+
if (!this.isGrid) return [];
|
|
347
|
+
|
|
348
|
+
const columns = this.opts.columns.current ?? 1;
|
|
349
|
+
|
|
350
|
+
const items = this.getVisibleItems();
|
|
351
|
+
|
|
352
|
+
const grid: ItemsGrid = [[]];
|
|
353
|
+
|
|
354
|
+
let currentGroup = items[0]?.getAttribute('data-group');
|
|
355
|
+
let column = 0;
|
|
356
|
+
let row = 0;
|
|
357
|
+
|
|
358
|
+
for (let i = 0; i < items.length; i++) {
|
|
359
|
+
const item = items[i];
|
|
360
|
+
const itemGroup = item?.getAttribute('data-group');
|
|
361
|
+
|
|
362
|
+
if (currentGroup !== itemGroup) {
|
|
363
|
+
currentGroup = itemGroup;
|
|
364
|
+
column = 1;
|
|
365
|
+
row++;
|
|
366
|
+
grid.push([{ index: i, firstRowOfGroup: true, ref: item! }]);
|
|
367
|
+
} else {
|
|
368
|
+
column++;
|
|
369
|
+
|
|
370
|
+
if (column > columns) {
|
|
371
|
+
row++;
|
|
372
|
+
column = 1;
|
|
373
|
+
grid.push([]);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
grid[row]?.push({
|
|
377
|
+
index: i,
|
|
378
|
+
firstRowOfGroup: grid[row]?.[0]?.firstRowOfGroup ?? i === 0,
|
|
379
|
+
ref: item!,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return grid;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* @returns Selected element or null
|
|
389
|
+
*/
|
|
390
|
+
#getSelectedItem(): HTMLElement | null {
|
|
391
|
+
return this.opts.ref.current?.querySelector(`${COMMAND_VALID_ITEM_SELECTOR}[data-selected]`) ?? null;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Scrolls selected item into view.
|
|
396
|
+
* Special handling for first items in groups.
|
|
397
|
+
*/
|
|
398
|
+
#scrollSelectedIntoView(): void {
|
|
399
|
+
tick().then(() => {
|
|
400
|
+
const item = this.#getSelectedItem();
|
|
401
|
+
if (!item) return;
|
|
402
|
+
const grandparent = item.parentElement?.parentElement;
|
|
403
|
+
if (!grandparent) return;
|
|
404
|
+
|
|
405
|
+
if (this.isGrid) item.scrollIntoView({ block: 'nearest' });
|
|
406
|
+
|
|
407
|
+
const scrollHeader = this.isGrid
|
|
408
|
+
? !!this.itemsGrid.flat().find((cell) => cell?.ref === item)?.firstRowOfGroup
|
|
409
|
+
: (grandparent.firstElementChild?.getAttribute('data-value') ?? undefined) === item.dataset?.value;
|
|
410
|
+
|
|
411
|
+
if (scrollHeader) {
|
|
412
|
+
item.closest(COMMAND_GROUP_SELECTOR)?.querySelector(COMMAND_GROUP_HEADING_SELECTOR)?.scrollIntoView({ block: 'nearest' });
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
item.scrollIntoView({ block: 'nearest' });
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Sets selection to item at specified index in valid items array.
|
|
422
|
+
* If index is out of bounds, does nothing.
|
|
423
|
+
*
|
|
424
|
+
* @param index - Zero-based index of item to select
|
|
425
|
+
* @remarks
|
|
426
|
+
* Uses `getValidItems()` to get selectable items, filtering out disabled/hidden ones.
|
|
427
|
+
* Access valid items directly via `getValidItems()` to check bounds before calling.
|
|
428
|
+
*
|
|
429
|
+
* @example
|
|
430
|
+
* // get valid items length for bounds check
|
|
431
|
+
* const items = getValidItems()
|
|
432
|
+
* if (index < items.length) {
|
|
433
|
+
* updateSelectedToIndex(index)
|
|
434
|
+
* }
|
|
435
|
+
*/
|
|
436
|
+
updateSelectedToIndex(index: number) {
|
|
437
|
+
const item = this.getValidItems()[index];
|
|
438
|
+
if (!item) return;
|
|
439
|
+
this.setValue(item.getAttribute(COMMAND_VALUE_ATTR) ?? '');
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Updates selected item by moving up/down relative to current selection.
|
|
444
|
+
* Handles wrapping when loop option is enabled.
|
|
445
|
+
*
|
|
446
|
+
* @param change - Direction to move: 1 for next item, -1 for previous item
|
|
447
|
+
* @remarks
|
|
448
|
+
* The loop behavior wraps:
|
|
449
|
+
* - From last item to first when moving next
|
|
450
|
+
* - From first item to last when moving previous
|
|
451
|
+
*
|
|
452
|
+
* Uses `getValidItems()` to get all selectable items, which filters out disabled/hidden items.
|
|
453
|
+
* You can call `getValidItems()` directly to get the current valid items array.
|
|
454
|
+
*
|
|
455
|
+
* @example
|
|
456
|
+
* // select next item
|
|
457
|
+
* updateSelectedByItem(1)
|
|
458
|
+
*
|
|
459
|
+
* // get all valid items
|
|
460
|
+
* const items = getValidItems()
|
|
461
|
+
*/
|
|
462
|
+
updateSelectedByItem(change: number): void {
|
|
463
|
+
const selected = this.#getSelectedItem();
|
|
464
|
+
const items = this.getValidItems();
|
|
465
|
+
const index = items.findIndex((item) => item === selected);
|
|
466
|
+
|
|
467
|
+
// Get item at this index
|
|
468
|
+
let newSelected = items[index + change];
|
|
469
|
+
|
|
470
|
+
if (this.opts.loop.current) {
|
|
471
|
+
newSelected = index + change < 0 ? items[items.length - 1] : index + change === items.length ? items[0] : items[index + change];
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (newSelected) {
|
|
475
|
+
this.setValue(newSelected.getAttribute(COMMAND_VALUE_ATTR) ?? '');
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Moves selection to the first valid item in the next/previous group.
|
|
481
|
+
* If no group is found, falls back to selecting the next/previous item globally.
|
|
482
|
+
*
|
|
483
|
+
* @param change - Direction to move: 1 for next group, -1 for previous group
|
|
484
|
+
* @example
|
|
485
|
+
* // move to first item in next group
|
|
486
|
+
* updateSelectedByGroup(1)
|
|
487
|
+
*
|
|
488
|
+
* // move to first item in previous group
|
|
489
|
+
* updateSelectedByGroup(-1)
|
|
490
|
+
*/
|
|
491
|
+
updateSelectedByGroup(change: 1 | -1): void {
|
|
492
|
+
const selected = this.#getSelectedItem();
|
|
493
|
+
let group = selected?.closest(COMMAND_GROUP_SELECTOR);
|
|
494
|
+
let item: HTMLElement | null | undefined;
|
|
495
|
+
|
|
496
|
+
while (group && !item) {
|
|
497
|
+
group = change > 0 ? findNextSibling(group, COMMAND_GROUP_SELECTOR) : findPreviousSibling(group, COMMAND_GROUP_SELECTOR);
|
|
498
|
+
item = group?.querySelector(COMMAND_VALID_ITEM_SELECTOR);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (item) {
|
|
502
|
+
this.setValue(item.getAttribute(COMMAND_VALUE_ATTR) ?? '');
|
|
503
|
+
} else {
|
|
504
|
+
this.updateSelectedByItem(change);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Maps item id to display value and search keywords.
|
|
510
|
+
* Returns cleanup function to remove mapping.
|
|
511
|
+
*
|
|
512
|
+
* @param id - Unique item identifier
|
|
513
|
+
* @param value - Display text
|
|
514
|
+
* @param keywords - Optional search boost terms
|
|
515
|
+
* @returns Cleanup function
|
|
516
|
+
*/
|
|
517
|
+
registerValue(value: string, keywords?: string[]): () => void {
|
|
518
|
+
if (!(value && value === this.allIds.get(value)?.value)) {
|
|
519
|
+
this.allIds.set(value, { value, keywords });
|
|
520
|
+
}
|
|
521
|
+
this._commandState.filtered.items.set(value, this.#score(value, keywords));
|
|
522
|
+
|
|
523
|
+
// Schedule sorting to run after this tick when all items are added not each time an item is added
|
|
524
|
+
if (!this.sortAfterTick) {
|
|
525
|
+
this.sortAfterTick = true;
|
|
526
|
+
tick().then(() => {
|
|
527
|
+
this.#sort();
|
|
528
|
+
this.sortAfterTick = false;
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return () => {
|
|
533
|
+
this.allIds.delete(value);
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Registers item in command list and its group.
|
|
539
|
+
* Handles filtering, sorting and selection updates.
|
|
540
|
+
*
|
|
541
|
+
* @param id - Item identifier
|
|
542
|
+
* @param groupId - Optional group to add item to
|
|
543
|
+
* @returns Cleanup function that handles selection
|
|
544
|
+
*/
|
|
545
|
+
registerItem(id: string, groupId: string | undefined): () => void {
|
|
546
|
+
this.allItems.add(id);
|
|
547
|
+
|
|
548
|
+
// Track this item within the group
|
|
549
|
+
if (groupId) {
|
|
550
|
+
if (!this.allGroups.has(groupId)) {
|
|
551
|
+
this.allGroups.set(groupId, new Set([id]));
|
|
552
|
+
} else {
|
|
553
|
+
this.allGroups.get(groupId)!.add(id);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Schedule sorting and filtering to run after this tick when all items are added not each time an item is added
|
|
558
|
+
if (!this.sortAndFilterAfterTick) {
|
|
559
|
+
this.sortAndFilterAfterTick = true;
|
|
560
|
+
tick().then(() => {
|
|
561
|
+
this.#filterItems();
|
|
562
|
+
this.#sort();
|
|
563
|
+
this.sortAndFilterAfterTick = false;
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
this.#scheduleUpdate();
|
|
568
|
+
return () => {
|
|
569
|
+
const selectedItem = this.#getSelectedItem();
|
|
570
|
+
this.allItems.delete(id);
|
|
571
|
+
this.commandState.filtered.items.delete(id);
|
|
572
|
+
|
|
573
|
+
this.#filterItems();
|
|
574
|
+
|
|
575
|
+
// The item removed have been the selected one,
|
|
576
|
+
// so selection should be moved to the first
|
|
577
|
+
if (selectedItem?.getAttribute('id') === id) {
|
|
578
|
+
this.#selectFirstItem();
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
this.#scheduleUpdate();
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Creates empty group if not exists.
|
|
587
|
+
*
|
|
588
|
+
* @param id - Group identifier
|
|
589
|
+
* @returns Cleanup function
|
|
590
|
+
*/
|
|
591
|
+
registerGroup(id: string): () => void {
|
|
592
|
+
if (!this.allGroups.has(id)) {
|
|
593
|
+
this.allGroups.set(id, new Set());
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
return () => {
|
|
597
|
+
this.allIds.delete(id);
|
|
598
|
+
this.allGroups.delete(id);
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
get isGrid() {
|
|
603
|
+
return this.opts.columns.current !== null;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Selects last valid item.
|
|
608
|
+
*/
|
|
609
|
+
#last() {
|
|
610
|
+
return this.updateSelectedToIndex(this.getValidItems().length - 1);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Handles next item selection:
|
|
615
|
+
* - Meta: Jump to last
|
|
616
|
+
* - Alt: Next group
|
|
617
|
+
* - Default: Next item
|
|
618
|
+
*
|
|
619
|
+
* @param e - Keyboard event
|
|
620
|
+
*/
|
|
621
|
+
#next(e: BitsKeyboardEvent) {
|
|
622
|
+
e.preventDefault();
|
|
623
|
+
|
|
624
|
+
if (e.metaKey) {
|
|
625
|
+
this.#last();
|
|
626
|
+
} else if (e.altKey) {
|
|
627
|
+
this.updateSelectedByGroup(1);
|
|
628
|
+
} else {
|
|
629
|
+
this.updateSelectedByItem(1);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
#down(e: BitsKeyboardEvent) {
|
|
634
|
+
if (this.opts.columns.current === null) return;
|
|
635
|
+
|
|
636
|
+
e.preventDefault();
|
|
637
|
+
|
|
638
|
+
if (e.metaKey) {
|
|
639
|
+
this.updateSelectedByGroup(1);
|
|
640
|
+
} else {
|
|
641
|
+
this.updateSelectedByItem(this.#nextRowColumnOffset(e));
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
#getColumn(item: HTMLElement, grid: ItemsGrid): { columnIndex: number; rowIndex: number } | null {
|
|
646
|
+
if (grid.length === 0) return null;
|
|
647
|
+
|
|
648
|
+
for (let r = 0; r < grid.length; r++) {
|
|
649
|
+
const row = grid[r];
|
|
650
|
+
if (row === undefined) continue;
|
|
651
|
+
|
|
652
|
+
for (let c = 0; c < row.length; c++) {
|
|
653
|
+
const column = row[c];
|
|
654
|
+
if (column === undefined || column.ref !== item) continue;
|
|
655
|
+
|
|
656
|
+
return { columnIndex: c, rowIndex: r };
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
return null;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
#nextRowColumnOffset(e: BitsKeyboardEvent): number {
|
|
664
|
+
const grid = this.itemsGrid;
|
|
665
|
+
const selected = this.#getSelectedItem();
|
|
666
|
+
if (!selected) return 0;
|
|
667
|
+
const column = this.#getColumn(selected, grid);
|
|
668
|
+
if (!column) return 0;
|
|
669
|
+
|
|
670
|
+
let newItem: HTMLElement | null = null;
|
|
671
|
+
|
|
672
|
+
const skipRows = e.altKey ? 1 : 0;
|
|
673
|
+
|
|
674
|
+
// if this is the second to last row then we need to go to the last row when skipping and not in loop mode
|
|
675
|
+
if (e.altKey && column.rowIndex === grid.length - 2 && !this.opts.loop.current) {
|
|
676
|
+
newItem = this.#findNextNonDisabledItem({
|
|
677
|
+
start: grid.length - 1,
|
|
678
|
+
end: grid.length,
|
|
679
|
+
expectedColumnIndex: column.columnIndex,
|
|
680
|
+
grid,
|
|
681
|
+
});
|
|
682
|
+
} else if (column.rowIndex === grid.length - 1) {
|
|
683
|
+
// if this is the last row we apply the loop logic
|
|
684
|
+
if (!this.opts.loop.current) return 0;
|
|
685
|
+
|
|
686
|
+
newItem = this.#findNextNonDisabledItem({
|
|
687
|
+
start: 0 + skipRows,
|
|
688
|
+
end: column.rowIndex,
|
|
689
|
+
expectedColumnIndex: column.columnIndex,
|
|
690
|
+
grid,
|
|
691
|
+
});
|
|
692
|
+
} else {
|
|
693
|
+
newItem = this.#findNextNonDisabledItem({
|
|
694
|
+
start: column.rowIndex + 1 + skipRows,
|
|
695
|
+
end: grid.length,
|
|
696
|
+
expectedColumnIndex: column.columnIndex,
|
|
697
|
+
grid,
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
// this happens if there were no non-disabled columns below the current column
|
|
701
|
+
// we can now try starting from the beginning to find the right column
|
|
702
|
+
if (newItem === null && this.opts.loop.current) {
|
|
703
|
+
newItem = this.#findNextNonDisabledItem({
|
|
704
|
+
start: 0,
|
|
705
|
+
end: column.rowIndex,
|
|
706
|
+
expectedColumnIndex: column.columnIndex,
|
|
707
|
+
grid,
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
return this.#calculateOffset(selected, newItem);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/** Attempts to find the next non-disabled column that matches the expected column.
|
|
716
|
+
*
|
|
717
|
+
* @remarks
|
|
718
|
+
* - Skips over disabled columns
|
|
719
|
+
* - When a row is shorter than the expected column it defaults to the last item in the row
|
|
720
|
+
*
|
|
721
|
+
* @param param0
|
|
722
|
+
* @returns
|
|
723
|
+
*/
|
|
724
|
+
#findNextNonDisabledItem({
|
|
725
|
+
start,
|
|
726
|
+
end,
|
|
727
|
+
grid,
|
|
728
|
+
expectedColumnIndex,
|
|
729
|
+
}: {
|
|
730
|
+
start: number;
|
|
731
|
+
end: number;
|
|
732
|
+
grid: ItemsGrid;
|
|
733
|
+
expectedColumnIndex: number;
|
|
734
|
+
}): HTMLElement | null {
|
|
735
|
+
for (let r = start; r < end; r++) {
|
|
736
|
+
const row = grid[r];
|
|
737
|
+
if (!row) continue;
|
|
738
|
+
|
|
739
|
+
const atColumn = row[expectedColumnIndex]?.ref ?? null;
|
|
740
|
+
|
|
741
|
+
// preferred: the cell at the expected column, if it exists and isn't disabled
|
|
742
|
+
if (atColumn) {
|
|
743
|
+
if (!itemIsDisabled(atColumn)) return atColumn;
|
|
744
|
+
continue; // expected column is disabled — try the next row
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// the expected column doesn't exist (short row) — fall back to the
|
|
748
|
+
// first non-disabled cell in the row, else keep scanning
|
|
749
|
+
const fallback = row.find((cell) => cell && !itemIsDisabled(cell.ref))?.ref ?? null;
|
|
750
|
+
if (fallback) return fallback;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
return null;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
#calculateOffset(selected: HTMLElement, newSelected: HTMLElement | null): number {
|
|
757
|
+
if (newSelected === null) return 0;
|
|
758
|
+
|
|
759
|
+
const items = this.getValidItems();
|
|
760
|
+
|
|
761
|
+
const ogIndex = items.findIndex((item) => item === selected);
|
|
762
|
+
const newIndex = items.findIndex((item) => item === newSelected);
|
|
763
|
+
|
|
764
|
+
return newIndex - ogIndex;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
#up(e: BitsKeyboardEvent) {
|
|
768
|
+
if (this.opts.columns.current === null) return;
|
|
769
|
+
|
|
770
|
+
e.preventDefault();
|
|
771
|
+
|
|
772
|
+
if (e.metaKey) {
|
|
773
|
+
this.updateSelectedByGroup(-1);
|
|
774
|
+
} else {
|
|
775
|
+
this.updateSelectedByItem(this.#previousRowColumnOffset(e));
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
#previousRowColumnOffset(e: BitsKeyboardEvent) {
|
|
780
|
+
const grid = this.itemsGrid;
|
|
781
|
+
const selected = this.#getSelectedItem();
|
|
782
|
+
if (!selected) return 0;
|
|
783
|
+
const column = this.#getColumn(selected, grid);
|
|
784
|
+
if (column === null) return 0;
|
|
785
|
+
|
|
786
|
+
let newItem: HTMLElement | null = null;
|
|
787
|
+
|
|
788
|
+
const skipRows = e.altKey ? 1 : 0;
|
|
789
|
+
|
|
790
|
+
// if this is the second row then we need to go to the top when skipping and not in loop mode
|
|
791
|
+
if (e.altKey && column.rowIndex === 1 && this.opts.loop.current === false) {
|
|
792
|
+
newItem = this.#findNextNonDisabledItemDesc({
|
|
793
|
+
start: 0,
|
|
794
|
+
end: 0,
|
|
795
|
+
expectedColumnIndex: column.columnIndex,
|
|
796
|
+
grid,
|
|
797
|
+
});
|
|
798
|
+
} else if (column.rowIndex === 0) {
|
|
799
|
+
// if this is the last row we apply the loop logic
|
|
800
|
+
if (this.opts.loop.current === false) return 0;
|
|
801
|
+
|
|
802
|
+
newItem = this.#findNextNonDisabledItemDesc({
|
|
803
|
+
start: grid.length - 1 - skipRows,
|
|
804
|
+
end: column.rowIndex + 1,
|
|
805
|
+
expectedColumnIndex: column.columnIndex,
|
|
806
|
+
grid,
|
|
807
|
+
});
|
|
808
|
+
} else {
|
|
809
|
+
newItem = this.#findNextNonDisabledItemDesc({
|
|
810
|
+
start: column.rowIndex - 1 - skipRows,
|
|
811
|
+
end: 0,
|
|
812
|
+
expectedColumnIndex: column.columnIndex,
|
|
813
|
+
grid,
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
// this happens if there were no non-disabled columns below the current column
|
|
817
|
+
// we can now try starting from the beginning to find the right column
|
|
818
|
+
if (newItem === null && this.opts.loop.current) {
|
|
819
|
+
newItem = this.#findNextNonDisabledItemDesc({
|
|
820
|
+
start: grid.length - 1,
|
|
821
|
+
end: column.rowIndex + 1,
|
|
822
|
+
expectedColumnIndex: column.columnIndex,
|
|
823
|
+
grid,
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
return this.#calculateOffset(selected, newItem);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* Attempts to find the next non-disabled column that matches the expected column.
|
|
833
|
+
*
|
|
834
|
+
* @remarks
|
|
835
|
+
* - Skips over disabled columns
|
|
836
|
+
* - When a row is shorter than the expected column it defaults to the last item in the row
|
|
837
|
+
*/
|
|
838
|
+
#findNextNonDisabledItemDesc({
|
|
839
|
+
start,
|
|
840
|
+
end,
|
|
841
|
+
grid,
|
|
842
|
+
expectedColumnIndex,
|
|
843
|
+
}: {
|
|
844
|
+
start: number;
|
|
845
|
+
end: number;
|
|
846
|
+
grid: ItemsGrid;
|
|
847
|
+
expectedColumnIndex: number;
|
|
848
|
+
}): HTMLElement | null {
|
|
849
|
+
for (let r = start; r >= end; r--) {
|
|
850
|
+
const row = grid[r];
|
|
851
|
+
if (!row) continue;
|
|
852
|
+
|
|
853
|
+
const atColumn = row[expectedColumnIndex]?.ref ?? null;
|
|
854
|
+
|
|
855
|
+
// preferred: the cell at the expected column, if it exists and isn't disabled
|
|
856
|
+
if (atColumn && !itemIsDisabled(atColumn)) return atColumn;
|
|
857
|
+
|
|
858
|
+
// the expected column was disabled — this row yields nothing
|
|
859
|
+
if (atColumn) return null;
|
|
860
|
+
|
|
861
|
+
// the expected column doesn't exist (short row) — fall back to the
|
|
862
|
+
// last non-disabled cell in the row
|
|
863
|
+
return row.findLast((cell) => cell && !itemIsDisabled(cell.ref))?.ref ?? null;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
return null;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* Handles previous item selection:
|
|
871
|
+
* - Meta: Jump to first
|
|
872
|
+
* - Alt: Previous group
|
|
873
|
+
* - Default: Previous item
|
|
874
|
+
*
|
|
875
|
+
* @param e - Keyboard event
|
|
876
|
+
*/
|
|
877
|
+
#prev(e: BitsKeyboardEvent) {
|
|
878
|
+
e.preventDefault();
|
|
879
|
+
if (e.metaKey) this.updateSelectedToIndex(0);
|
|
880
|
+
else if (e.altKey) this.updateSelectedByGroup(-1);
|
|
881
|
+
else this.updateSelectedByItem(-1);
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
onkeydown(e: BitsKeyboardEvent) {
|
|
885
|
+
const isVim = this.opts.vimBindings.current && e.ctrlKey;
|
|
886
|
+
switch (e.key) {
|
|
887
|
+
case kbd.n:
|
|
888
|
+
case kbd.j: {
|
|
889
|
+
// vim down
|
|
890
|
+
if (isVim) {
|
|
891
|
+
if (this.isGrid) {
|
|
892
|
+
this.#down(e);
|
|
893
|
+
} else {
|
|
894
|
+
this.#next(e);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
break;
|
|
898
|
+
}
|
|
899
|
+
case kbd.l: {
|
|
900
|
+
// vim right
|
|
901
|
+
if (isVim) {
|
|
902
|
+
if (this.isGrid) {
|
|
903
|
+
this.#next(e);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
break;
|
|
907
|
+
}
|
|
908
|
+
case kbd.ARROW_DOWN:
|
|
909
|
+
if (this.isGrid) {
|
|
910
|
+
this.#down(e);
|
|
911
|
+
} else {
|
|
912
|
+
this.#next(e);
|
|
913
|
+
}
|
|
914
|
+
break;
|
|
915
|
+
case kbd.ARROW_RIGHT:
|
|
916
|
+
if (!this.isGrid) break;
|
|
917
|
+
|
|
918
|
+
this.#next(e);
|
|
919
|
+
|
|
920
|
+
break;
|
|
921
|
+
case kbd.p:
|
|
922
|
+
case kbd.k: {
|
|
923
|
+
// vim up
|
|
924
|
+
if (isVim) {
|
|
925
|
+
if (this.isGrid) {
|
|
926
|
+
this.#up(e);
|
|
927
|
+
} else {
|
|
928
|
+
this.#prev(e);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
break;
|
|
932
|
+
}
|
|
933
|
+
case kbd.h: {
|
|
934
|
+
// vim left
|
|
935
|
+
if (isVim && this.isGrid) {
|
|
936
|
+
this.#prev(e);
|
|
937
|
+
}
|
|
938
|
+
break;
|
|
939
|
+
}
|
|
940
|
+
case kbd.ARROW_UP:
|
|
941
|
+
if (this.isGrid) {
|
|
942
|
+
this.#up(e);
|
|
943
|
+
} else {
|
|
944
|
+
this.#prev(e);
|
|
945
|
+
}
|
|
946
|
+
break;
|
|
947
|
+
case kbd.ARROW_LEFT:
|
|
948
|
+
if (!this.isGrid) break;
|
|
949
|
+
this.#prev(e);
|
|
950
|
+
break;
|
|
951
|
+
case kbd.HOME:
|
|
952
|
+
// first item
|
|
953
|
+
e.preventDefault();
|
|
954
|
+
this.updateSelectedToIndex(0);
|
|
955
|
+
break;
|
|
956
|
+
case kbd.END:
|
|
957
|
+
// last item
|
|
958
|
+
e.preventDefault();
|
|
959
|
+
this.#last();
|
|
960
|
+
break;
|
|
961
|
+
case kbd.ENTER: {
|
|
962
|
+
/**
|
|
963
|
+
* Check if IME composition is finished before triggering the select event.
|
|
964
|
+
* This prevents unwanted triggering while user is still inputting text with IME.
|
|
965
|
+
* e.keyCode === 229 is for the Japanese IME && Safari as `isComposing` does not
|
|
966
|
+
* work with Japanese IME and Safari in combination.
|
|
967
|
+
*/
|
|
968
|
+
if (!e.isComposing && e.keyCode !== 229) {
|
|
969
|
+
e.preventDefault();
|
|
970
|
+
const item = this.#getSelectedItem();
|
|
971
|
+
if (item) {
|
|
972
|
+
item?.click();
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
props = $derived.by(
|
|
980
|
+
() =>
|
|
981
|
+
({
|
|
982
|
+
id: this.opts.id.current,
|
|
983
|
+
role: 'application',
|
|
984
|
+
[commandAttrs.root]: '',
|
|
985
|
+
tabindex: -1,
|
|
986
|
+
onkeydown: this.onkeydown,
|
|
987
|
+
...this.attachment,
|
|
988
|
+
}) as const,
|
|
989
|
+
);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
function itemIsDisabled(item: HTMLElement) {
|
|
993
|
+
return item.getAttribute('aria-disabled') === 'true';
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
interface CommandEmptyStateOpts
|
|
997
|
+
extends
|
|
998
|
+
WithRefOpts,
|
|
999
|
+
ReadableBoxedValues<{
|
|
1000
|
+
forceMount: boolean;
|
|
1001
|
+
}> {}
|
|
1002
|
+
|
|
1003
|
+
export class CommandEmptyState {
|
|
1004
|
+
static create(opts: CommandEmptyStateOpts) {
|
|
1005
|
+
return new CommandEmptyState(opts, getCommandRoot());
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
readonly opts: CommandEmptyStateOpts;
|
|
1009
|
+
readonly root: CommandRootState;
|
|
1010
|
+
readonly attachment: RefAttachment;
|
|
1011
|
+
readonly shouldRender = $derived.by(() => {
|
|
1012
|
+
return (this.root._commandState.filtered.count === 0 && this.#isInitialRender === false) || this.opts.forceMount.current;
|
|
1013
|
+
});
|
|
1014
|
+
#isInitialRender = true;
|
|
1015
|
+
|
|
1016
|
+
constructor(opts: CommandEmptyStateOpts, root: CommandRootState) {
|
|
1017
|
+
this.opts = opts;
|
|
1018
|
+
this.root = root;
|
|
1019
|
+
this.attachment = attachRef(this.opts.ref);
|
|
1020
|
+
|
|
1021
|
+
$effect.pre(() => {
|
|
1022
|
+
this.#isInitialRender = false;
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
readonly props = $derived.by(
|
|
1027
|
+
() =>
|
|
1028
|
+
({
|
|
1029
|
+
id: this.opts.id.current,
|
|
1030
|
+
role: 'presentation',
|
|
1031
|
+
[commandAttrs.empty]: '',
|
|
1032
|
+
...this.attachment,
|
|
1033
|
+
}) as const,
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
interface CommandGroupContainerStateOpts
|
|
1038
|
+
extends
|
|
1039
|
+
WithRefOpts,
|
|
1040
|
+
ReadableBoxedValues<{
|
|
1041
|
+
value: string;
|
|
1042
|
+
forceMount: boolean;
|
|
1043
|
+
}> {}
|
|
1044
|
+
|
|
1045
|
+
export class CommandGroupContainerState {
|
|
1046
|
+
static create(opts: CommandGroupContainerStateOpts) {
|
|
1047
|
+
return setCommandGroupContainer(new CommandGroupContainerState(opts, getCommandRoot()));
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
readonly opts: CommandGroupContainerStateOpts;
|
|
1051
|
+
readonly root: CommandRootState;
|
|
1052
|
+
readonly attachment: RefAttachment;
|
|
1053
|
+
readonly shouldRender = $derived.by(() => {
|
|
1054
|
+
if (this.opts.forceMount.current) return true;
|
|
1055
|
+
if (this.root.opts.shouldFilter.current === false) return true;
|
|
1056
|
+
if (!this.root.commandState.search) return true;
|
|
1057
|
+
return this.root._commandState.filtered.groups.has(this.trueValue);
|
|
1058
|
+
});
|
|
1059
|
+
|
|
1060
|
+
headingNode = $state<HTMLElement | null>(null);
|
|
1061
|
+
trueValue = $state('');
|
|
1062
|
+
|
|
1063
|
+
constructor(opts: CommandGroupContainerStateOpts, root: CommandRootState) {
|
|
1064
|
+
this.opts = opts;
|
|
1065
|
+
this.root = root;
|
|
1066
|
+
this.attachment = attachRef(this.opts.ref);
|
|
1067
|
+
this.trueValue = opts.value.current ?? opts.id.current;
|
|
1068
|
+
|
|
1069
|
+
$effect(() => {
|
|
1070
|
+
this.trueValue;
|
|
1071
|
+
return untrack(() => {
|
|
1072
|
+
return this.root.registerGroup(this.trueValue);
|
|
1073
|
+
});
|
|
1074
|
+
});
|
|
1075
|
+
|
|
1076
|
+
$effect(() => {
|
|
1077
|
+
if (this.opts.value.current) {
|
|
1078
|
+
this.trueValue = this.opts.value.current;
|
|
1079
|
+
return this.root.registerValue(this.opts.value.current);
|
|
1080
|
+
} else if (this.headingNode && this.headingNode.textContent) {
|
|
1081
|
+
this.trueValue = this.headingNode.textContent.trim().toLowerCase();
|
|
1082
|
+
return this.root.registerValue(this.trueValue);
|
|
1083
|
+
} else {
|
|
1084
|
+
this.trueValue = `-----${this.opts.id.current}`;
|
|
1085
|
+
return this.root.registerValue(this.trueValue);
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
readonly props = $derived.by(
|
|
1091
|
+
() =>
|
|
1092
|
+
({
|
|
1093
|
+
id: this.opts.id.current,
|
|
1094
|
+
role: 'presentation',
|
|
1095
|
+
hidden: this.shouldRender ? undefined : true,
|
|
1096
|
+
'data-value': this.trueValue,
|
|
1097
|
+
[commandAttrs.group]: '',
|
|
1098
|
+
...this.attachment,
|
|
1099
|
+
}) as const,
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
interface CommandGroupHeadingStateOpts extends WithRefOpts {}
|
|
1104
|
+
|
|
1105
|
+
export class CommandGroupHeadingState {
|
|
1106
|
+
static create(opts: CommandGroupHeadingStateOpts) {
|
|
1107
|
+
return new CommandGroupHeadingState(opts, getCommandGroupContainer());
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
readonly opts: CommandGroupHeadingStateOpts;
|
|
1111
|
+
readonly group: CommandGroupContainerState;
|
|
1112
|
+
readonly attachment: RefAttachment;
|
|
1113
|
+
|
|
1114
|
+
constructor(opts: CommandGroupHeadingStateOpts, group: CommandGroupContainerState) {
|
|
1115
|
+
this.opts = opts;
|
|
1116
|
+
this.group = group;
|
|
1117
|
+
this.attachment = attachRef(this.opts.ref, (v) => (this.group.headingNode = v));
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
readonly props = $derived.by(
|
|
1121
|
+
() =>
|
|
1122
|
+
({
|
|
1123
|
+
id: this.opts.id.current,
|
|
1124
|
+
[commandAttrs['group-heading']]: '',
|
|
1125
|
+
...this.attachment,
|
|
1126
|
+
}) as const,
|
|
1127
|
+
);
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
interface CommandGroupItemsStateOpts extends WithRefOpts {}
|
|
1131
|
+
|
|
1132
|
+
export class CommandGroupItemsState {
|
|
1133
|
+
static create(opts: CommandGroupItemsStateOpts) {
|
|
1134
|
+
return new CommandGroupItemsState(opts, getCommandGroupContainer());
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
readonly opts: CommandGroupItemsStateOpts;
|
|
1138
|
+
readonly group: CommandGroupContainerState;
|
|
1139
|
+
readonly attachment: RefAttachment;
|
|
1140
|
+
|
|
1141
|
+
constructor(opts: CommandGroupItemsStateOpts, group: CommandGroupContainerState) {
|
|
1142
|
+
this.opts = opts;
|
|
1143
|
+
this.group = group;
|
|
1144
|
+
this.attachment = attachRef(this.opts.ref);
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
readonly props = $derived.by(
|
|
1148
|
+
() =>
|
|
1149
|
+
({
|
|
1150
|
+
id: this.opts.id.current,
|
|
1151
|
+
role: 'group',
|
|
1152
|
+
[commandAttrs['group-items']]: '',
|
|
1153
|
+
'aria-labelledby': this.group.headingNode?.id ?? undefined,
|
|
1154
|
+
...this.attachment,
|
|
1155
|
+
}) as const,
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
interface CommandInputStateOpts
|
|
1160
|
+
extends
|
|
1161
|
+
WithRefOpts,
|
|
1162
|
+
WritableBoxedValues<{
|
|
1163
|
+
value: string;
|
|
1164
|
+
}>,
|
|
1165
|
+
ReadableBoxedValues<{
|
|
1166
|
+
autofocus: boolean;
|
|
1167
|
+
}> {}
|
|
1168
|
+
|
|
1169
|
+
export class CommandInputState {
|
|
1170
|
+
static create(opts: CommandInputStateOpts) {
|
|
1171
|
+
return new CommandInputState(opts, getCommandRoot());
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
readonly opts: CommandInputStateOpts;
|
|
1175
|
+
readonly root: CommandRootState;
|
|
1176
|
+
readonly attachment: RefAttachment;
|
|
1177
|
+
readonly #selectedItemId = $derived.by(() => {
|
|
1178
|
+
const item = this.root.viewportNode?.querySelector<HTMLElement>(
|
|
1179
|
+
`${COMMAND_ITEM_SELECTOR}[${COMMAND_VALUE_ATTR}="${CSS.escape(this.root.opts.value.current)}"]`,
|
|
1180
|
+
);
|
|
1181
|
+
if (item === undefined || item === null) return;
|
|
1182
|
+
return item.getAttribute('id') ?? undefined;
|
|
1183
|
+
});
|
|
1184
|
+
|
|
1185
|
+
constructor(opts: CommandInputStateOpts, root: CommandRootState) {
|
|
1186
|
+
this.opts = opts;
|
|
1187
|
+
this.root = root;
|
|
1188
|
+
this.attachment = attachRef(this.opts.ref, (v) => (this.root.inputNode = v));
|
|
1189
|
+
$effect(() => {
|
|
1190
|
+
this.opts.ref.current;
|
|
1191
|
+
untrack(() => {
|
|
1192
|
+
const node = this.opts.ref.current;
|
|
1193
|
+
if (node && this.opts.autofocus.current) {
|
|
1194
|
+
setTimeout(() => node.focus(), 10);
|
|
1195
|
+
}
|
|
1196
|
+
});
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
$effect(() => {
|
|
1200
|
+
this.opts.value.current;
|
|
1201
|
+
untrack(() => {
|
|
1202
|
+
if (this.root.commandState.search !== this.opts.value.current) {
|
|
1203
|
+
this.root.setState('search', this.opts.value.current);
|
|
1204
|
+
}
|
|
1205
|
+
});
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
readonly props = $derived.by(
|
|
1210
|
+
() =>
|
|
1211
|
+
({
|
|
1212
|
+
id: this.opts.id.current,
|
|
1213
|
+
type: 'text',
|
|
1214
|
+
[commandAttrs.input]: '',
|
|
1215
|
+
autocomplete: 'off',
|
|
1216
|
+
autocorrect: 'off',
|
|
1217
|
+
spellcheck: false,
|
|
1218
|
+
'aria-autocomplete': 'list',
|
|
1219
|
+
role: 'combobox',
|
|
1220
|
+
'aria-expanded': boolToStr(true),
|
|
1221
|
+
'aria-controls': this.root.viewportNode?.id ?? undefined,
|
|
1222
|
+
'aria-labelledby': this.root.labelNode?.id ?? undefined,
|
|
1223
|
+
'aria-activedescendant': this.#selectedItemId,
|
|
1224
|
+
...this.attachment,
|
|
1225
|
+
}) as const,
|
|
1226
|
+
);
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
interface CommandItemStateOpts
|
|
1230
|
+
extends
|
|
1231
|
+
WithRefOpts,
|
|
1232
|
+
ReadableBoxedValues<{
|
|
1233
|
+
value: string;
|
|
1234
|
+
disabled: boolean;
|
|
1235
|
+
onSelect: () => void;
|
|
1236
|
+
forceMount: boolean;
|
|
1237
|
+
keywords: string[];
|
|
1238
|
+
}> {
|
|
1239
|
+
group: CommandGroupContainerState | null;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
export class CommandItemState {
|
|
1243
|
+
static create(opts: Omit<CommandItemStateOpts, 'group'>) {
|
|
1244
|
+
const group = hasCommandGroupContainer() ? getCommandGroupContainer() : null;
|
|
1245
|
+
return new CommandItemState({ ...opts, group }, getCommandRoot());
|
|
1246
|
+
}
|
|
1247
|
+
readonly opts: CommandItemStateOpts;
|
|
1248
|
+
readonly root: CommandRootState;
|
|
1249
|
+
readonly attachment: RefAttachment;
|
|
1250
|
+
readonly #trueForceMount = $derived.by(() => {
|
|
1251
|
+
return this.opts.forceMount.current || this.opts.group?.opts.forceMount.current === true;
|
|
1252
|
+
});
|
|
1253
|
+
readonly shouldRender = $derived.by(() => {
|
|
1254
|
+
this.opts.ref.current;
|
|
1255
|
+
if (this.#trueForceMount || this.root.opts.shouldFilter.current === false || !this.root.commandState.search) {
|
|
1256
|
+
return true;
|
|
1257
|
+
}
|
|
1258
|
+
const currentScore = this.root.commandState.filtered.items.get(this.trueValue);
|
|
1259
|
+
if (currentScore === undefined) return false;
|
|
1260
|
+
return currentScore > 0;
|
|
1261
|
+
});
|
|
1262
|
+
|
|
1263
|
+
readonly isSelected = $derived.by(() => this.root.opts.value.current === this.trueValue && this.trueValue !== '');
|
|
1264
|
+
trueValue = $state('');
|
|
1265
|
+
|
|
1266
|
+
constructor(opts: CommandItemStateOpts, root: CommandRootState) {
|
|
1267
|
+
this.opts = opts;
|
|
1268
|
+
this.root = root;
|
|
1269
|
+
this.trueValue = opts.value.current;
|
|
1270
|
+
this.attachment = attachRef(this.opts.ref);
|
|
1271
|
+
|
|
1272
|
+
$effect(() => {
|
|
1273
|
+
this.trueValue;
|
|
1274
|
+
this.opts.group?.trueValue;
|
|
1275
|
+
this.opts.forceMount.current;
|
|
1276
|
+
return untrack(() => {
|
|
1277
|
+
if (this.opts.forceMount.current || !this.trueValue) return;
|
|
1278
|
+
return this.root.registerItem(this.trueValue, this.opts.group?.trueValue);
|
|
1279
|
+
});
|
|
1280
|
+
});
|
|
1281
|
+
|
|
1282
|
+
$effect(() => {
|
|
1283
|
+
this.opts.value.current;
|
|
1284
|
+
this.opts.ref.current;
|
|
1285
|
+
untrack(() => {
|
|
1286
|
+
if (this.opts.value.current) {
|
|
1287
|
+
this.trueValue = this.opts.value.current;
|
|
1288
|
+
} else if (this.opts.ref.current?.textContent) {
|
|
1289
|
+
this.trueValue = this.opts.ref.current.textContent.trim();
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
if (this.trueValue) {
|
|
1293
|
+
this.root.registerValue(
|
|
1294
|
+
this.trueValue,
|
|
1295
|
+
opts.keywords.current.map((kw) => kw.trim()),
|
|
1296
|
+
);
|
|
1297
|
+
this.opts.ref.current?.setAttribute(COMMAND_VALUE_ATTR, this.trueValue);
|
|
1298
|
+
}
|
|
1299
|
+
});
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
// bindings
|
|
1303
|
+
this.onclick = this.onclick.bind(this);
|
|
1304
|
+
this.onpointermove = this.onpointermove.bind(this);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
#onSelect() {
|
|
1308
|
+
if (this.opts.disabled.current) return;
|
|
1309
|
+
this.#select();
|
|
1310
|
+
this.opts.onSelect?.current();
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
#select() {
|
|
1314
|
+
if (this.opts.disabled.current) return;
|
|
1315
|
+
this.root.setValue(this.trueValue, true);
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
onpointermove(_: BitsPointerEvent) {
|
|
1319
|
+
if (this.opts.disabled.current || this.root.opts.disablePointerSelection.current) return;
|
|
1320
|
+
this.#select();
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
onclick(_: BitsMouseEvent) {
|
|
1324
|
+
if (this.opts.disabled.current) return;
|
|
1325
|
+
this.#onSelect();
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
readonly props = $derived.by(
|
|
1329
|
+
() =>
|
|
1330
|
+
({
|
|
1331
|
+
id: this.opts.id.current,
|
|
1332
|
+
'aria-disabled': boolToStr(this.opts.disabled.current),
|
|
1333
|
+
'aria-selected': boolToStr(this.isSelected),
|
|
1334
|
+
'data-disabled': boolToEmptyStrOrUndef(this.opts.disabled.current),
|
|
1335
|
+
'data-selected': boolToEmptyStrOrUndef(this.isSelected),
|
|
1336
|
+
'data-value': this.trueValue,
|
|
1337
|
+
'data-group': this.opts.group?.trueValue,
|
|
1338
|
+
[commandAttrs.item]: '',
|
|
1339
|
+
role: 'option',
|
|
1340
|
+
onpointermove: this.onpointermove,
|
|
1341
|
+
onclick: this.onclick,
|
|
1342
|
+
...this.attachment,
|
|
1343
|
+
}) as const,
|
|
1344
|
+
);
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
interface CommandLoadingStateOpts
|
|
1348
|
+
extends
|
|
1349
|
+
WithRefOpts,
|
|
1350
|
+
ReadableBoxedValues<{
|
|
1351
|
+
progress: number;
|
|
1352
|
+
}> {}
|
|
1353
|
+
|
|
1354
|
+
export class CommandLoadingState {
|
|
1355
|
+
static create(opts: CommandLoadingStateOpts) {
|
|
1356
|
+
return new CommandLoadingState(opts);
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
readonly opts: CommandLoadingStateOpts;
|
|
1360
|
+
readonly attachment: RefAttachment;
|
|
1361
|
+
constructor(opts: CommandLoadingStateOpts) {
|
|
1362
|
+
this.opts = opts;
|
|
1363
|
+
this.attachment = attachRef(this.opts.ref);
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
readonly props = $derived.by(
|
|
1367
|
+
() =>
|
|
1368
|
+
({
|
|
1369
|
+
id: this.opts.id.current,
|
|
1370
|
+
role: 'progressbar',
|
|
1371
|
+
'aria-valuenow': this.opts.progress.current,
|
|
1372
|
+
'aria-valuemin': 0,
|
|
1373
|
+
'aria-valuemax': 100,
|
|
1374
|
+
'aria-label': 'Loading...',
|
|
1375
|
+
[commandAttrs.loading]: '',
|
|
1376
|
+
...this.attachment,
|
|
1377
|
+
}) as const,
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
interface CommandSeparatorStateOpts
|
|
1382
|
+
extends
|
|
1383
|
+
WithRefOpts,
|
|
1384
|
+
ReadableBoxedValues<{
|
|
1385
|
+
forceMount: boolean;
|
|
1386
|
+
}> {}
|
|
1387
|
+
|
|
1388
|
+
export class CommandSeparatorState {
|
|
1389
|
+
static create(opts: CommandSeparatorStateOpts) {
|
|
1390
|
+
return new CommandSeparatorState(opts, getCommandRoot());
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
readonly opts: CommandSeparatorStateOpts;
|
|
1394
|
+
readonly root: CommandRootState;
|
|
1395
|
+
readonly attachment: RefAttachment;
|
|
1396
|
+
readonly shouldRender = $derived.by(() => !this.root._commandState.search || this.opts.forceMount.current);
|
|
1397
|
+
|
|
1398
|
+
constructor(opts: CommandSeparatorStateOpts, root: CommandRootState) {
|
|
1399
|
+
this.opts = opts;
|
|
1400
|
+
this.root = root;
|
|
1401
|
+
this.attachment = attachRef(this.opts.ref);
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
readonly props = $derived.by(
|
|
1405
|
+
() =>
|
|
1406
|
+
({
|
|
1407
|
+
id: this.opts.id.current,
|
|
1408
|
+
// role="separator" cannot belong to a role="listbox"
|
|
1409
|
+
'aria-hidden': 'true',
|
|
1410
|
+
[commandAttrs.separator]: '',
|
|
1411
|
+
...this.attachment,
|
|
1412
|
+
}) as const,
|
|
1413
|
+
);
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
interface CommandListStateOpts
|
|
1417
|
+
extends
|
|
1418
|
+
WithRefOpts,
|
|
1419
|
+
ReadableBoxedValues<{
|
|
1420
|
+
ariaLabel: string;
|
|
1421
|
+
}> {}
|
|
1422
|
+
|
|
1423
|
+
export class CommandListState {
|
|
1424
|
+
static create(opts: CommandListStateOpts) {
|
|
1425
|
+
return setCommandList(new CommandListState(opts, getCommandRoot()));
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
readonly opts: CommandListStateOpts;
|
|
1429
|
+
readonly root: CommandRootState;
|
|
1430
|
+
readonly attachment: RefAttachment;
|
|
1431
|
+
|
|
1432
|
+
constructor(opts: CommandListStateOpts, root: CommandRootState) {
|
|
1433
|
+
this.opts = opts;
|
|
1434
|
+
this.root = root;
|
|
1435
|
+
this.attachment = attachRef(this.opts.ref);
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
readonly props = $derived.by(
|
|
1439
|
+
() =>
|
|
1440
|
+
({
|
|
1441
|
+
id: this.opts.id.current,
|
|
1442
|
+
role: 'listbox',
|
|
1443
|
+
'aria-label': this.opts.ariaLabel.current,
|
|
1444
|
+
[commandAttrs.list]: '',
|
|
1445
|
+
...this.attachment,
|
|
1446
|
+
}) as const,
|
|
1447
|
+
);
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
interface CommandLabelStateOpts
|
|
1451
|
+
extends
|
|
1452
|
+
WithRefOpts,
|
|
1453
|
+
ReadableBoxedValues<{
|
|
1454
|
+
for?: string;
|
|
1455
|
+
}> {}
|
|
1456
|
+
|
|
1457
|
+
export class CommandLabelState {
|
|
1458
|
+
static create(opts: CommandLabelStateOpts) {
|
|
1459
|
+
return new CommandLabelState(opts, getCommandRoot());
|
|
1460
|
+
}
|
|
1461
|
+
readonly opts: CommandLabelStateOpts;
|
|
1462
|
+
readonly root: CommandRootState;
|
|
1463
|
+
readonly attachment: RefAttachment;
|
|
1464
|
+
constructor(opts: CommandLabelStateOpts, root: CommandRootState) {
|
|
1465
|
+
this.opts = opts;
|
|
1466
|
+
this.root = root;
|
|
1467
|
+
this.attachment = attachRef(this.opts.ref, (v) => (this.root.labelNode = v));
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
readonly props = $derived.by(
|
|
1471
|
+
() =>
|
|
1472
|
+
({
|
|
1473
|
+
id: this.opts.id.current,
|
|
1474
|
+
[commandAttrs['input-label']]: '',
|
|
1475
|
+
for: this.opts.for?.current,
|
|
1476
|
+
style: srOnlyStyles,
|
|
1477
|
+
...this.attachment,
|
|
1478
|
+
}) as const,
|
|
1479
|
+
);
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
interface CommandViewportStateOpts extends WithRefOpts {}
|
|
1483
|
+
|
|
1484
|
+
export class CommandViewportState {
|
|
1485
|
+
static create(opts: CommandViewportStateOpts) {
|
|
1486
|
+
return new CommandViewportState(opts, getCommandList());
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
readonly opts: CommandViewportStateOpts;
|
|
1490
|
+
readonly list: CommandListState;
|
|
1491
|
+
readonly attachment: RefAttachment;
|
|
1492
|
+
constructor(opts: CommandViewportStateOpts, list: CommandListState) {
|
|
1493
|
+
this.opts = opts;
|
|
1494
|
+
this.list = list;
|
|
1495
|
+
this.attachment = attachRef(this.opts.ref, (v) => (this.list.root.viewportNode = v));
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
readonly props = $derived.by(
|
|
1499
|
+
() =>
|
|
1500
|
+
({
|
|
1501
|
+
id: this.opts.id.current,
|
|
1502
|
+
[commandAttrs.viewport]: '',
|
|
1503
|
+
...this.attachment,
|
|
1504
|
+
}) as const,
|
|
1505
|
+
);
|
|
1506
|
+
}
|