vectis-ui 0.1.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -6
- package/dist/components/VAccordion/VAccordion.css +1 -1
- package/dist/components/VAccordion/VAccordion.d.ts +9 -16
- package/dist/components/VAccordion/VAccordion.js.map +1 -1
- package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAccordion/VAccordionItem.css +1 -1
- package/dist/components/VAccordion/VAccordionItem.d.ts +33 -19
- package/dist/components/VAccordion/VAccordionItem.js.map +1 -1
- package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js +50 -38
- package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAccordion/context.d.ts +1 -8
- package/dist/components/VAccordion/context.js.map +1 -1
- package/dist/components/VAvatar/VAvatar.css +1 -1
- package/dist/components/VAvatar/VAvatar.d.ts +44 -29
- package/dist/components/VAvatar/VAvatar.js.map +1 -1
- package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js +38 -35
- package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAvatar/VAvatarGroup.d.ts +5 -16
- package/dist/components/VAvatar/VAvatarGroup.js.map +1 -1
- package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js +7 -7
- package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAvatar/context.d.ts +6 -10
- package/dist/components/VAvatar/context.js.map +1 -1
- package/dist/components/VBadge/VBadge.css +1 -1
- package/dist/components/VBadge/VBadge.d.ts +31 -10
- package/dist/components/VBadge/VBadge.js.map +1 -1
- package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js +39 -24
- package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VBreadcrumb/VBreadcrumb.css +1 -1
- package/dist/components/VBreadcrumb/VBreadcrumb.d.ts +4 -17
- package/dist/components/VBreadcrumb/VBreadcrumb.js.map +1 -1
- package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js +23 -25
- package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VButton/VButton.css +1 -1
- package/dist/components/VButton/VButton.d.ts +30 -10
- package/dist/components/VButton/VButton.js.map +1 -1
- package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js +39 -30
- package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VButton/VButtonGroup.css +1 -1
- package/dist/components/VButton/VButtonGroup.d.ts +76 -13
- package/dist/components/VButton/VButtonGroup.js.map +1 -1
- package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js +74 -8
- package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VButton/context.d.ts +30 -0
- package/dist/components/VButton/context.js +6 -0
- package/dist/components/VButton/context.js.map +1 -0
- package/dist/components/VCalendar/VCalendar.css +1 -1
- package/dist/components/VCalendar/VCalendar.d.ts +50 -36
- package/dist/components/VCalendar/VCalendar.js.map +1 -1
- package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js +99 -85
- package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendarEvent.css +1 -1
- package/dist/components/VCalendar/VCalendarEvent.d.ts +5 -11
- package/dist/components/VCalendar/VCalendarEvent.js.map +1 -1
- package/dist/components/VCalendar/VCalendarEvent.vue_vue_type_script_setup_true_lang.js +7 -1
- package/dist/components/VCalendar/VCalendarEvent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendarMonth.css +1 -1
- package/dist/components/VCalendar/VCalendarMonth.d.ts +10 -13
- package/dist/components/VCalendar/VCalendarMonth.js.map +1 -1
- package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js +183 -244
- package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendarTimeGrid.css +1 -1
- package/dist/components/VCalendar/VCalendarTimeGrid.d.ts +15 -20
- package/dist/components/VCalendar/VCalendarTimeGrid.js.map +1 -1
- package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js +385 -448
- package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendarYear.css +1 -1
- package/dist/components/VCalendar/VCalendarYear.js.map +1 -1
- package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js +53 -49
- package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/gesture.d.ts +158 -0
- package/dist/components/VCalendar/gesture.js +170 -0
- package/dist/components/VCalendar/gesture.js.map +1 -0
- package/dist/components/VCalendar/keyboard.d.ts +9 -3
- package/dist/components/VCalendar/keyboard.js +2 -6
- package/dist/components/VCalendar/keyboard.js.map +1 -1
- package/dist/components/VCalendar/layout.d.ts +67 -54
- package/dist/components/VCalendar/layout.js +77 -69
- package/dist/components/VCalendar/layout.js.map +1 -1
- package/dist/components/VCalendar/timeGrid.d.ts +55 -0
- package/dist/components/VCalendar/timeGrid.js +71 -0
- package/dist/components/VCalendar/timeGrid.js.map +1 -0
- package/dist/components/VCalendar/types.d.ts +41 -16
- package/dist/components/VCarousel/VCarousel.css +1 -1
- package/dist/components/VCarousel/VCarousel.d.ts +21 -41
- package/dist/components/VCarousel/VCarousel.js.map +1 -1
- package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js +157 -129
- package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCarousel/VCarouselItem.d.ts +0 -17
- package/dist/components/VCarousel/VCarouselItem.js.map +1 -1
- package/dist/components/VCarousel/VCarouselItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCarousel/context.d.ts +0 -12
- package/dist/components/VCarousel/context.js.map +1 -1
- package/dist/components/VCheckbox/VCheckbox.css +1 -1
- package/dist/components/VCheckbox/VCheckbox.d.ts +31 -16
- package/dist/components/VCheckbox/VCheckbox.js.map +1 -1
- package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js +62 -29
- package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VChip/VChip.css +1 -1
- package/dist/components/VChip/VChip.d.ts +36 -26
- package/dist/components/VChip/VChip.js.map +1 -1
- package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js +57 -48
- package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCombobox/VCombobox.css +1 -1
- package/dist/components/VCombobox/VCombobox.d.ts +127 -31
- package/dist/components/VCombobox/VCombobox.js.map +1 -1
- package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +336 -247
- package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCombobox/VComboboxGroup.css +1 -1
- package/dist/components/VCombobox/VComboboxGroup.d.ts +0 -9
- package/dist/components/VCombobox/VComboboxGroup.js.map +1 -1
- package/dist/components/VCombobox/VComboboxGroup.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/VCombobox/VComboboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCombobox/VComboboxOption.css +1 -1
- package/dist/components/VCombobox/VComboboxOption.d.ts +0 -15
- package/dist/components/VCombobox/VComboboxOption.js.map +1 -1
- package/dist/components/VCombobox/VComboboxOption.vue_vue_type_script_setup_true_lang.js +4 -4
- package/dist/components/VCombobox/VComboboxOption.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCombobox/VComboboxSeparator.js.map +1 -1
- package/dist/components/VCombobox/VComboboxSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCombobox/infiniteScroll.js.map +1 -1
- package/dist/components/VDataTable/VDataTable.css +1 -1
- package/dist/components/VDataTable/VDataTable.d.ts +50 -28
- package/dist/components/VDataTable/VDataTable.js.map +1 -1
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +183 -194
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDateInput/VDateInput.css +1 -1
- package/dist/components/VDateInput/VDateInput.d.ts +90 -42
- package/dist/components/VDateInput/VDateInput.js.map +1 -1
- package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +159 -191
- package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDatePicker/VDatePicker.css +1 -1
- package/dist/components/VDatePicker/VDatePicker.d.ts +61 -30
- package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
- package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js +243 -221
- package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDialog/VDialog.css +1 -1
- package/dist/components/VDialog/VDialog.d.ts +45 -33
- package/dist/components/VDialog/VDialog.js.map +1 -1
- package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js +65 -64
- package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDialog/VDialogAlert.d.ts +24 -20
- package/dist/components/VDialog/VDialogAlert.js.map +1 -1
- package/dist/components/VDialog/VDialogAlert.vue_vue_type_script_setup_true_lang.js +36 -25
- package/dist/components/VDialog/VDialogAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VFileInput/VFileInput.css +1 -1
- package/dist/components/VFileInput/VFileInput.d.ts +78 -26
- package/dist/components/VFileInput/VFileInput.js.map +1 -1
- package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +144 -132
- package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VFilePicker/VFilePicker.css +1 -1
- package/dist/components/VFilePicker/VFilePicker.d.ts +47 -30
- package/dist/components/VFilePicker/VFilePicker.js.map +1 -1
- package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js +166 -163
- package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VFilePicker/fileKind.d.ts +2 -27
- package/dist/components/VFilePicker/fileKind.js +9 -8
- package/dist/components/VFilePicker/fileKind.js.map +1 -1
- package/dist/components/VHotkeys/VHotkeys.css +1 -1
- package/dist/components/VHotkeys/VHotkeys.d.ts +8 -30
- package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
- package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js +4 -4
- package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VHotkeys/platform.js.map +1 -1
- package/dist/components/VIcon/VIcon.css +1 -1
- package/dist/components/VIcon/VIcon.d.ts +15 -6
- package/dist/components/VIcon/VIcon.js.map +1 -1
- package/dist/components/VIcon/VIcon.vue_vue_type_script_setup_true_lang.js +45 -37
- package/dist/components/VIcon/VIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VIcon/iconProps.js.map +1 -1
- package/dist/components/VIcon/icons/index.d.ts +1 -1
- package/dist/components/VIcon/icons/index.js.map +1 -1
- package/dist/components/VIcon/icons/names.d.ts +1 -1
- package/dist/components/VIcon/icons/names.js.map +1 -1
- package/dist/components/VIcon/resolver.d.ts +3 -3
- package/dist/components/VIcon/resolver.js.map +1 -1
- package/dist/components/VIcon/types.d.ts +12 -12
- package/dist/components/VIconButton/VIconButton.css +1 -1
- package/dist/components/VIconButton/VIconButton.d.ts +28 -11
- package/dist/components/VIconButton/VIconButton.js.map +1 -1
- package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js +26 -20
- package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/VInput.css +1 -1
- package/dist/components/VInput/VInput.d.ts +26 -7
- package/dist/components/VInput/VInput.js.map +1 -1
- package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +86 -75
- package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/VInputGroup.css +2 -0
- package/dist/components/VInput/VInputGroup.d.ts +47 -0
- package/dist/components/VInput/VInputGroup.js +9 -0
- package/dist/components/VInput/VInputGroup.js.map +1 -0
- package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js +80 -0
- package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VInput/context.d.ts +11 -0
- package/dist/components/VInput/context.js +6 -0
- package/dist/components/VInput/context.js.map +1 -0
- package/dist/components/VInputOTP/VInputOTP.css +1 -1
- package/dist/components/VInputOTP/VInputOTP.d.ts +37 -22
- package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
- package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +98 -61
- package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenu.d.ts +23 -30
- package/dist/components/VMenu/VMenu.js.map +1 -1
- package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js +31 -27
- package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenuGroup.css +1 -1
- package/dist/components/VMenu/VMenuGroup.d.ts +12 -8
- package/dist/components/VMenu/VMenuGroup.js.map +1 -1
- package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js +11 -11
- package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenuItem.css +1 -1
- package/dist/components/VMenu/VMenuItem.d.ts +11 -19
- package/dist/components/VMenu/VMenuItem.js.map +1 -1
- package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js +65 -57
- package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenuPanel.d.ts +14 -30
- package/dist/components/VMenu/VMenuPanel.js.map +1 -1
- package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +43 -44
- package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/context.d.ts +2 -6
- package/dist/components/VMenu/context.js.map +1 -1
- package/dist/components/VPagination/VPagination.css +1 -1
- package/dist/components/VPagination/VPagination.d.ts +60 -22
- package/dist/components/VPagination/VPagination.js.map +1 -1
- package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js +113 -161
- package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VPopover/VPopover.css +1 -1
- package/dist/components/VPopover/VPopover.d.ts +26 -7
- package/dist/components/VPopover/VPopover.js.map +1 -1
- package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js +43 -37
- package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VProgressCircular/VProgressCircular.css +1 -1
- package/dist/components/VProgressCircular/VProgressCircular.d.ts +15 -4
- package/dist/components/VProgressCircular/VProgressCircular.js.map +1 -1
- package/dist/components/VProgressCircular/VProgressCircular.vue_vue_type_script_setup_true_lang.js +32 -30
- package/dist/components/VProgressCircular/VProgressCircular.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VProgressLinear/VProgressLinear.css +1 -1
- package/dist/components/VProgressLinear/VProgressLinear.d.ts +23 -8
- package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
- package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js +34 -32
- package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VRadio/VRadio.css +1 -1
- package/dist/components/VRadio/VRadio.d.ts +36 -18
- package/dist/components/VRadio/VRadio.js.map +1 -1
- package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js +55 -26
- package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSeparator/VSeparator.d.ts +4 -2
- package/dist/components/VSeparator/VSeparator.js.map +1 -1
- package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigation.css +1 -1
- package/dist/components/VSideNavigation/VSideNavigation.d.ts +5 -19
- package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +5 -5
- package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationGroup.css +1 -1
- package/dist/components/VSideNavigation/VSideNavigationGroup.d.ts +9 -17
- package/dist/components/VSideNavigation/VSideNavigationGroup.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationItem.css +1 -1
- package/dist/components/VSideNavigation/VSideNavigationItem.d.ts +22 -30
- package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js +77 -87
- package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/context.d.ts +0 -14
- package/dist/components/VSideNavigation/context.js.map +1 -1
- package/dist/components/VSkeletonLoader/VSkeletonLoader.css +1 -1
- package/dist/components/VSkeletonLoader/VSkeletonLoader.d.ts +9 -9
- package/dist/components/VSkeletonLoader/VSkeletonLoader.js.map +1 -1
- package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSlider/VSlider.css +1 -1
- package/dist/components/VSlider/VSlider.d.ts +52 -27
- package/dist/components/VSlider/VSlider.js.map +1 -1
- package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js +202 -118
- package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSnackbar/VSnackbar.css +1 -1
- package/dist/components/VSnackbar/VSnackbar.d.ts +0 -23
- package/dist/components/VSnackbar/VSnackbar.js.map +1 -1
- package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js +38 -38
- package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSnackbar/state.d.ts +0 -27
- package/dist/components/VSnackbar/state.js.map +1 -1
- package/dist/components/VSpinner/VSpinner.css +1 -1
- package/dist/components/VSpinner/VSpinner.d.ts +6 -6
- package/dist/components/VSpinner/VSpinner.js.map +1 -1
- package/dist/components/VSpinner/VSpinner.vue_vue_type_script_setup_true_lang.js +10 -9
- package/dist/components/VSpinner/VSpinner.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSwitch/VSwitch.css +1 -1
- package/dist/components/VSwitch/VSwitch.d.ts +35 -13
- package/dist/components/VSwitch/VSwitch.js.map +1 -1
- package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js +62 -21
- package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/VTab.css +1 -1
- package/dist/components/VTabs/VTab.d.ts +12 -12
- package/dist/components/VTabs/VTab.js.map +1 -1
- package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js +36 -24
- package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/VTabPanel.d.ts +2 -10
- package/dist/components/VTabs/VTabPanel.js.map +1 -1
- package/dist/components/VTabs/VTabPanel.vue_vue_type_script_setup_true_lang.js +4 -4
- package/dist/components/VTabs/VTabPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/VTabs.css +1 -1
- package/dist/components/VTabs/VTabs.d.ts +27 -18
- package/dist/components/VTabs/VTabs.js.map +1 -1
- package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js +92 -81
- package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/context.d.ts +9 -19
- package/dist/components/VTabs/context.js +1 -1
- package/dist/components/VTabs/context.js.map +1 -1
- package/dist/components/VTextarea/VTextarea.css +1 -1
- package/dist/components/VTextarea/VTextarea.d.ts +34 -6
- package/dist/components/VTextarea/VTextarea.js.map +1 -1
- package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js +91 -74
- package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeInput/VTimeInput.css +1 -1
- package/dist/components/VTimeInput/VTimeInput.d.ts +123 -33
- package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
- package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +294 -291
- package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeInput/search.d.ts +24 -0
- package/dist/components/VTimeInput/search.js +22 -0
- package/dist/components/VTimeInput/search.js.map +1 -0
- package/dist/components/VTimePicker/VTimePicker.css +1 -1
- package/dist/components/VTimePicker/VTimePicker.d.ts +51 -30
- package/dist/components/VTimePicker/VTimePicker.js.map +1 -1
- package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js +216 -151
- package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimePicker/limits.d.ts +88 -0
- package/dist/components/VTimePicker/limits.js +70 -0
- package/dist/components/VTimePicker/limits.js.map +1 -0
- package/dist/components/VToast/VToast.css +1 -1
- package/dist/components/VToast/VToast.d.ts +0 -10
- package/dist/components/VToast/VToast.js.map +1 -1
- package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js +10 -10
- package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToast/VToaster.css +1 -1
- package/dist/components/VToast/VToaster.d.ts +0 -17
- package/dist/components/VToast/VToaster.js.map +1 -1
- package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js +29 -28
- package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToast/state.d.ts +10 -19
- package/dist/components/VToast/state.js +1 -1
- package/dist/components/VToast/state.js.map +1 -1
- package/dist/components/VToggle/VToggle.css +1 -1
- package/dist/components/VToggle/VToggle.d.ts +56 -11
- package/dist/components/VToggle/VToggle.js.map +1 -1
- package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js +80 -46
- package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToggle/VToggleItem.d.ts +16 -2
- package/dist/components/VToggle/VToggleItem.js +0 -2
- package/dist/components/VToggle/VToggleItem.js.map +1 -1
- package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js +23 -15
- package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToggle/context.d.ts +4 -24
- package/dist/components/VToggle/context.js.map +1 -1
- package/dist/components/VTooltip/VTooltip.css +1 -1
- package/dist/components/VTooltip/VTooltip.d.ts +9 -33
- package/dist/components/VTooltip/VTooltip.js.map +1 -1
- package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js +28 -24
- package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTypography/VTypography.js.map +1 -1
- package/dist/components/VTypography/VTypography.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/composables/useAriaLabel.d.ts +0 -17
- package/dist/composables/useAriaLabel.js.map +1 -1
- package/dist/composables/useClearable.d.ts +42 -0
- package/dist/composables/useClearable.js +19 -0
- package/dist/composables/useClearable.js.map +1 -0
- package/dist/composables/useControlShape.d.ts +22 -0
- package/dist/composables/useControlShape.js +13 -0
- package/dist/composables/useControlShape.js.map +1 -0
- package/dist/composables/useDetailsOpen.d.ts +22 -0
- package/dist/composables/useDetailsOpen.js +21 -0
- package/dist/composables/useDetailsOpen.js.map +1 -0
- package/dist/composables/useFieldIds.d.ts +0 -15
- package/dist/composables/useFieldIds.js +8 -7
- package/dist/composables/useFieldIds.js.map +1 -1
- package/dist/composables/useFieldPanel.d.ts +21 -18
- package/dist/composables/useFieldPanel.js +43 -23
- package/dist/composables/useFieldPanel.js.map +1 -1
- package/dist/composables/useFileDrop.d.ts +0 -22
- package/dist/composables/useFileDrop.js.map +1 -1
- package/dist/composables/useFileField.d.ts +45 -17
- package/dist/composables/useFileField.js +40 -20
- package/dist/composables/useFileField.js.map +1 -1
- package/dist/composables/useFocusoutDismiss.d.ts +0 -15
- package/dist/composables/useFocusoutDismiss.js.map +1 -1
- package/dist/composables/useIconClickHandlers.d.ts +29 -14
- package/dist/composables/useIconClickHandlers.js +15 -5
- package/dist/composables/useIconClickHandlers.js.map +1 -1
- package/dist/composables/useInertLink.d.ts +20 -0
- package/dist/composables/useInertLink.js +23 -0
- package/dist/composables/useInertLink.js.map +1 -0
- package/dist/composables/useMaskedField.d.ts +33 -10
- package/dist/composables/useMaskedField.js +45 -1
- package/dist/composables/useMaskedField.js.map +1 -1
- package/dist/composables/usePopover.d.ts +15 -1
- package/dist/composables/usePopover.js +23 -8
- package/dist/composables/usePopover.js.map +1 -1
- package/dist/composables/useProgressValue.d.ts +5 -8
- package/dist/composables/useProgressValue.js +6 -3
- package/dist/composables/useProgressValue.js.map +1 -1
- package/dist/composables/useRootAttrs.d.ts +12 -19
- package/dist/composables/useRootAttrs.js.map +1 -1
- package/dist/composables/useTimer.d.ts +8 -10
- package/dist/composables/useTimer.js.map +1 -1
- package/dist/i18n/en.d.ts +2 -10
- package/dist/i18n/en.js +18 -19
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/fr.d.ts +2 -18
- package/dist/i18n/fr.js +18 -19
- package/dist/i18n/fr.js.map +1 -1
- package/dist/i18n/state.d.ts +44 -47
- package/dist/i18n/state.js +20 -17
- package/dist/i18n/state.js.map +1 -1
- package/dist/i18n/types.d.ts +53 -44
- package/dist/icons.d.ts +3 -3
- package/dist/index.d.ts +43 -17
- package/dist/index.js +45 -44
- package/dist/styles.css +1 -1
- package/dist/tokens/css.js.map +1 -1
- package/dist/tokens/index.d.ts +5 -1
- package/dist/tokens/primitives.js.map +1 -1
- package/dist/tokens/semantic.d.ts +5 -1
- package/dist/tokens/semantic.js +6 -2
- package/dist/tokens/semantic.js.map +1 -1
- package/dist/tokens/themes/dark.js.map +1 -1
- package/dist/tokens/types.d.ts +7 -15
- package/dist/tokens/types.js.map +1 -1
- package/dist/tokens.json +25 -5
- package/dist/types.d.ts +24 -0
- package/dist/utils/array.d.ts +3 -6
- package/dist/utils/array.js.map +1 -1
- package/dist/utils/arrowNav.d.ts +17 -17
- package/dist/utils/arrowNav.js +5 -3
- package/dist/utils/arrowNav.js.map +1 -1
- package/dist/utils/chip.d.ts +11 -15
- package/dist/utils/chip.js.map +1 -1
- package/dist/utils/css.d.ts +22 -11
- package/dist/utils/css.js +5 -2
- package/dist/utils/css.js.map +1 -1
- package/dist/utils/date.d.ts +42 -19
- package/dist/utils/date.js +97 -98
- package/dist/utils/date.js.map +1 -1
- package/dist/utils/direction.d.ts +6 -12
- package/dist/utils/direction.js.map +1 -1
- package/dist/utils/env.d.ts +5 -6
- package/dist/utils/file.d.ts +46 -58
- package/dist/utils/file.js +43 -36
- package/dist/utils/file.js.map +1 -1
- package/dist/utils/focus.d.ts +8 -17
- package/dist/utils/focus.js.map +1 -1
- package/dist/utils/hostWarns.d.ts +12 -0
- package/dist/utils/hostWarns.js +6 -0
- package/dist/utils/hostWarns.js.map +1 -0
- package/dist/utils/ids.d.ts +10 -0
- package/dist/utils/ids.js +8 -0
- package/dist/utils/ids.js.map +1 -0
- package/dist/utils/matcher.d.ts +5 -9
- package/dist/utils/matcher.js.map +1 -1
- package/dist/utils/memo.d.ts +12 -0
- package/dist/utils/memo.js +9 -0
- package/dist/utils/memo.js.map +1 -0
- package/dist/utils/number.d.ts +3 -4
- package/dist/utils/number.js.map +1 -1
- package/dist/utils/text.d.ts +20 -9
- package/dist/utils/text.js +17 -3
- package/dist/utils/text.js.map +1 -1
- package/dist/utils/time.d.ts +53 -61
- package/dist/utils/time.js +49 -45
- package/dist/utils/time.js.map +1 -1
- package/dist/utils/vnode.d.ts +8 -15
- package/dist/utils/vnode.js.map +1 -1
- package/package.json +6 -6
- package/dist/components/VToggle/VToggleItem.css +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VCombobox.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VCombobox/VCombobox.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, reactive, ref, useId, watch, watchEffect } from 'vue'\n\nimport VChip from '../VChip/VChip.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport type { IconSource } from '../VIcon/types'\nimport VInput from '../VInput/VInput.vue'\nimport VPopover from '../VPopover/VPopover.vue'\nimport VComboboxOption from './VComboboxOption.vue'\nimport VComboboxGroup from './VComboboxGroup.vue'\nimport VComboboxSeparator from './VComboboxSeparator.vue'\nimport { useInfiniteScroll } from './infiniteScroll'\nimport VSpinner from '../VSpinner/VSpinner.vue'\n\nimport { toggleValue } from '../../utils/array'\nimport { chipScaleFor } from '../../utils/chip'\nimport { normalizeText } from '../../utils/text'\n\nimport { useRootAttrs } from '../../composables/useRootAttrs'\n\nimport { useFocusoutDismiss } from '../../composables/useFocusoutDismiss'\n\nimport { useTimer } from '../../composables/useTimer'\nimport { useMessages } from '../../i18n/state'\n\n// @a11y @keyboard @core\n/**\n * A field one types into to search a list, and picks one or several values from. It is\n * built out of a text field, a floating panel holding the list, and chips for the values\n * already chosen.\n *\n * The platform offers nothing to build this on: its own suggestion list can be neither\n * styled nor made to accept several values. So the JavaScript here implements the whole\n * pattern assistive technology expects of a combobox — the filtering, the moving of the\n * highlight, and the selection.\n *\n * One decision shapes all the rest: the DOM focus NEVER leaves the text field. What\n * moves as the reader presses the arrows is a pointer naming the current option, which\n * is why the panel has no keyboard of its own and why this component owns the entire\n * accessibility contract of the field.\n *\n * The panel is opened in the mode where nothing dismisses it by itself, and closing is\n * decided by the focus leaving the component — which the field being OUTSIDE the panel\n * makes possible.\n */\n/** One thing that can be chosen. */\nexport interface ComboboxOption {\n /** What choosing it means: this is what the value holds. */\n value: string\n /** What it is called on screen, and what the search matches against. */\n label: string\n /** An icon before the label: an icon name, or an explicit render. */\n icon?: IconSource\n /** Shows the option without allowing it to be chosen. */\n disabled?: boolean\n}\n\n/**\n * A named block of options — the equivalent of the grouping a native list offers. A\n * group none of whose options survive the search disappears entirely, its name\n * included.\n */\nexport interface ComboboxGroup {\n /** The name of the block. */\n label: string\n /** The options it holds. */\n options: ComboboxOption[]\n}\n\n/**\n * A rule drawn between two blocks of options. It is purely decorative, and a separator\n * the search leaves stranded — at the top, at the bottom, or against another one — is\n * simply not drawn.\n */\nexport interface ComboboxSeparator {\n separator: true\n}\n\n/** Anything the list may hold: an option, a named block, or a separator. */\nexport type ComboboxItem = ComboboxOption | ComboboxGroup | ComboboxSeparator\n\nconst isGroup = (item: ComboboxItem): item is ComboboxGroup => 'options' in item\nconst isSeparator = (item: ComboboxItem): item is ComboboxSeparator => 'separator' in item\n\n/**\n * How the list is narrowed as one types: by the component itself, not at all — when the\n * options already arrive filtered by a server — or by a rule of your own.\n */\nexport type ComboboxFilter = boolean | ((option: ComboboxOption, query: string) => boolean)\n\ninterface ComboboxProps {\n /**\n * What the list offers. An entry may be an option, a named block of options, or a\n * separator; a plain list of options remains perfectly valid.\n */\n options: ComboboxItem[]\n /**\n * Allows several values to be chosen, which makes the value a list and shows what has\n * been chosen as chips inside the field.\n */\n multiple?: boolean\n /** The height of the field: 32, 40 or 48 pixels. */\n size?: 'sm' | 'md' | 'lg'\n /** Takes 4px off the height, as everywhere else in the design system. */\n compact?: boolean\n /** What the field says while nothing is chosen and nothing has been typed. */\n placeholder?: string\n /** Makes the field unusable, greyed out through the colour tokens. */\n disabled?: boolean\n /** Marks the field as invalid — for a rule of your own. */\n invalid?: boolean\n /** Offers a cross that empties both the selection and the search. */\n clearable?: boolean\n /** What the panel says when the search matches nothing. */\n emptyText?: string\n /**\n * How the list is narrowed as one types. Turning it off means the options already\n * arrive filtered by their source and are shown exactly as they come.\n *\n * A rule of your own receives the query as it was TYPED, merely trimmed — not the\n * accent-insensitive form used internally.\n */\n filter?: ComboboxFilter\n /**\n * How long to wait before telling the source what is being searched for, in\n * milliseconds. Zero tells it at once, which suits a source that is not a network\n * request.\n */\n searchDebounce?: number\n /**\n * Says that something is being loaded. With no option yet, the whole panel says so;\n * with options already listed, a spinner appears at the foot of the list, since what\n * is loading is then the next page. Either way the field replaces its chevron with a\n * spinner.\n */\n loading?: boolean\n /** What is said while loading, and what the spinner is announced as. */\n loadingText?: string\n /**\n * Says that there are more pages to come, which is what makes the component ask for\n * the next one as the end of the list comes into view.\n */\n hasMore?: boolean\n}\n\nconst props = withDefaults(defineProps<ComboboxProps>(), {\n multiple: false,\n size: 'md',\n compact: false,\n placeholder: undefined,\n disabled: false,\n invalid: false,\n clearable: true,\n emptyText: undefined,\n filter: true,\n searchDebounce: 250,\n loading: false,\n loadingText: undefined,\n hasMore: false,\n})\n\nconst m = useMessages()\nconst resolvedEmptyText = computed(() => props.emptyText ?? m.value.combobox.empty)\nconst resolvedLoadingText = computed(() => props.loadingText ?? m.value.common.loading)\n\nconst emit = defineEmits<{\n /**\n * What is being searched for, to be sent to the source. It is delayed by\n * `searchDebounce` while typing, and emitted at once when the panel opens so that a\n * first page can be loaded.\n *\n * The same term is never emitted twice in a row, so reopening the panel does not\n * repeat a request that has already been answered.\n */\n search: [query: string]\n /** The end of the list has come into view: send the next page. */\n 'load-more': []\n}>()\n\ndefineSlots<{\n /**\n * What a row of the list shows, in place of the plain label — a subtitle, an avatar,\n * a badge. It is told whether the row is the highlighted one and whether it is\n * already chosen, so the rendering can react to both.\n */\n option?(props: {\n option: ComboboxOption\n index: number\n active: boolean\n selected: boolean\n }): unknown\n /**\n * Replaces the chip standing for one chosen value.\n *\n * Three of the values it receives are what make it usable without regressions:\n * `remove`, without which the value could no longer be taken back, and the size and\n * density worked out to sit inside the field, which cannot be guessed from outside.\n * The option itself may be missing, if that value has never appeared among the\n * options.\n */\n chip?(props: {\n value: string\n option: ComboboxOption | undefined\n label: string\n remove: () => void\n size: 'xs' | 'sm'\n compact: boolean\n }): unknown\n /** What the panel shows when nothing matches. It receives the term that was searched. */\n empty?(props: { query: string }): unknown\n /** What the panel shows while loading its first options. */\n loading?(): unknown\n}>()\n\n// The size, the density and the HEIGHT of the chips sitting inside the field, worked out\n// once in `utils/chip.ts` and shared with VFileInput. The height is set inline below\n// rather than restated as a table of CSS rules: it is the height the field forces on its\n// input, and deriving both from the same pair is what stops them drifting apart.\nconst chipScale = computed(() => chipScaleFor(props.size, props.compact))\n\nconst model = defineModel<string | string[]>({ default: '' })\n\n// `class` and `style` stay on the wrapper, where a consumer expects to style the\n// component; everything else — a name above all — goes down to the text field, which is\n// the element assistive technology treats as the combobox.\ndefineOptions({ inheritAttrs: false })\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst inputRef = ref<InstanceType<typeof VInput> | null>(null)\n// One generated identifier serves twice: as the panel's own id, which the field points\n// at, and as the prefix of every option's id. The two strings can never collide, one\n// being a prefix of longer ones.\nconst optionsId = useId()\n\nconst open = ref(false)\nconst query = ref('')\nconst activeIndex = ref(-1)\nconst focused = ref(false)\n// The text in the field serves two purposes at once: it SHOWS the chosen label when a\n// single value is picked, and it is what one searches with. This flag tells the two\n// apart — while it is false the text is merely a label and narrows nothing, so reopening\n// the panel offers the whole list again. It only becomes true once the reader types.\nconst typed = ref(false)\n\nconst selectedValues = computed<string[]>(() => {\n if (props.multiple) return Array.isArray(model.value) ? model.value : []\n return typeof model.value === 'string' && model.value ? [model.value] : []\n})\n\n// Every option, flattened: the blocks unwrapped, the separators dropped, in the order\n// they were given. This is the ONE reading of the options the rest of the component\n// does — the filtering, the memory of what was chosen, the labels and the paging all\n// ignore the hierarchy entirely. Only the rendering below knows about it.\nconst allOptions = computed<ComboboxOption[]>(() =>\n props.options.flatMap((item) => {\n if (isSeparator(item)) return []\n return isGroup(item) ? item.options : [item]\n }),\n)\n\n// A memory of the options that were CHOSEN. With a source that answers over the network,\n// the options only ever hold the latest results, and something already chosen is usually\n// absent from them — without this, a chip would show a raw identifier instead of a name,\n// and a custom chip would lose the option's icon.\n//\n// It is kept up to date by an effect rather than by watching the options, because the\n// effect tracks the ITERATION and therefore also notices a page appended in place. And\n// it is bounded to what is currently chosen: everything else is read from the options\n// themselves.\nconst optionCache = reactive(new Map<string, ComboboxOption>())\nwatchEffect(() => {\n const wanted = new Set(selectedValues.value)\n for (const option of allOptions.value) {\n if (wanted.has(option.value)) optionCache.set(option.value, option)\n }\n // What is no longer chosen is forgotten. Without this the memory would grow with every\n // value chosen during the session rather than with the current selection — a slow leak\n // in a long-lived multiple field fed by a paginated source.\n for (const value of optionCache.keys()) if (!wanted.has(value)) optionCache.delete(value)\n})\n\n/*\n * A lookup from value to option, built once per list of options rather than searched\n * each time. Finding an option is done several times per chip and per render — for the\n * chip itself, its label and the name of its remove button — so a linear search here\n * would cost the number of chips times the number of options, on every keystroke.\n *\n * The FIRST match wins, exactly as a search would have done, so a duplicated value keeps\n * the option that came first.\n */\nconst optionsByValue = computed(() => {\n const map = new Map<string, ComboboxOption>()\n for (const option of allOptions.value) if (!map.has(option.value)) map.set(option.value, option)\n return map\n})\n\n/**\n * The selection as a set, so that asking \"is this one chosen?\" costs nothing however\n * many are — the same device VDataTable uses for its selected rows.\n */\nconst selectedSet = computed(() => new Set(selectedValues.value))\n\n/**\n * The option a value stands for: from the current options first, and from the memory of\n * chosen ones failing that.\n *\n * TRAP — an option coming from that memory is a reactive PROXY, since making the map\n * reactive converts the objects inside it. Compare options by their value and never by\n * identity, or the two forms of the same option will not match.\n */\nfunction optionOf(value: string) {\n return optionsByValue.value.get(value) ?? optionCache.get(value)\n}\n\nfunction labelOf(value: string) {\n return optionOf(value)?.label ?? value\n}\n\n/*\n * The labels in their accent-insensitive form, remembered per option and re-checked\n * against the label itself.\n *\n * Normalizing a string means decomposing it, stripping what it decomposed into, and\n * lowercasing it — and the filter below runs on every keystroke. Without this memory the\n * entire list would be normalized again for each character typed.\n *\n * Reading the label on every call, rather than deriving the whole table once, is what\n * keeps the filter reactive: a derived value keyed on the list alone would not notice a\n * label changed in place, since flattening the options only ever touches the containers.\n */\nconst normalizedLabels = new WeakMap<ComboboxOption, { label: string; normalized: string }>()\nfunction normalizedLabelOf(option: ComboboxOption): string {\n const hit = normalizedLabels.get(option)\n if (hit && hit.label === option.label) return hit.normalized\n const normalized = normalizeText(option.label)\n normalizedLabels.set(option, { label: option.label, normalized })\n return normalized\n}\n\n// ONE search term, read both by the local filter and by what is sent to the source: the\n// list on screen and the request in flight therefore cannot describe different searches.\n// With nothing typed the term is empty, so a label merely being displayed never narrows\n// the list.\nconst searchTerm = computed(() => (typed.value ? query.value.trim() : ''))\n\n// The options that survive the search, flat and in the order they are shown. This is\n// what the keyboard counts through: blocks and separators do not appear in it, which is\n// exactly why the arrows never stop on one.\nconst filtered = computed(() => {\n const matcher = props.filter\n const list = allOptions.value\n if (matcher === false) return list\n const q = searchTerm.value\n if (!q) return list\n if (typeof matcher === 'function') return list.filter((o) => matcher(o, q))\n const needle = normalizeText(q)\n return list.filter((o) => normalizedLabelOf(o).includes(needle))\n})\n\n// @a11y\n/**\n * What is announced about the state of the panel — that nothing matches, or that\n * something is loading. It is emptied as soon as there are options to show: a region\n * that announces its changes must only ever hold what is new, or the same sentence is\n * read out again at every keystroke.\n */\nconst stateAnnouncement = computed(() => {\n if (!open.value || filtered.value.length > 0) return ''\n return props.loading ? resolvedLoadingText.value : resolvedEmptyText.value\n})\n\n// The render tree: the only place that knows about the hierarchy. Each option there\n// carries its index IN `filtered` (and not its position in the tree): ids, highlight\n// and the #option slot stay aligned on the keyboard navigation.\ntype RenderedOption = { kind: 'option'; key: string; option: ComboboxOption; index: number }\ntype RenderedNode =\n | RenderedOption\n | { kind: 'group'; key: string; label: string; options: RenderedOption[] }\n | { kind: 'separator'; key: string }\n\nconst rendered = computed<RenderedNode[]>(() => {\n const indexOf = new Map<string, number>()\n filtered.value.forEach((option, index) => indexOf.set(option.value, index))\n\n const entryOf = (option: ComboboxOption): RenderedOption | null => {\n const index = indexOf.get(option.value)\n if (index === undefined) return null\n return { kind: 'option', key: `option:${option.value}`, option, index }\n }\n\n const nodes: RenderedNode[] = []\n // A separator is held back and only drawn once something has come BEFORE it and\n // something comes AFTER it. That single rule covers all three ways the filtering can\n // strand one: at the top of the list, at the bottom, and two in a row.\n let pendingSeparator: string | null = null\n\n for (const [i, item] of props.options.entries()) {\n if (isSeparator(item)) {\n if (nodes.length > 0) pendingSeparator = `sep:${i}`\n continue\n }\n\n let node: RenderedNode | null\n if (isGroup(item)) {\n const options = item.options\n .map(entryOf)\n .filter((entry): entry is RenderedOption => entry !== null)\n // A block none of whose options survived is dropped entirely, its name included:\n // a heading over nothing is worse than no heading.\n node =\n options.length > 0 ? { kind: 'group', key: `group:${i}`, label: item.label, options } : null\n } else {\n node = entryOf(item)\n }\n if (!node) continue\n\n if (pendingSeparator !== null) {\n nodes.push({ kind: 'separator', key: pendingSeparator })\n pendingSeparator = null\n }\n nodes.push(node)\n }\n\n return nodes\n})\n\n// Telling an outside source what is being searched for. Nothing native waits before\n// sending a request, or refrains from sending the same one twice, so both have to be\n// written. The waiting itself is delegated to `useTimer` — re-arming, a delay of zero\n// running at once, cancellation when the component goes away — and what stays here is\n// specific to this component: not repeating a term, and giving up when the panel closes.\nconst searchTimer = useTimer()\n// The last term actually sent, or nothing if none ever was. It is what stops reopening\n// the panel on an unchanged term from firing the request again — a consumer with a cache\n// of their own is of course free to answer instantly.\nlet lastEmitted: string | undefined\n\nconst cancelSearch = searchTimer.cancel\n\nfunction emitSearch(term: string, immediate = false) {\n cancelSearch()\n if (term === lastEmitted) return\n searchTimer.start(\n () => {\n // The panel may have closed while we were waiting: there is then nothing left to\n // load, and the check has to happen HERE rather than before arming the timer.\n if (!open.value) return\n lastEmitted = term\n emit('search', term)\n },\n immediate ? 0 : props.searchDebounce,\n )\n}\n\nwatch(searchTerm, (term) => {\n if (!open.value) return\n // A new search means a new list, so the highlight cannot survive it: with an outside\n // source the options are replaced wholesale, and the old position would name a\n // completely unrelated option.\n //\n // It is moved to the first available option rather than dropped altogether, and that\n // matters when the filtering is left to the source: the visible list keeps the very\n // same reference until the answer arrives, so the watcher below would never fire and\n // Enter would do nothing in the meantime. When the new list does arrive, the position\n // is still within it and simply names its first option.\n activeIndex.value = filtered.value.findIndex((o) => !o.disabled)\n emitSearch(term)\n})\n\nwatch(filtered, (list) => {\n // While the panel is open there must always be a valid option highlighted. It is moved\n // back to the first result whenever the highlighted one has fallen out of the list OR\n // there was none at all — without that second case, a search that passed through \"no\n // result\" would leave nothing highlighted, and Enter would not choose the single\n // result that came back.\n if (!open.value) return\n if (activeIndex.value < 0 || activeIndex.value >= list.length) {\n activeIndex.value = list.findIndex((o) => !o.disabled)\n }\n})\n\n// With a single value, the field shows its label as ordinary text whenever it is not\n// being edited.\nif (!props.multiple && typeof model.value === 'string' && model.value) {\n query.value = labelOf(model.value)\n}\n\n// That label is a COPY and not something derived, so it has to be refreshed when the\n// options arrive later — otherwise a field mounted with a value but no options yet would\n// show a raw identifier for ever.\n//\n// ONLY the options are watched. Watching the value as well would bring back the\n// premature read that selecting deliberately avoids, and the two guards below are what\n// keep the refresh from ever overwriting something being typed.\nwatch(\n allOptions,\n () => {\n if (props.multiple || open.value || typed.value) return\n if (typeof model.value === 'string' && model.value) query.value = labelOf(model.value)\n },\n { flush: 'post' },\n)\n\n// With several values chosen and the field unfocused, the search input folds away so the\n// chips are not followed by an empty gap. It stays in the page and stays focusable —\n// only its width goes.\nconst collapsed = computed(\n () => props.multiple && !focused.value && selectedValues.value.length > 0,\n)\n\n// The clear cross has to appear as soon as there is SOMETHING to clear — chosen values,\n// or a search in progress — and not merely when the text field holds text. The field\n// cannot work that out for itself, since the chips live outside its value, so the answer\n// is given to it explicitly, and the room for the cross is reserved accordingly.\nconst canClear = computed(\n () =>\n props.clearable &&\n !props.disabled &&\n (selectedValues.value.length > 0 || query.value.length > 0),\n)\n\nconst optionId = (index: number) => `${optionsId}-option-${index}`\n\n// A row has to be written twice in the template, once inside a block and once at the top\n// level of the panel, a Vue template having no way to declare a reusable fragment. These\n// two builders are what reduce that duplication to a single line on each side.\nfunction rowProps(entry: RenderedOption) {\n return {\n id: optionId(entry.index),\n icon: entry.option.icon,\n active: entry.index === activeIndex.value,\n selected: selectedSet.value.has(entry.option.value),\n disabled: entry.option.disabled,\n }\n}\n\nfunction optionSlotProps(entry: RenderedOption) {\n return {\n option: entry.option,\n index: entry.index,\n active: entry.index === activeIndex.value,\n selected: selectedSet.value.has(entry.option.value),\n }\n}\n\nfunction hover(entry: RenderedOption) {\n if (!entry.option.disabled) activeIndex.value = entry.index\n}\n\n// @a11y\n// Since the focus never leaves the field, the browser has no reason to scroll the\n// highlighted option into view — nothing was focused. It is brought into view by hand\n// instead, and asked for the SMALLEST movement that reveals it, so an option already\n// visible does not make the panel jump.\nwatch(activeIndex, (index) => {\n if (index < 0) return\n nextTick(() => {\n // Called optionally: the unit-test environment implements no scrolling at all.\n document.getElementById(optionId(index))?.scrollIntoView?.({ block: 'nearest' })\n })\n})\n\nfunction openPanel() {\n if (props.disabled || open.value) return\n open.value = true\n const list = filtered.value\n const selectedIdx = list.findIndex((o) => !o.disabled && selectedSet.value.has(o.value))\n activeIndex.value = selectedIdx >= 0 ? selectedIdx : list.findIndex((o) => !o.disabled)\n // Told to the source AFTER the panel is marked open — the delayed emission checks that\n // flag before firing — which is what lets an outside source load its first page as the\n // panel appears.\n emitSearch(searchTerm.value, true)\n}\n\n// Asking for the next page as the end of the list is reached. The observer itself, its\n// re-arming after each page and the lock that stops it firing twice all live in\n// `./infiniteScroll`; it is declared here so that closing the panel can reset it.\nconst sentinelEl = ref<HTMLElement | null>(null)\n\nconst infiniteScroll = useInfiniteScroll({\n sentinelEl,\n canLoad: () => open.value && props.hasMore && !props.loading,\n loadedCount: () => allOptions.value.length,\n onLoadMore: () => emit('load-more'),\n})\n\nfunction closePanel() {\n if (!open.value) return\n cancelSearch()\n // A page that never arrived — a request that failed — must not leave the paging\n // frozen for good, so the lock is released here.\n infiniteScroll.reset()\n open.value = false\n activeIndex.value = -1\n typed.value = false\n query.value =\n !props.multiple && typeof model.value === 'string' && model.value ? labelOf(model.value) : ''\n}\n\n/**\n * Closes as soon as the focus leaves the component — the panel included, which is a\n * descendant of it even while floating above the page.\n */\nconst onFocusout = useFocusoutDismiss(rootEl, closePanel)\n\n// @a11y\n/*\n * TRAP — the focus must never leave the field, and this is what prevents it. Without\n * cancelling the press, clicking an option takes the focus off the field, the handler\n * above closes the panel BEFORE the click is turned into a selection, and choosing with\n * the mouse stops working entirely.\n *\n * It is invisible in the unit tests, where clicking moves no focus; a browser test\n * covers it.\n */\nfunction onPanelMousedown(event: MouseEvent) {\n event.preventDefault()\n}\n\n/** The reader is typing: the text becomes a search, and the panel opens. */\nfunction onInput() {\n typed.value = true\n openPanel()\n}\n\n/**\n * With a single value chosen, the label shown in the field is selected on focus, so that\n * typing replaces it rather than appending to it — and until something IS typed, the\n * whole list stays on offer.\n */\nfunction selectQuery() {\n if (!props.multiple && query.value) inputRef.value?.select()\n}\n\nfunction onFocus() {\n focused.value = true\n selectQuery()\n}\n\n/** A click anywhere on the field focuses it and opens the panel. */\nfunction onControlClick() {\n if (props.disabled) return\n inputRef.value?.focus()\n openPanel()\n // Selected again AFTER the click, which has just placed the caret somewhere in the\n // middle of the label.\n selectQuery()\n}\n\nfunction select(option: ComboboxOption) {\n if (option.disabled) return\n // Remembered immediately: the option may vanish from the list — on the next search —\n // before the parent has even passed the new value back down.\n optionCache.set(option.value, option)\n typed.value = false\n if (props.multiple) {\n model.value = toggleValue(selectedValues.value, option.value)\n query.value = ''\n inputRef.value?.focus()\n } else {\n model.value = option.value\n // This path does not go through the closing function, so the pending search has to\n // be cancelled here as well — otherwise the last keystroke would fire its request\n // after the panel had closed.\n cancelSearch()\n // TRAP — the label is taken from the option just chosen and NOT by reading the value\n // back. With a value bound by the parent, reading it immediately after writing still\n // returns the OLD one, and the field would show the previously selected label.\n query.value = option.label\n open.value = false\n activeIndex.value = -1\n }\n}\n\nfunction removeValue(value: string) {\n if (!props.multiple) return\n model.value = selectedValues.value.filter((v) => v !== value)\n inputRef.value?.focus()\n}\n\n/**\n * The clear cross was pressed. The field has already emptied the text it holds; what is\n * left to empty is the selection itself — the single value, or all the chips.\n */\nfunction onClear() {\n model.value = props.multiple ? [] : ''\n typed.value = false\n activeIndex.value = -1\n}\n\n// @keyboard — moves the highlight, skipping over the options that cannot be chosen and\n// wrapping round from one end of the list to the other.\nfunction move(delta: number) {\n const list = filtered.value\n if (list.length === 0) return\n let i = activeIndex.value\n for (let step = 0; step < list.length; step++) {\n i = (i + delta + list.length) % list.length\n if (!list[i]?.disabled) break\n }\n activeIndex.value = i\n}\n\n// @keyboard @a11y — the whole keyboard of the pattern. The arrows move the POINTER\n// naming the current option and never the focus itself, which stays in the field\n// throughout; Backspace on an empty field takes back the last chip, the usual gesture in\n// a field holding several values.\nfunction onKeydown(event: KeyboardEvent) {\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n if (open.value) move(1)\n else openPanel()\n break\n case 'ArrowUp':\n event.preventDefault()\n if (open.value) move(-1)\n else openPanel()\n break\n case 'Enter':\n if (open.value && activeIndex.value >= 0) {\n event.preventDefault()\n const option = filtered.value[activeIndex.value]\n if (option) select(option)\n }\n break\n case 'Escape':\n closePanel()\n break\n case 'Tab':\n closePanel()\n break\n case 'Backspace':\n if (props.multiple && !query.value) {\n const last = selectedValues.value.at(-1)\n if (last) removeValue(last)\n }\n break\n }\n}\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n class=\"v-combobox\"\n :class=\"rootClass\"\n :style=\"[{ '--chip-height': chipScale.height }, rootStyle]\"\n :data-size=\"size\"\n :data-compact=\"compact ? '' : undefined\"\n :data-multiple=\"multiple ? '' : undefined\"\n :data-collapsed=\"collapsed ? '' : undefined\"\n :data-open=\"open ? '' : undefined\"\n :data-can-clear=\"canClear ? '' : undefined\"\n @focusout=\"onFocusout\"\n >\n <div class=\"v-combobox-control\" @click=\"onControlClick\">\n <VInput\n ref=\"inputRef\"\n v-model=\"query\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n :aria-expanded=\"open\"\n :aria-controls=\"optionsId\"\n v-bind=\"forwardedAttrs\"\n :size=\"size\"\n :compact=\"compact\"\n :invalid=\"invalid\"\n :disabled=\"disabled\"\n :clearable=\"clearable\"\n :clear-visible=\"canClear\"\n :clear-label=\"m.combobox.clear\"\n :placeholder=\"selectedValues.length === 0 ? placeholder : undefined\"\n :aria-activedescendant=\"open && activeIndex >= 0 ? optionId(activeIndex) : undefined\"\n @input=\"onInput\"\n @keydown=\"onKeydown\"\n @focus=\"onFocus\"\n @blur=\"focused = false\"\n @clear=\"onClear\"\n >\n <template v-if=\"multiple\" #start>\n <template v-for=\"value in selectedValues\" :key=\"value\">\n <slot\n name=\"chip\"\n :value=\"value\"\n :option=\"optionOf(value)\"\n :label=\"labelOf(value)\"\n :remove=\"() => removeValue(value)\"\n :size=\"chipScale.size\"\n :compact=\"chipScale.compact\"\n >\n <VChip\n tone=\"accent\"\n :size=\"chipScale.size\"\n :compact=\"chipScale.compact\"\n dismissible\n :dismiss-label=\"m.combobox.remove(labelOf(value))\"\n :disabled=\"disabled\"\n @dismiss=\"removeValue(value)\"\n >{{ labelOf(value) }}</VChip\n >\n </slot>\n </template>\n </template>\n\n <!-- The chevron sits at the end of the field and turns as the panel opens; the\n clear cross is rendered by the field itself, to its left.\n\n While loading, the spinner takes EXACTLY the chevron's place — the field\n gives a spinner among its direct children the size of an icon — so nothing\n shifts. The field's own loading option is deliberately not used: it would\n overwrite this slot, chevron and all.\n\n The spinner is hidden from screen readers, which neutralizes the status role\n it carries: what announces the loading is the panel, and once is enough. -->\n <template #end>\n <VSpinner v-if=\"loading\" class=\"v-combobox-spinner\" aria-hidden=\"true\" />\n <VIcon v-else :name=\"expandMoreIcon\" class=\"v-combobox-chevron\" aria-hidden=\"true\" />\n </template>\n </VInput>\n </div>\n\n <!-- TRAP — the panel itself carries BOTH the listbox role and the scrolling. That\n single fact is what the observer watching for the end of the list and the\n scrolling of the highlighted option both rely on: inserting any wrapper between\n them breaks the two at once. -->\n <VPopover\n :id=\"optionsId\"\n v-model:open=\"open\"\n mode=\"manual\"\n anchor=\"--combobox-anchor\"\n placement=\"bottom-start\"\n role=\"listbox\"\n class=\"v-combobox-panel v-control\"\n :data-size=\"size\"\n :data-compact=\"compact ? '' : undefined\"\n :aria-multiselectable=\"multiple ? 'true' : undefined\"\n @mousedown=\"onPanelMousedown\"\n >\n <!-- Blocks and separators exist for the eye alone: the keyboard counts through the\n flat list of surviving options and therefore never encounters one. -->\n <template v-for=\"node in rendered\" :key=\"node.key\">\n <VComboboxSeparator v-if=\"node.kind === 'separator'\" />\n\n <VComboboxGroup v-else-if=\"node.kind === 'group'\" :label=\"node.label\">\n <VComboboxOption\n v-for=\"entry in node.options\"\n :key=\"entry.key\"\n v-bind=\"rowProps(entry)\"\n @select=\"select(entry.option)\"\n @pointermove=\"hover(entry)\"\n >\n <slot name=\"option\" v-bind=\"optionSlotProps(entry)\">{{ entry.option.label }}</slot>\n </VComboboxOption>\n </VComboboxGroup>\n\n <VComboboxOption\n v-else\n v-bind=\"rowProps(node)\"\n @select=\"select(node.option)\"\n @pointermove=\"hover(node)\"\n >\n <slot name=\"option\" v-bind=\"optionSlotProps(node)\">{{ node.option.label }}</slot>\n </VComboboxOption>\n </template>\n\n <!-- The order matters: loading is checked BEFORE emptiness, so that a panel\n waiting for an answer never claims there is no result.\n\n Both are hidden from screen readers, and that is not an oversight. A listbox\n may contain nothing but options and blocks of options, so any other visible\n child would make the panel invalid — where an EMPTY listbox is perfectly\n acceptable. What they say is announced instead by the live region outside the\n panel, which is also the only way a screen reader would ever hear it: plain\n text sitting inside a combobox's popup is not part of what the field walks\n through. -->\n <div v-if=\"loading && filtered.length === 0\" class=\"v-combobox-state\" aria-hidden=\"true\">\n <slot name=\"loading\">\n <VSpinner :label=\"resolvedLoadingText\" />\n <span>{{ resolvedLoadingText }}</span>\n </slot>\n </div>\n <div v-else-if=\"filtered.length === 0\" class=\"v-combobox-state\" aria-hidden=\"true\">\n <slot name=\"empty\" :query=\"searchTerm\">{{ resolvedEmptyText }}</slot>\n </div>\n\n <!-- One element serves as both the marker watched for the end of the list and the\n place the next-page spinner appears. TRAP — it has to stay a SINGLE element:\n rendering the spinner conditionally in its place would destroy the marker and\n silently cancel the observation, and no further page would ever be asked\n for. -->\n <div v-if=\"hasMore\" ref=\"sentinelEl\" class=\"v-combobox-more\" aria-hidden=\"true\">\n <VSpinner v-if=\"loading\" :label=\"resolvedLoadingText\" />\n </div>\n </VPopover>\n\n <!-- The spoken counterpart of the two panel states above. It sits OUTSIDE the\n listbox, being exactly the kind of content a listbox may not contain. -->\n <span class=\"v-visually-hidden\" role=\"status\">{{ stateAnnouncement }}</span>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-combobox {\n /* Confines the anchor name to this instance. It is declared on the root because that\n is the common ancestor of the field and the panel — a panel drawn above the page\n remains a descendant of it in the document. */\n anchor-scope: --combobox-anchor;\n width: 100%;\n font-family: var(--vectis-text-family);\n }\n\n .v-combobox-control {\n anchor-name: --combobox-anchor;\n display: block;\n }\n\n /* The panel comes from VPopover, which brings the floating element, its open state, its\n anchoring, its placement and its surface. It also carries the shared size class, so\n the options and the state rows read their dimensions from it with no size table\n here. What stays are the two things specific to a list: a width tied to the field\n below it, and the scrolling. */\n .v-combobox-panel {\n min-inline-size: anchor-size(width);\n max-block-size: var(--vectis-control-size-combobox-list-max-block);\n overflow: auto;\n }\n\n /* The chevron and the clear cross are lifted out of the field's flow, so that they stay\n pinned to its end and vertically centred whatever the chips do and whether or not the\n input has folded away.\n\n The room they occupy is then reserved with padding, or the text and the chips would\n run underneath them. That reservation is written as exactly what the flow would have\n produced with them left in place — one glyph's width each from the field's padding,\n one gap between two of them — which is what keeps this field's spacing identical to\n every other field in the design system. It reads from the same two variables as the\n insets below, so the room reserved and the glyphs it protects cannot drift apart.\n\n Vertically they are centred with a translation kept SEPARATE from the rotation, since\n the chevron turns when the panel opens. */\n .v-combobox .v-input-field {\n position: relative;\n padding-inline-end: calc(\n var(--control-padding-inline-field) + var(--vectis-icon-size) + var(--control-gap)\n );\n }\n\n .v-combobox[data-can-clear] .v-input-field {\n padding-inline-end: calc(\n var(--control-padding-inline-field) + 2 * var(--vectis-icon-size) + 2 * var(--control-gap)\n );\n }\n\n /* The chevron and the spinner take turns in the same place and occupy the same box —\n the field gives a spinner among its direct children the size of an icon — so the\n padding reserved above serves both, and swapping one for the other shifts\n nothing. */\n .v-combobox-chevron,\n .v-combobox-spinner {\n position: absolute;\n inset-inline-end: var(--control-padding-inline-field);\n top: 50%;\n translate: 0 -50%;\n color: var(--vectis-color-text-muted);\n }\n\n .v-combobox-chevron {\n transition: rotate var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-combobox[data-open] .v-combobox-chevron {\n rotate: 180deg;\n }\n\n /* The clear cross goes just before the chevron, centred the same way.\n\n TRAP — both insets are measured to the GLYPH's edge, and they get there by different\n routes: the chevron is a bare icon, whose box IS its glyph, while the cross is one of\n the field's buttons, whose negative margin — half the difference between icon and\n button — cancels its own overhang. Because of that the arithmetic reads directly: one\n glyph's width from the field's padding, then one gap between the two.\n\n That gap is the field's own, which is the entire point: a number written here instead\n would drift from every other field in the design system, with nothing to signal\n it. */\n .v-combobox .v-input-clear {\n position: absolute;\n inset-inline-end: calc(\n var(--control-padding-inline-field) + var(--vectis-icon-size) + var(--control-gap)\n );\n top: 50%;\n translate: 0 -50%;\n }\n\n /* With several values allowed, the field holds the chips and lets them WRAP onto\n several rows, growing instead of scrolling — but never shrinking below the height of\n an ordinary control.\n\n The chips' height is written inline by the same helper that gives them their size,\n because it belongs to their own subtree and the field cannot read it. The input is\n then forced to that SAME height rather than the full height it inherits: its natural\n height is greater than a chip's, and the field would grow the moment it was focused.\n\n The result is a field that stays exactly one control tall, an input never taller than\n the chips beside it, and no jump when the focus arrives. */\n .v-combobox[data-multiple] .v-input-field {\n flex-wrap: wrap;\n height: auto;\n min-height: var(--control-height);\n padding-block: var(--vectis-space-1);\n }\n\n .v-combobox[data-multiple] .v-input-control {\n height: var(--chip-height);\n }\n\n /* Folded away, the input is taken out of the flow entirely rather than merely narrowed:\n even at zero width it would still occupy a line of its own under the chips and leave\n a visible gap.\n\n It remains in the page and remains focusable — clicking the field or tabbing into it\n brings it straight back, the folded state ending as soon as it has the focus. */\n .v-combobox[data-collapsed] .v-input-control {\n position: absolute;\n width: 0;\n height: 0;\n padding: 0;\n }\n\n /* The rows saying \"nothing matches\" or \"loading\" are built like an option: they read\n the same inherited dimensions from the panel, and the spinner follows through the\n same block's icon context. They refuse to shrink, the panel being a bounded column\n that would otherwise squash them. */\n .v-combobox-state {\n display: flex;\n flex: none;\n align-items: center;\n gap: var(--control-gap);\n min-height: var(--control-height);\n padding: var(--vectis-space-1) var(--control-padding-inline);\n font-size: var(--control-font-size);\n color: var(--vectis-color-text-muted);\n }\n\n /* The foot of the list: the marker watched for the end, and the place the next-page\n spinner appears. It is given a real height on purpose — a box of no height at all\n makes the crossing it is watched for unreliable. */\n .v-combobox-more {\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n min-height: var(--vectis-space-6);\n color: var(--vectis-color-text-muted);\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-combobox-chevron {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkFA,IAAM,KAAW,MAA8C,aAAa,GACtE,MAAe,MAAkD,eAAe,GA+DhF,IAAQ,GAgBR,IAAI,EAAY,GAChB,KAAoB,QAAe,EAAM,aAAa,EAAE,MAAM,SAAS,KAAK,GAC5E,IAAsB,QAAe,EAAM,eAAe,EAAE,MAAM,OAAO,OAAO,GAEhF,IAAO,GAqDP,IAAY,QAAe,GAAa,EAAM,MAAM,EAAM,OAAO,CAAC,GAElE,IAAQ,GAA8B,GAAA,YAAgB,GAMtD,EAAE,eAAW,eAAW,uBAAmB,EAAa,GAExD,KAAS,EAAwB,IAAI,GACrC,IAAW,EAAwC,IAAI,GAIvD,IAAY,GAAM,GAElB,IAAO,EAAI,EAAK,GAChB,IAAQ,EAAI,EAAE,GACd,IAAc,EAAI,EAAE,GACpB,IAAU,EAAI,EAAK,GAKnB,IAAQ,EAAI,EAAK,GAEjB,IAAiB,QACjB,EAAM,WAAiB,MAAM,QAAQ,EAAM,KAAK,IAAI,EAAM,QAAQ,CAAC,IAChE,OAAO,EAAM,SAAU,YAAY,EAAM,QAAQ,CAAC,EAAM,KAAK,IAAI,CAAC,CAC1E,GAMK,IAAa,QACjB,EAAM,QAAQ,SAAS,MACjB,GAAY,CAAI,IAAU,CAAC,IACxB,EAAQ,CAAI,IAAI,EAAK,UAAU,CAAC,CAAI,CAC5C,CACH,GAWM,IAAc,mBAAS,IAAI,IAA4B,CAAC;EAC9D,SAAkB;GAChB,IAAM,IAAS,IAAI,IAAI,EAAe,KAAK;GAC3C,KAAK,IAAM,KAAU,EAAW,OAC9B,AAAI,EAAO,IAAI,EAAO,KAAK,KAAG,EAAY,IAAI,EAAO,OAAO,CAAM;GAKpE,KAAK,IAAM,KAAS,EAAY,KAAK,GAAG,AAAK,EAAO,IAAI,CAAK,KAAG,EAAY,OAAO,CAAK;EAC1F,CAAC;EAWD,IAAM,KAAiB,QAAe;GACpC,IAAM,oBAAM,IAAI,IAA4B;GAC5C,KAAK,IAAM,KAAU,EAAW,OAAO,AAAK,EAAI,IAAI,EAAO,KAAK,KAAG,EAAI,IAAI,EAAO,OAAO,CAAM;GAC/F,OAAO;EACT,CAAC,GAMK,IAAc,QAAe,IAAI,IAAI,EAAe,KAAK,CAAC;EAUhE,SAAS,GAAS,GAAe;GAC/B,OAAO,GAAe,MAAM,IAAI,CAAK,KAAK,EAAY,IAAI,CAAK;EACjE;EAEA,SAAS,EAAQ,GAAe;GAC9B,OAAO,GAAS,CAAK,CAAC,EAAE,SAAS;EACnC;EAcA,IAAM,qBAAmB,IAAI,QAA+D;EAC5F,SAAS,GAAkB,GAAgC;GACzD,IAAM,IAAM,GAAiB,IAAI,CAAM;GACvC,IAAI,KAAO,EAAI,UAAU,EAAO,OAAO,OAAO,EAAI;GAClD,IAAM,IAAa,GAAc,EAAO,KAAK;GAE7C,OADA,GAAiB,IAAI,GAAQ;IAAE,OAAO,EAAO;IAAO;GAAW,CAAC,GACzD;EACT;EAMA,IAAM,IAAa,QAAgB,EAAM,QAAQ,EAAM,MAAM,KAAK,IAAI,EAAG,GAKnE,IAAW,QAAe;GAC9B,IAAM,IAAU,EAAM,QAChB,IAAO,EAAW;GACxB,IAAI,MAAY,IAAO,OAAO;GAC9B,IAAM,IAAI,EAAW;GACrB,IAAI,CAAC,GAAG,OAAO;GACf,IAAI,OAAO,KAAY,YAAY,OAAO,EAAK,QAAQ,MAAM,EAAQ,GAAG,CAAC,CAAC;GAC1E,IAAM,IAAS,GAAc,CAAC;GAC9B,OAAO,EAAK,QAAQ,MAAM,GAAkB,CAAC,CAAC,CAAC,SAAS,CAAM,CAAC;EACjE,CAAC,GASK,KAAoB,QACpB,CAAC,EAAK,SAAS,EAAS,MAAM,SAAS,IAAU,KAC9C,EAAM,UAAU,EAAoB,QAAQ,GAAkB,KACtE,GAWK,KAAW,QAA+B;GAC9C,IAAM,oBAAU,IAAI,IAAoB;GACxC,EAAS,MAAM,SAAS,GAAQ,MAAU,EAAQ,IAAI,EAAO,OAAO,CAAK,CAAC;GAE1E,IAAM,KAAW,MAAkD;IACjE,IAAM,IAAQ,EAAQ,IAAI,EAAO,KAAK;IAEtC,OADI,MAAU,KAAA,IAAkB,OACzB;KAAE,MAAM;KAAU,KAAK,UAAU,EAAO;KAAS;KAAQ;IAAM;GACxE,GAEM,IAAwB,CAAC,GAI3B,IAAkC;GAEtC,KAAK,IAAM,CAAC,GAAG,MAAS,EAAM,QAAQ,QAAQ,GAAG;IAC/C,IAAI,GAAY,CAAI,GAAG;KACrB,AAAI,EAAM,SAAS,MAAG,IAAmB,OAAO;KAChD;IACF;IAEA,IAAI;IACJ,IAAI,EAAQ,CAAI,GAAG;KACjB,IAAM,IAAU,EAAK,QAClB,IAAI,CAAO,CAAA,CACX,QAAQ,MAAmC,MAAU,IAAI;KAG5D,IACE,EAAQ,SAAS,IAAI;MAAE,MAAM;MAAS,KAAK,SAAS;MAAK,OAAO,EAAK;MAAO;KAAQ,IAAI;IAC5F,OACE,IAAO,EAAQ,CAAI;IAEhB,MAED,MAAqB,SACvB,EAAM,KAAK;KAAE,MAAM;KAAa,KAAK;IAAiB,CAAC,GACvD,IAAmB,OAErB,EAAM,KAAK,CAAI;GACjB;GAEA,OAAO;EACT,CAAC,GAOK,KAAc,GAAS,GAIzB,IAEE,IAAe,GAAY;EAEjC,SAAS,GAAW,GAAc,IAAY,IAAO;GACnD,EAAa,GACT,MAAS,MACb,GAAY,YACJ;IAGC,EAAK,UACV,KAAc,GACd,EAAK,UAAU,CAAI;GACrB,GACA,IAAY,IAAI,EAAM,cACxB;EACF;EA0CA,AAxCA,EAAM,IAAa,MAAS;GACrB,EAAK,UAUV,EAAY,QAAQ,EAAS,MAAM,WAAW,MAAM,CAAC,EAAE,QAAQ,GAC/D,GAAW,CAAI;EACjB,CAAC,GAED,EAAM,IAAW,MAAS;GAMnB,EAAK,UACN,EAAY,QAAQ,KAAK,EAAY,SAAS,EAAK,YACrD,EAAY,QAAQ,EAAK,WAAW,MAAM,CAAC,EAAE,QAAQ;EAEzD,CAAC,GAIG,CAAC,EAAM,YAAY,OAAO,EAAM,SAAU,YAAY,EAAM,UAC9D,EAAM,QAAQ,EAAQ,EAAM,KAAK,IAUnC,EACE,SACM;GACA,EAAM,YAAY,EAAK,SAAS,EAAM,SACtC,OAAO,EAAM,SAAU,YAAY,EAAM,UAAO,EAAM,QAAQ,EAAQ,EAAM,KAAK;EACvF,GACA,EAAE,OAAO,OAAO,CAClB;EAKA,IAAM,KAAY,QACV,EAAM,YAAY,CAAC,EAAQ,SAAS,EAAe,MAAM,SAAS,CAC1E,GAMM,KAAW,QAEb,EAAM,aACN,CAAC,EAAM,aACN,EAAe,MAAM,SAAS,KAAK,EAAM,MAAM,SAAS,EAC7D,GAEM,KAAY,MAAkB,GAAG,EAAU,UAAU;EAK3D,SAAS,GAAS,GAAuB;GACvC,OAAO;IACL,IAAI,EAAS,EAAM,KAAK;IACxB,MAAM,EAAM,OAAO;IACnB,QAAQ,EAAM,UAAU,EAAY;IACpC,UAAU,EAAY,MAAM,IAAI,EAAM,OAAO,KAAK;IAClD,UAAU,EAAM,OAAO;GACzB;EACF;EAEA,SAAS,GAAgB,GAAuB;GAC9C,OAAO;IACL,QAAQ,EAAM;IACd,OAAO,EAAM;IACb,QAAQ,EAAM,UAAU,EAAY;IACpC,UAAU,EAAY,MAAM,IAAI,EAAM,OAAO,KAAK;GACpD;EACF;EAEA,SAAS,GAAM,GAAuB;GACpC,AAAK,EAAM,OAAO,aAAU,EAAY,QAAQ,EAAM;EACxD;EAOA,EAAM,IAAc,MAAU;GACxB,IAAQ,KACZ,SAAe;IAEb,SAAS,eAAe,EAAS,CAAK,CAAC,CAAC,EAAE,iBAAiB,EAAE,OAAO,UAAU,CAAC;GACjF,CAAC;EACH,CAAC;EAED,SAAS,IAAY;GACnB,IAAI,EAAM,YAAY,EAAK,OAAO;GAClC,EAAK,QAAQ;GACb,IAAM,IAAO,EAAS,OAChB,IAAc,EAAK,WAAW,MAAM,CAAC,EAAE,YAAY,EAAY,MAAM,IAAI,EAAE,KAAK,CAAC;GAKvF,AAJA,EAAY,QAAQ,KAAe,IAAI,IAAc,EAAK,WAAW,MAAM,CAAC,EAAE,QAAQ,GAItF,GAAW,EAAW,OAAO,EAAI;EACnC;EAKA,IAAM,KAAa,EAAwB,IAAI,GAEzC,KAAiB,GAAkB;GACvC;GACA,eAAe,EAAK,SAAS,EAAM,WAAW,CAAC,EAAM;GACrD,mBAAmB,EAAW,MAAM;GACpC,kBAAkB,EAAK,WAAW;EACpC,CAAC;EAED,SAAS,IAAa;GACf,EAAK,UACV,EAAa,GAGb,GAAe,MAAM,GACrB,EAAK,QAAQ,IACb,EAAY,QAAQ,IACpB,EAAM,QAAQ,IACd,EAAM,QACJ,CAAC,EAAM,YAAY,OAAO,EAAM,SAAU,YAAY,EAAM,QAAQ,EAAQ,EAAM,KAAK,IAAI;EAC/F;EAMA,IAAM,KAAa,GAAmB,IAAQ,CAAU;EAYxD,SAAS,GAAiB,GAAmB;GAC3C,EAAM,eAAe;EACvB;EAGA,SAAS,KAAU;GAEjB,AADA,EAAM,QAAQ,IACd,EAAU;EACZ;EAOA,SAAS,IAAc;GACrB,AAAI,CAAC,EAAM,YAAY,EAAM,SAAO,EAAS,OAAO,OAAO;EAC7D;EAEA,SAAS,KAAU;GAEjB,AADA,EAAQ,QAAQ,IAChB,EAAY;EACd;EAGA,SAAS,KAAiB;GACpB,EAAM,aACV,EAAS,OAAO,MAAM,GACtB,EAAU,GAGV,EAAY;EACd;EAEA,SAAS,EAAO,GAAwB;GAClC,EAAO,aAGX,EAAY,IAAI,EAAO,OAAO,CAAM,GACpC,EAAM,QAAQ,IACV,EAAM,YACR,EAAM,QAAQ,GAAY,EAAe,OAAO,EAAO,KAAK,GAC5D,EAAM,QAAQ,IACd,EAAS,OAAO,MAAM,MAEtB,EAAM,QAAQ,EAAO,OAIrB,EAAa,GAIb,EAAM,QAAQ,EAAO,OACrB,EAAK,QAAQ,IACb,EAAY,QAAQ;EAExB;EAEA,SAAS,EAAY,GAAe;GAC7B,EAAM,aACX,EAAM,QAAQ,EAAe,MAAM,QAAQ,MAAM,MAAM,CAAK,GAC5D,EAAS,OAAO,MAAM;EACxB;EAMA,SAAS,KAAU;GAGjB,AAFA,EAAM,QAAQ,EAAM,WAAW,CAAC,IAAI,IACpC,EAAM,QAAQ,IACd,EAAY,QAAQ;EACtB;EAIA,SAAS,EAAK,GAAe;GAC3B,IAAM,IAAO,EAAS;GACtB,IAAI,EAAK,WAAW,GAAG;GACvB,IAAI,IAAI,EAAY;GACpB,KAAK,IAAI,IAAO,GAAG,IAAO,EAAK,WAC7B,KAAK,IAAI,IAAQ,EAAK,UAAU,EAAK,QAChC,EAAK,EAAE,EAAE,WAFuB;GAIvC,EAAY,QAAQ;EACtB;EAMA,SAAS,GAAU,GAAsB;GACvC,QAAQ,EAAM,KAAd;IACE,KAAK;KAEH,AADA,EAAM,eAAe,GACjB,EAAK,QAAO,EAAK,CAAC,IACjB,EAAU;KACf;IACF,KAAK;KAEH,AADA,EAAM,eAAe,GACjB,EAAK,QAAO,EAAK,EAAE,IAClB,EAAU;KACf;IACF,KAAK;KACH,IAAI,EAAK,SAAS,EAAY,SAAS,GAAG;MACxC,EAAM,eAAe;MACrB,IAAM,IAAS,EAAS,MAAM,EAAY;MAC1C,AAAI,KAAQ,EAAO,CAAM;KAC3B;KACA;IACF,KAAK;KACH,EAAW;KACX;IACF,KAAK;KACH,EAAW;KACX;IACF,KAAK;KACH,IAAI,EAAM,YAAY,CAAC,EAAM,OAAO;MAClC,IAAM,IAAO,EAAe,MAAM,GAAG,EAAE;MACvC,AAAI,KAAM,EAAY,CAAI;KAC5B;KACA;GACJ;EACF;yBAIE,EA6JM,OAAA;YA5JA;GAAJ,KAAI;GACJ,OAAK,GAAA,CAAC,cACE,EAAA,EAAA,CAAS,CAAA;GAChB,OAAK,GAAA,CAAA,EAAA,iBAAsB,EAAA,MAAU,OAAM,GAAI,EAAA,EAAA,CAAS,CAAA;GACxD,aAAW,EAAA;GACX,gBAAc,EAAA,UAAO,KAAQ,KAAA;GAC7B,iBAAe,EAAA,WAAQ,KAAQ,KAAA;GAC/B,kBAAgB,GAAA,QAAS,KAAQ,KAAA;GACjC,aAAW,EAAA,QAAI,KAAQ,KAAA;GACvB,kBAAgB,GAAA,QAAQ,KAAQ,KAAA;GAChC,YAAQ,AAAA,EAAA,QAAA,GAAA,MAAE,EAAA,EAAA,KAAA,EAAA,EAAA,CAAA,CAAA,GAAA,CAAA;;GAEX,EAiEM,OAAA;IAjED,OAAM;IAAsB,SAAO;OACtC,EA+DS,IA/DT,EA+DS;aA9DH;IAAJ,KAAI;gBACK,EAAA;6CAAK,QAAA;IACd,MAAK;IACL,iBAAc;IACd,qBAAkB;IACjB,iBAAe,EAAA;IACf,iBAAe,EAAA,CAAA;MACR,EAAA,EAAA,GAAc;IACrB,MAAM,EAAA;IACN,SAAS,EAAA;IACT,SAAS,EAAA;IACT,UAAU,EAAA;IACV,WAAW,EAAA;IACX,iBAAe,GAAA;IACf,eAAa,EAAA,CAAA,CAAC,CAAC,SAAS;IACxB,aAAa,EAAA,MAAe,WAAM,IAAS,EAAA,cAAc,KAAA;IACzD,yBAAuB,EAAA,SAAQ,EAAA,SAAW,IAAQ,EAAS,EAAA,KAAW,IAAI,KAAA;IACnE;IACE;IACF;IACP,QAAI,AAAA,EAAA,QAAA,MAAE,EAAA,QAAO;IACN;;IAqCG,KAAG,QAC6D,CAAzD,EAAA,WAAA,EAAA,GAAhB,EAAyE,GAAA;;KAAhD,OAAM;KAAqB,eAAY;gBAChE,EAAqF,GAAA;;KAAtE,MAAM,EAAA,CAAA;KAAgB,OAAM;KAAqB,eAAY;;;OArC9D,EAAA,WAAA;UAAW;gBACgB,EAAA,EAAA,EAAA,GAAzC,EAqBW,GAAA,MAAA,EArBe,EAAA,QAAT,MACf,EAmBO,EAAA,QAAA,QAAA;KAjBG;KACP,QAAQ,GAAS,CAAK;KACtB,OAAO,EAAQ,CAAK;KACpB,cAAc,EAAY,CAAK;KAC/B,MAAM,EAAA,MAAU;KAChB,SAAS,EAAA,MAAU;aAYf,CAVL,EASC,IAAA;KARC,MAAK;KACJ,MAAM,EAAA,MAAU;KAChB,SAAS,EAAA,MAAU;KACpB,aAAA;KACC,iBAAe,EAAA,CAAA,CAAC,CAAC,SAAS,OAAO,EAAQ,CAAK,CAAA;KAC9C,UAAU,EAAA;KACV,YAAO,MAAE,EAAY,CAAK;;sBACN,CAAA,EAAA,EAAjB,EAAQ,CAAK,CAAA,GAAA,CAAA,CAAA,CAAA;;;;;;;;iBAlByB,CAAK;;;;;;;;;;;;;;;;GA6C3D,EAoEW,GAAA;IAnER,IAAI,EAAA,CAAA;IACG,MAAM,EAAA;uCAAI,QAAA;IAClB,MAAK;IACL,QAAO;IACP,WAAU;IACV,MAAK;IACL,OAAM;IACL,aAAW,EAAA;IACX,gBAAc,EAAA,UAAO,KAAQ,KAAA;IAC7B,wBAAsB,EAAA,WAAQ,SAAY,KAAA;IAC1C,aAAW;;qBAIsB;aAAlC,EAuBW,GAAA,MAAA,EAvBc,GAAA,QAAR,wBAAwB,EAAK,IAAA,GAAA,CAClB,EAAK,SAAI,eAAA,EAAA,GAAnC,EAAuD,IAAA,EAAA,KAAA,EAAA,CAAA,KAE5B,EAAK,SAAI,WAAA,EAAA,GAApC,EAUiB,IAAA;;MAVkC,OAAO,EAAK;;uBAE9B,EAAA,EAAA,EAAA,GAD/B,EAQkB,GAAA,MAAA,EAPA,EAAK,UAAd,YADT,EAQkB,IARlB,EAQkB,EANf,KAAK,EAAM,IAAA,GAAA,EAAA,SAAA,GAAA,GACJ,GAAS,CAAK,GAAA;OACrB,WAAM,MAAE,EAAO,EAAM,MAAM;OAC3B,gBAAW,MAAE,GAAM,CAAK;;wBAE0D,CAAnF,EAAmF,EAAA,QAAA,UAAnF,EAAmF,EAAA,SAAA,GAAA,GAAvD,GAAgB,CAAK,CAAA,SAAkC,CAAA,EAAA,EAA5B,EAAM,OAAO,KAAK,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA;;;;kCAI7E,EAOkB,IAPlB,EAOkB;;;QALR,GAAS,CAAI,GAAA;MACpB,WAAM,MAAE,EAAO,EAAK,MAAM;MAC1B,gBAAW,MAAE,GAAM,CAAI;;uBAEyD,CAAjF,EAAiF,EAAA,QAAA,UAAjF,EAAiF,EAAA,SAAA,GAAA,GAArD,GAAgB,CAAI,CAAA,SAAiC,CAAA,EAAA,EAA3B,EAAK,OAAO,KAAK,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA;;;KAchE,EAAA,WAAW,EAAA,MAAS,WAAM,KAAA,EAAA,GAArC,EAKM,OALN,IAKM,CAJJ,EAGO,EAAA,QAAA,WAAA,CAAA,SAAA,CAFL,EAAyC,GAAA,EAA9B,OAAO,EAAA,MAAmB,GAAA,MAAA,GAAA,CAAA,OAAA,CAAA,GACrC,EAAsC,QAAA,MAAA,EAA7B,EAAA,KAAmB,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,KAGhB,EAAA,MAAS,WAAM,KAAA,EAAA,GAA/B,EAEM,OAFN,IAEM,CADJ,EAAqE,EAAA,QAAA,SAAA,EAAjD,OAAO,EAAA,MAAU,SAAgC,CAAA,EAAA,EAA3B,GAAA,KAAiB,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,KAAA,EAAA,IAAA,EAAA;KAQlD,EAAA,WAAA,EAAA,GAAX,EAEM,OAAA;;eAFkB;MAAJ,KAAI;MAAa,OAAM;MAAkB,eAAY;SACvD,EAAA,WAAA,EAAA,GAAhB,EAAwD,GAAA;;MAA9B,OAAO,EAAA;;;;;;;;;;;GAMrC,EAA4E,QAA5E,IAA4E,EAA3B,GAAA,KAAiB,GAAA,CAAA"}
|
|
1
|
+
{"version":3,"file":"VCombobox.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../src/components/VCombobox/VCombobox.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @keyboard @core\n/**\n * A field typed into to search a list, and to pick one or several values from. Composed of a\n * VInput, a VPopover holding the `role=\"listbox\"` panel, and VChips for the chosen values.\n *\n * The platform offers nothing to build on — `<datalist>` can be neither styled nor made\n * multi-select — so the JS here implements the whole ARIA combobox pattern: the filtering,\n * the highlight and the selection.\n *\n * ONE decision shapes the rest: DOM focus NEVER leaves the text field. What moves under the\n * arrows is `aria-activedescendant`, which is why the panel has no keyboard of its own and\n * why this component owns the field's entire accessibility contract.\n *\n * The panel is `mode=\"manual\"` (no light dismiss) and closes on `focusout` — which works\n * only because the field is OUTSIDE the panel.\n */\n\nimport { computed, inject, nextTick, reactive, ref, useId, watch, watchEffect } from 'vue'\n\nimport VChip from '../VChip/VChip.vue'\nimport type { ChipSize } from '../VChip/VChip.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport type { IconSource } from '../VIcon/types'\nimport VInput from '../VInput/VInput.vue'\nimport VPopover from '../VPopover/VPopover.vue'\nimport VComboboxOption from './VComboboxOption.vue'\nimport VComboboxGroup from './VComboboxGroup.vue'\nimport VComboboxSeparator from './VComboboxSeparator.vue'\nimport { useInfiniteScroll } from './infiniteScroll'\nimport VSpinner from '../VSpinner/VSpinner.vue'\n\nimport { toggleValue } from '../../utils/array'\nimport type { ItemValue } from '../../types'\nimport { inputGroupKey } from '../VInput/context'\n\nimport { chipScaleFor } from '../../utils/chip'\nimport { createNormalizedCache, normalizeText } from '../../utils/text'\n\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\n\nimport { useFocusoutDismiss } from '../../composables/useFocusoutDismiss'\n\nimport { canClear } from '../../composables/useClearable'\nimport { iconStartListener } from '../../composables/useIconClickHandlers'\n\nimport { useTimer } from '../../composables/useTimer'\nimport { useMessages } from '../../i18n/state'\n\n/** One thing that can be chosen. */\nexport interface ComboboxOption {\n /**\n * What choosing it means: this is what the value holds. A number is admitted because a\n * list of options almost always comes from somewhere that keys its rows by one.\n */\n value: ItemValue\n /** What it is called on screen, and what the search matches against. */\n label: string\n /** An icon before the label: an icon name, or an explicit render. */\n icon?: IconSource\n /** Shows the option without allowing it to be chosen. */\n disabled?: boolean\n}\n\n/**\n * A named block of options — the equivalent of the grouping a native list offers. A\n * group none of whose options survive the search disappears entirely, its name\n * included.\n */\nexport interface ComboboxGroup {\n /** The name of the block. */\n label: string\n /** The options it holds. */\n options: ComboboxOption[]\n}\n\n/**\n * A rule drawn between two blocks of options. It is purely decorative, and a separator\n * the search leaves stranded — at the top, at the bottom, or against another one — is\n * simply not drawn.\n */\nexport interface ComboboxSeparator {\n separator: true\n}\n\n/** Anything the list may hold: an option, a named block, or a separator. */\nexport type ComboboxItem = ComboboxOption | ComboboxGroup | ComboboxSeparator\n\nconst isGroup = (item: ComboboxItem): item is ComboboxGroup => 'options' in item\nconst isSeparator = (item: ComboboxItem): item is ComboboxSeparator => 'separator' in item\n\n/**\n * How the list is narrowed as one types: by the component itself, not at all — when the\n * options already arrive filtered by a server — or by a rule of your own.\n */\nexport type ComboboxFilter = boolean | ((option: ComboboxOption, query: string) => boolean)\n\n/** Where the list opens relative to the field. */\nexport type ComboboxPlacement =\n 'bottom' | 'bottom-start' | 'bottom-end' | 'top' | 'top-start' | 'top-end'\n\n/** The height of the field: 32, 40 or 48 pixels. */\nexport type ComboboxSize = 'sm' | 'md' | 'lg'\n\n/**\n * How the chosen values are shown in a multiple field: one chip each, or their labels\n * joined by commas.\n */\nexport type ComboboxDisplay = 'chip' | 'text'\n\ninterface ComboboxProps {\n /**\n * What the list offers. An entry may be an option, a named block of options, or a\n * separator; a plain list of options remains perfectly valid.\n */\n options: ComboboxItem[]\n /**\n * Allows several values to be chosen, which makes the value a list and shows what has\n * been chosen inside the field, as chips or as text depending on `display`.\n */\n multiple?: boolean\n /**\n * How the chosen values are shown when several can be chosen: one dismissible chip\n * each, or their labels joined by commas on a single line, cut short with an ellipsis\n * when they run out of room. It changes nothing for a single value, which is always\n * text.\n */\n display?: ComboboxDisplay\n /**\n * How many chosen values to show before the rest are summed up as \"+X\", as chips or as\n * text alike. It applies while the search input is folded away, which is to say out of\n * focus: focused, every value comes back so it can be seen and taken back. Left out, or\n * set to 0, every value is shown. It changes nothing without `multiple`.\n */\n max?: number\n /**\n * Rephrases the \"+X\" standing for the values beyond `max`, \"+5 products\" for instance.\n * It receives the number of values being hidden.\n */\n overflowText?: (count: number) => string\n /** The label above the field, tied to it so that clicking it focuses the field. */\n label?: string\n /**\n * A line of help under the field. It is tied to the field for assistive technology,\n * so it is read out along with the label.\n */\n hint?: string\n /** The height of the field: 32, 40 or 48 pixels. */\n size?: ComboboxSize\n /** Takes 4px off the height, as everywhere else in the design system. */\n compact?: boolean\n /** What the field says while nothing is chosen and nothing has been typed. */\n placeholder?: string\n /** Makes the field unusable, greyed out through the colour tokens. */\n disabled?: boolean\n /**\n * Shows what has been chosen without letting it be changed: nothing can be typed,\n * the list never opens, the chips lose their crosses and no clear cross is offered.\n * The field stays focusable and can be copied from, which is what separates it from\n * `disabled`.\n */\n readonly?: boolean\n /** Marks the field as invalid — for a rule of your own. */\n invalid?: boolean\n /**\n * An icon inside the field, at the start — before the chips when several values can\n * be chosen. It is decorative by default and becomes a real button as soon as a\n * `@click:icon-start` listener is attached, in which case it needs `iconStartLabel`.\n */\n iconStart?: IconSource\n /** What the start icon does, in words, once it is clickable. */\n iconStartLabel?: string\n /**\n * The chevron at the end of the field, which turns as the list opens: an icon name,\n * or an explicit render. Clicking it while the list is open closes the list. It is\n * decoration all the same — the field itself is what opens the list, and Escape what\n * closes it from the keyboard — so the chevron is hidden from screen readers and takes\n * no label.\n */\n expandIcon?: IconSource\n /** Offers a cross that empties both the selection and the search. */\n clearable?: boolean\n /** What that cross does, in words. It falls back to the design system dictionary. */\n clearLabel?: string\n /** What the panel says when the search matches nothing. */\n emptyText?: string\n /**\n * How the list is narrowed as one types. Turning it off means the options already\n * arrive filtered by their source and are shown exactly as they come.\n *\n * A rule of your own receives the query as it was TYPED, merely trimmed — not the\n * accent-insensitive form used internally.\n */\n filter?: ComboboxFilter\n /**\n * How long to wait before telling the source what is being searched for, in\n * milliseconds. Zero tells it at once, which suits a source that is not a network\n * request.\n */\n searchDebounce?: number\n /**\n * Says that something is being loaded. With no option yet, the whole panel says so;\n * with options already listed, a spinner appears at the foot of the list, since what\n * is loading is then the next page. Either way the field replaces its chevron with a\n * spinner.\n */\n loading?: boolean\n /** What is said while loading, and what the spinner is announced as. */\n loadingText?: string\n /**\n * Says that there are more pages to come, which is what makes the component ask for\n * the next one as the end of the list comes into view.\n */\n hasMore?: boolean\n /** Where the list opens relative to the field. */\n placement?: ComboboxPlacement\n}\n\nconst props = withDefaults(defineProps<ComboboxProps>(), {\n multiple: false,\n display: 'chip',\n max: undefined,\n overflowText: undefined,\n label: undefined,\n hint: undefined,\n size: 'md',\n compact: false,\n placeholder: undefined,\n disabled: false,\n readonly: false,\n invalid: false,\n iconStart: undefined,\n iconStartLabel: undefined,\n expandIcon: () => expandMoreIcon,\n clearable: false,\n clearLabel: undefined,\n emptyText: undefined,\n filter: true,\n searchDebounce: 250,\n loading: false,\n loadingText: undefined,\n hasMore: false,\n placement: 'bottom-start',\n})\n\nconst m = useMessages()\nconst resolvedEmptyText = computed(() => props.emptyText ?? m.value.combobox.empty)\nconst resolvedClearLabel = computed(() => props.clearLabel ?? m.value.combobox.clear)\nconst resolvedLoadingText = computed(() => props.loadingText ?? m.value.common.loading)\n\nconst emit = defineEmits<{\n /**\n * What is being searched for, to be sent to the source. It is delayed by\n * `searchDebounce` while typing, and emitted at once when the panel opens so that a\n * first page can be loaded.\n *\n * The same term is never emitted twice in a row, so reopening the panel does not\n * repeat a request that has already been answered.\n */\n search: [query: string]\n /** The end of the list has come into view: send the next page. */\n 'load-more': []\n /** The clear cross emptied the selection and the search. */\n clear: []\n /** The start icon was clicked. Attaching this listener is what makes it a button. */\n 'click:icon-start': [event: MouseEvent]\n}>()\n\ndefineSlots<{\n /**\n * Content at the start of the field, rendered after the chips standing for the chosen\n * values rather than in their place.\n */\n start?(): unknown\n /**\n * Controls of your own inside the field, placed before the ones the field owns — the\n * clear cross and the chevron. Those two are this component's own affordance, which is\n * why there is no `#end` here: it would replace them.\n */\n 'value-end'?(): unknown\n /**\n * What a row of the list shows, in place of the plain label — a subtitle, an avatar,\n * a badge. It is told whether the row is the highlighted one and whether it is\n * already chosen, so the rendering can react to both.\n */\n option?(props: {\n option: ComboboxOption\n index: number\n active: boolean\n selected: boolean\n }): unknown\n /**\n * Replaces the chip standing for one chosen value.\n *\n * Three of the values it receives are what make it usable without regressions:\n * `remove`, without which the value could no longer be taken back, and the size and\n * density worked out to sit inside the field, which cannot be guessed from outside.\n * The option itself may be missing, if that value has never appeared among the\n * options.\n */\n chip?(props: {\n value: ItemValue\n option: ComboboxOption | undefined\n label: string\n remove: () => void\n size: ChipSize\n compact: boolean\n }): unknown\n /**\n * Replaces the \"+X\" standing for the values beyond `max`. It receives `count`, the number\n * of values being hidden, and the size and density of the chips inside the field, so that a\n * chip of your own lines up with the others.\n */\n overflow?(props: { count: number; size: ChipSize; compact: boolean }): unknown\n /** What the panel shows when nothing matches. It receives the term that was searched. */\n empty?(props: { query: string }): unknown\n /** What the panel shows while loading its first options. */\n loading?(): unknown\n}>()\n\n// A VInputGroup joins several controls into one object, so the shape of this field is the\n// row's decision rather than its own (VInput/context.ts). Everything below reads the\n// RESOLVED values and never the props: the chips, the panel and the field would otherwise\n// come out at three different scales inside the same row.\nconst group = inject(inputGroupKey, null)\nconst {\n size: resolvedSize,\n compact: resolvedCompact,\n disabled: resolvedDisabled,\n} = useControlShape(props, group)\n\n// The size, the density and the HEIGHT of the chips sitting inside the field, worked out\n// once in `utils/chip.ts` and shared with VFileInput. The height is set inline below\n// rather than restated as a table of CSS rules: it is the height the field forces on its\n// input, and deriving both from the same pair is what stops them drifting apart.\nconst chipScale = computed(() => chipScaleFor(resolvedSize.value, resolvedCompact.value))\n\n/**\n * The chosen option's `value`, or the list of them when `multiple` is set. It is an empty\n * string to begin with, and the array is never mutated in place: each change is a new one,\n * which is what wakes the consumer's binding.\n */\nconst model = defineModel<ItemValue | ItemValue[]>({ default: '' })\n\n// `class` and `style` stay on the wrapper, where a consumer expects to style the\n// component; everything else — a name above all — goes down to the text field, which is\n// the element assistive technology treats as the combobox.\ndefineOptions({ inheritAttrs: false })\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\n// Declared as this component's own event, `click:icon-start` is out of `$attrs`, so the\n// listener is relayed to the field by hand — and only when the consumer wrote one.\nconst iconStartClick = iconStartListener((event) => emit('click:icon-start', event))\n\n/** What reaches the field: the consumer's own attributes, plus that listener. */\nconst fieldAttrs = computed(() => ({ ...forwardedAttrs.value, ...iconStartClick }))\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst inputRef = ref<InstanceType<typeof VInput> | null>(null)\n// One generated identifier serves twice: as the panel's own id, which the field points\n// at, and as the prefix of every option's id. The two strings can never collide, one\n// being a prefix of longer ones.\nconst optionsId = useId()\n\nconst open = ref(false)\nconst query = ref('')\nconst activeIndex = ref(-1)\nconst focused = ref(false)\n// The text in the field serves two purposes at once: it SHOWS the chosen label when a\n// single value is picked, and it is what one searches with. This flag tells the two\n// apart — while it is false the text is merely a label and narrows nothing, so reopening\n// the panel offers the whole list again. It only becomes true once the reader types.\nconst typed = ref(false)\n\nconst selectedValues = computed<ItemValue[]>(() => {\n if (props.multiple) return Array.isArray(model.value) ? model.value : []\n // An empty string is what an emptied combobox says, and it is not a value. A NUMBER, on\n // the other hand, is a value even at zero — hence the test on the empty string alone\n // rather than on falsiness, which would drop the option keyed `0`.\n return !Array.isArray(model.value) && model.value !== '' ? [model.value] : []\n})\n\n// Every option, flattened: the blocks unwrapped, the separators dropped, in the order\n// they were given. This is the ONE reading of the options the rest of the component\n// does — the filtering, the memory of what was chosen, the labels and the paging all\n// ignore the hierarchy entirely. Only the rendering below knows about it.\nconst allOptions = computed<ComboboxOption[]>(() =>\n props.options.flatMap((item) => {\n if (isSeparator(item)) return []\n return isGroup(item) ? item.options : [item]\n }),\n)\n\n// A memory of the options that were CHOSEN. With a source that answers over the network,\n// the options only ever hold the latest results, and something already chosen is usually\n// absent from them — without this, a chip would show a raw identifier instead of a name,\n// and a custom chip would lose the option's icon.\n//\n// It is kept up to date by an effect rather than by watching the options, because the\n// effect tracks the ITERATION and therefore also notices a page appended in place. And\n// it is bounded to what is currently chosen: everything else is read from the options\n// themselves.\nconst optionCache = reactive(new Map<ItemValue, ComboboxOption>())\nwatchEffect(() => {\n const wanted = new Set(selectedValues.value)\n for (const option of allOptions.value) {\n if (wanted.has(option.value)) optionCache.set(option.value, option)\n }\n // What is no longer chosen is forgotten. Without this the memory would grow with every\n // value chosen during the session rather than with the current selection — a slow leak\n // in a long-lived multiple field fed by a paginated source.\n for (const value of optionCache.keys()) if (!wanted.has(value)) optionCache.delete(value)\n})\n\n/*\n * A lookup from value to option, built once per list of options rather than searched\n * each time. Finding an option is done several times per chip and per render — for the\n * chip itself, its label and the name of its remove button — so a linear search here\n * would cost the number of chips times the number of options, on every keystroke.\n *\n * The FIRST match wins, exactly as a search would have done, so a duplicated value keeps\n * the option that came first.\n */\nconst optionsByValue = computed(() => {\n const map = new Map<ItemValue, ComboboxOption>()\n for (const option of allOptions.value) if (!map.has(option.value)) map.set(option.value, option)\n return map\n})\n\n/**\n * The selection as a set, so that asking \"is this one chosen?\" costs nothing however\n * many are — the same device VDataTable uses for its selected rows.\n */\nconst selectedSet = computed(() => new Set(selectedValues.value))\n\n/**\n * The option a value stands for: from the current options first, and from the memory of\n * chosen ones failing that.\n *\n * TRAP — an option coming from that memory is a reactive PROXY, since making the map\n * reactive converts the objects inside it. Compare options by their value and never by\n * identity, or the two forms of the same option will not match.\n */\nfunction optionOf(value: ItemValue) {\n return optionsByValue.value.get(value) ?? optionCache.get(value)\n}\n\nfunction labelOf(value: ItemValue) {\n return optionOf(value)?.label ?? String(value)\n}\n\n/*\n * The labels in their accent-insensitive form, remembered per option. Reading the label on\n * every call is what keeps the filter reactive: flattening the options only ever touches the\n * containers, so a label changed in place would go unnoticed by anything keyed on the list.\n */\nconst normalizedOf = createNormalizedCache<ComboboxOption>()\nconst normalizedLabelOf = (option: ComboboxOption) => normalizedOf(option, option.label)\n\n// ONE search term, read both by the local filter and by what is sent to the source: the\n// list on screen and the request in flight therefore cannot describe different searches.\n// With nothing typed the term is empty, so a label merely being displayed never narrows\n// the list.\nconst searchTerm = computed(() => (typed.value ? query.value.trim() : ''))\n\n// The options that survive the search, flat and in the order they are shown. This is\n// what the keyboard counts through: blocks and separators do not appear in it, which is\n// exactly why the arrows never stop on one.\nconst filtered = computed(() => {\n const matcher = props.filter\n const list = allOptions.value\n if (matcher === false) return list\n const q = searchTerm.value\n if (!q) return list\n if (typeof matcher === 'function') return list.filter((o) => matcher(o, q))\n const needle = normalizeText(q)\n return list.filter((o) => normalizedLabelOf(o).includes(needle))\n})\n\n// @a11y\n/**\n * What is announced about the state of the panel — that nothing matches, or that\n * something is loading. It is emptied as soon as there are options to show: a region\n * that announces its changes must only ever hold what is new, or the same sentence is\n * read out again at every keystroke.\n */\nconst stateAnnouncement = computed(() => {\n if (!open.value || filtered.value.length > 0) return ''\n return props.loading ? resolvedLoadingText.value : resolvedEmptyText.value\n})\n\n// The render tree: the only place that knows about the hierarchy. Each option there\n// carries its index IN `filtered` (and not its position in the tree): ids, highlight\n// and the #option slot stay aligned on the keyboard navigation.\ntype RenderedOption = { kind: 'option'; key: string; option: ComboboxOption; index: number }\ntype RenderedNode =\n | RenderedOption\n | { kind: 'group'; key: string; label: string; options: RenderedOption[] }\n | { kind: 'separator'; key: string }\n\n//\n// TRAP — nothing here is keyed by VALUE. Two options may share one (a consumer's\n// duplicate, or `1` beside `'1'`, which a template literal spells the same), and a\n// value-keyed index hands both the same position, so both light up as active and Vue\n// patches two rows under one key. The index is COUNTED instead, walking the tree in the\n// order `allOptions` flattens it, which `filtered` keeps; the key is the position in the\n// tree, which filtering does not move.\nconst rendered = computed<RenderedNode[]>(() => {\n const kept = new Set(filtered.value)\n let next = 0\n\n const entryOf = (option: ComboboxOption, key: string): RenderedOption | null => {\n if (!kept.has(option)) return null\n return { kind: 'option', key: `option:${key}`, option, index: next++ }\n }\n\n const nodes: RenderedNode[] = []\n // A separator is held back and only drawn once something has come BEFORE it and\n // something comes AFTER it. That single rule covers all three ways the filtering can\n // strand one: at the top of the list, at the bottom, and two in a row.\n let pendingSeparator: string | null = null\n\n for (const [i, item] of props.options.entries()) {\n if (isSeparator(item)) {\n if (nodes.length > 0) pendingSeparator = `sep:${i}`\n continue\n }\n\n let node: RenderedNode | null\n if (isGroup(item)) {\n const options = item.options\n .map((option, j) => entryOf(option, `${i}.${j}`))\n .filter((entry): entry is RenderedOption => entry !== null)\n // A block none of whose options survived is dropped entirely, its name included:\n // a heading over nothing is worse than no heading.\n node =\n options.length > 0 ? { kind: 'group', key: `group:${i}`, label: item.label, options } : null\n } else {\n node = entryOf(item, String(i))\n }\n if (!node) continue\n\n if (pendingSeparator !== null) {\n nodes.push({ kind: 'separator', key: pendingSeparator })\n pendingSeparator = null\n }\n nodes.push(node)\n }\n\n return nodes\n})\n\n// Telling an outside source what is being searched for. Nothing native waits before\n// sending a request, or refrains from sending the same one twice, so both have to be\n// written. The waiting itself is delegated to `useTimer` — re-arming, a delay of zero\n// running at once, cancellation when the component goes away — and what stays here is\n// specific to this component: not repeating a term, and giving up when the panel closes.\nconst searchTimer = useTimer()\n// The last term actually sent, or nothing if none ever was. It is what stops reopening\n// the panel on an unchanged term from firing the request again — a consumer with a cache\n// of their own is of course free to answer instantly.\nlet lastEmitted: string | undefined\n\nconst cancelSearch = searchTimer.cancel\n\nfunction emitSearch(term: string, immediate = false) {\n cancelSearch()\n if (term === lastEmitted) return\n searchTimer.start(\n () => {\n // The panel may have closed while we were waiting: there is then nothing left to\n // load, and the check has to happen HERE rather than before arming the timer.\n if (!open.value) return\n lastEmitted = term\n emit('search', term)\n },\n immediate ? 0 : props.searchDebounce,\n )\n}\n\nwatch(searchTerm, (term) => {\n if (!open.value) return\n // A new search means a new list, so the highlight cannot survive it: with an outside\n // source the options are replaced wholesale, and the old position would name a\n // completely unrelated option.\n //\n // It is moved to the first available option rather than dropped altogether, and that\n // matters when the filtering is left to the source: the visible list keeps the very\n // same reference until the answer arrives, so the watcher below would never fire and\n // Enter would do nothing in the meantime. When the new list does arrive, the position\n // is still within it and simply names its first option.\n activeIndex.value = filtered.value.findIndex((o) => !o.disabled)\n emitSearch(term)\n})\n\nwatch(filtered, (list) => {\n // While the panel is open there must always be a valid option highlighted. It is moved\n // back to the first result whenever the highlighted one has fallen out of the list OR\n // there was none at all — without that second case, a search that passed through \"no\n // result\" would leave nothing highlighted, and Enter would not choose the single\n // result that came back.\n if (!open.value) return\n if (activeIndex.value < 0 || activeIndex.value >= list.length) {\n activeIndex.value = list.findIndex((o) => !o.disabled)\n }\n})\n\n// With a single value, the field shows its label as ordinary text whenever it is not\n// being edited. Read through `selectedValues`, which is what knows that a number is a\n// value even at zero: a test on the string type would leave a numeric value unlabelled.\nfunction singleLabel() {\n const value = props.multiple ? undefined : selectedValues.value[0]\n return value === undefined ? '' : labelOf(value)\n}\n\nquery.value = singleLabel()\n\n// That label is a COPY and not something derived, so it has to be refreshed when the\n// options arrive later — otherwise a field mounted with a value but no options yet would\n// show a raw identifier for ever.\n//\n// ONLY the options are watched. Watching the value as well would bring back the\n// premature read that selecting deliberately avoids, and the two guards below are what\n// keep the refresh from ever overwriting something being typed.\nwatch(\n allOptions,\n () => {\n if (props.multiple || open.value || typed.value) return\n if (selectedValues.value.length) query.value = singleLabel()\n },\n { flush: 'post' },\n)\n\n/** The chosen values are spelled out as one line of text rather than drawn as chips. */\nconst textDisplay = computed(() => props.multiple && props.display === 'text')\n\n// With several values chosen and the field unfocused, the search input folds away so the\n// chips are not followed by an empty gap. It stays in the page and stays focusable —\n// only its width goes.\n//\n// A read-only TEXT display keeps it folded under the focus too: nothing can be typed, and\n// the input's share of the row would only cut the line of values short for nothing.\nconst collapsed = computed(\n () =>\n props.multiple &&\n selectedValues.value.length > 0 &&\n (!focused.value || (textDisplay.value && props.readonly)),\n)\n\n// `max` holds only while the field is folded. Unfolded, the reader is working on the\n// selection, and every value must be there to be seen and taken back — Backspace included,\n// which would otherwise remove a value hidden behind the \"+X\". `max: 0` means no limit, as\n// on VAvatarGroup: a truthiness test, never `!= null`, which would hide every value.\nconst visibleValues = computed(() =>\n collapsed.value && props.max ? selectedValues.value.slice(0, props.max) : selectedValues.value,\n)\nconst overflowCount = computed(() => selectedValues.value.length - visibleValues.value.length)\n\n// Digits and a plus sign stay out of the dictionary, as VAvatarGroup's \"+N\" does: rephrasing\n// goes through `overflowText` or the `#overflow` slot.\nconst resolvedOverflowText = computed(() =>\n props.overflowText ? props.overflowText(overflowCount.value) : `+${overflowCount.value}`,\n)\n\n// The comma is universal punctuation rather than a word, so it stays out of the dictionary,\n// as in VFileInput's own text display.\nconst displayText = computed(() =>\n textDisplay.value ? visibleValues.value.map(labelOf).join(', ') : '',\n)\n\n// The clear cross has to appear as soon as there is SOMETHING to clear — chosen values,\n// or a search in progress — and not merely when the text field holds text. The field\n// cannot work that out for itself, since the chips live outside its value, so the answer\n// is given to it explicitly, and the room for the cross is reserved accordingly.\nconst clearVisible = computed(() =>\n canClear(\n props,\n resolvedDisabled.value,\n selectedValues.value.length > 0 || query.value.length > 0,\n ),\n)\n\nconst optionId = (index: number) => `${optionsId}-option-${index}`\n\n/**\n * A rendered option with its state worked out, ONCE per row: `row` is what the option\n * element takes, `slot` what the `#option` slot receives, and both read the same two answers.\n */\ntype OptionRow = RenderedOption & {\n row: { id: string; icon?: IconSource; active: boolean; selected: boolean; disabled?: boolean }\n slot: { option: ComboboxOption; index: number; active: boolean; selected: boolean }\n}\ntype RowNode =\n | OptionRow\n | { kind: 'group'; key: string; label: string; options: OptionRow[] }\n | { kind: 'separator'; key: string }\n\nfunction withState(entry: RenderedOption): OptionRow {\n const active = entry.index === activeIndex.value\n const selected = selectedSet.value.has(entry.option.value)\n return {\n ...entry,\n row: {\n id: optionId(entry.index),\n icon: entry.option.icon,\n active,\n selected,\n disabled: entry.option.disabled,\n },\n slot: { option: entry.option, index: entry.index, active, selected },\n }\n}\n\n// A row has to be written twice in the template, once inside a block and once at the top\n// level of the panel, a Vue template having no way to declare a reusable fragment. Kept\n// apart from `rendered` on purpose: the highlight moves on every arrow key, and only this\n// cheap pass over the tree follows it — the filtering and the grouping stay where they are.\nconst rows = computed<RowNode[]>(() =>\n rendered.value.map((node) => {\n if (node.kind === 'option') return withState(node)\n if (node.kind === 'group') return { ...node, options: node.options.map(withState) }\n return node\n }),\n)\n\nfunction hover(entry: RenderedOption) {\n if (!entry.option.disabled) activeIndex.value = entry.index\n}\n\n// @a11y\n// Since the focus never leaves the field, the browser has no reason to scroll the\n// highlighted option into view — nothing was focused. It is brought into view by hand\n// instead, and asked for the SMALLEST movement that reveals it, so an option already\n// visible does not make the panel jump.\nwatch(activeIndex, (index) => {\n if (index < 0) return\n nextTick(() => {\n // Called optionally: the unit-test environment implements no scrolling at all.\n document.getElementById(optionId(index))?.scrollIntoView?.({ block: 'nearest' })\n })\n})\n\n// The single cut-off point of a frozen field: every route into the list — a click on the\n// control, the focus, a keystroke, typing — ends up here, so `readonly` is refused once\n// rather than guarded in each handler.\nfunction openPanel() {\n if (resolvedDisabled.value || props.readonly || open.value) return\n open.value = true\n const list = filtered.value\n const selectedIdx = list.findIndex((o) => !o.disabled && selectedSet.value.has(o.value))\n activeIndex.value = selectedIdx >= 0 ? selectedIdx : list.findIndex((o) => !o.disabled)\n // Told to the source AFTER the panel is marked open — the delayed emission checks that\n // flag before firing — which is what lets an outside source load its first page as the\n // panel appears.\n emitSearch(searchTerm.value, true)\n}\n\n// Asking for the next page as the end of the list is reached. The observer itself, its\n// re-arming after each page and the lock that stops it firing twice all live in\n// `./infiniteScroll`; it is declared here so that closing the panel can reset it.\nconst sentinelEl = ref<HTMLElement | null>(null)\n\nconst infiniteScroll = useInfiniteScroll({\n sentinelEl,\n canLoad: () => open.value && props.hasMore && !props.loading,\n loadedCount: () => allOptions.value.length,\n onLoadMore: () => emit('load-more'),\n})\n\nfunction closePanel() {\n if (!open.value) return\n cancelSearch()\n // A page that never arrived — a request that failed — must not leave the paging\n // frozen for good, so the lock is released here.\n infiniteScroll.reset()\n open.value = false\n activeIndex.value = -1\n typed.value = false\n query.value = singleLabel()\n}\n\n/**\n * Closes as soon as the focus leaves the component — the panel included, which is a\n * descendant of it even while floating above the page.\n */\nconst onFocusout = useFocusoutDismiss(rootEl, closePanel)\n\n// @a11y\n/*\n * TRAP — the focus must never leave the field, and this is what prevents it. Without\n * cancelling the press, clicking an option takes the focus off the field, the handler\n * above closes the panel BEFORE the click is turned into a selection, and choosing with\n * the mouse stops working entirely.\n *\n * It is invisible in the unit tests, where clicking moves no focus; a browser test\n * covers it.\n */\nfunction onPanelMousedown(event: MouseEvent) {\n event.preventDefault()\n}\n\n/** The reader is typing: the text becomes a search, and the panel opens. */\nfunction onInput() {\n typed.value = true\n openPanel()\n}\n\n/**\n * With a single value chosen, the label shown in the field is selected on focus, so that\n * typing replaces it rather than appending to it — and until something IS typed, the\n * whole list stays on offer.\n */\nfunction selectQuery() {\n if (!props.multiple && query.value) inputRef.value?.select()\n}\n\nfunction onFocus() {\n focused.value = true\n selectQuery()\n}\n\n/** Whether a pointer event landed on the chevron, or on the spinner standing in its place. */\nconst onChevron = (event: Event) =>\n event.target instanceof Element &&\n event.target.closest('.v-combobox-chevron, .v-combobox-spinner') !== null\n\n// @core\n/*\n * TRAP — the chevron is not focusable, so pressing it would hand the focus to the page:\n * the root's `focusout` then closes the panel, and the click that follows reopens it. A\n * click meant to close would flash the list instead. Keeping the focus in the field is\n * what lets the click below decide.\n */\nfunction onControlMousedown(event: MouseEvent) {\n if (onChevron(event)) event.preventDefault()\n}\n\n/**\n * A click anywhere on the field focuses it and opens the panel — except on the chevron of\n * an open panel, which closes it.\n */\nfunction onControlClick(event: MouseEvent) {\n if (resolvedDisabled.value) return\n inputRef.value?.focus()\n if (open.value && onChevron(event)) {\n closePanel()\n return\n }\n openPanel()\n // Selected again AFTER the click, which has just placed the caret somewhere in the\n // middle of the label.\n selectQuery()\n}\n\nfunction select(option: ComboboxOption) {\n if (option.disabled) return\n // Remembered immediately: the option may vanish from the list — on the next search —\n // before the parent has even passed the new value back down.\n optionCache.set(option.value, option)\n typed.value = false\n if (props.multiple) {\n model.value = toggleValue(selectedValues.value, option.value)\n query.value = ''\n inputRef.value?.focus()\n } else {\n model.value = option.value\n // This path does not go through the closing function, so the pending search has to\n // be cancelled here as well — otherwise the last keystroke would fire its request\n // after the panel had closed.\n cancelSearch()\n // TRAP — the label is taken from the option just chosen and NOT by reading the value\n // back. With a value bound by the parent, reading it immediately after writing still\n // returns the OLD one, and the field would show the previously selected label.\n query.value = option.label\n open.value = false\n activeIndex.value = -1\n }\n}\n\nfunction removeValue(value: ItemValue) {\n if (!props.multiple || props.readonly) return\n model.value = selectedValues.value.filter((v) => v !== value)\n inputRef.value?.focus()\n}\n\n/**\n * The clear cross was pressed. The field has already emptied the text it holds; what is\n * left to empty is the selection itself — the single value, or all the chips.\n */\nfunction onClear() {\n model.value = props.multiple ? [] : ''\n typed.value = false\n activeIndex.value = -1\n emit('clear')\n}\n\n// @keyboard — moves the highlight, skipping over the options that cannot be chosen and\n// wrapping round from one end of the list to the other.\nfunction move(delta: number) {\n const list = filtered.value\n if (list.length === 0) return\n let i = activeIndex.value\n for (let step = 0; step < list.length; step++) {\n i = (i + delta + list.length) % list.length\n if (!list[i]?.disabled) break\n }\n activeIndex.value = i\n}\n\n// @keyboard @a11y — the whole keyboard of the pattern. The arrows move the POINTER\n// naming the current option and never the focus itself, which stays in the field\n// throughout; Backspace on an empty field takes back the last chip, the usual gesture in\n// a field holding several values.\nfunction onKeydown(event: KeyboardEvent) {\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n if (open.value) move(1)\n else openPanel()\n break\n case 'ArrowUp':\n event.preventDefault()\n if (open.value) move(-1)\n else openPanel()\n break\n case 'Enter':\n if (open.value && activeIndex.value >= 0) {\n event.preventDefault()\n const option = filtered.value[activeIndex.value]\n if (option) select(option)\n }\n break\n case 'Escape':\n closePanel()\n break\n case 'Tab':\n closePanel()\n break\n case 'Backspace':\n if (props.multiple && !query.value) {\n const last = selectedValues.value.at(-1)\n if (last !== undefined) removeValue(last)\n }\n break\n }\n}\n\ndefineExpose({\n /** Moves the focus to the search field. */\n focus: (options?: FocusOptions) => inputRef.value?.focus(options),\n /** Selects what has been typed in the search field. */\n select: () => inputRef.value?.select(),\n /** The real `<input>` behind the field, for what neither of the two above covers. */\n el: computed(() => inputRef.value?.el ?? null),\n})\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n class=\"v-combobox\"\n :class=\"rootClass\"\n :style=\"[{ '--chip-height': chipScale.height }, rootStyle]\"\n :data-size=\"resolvedSize\"\n :data-compact=\"resolvedCompact ? '' : undefined\"\n :data-multiple=\"multiple ? '' : undefined\"\n :data-collapsed=\"collapsed ? '' : undefined\"\n :data-open=\"open ? '' : undefined\"\n @focusout=\"onFocusout\"\n >\n <div class=\"v-combobox-control\" @mousedown=\"onControlMousedown\" @click=\"onControlClick\">\n <!-- The two arrangements VInput offers a field holding chips: its end controls lifted\n out of the flow, always — the chevron and the cross stay put whether or not the\n input has folded away — and the chips wrapping, when there are chips at all. A\n text display keeps the field on one line and needs only the first. -->\n <VInput\n ref=\"inputRef\"\n v-model=\"query\"\n class=\"v-input-end-pinned\"\n :class=\"{ 'v-input-chips': multiple && !textDisplay }\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n :aria-expanded=\"open\"\n :aria-controls=\"optionsId\"\n v-bind=\"fieldAttrs\"\n :label=\"label\"\n :hint=\"hint\"\n :size=\"resolvedSize\"\n :compact=\"resolvedCompact\"\n :invalid=\"invalid\"\n :disabled=\"resolvedDisabled\"\n :readonly=\"readonly\"\n :icon-start=\"iconStart\"\n :icon-start-label=\"iconStartLabel\"\n :clearable=\"clearable\"\n :clear-visible=\"clearVisible\"\n :clear-label=\"resolvedClearLabel\"\n :placeholder=\"selectedValues.length === 0 ? placeholder : undefined\"\n :aria-activedescendant=\"open && activeIndex >= 0 ? optionId(activeIndex) : undefined\"\n @input=\"onInput\"\n @keydown=\"onKeydown\"\n @focus=\"onFocus\"\n @blur=\"focused = false\"\n @clear=\"onClear\"\n >\n <template v-if=\"multiple || $slots.start\" #start>\n <span v-if=\"displayText\" class=\"v-combobox-text\">{{ displayText }}</span>\n <!-- A box of its own beside the line rather than the end of it: the labels are what\n the ellipsis cuts, and the count of what they do not show must survive it. -->\n <span v-if=\"textDisplay && overflowCount > 0\" class=\"v-combobox-overflow\">\n <slot\n name=\"overflow\"\n :count=\"overflowCount\"\n :size=\"chipScale.size\"\n :compact=\"chipScale.compact\"\n >{{ resolvedOverflowText }}</slot\n >\n </span>\n <template v-for=\"value in multiple && !textDisplay ? visibleValues : []\" :key=\"value\">\n <slot\n name=\"chip\"\n :value=\"value\"\n :option=\"optionOf(value)\"\n :label=\"labelOf(value)\"\n :remove=\"() => removeValue(value)\"\n :size=\"chipScale.size\"\n :compact=\"chipScale.compact\"\n >\n <VChip\n tone=\"accent\"\n :size=\"chipScale.size\"\n :compact=\"chipScale.compact\"\n :dismissible=\"!readonly && !resolvedDisabled\"\n :dismiss-label=\"m.common.remove(labelOf(value))\"\n :disabled=\"resolvedDisabled\"\n @dismiss=\"removeValue(value)\"\n >{{ labelOf(value) }}</VChip\n >\n </slot>\n </template>\n <!-- Not wrapped, unlike the text display's counter: a box around an inline-flex chip\n opens a line box, whose strut makes the field taller than its chips. Neutral\n and not dismissible, so it reads as a summary rather than one more value. -->\n <slot\n v-if=\"multiple && !textDisplay && overflowCount > 0\"\n name=\"overflow\"\n :count=\"overflowCount\"\n :size=\"chipScale.size\"\n :compact=\"chipScale.compact\"\n >\n <VChip\n class=\"v-combobox-overflow-chip\"\n tone=\"neutral\"\n :size=\"chipScale.size\"\n :compact=\"chipScale.compact\"\n :disabled=\"resolvedDisabled\"\n >{{ resolvedOverflowText }}</VChip\n >\n </slot>\n <slot name=\"start\" />\n </template>\n\n <template v-if=\"$slots['value-end']\" #value-end><slot name=\"value-end\" /></template>\n\n <!-- The chevron sits at the end of the field and turns as the panel opens; the\n clear cross is rendered by the field itself, to its left.\n\n While loading, the spinner takes EXACTLY the chevron's place — the field\n gives a spinner among its direct children the size of an icon — so nothing\n shifts. The field's own loading option is deliberately not used: it would\n overwrite this slot, chevron and all.\n\n The spinner is hidden from screen readers, which neutralizes the status role\n it carries: what announces the loading is the panel, and once is enough. -->\n <template #end>\n <VSpinner v-if=\"loading\" class=\"v-combobox-spinner v-input-icon-end\" aria-hidden=\"true\" />\n <VIcon\n v-else\n v-bind=\"iconProps(expandIcon)\"\n class=\"v-combobox-chevron v-input-icon-end\"\n aria-hidden=\"true\"\n />\n </template>\n </VInput>\n </div>\n\n <!-- TRAP — the panel itself carries BOTH the listbox role and the scrolling. That\n single fact is what the observer watching for the end of the list and the\n scrolling of the highlighted option both rely on: inserting any wrapper between\n them breaks the two at once. -->\n <VPopover\n :id=\"optionsId\"\n v-model:open=\"open\"\n mode=\"manual\"\n anchor=\"--combobox-anchor\"\n match-trigger\n :placement=\"placement\"\n role=\"listbox\"\n class=\"v-combobox-panel v-control\"\n :data-size=\"resolvedSize\"\n :data-compact=\"resolvedCompact ? '' : undefined\"\n :aria-multiselectable=\"multiple ? 'true' : undefined\"\n @mousedown=\"onPanelMousedown\"\n >\n <!-- Blocks and separators exist for the eye alone: the keyboard counts through the\n flat list of surviving options and therefore never encounters one. -->\n <template v-for=\"node in rows\" :key=\"node.key\">\n <VComboboxSeparator v-if=\"node.kind === 'separator'\" />\n\n <VComboboxGroup v-else-if=\"node.kind === 'group'\" :label=\"node.label\">\n <VComboboxOption\n v-for=\"entry in node.options\"\n :key=\"entry.key\"\n v-bind=\"entry.row\"\n @select=\"select(entry.option)\"\n @pointermove=\"hover(entry)\"\n >\n <slot name=\"option\" v-bind=\"entry.slot\">{{ entry.option.label }}</slot>\n </VComboboxOption>\n </VComboboxGroup>\n\n <VComboboxOption\n v-else\n v-bind=\"node.row\"\n @select=\"select(node.option)\"\n @pointermove=\"hover(node)\"\n >\n <slot name=\"option\" v-bind=\"node.slot\">{{ node.option.label }}</slot>\n </VComboboxOption>\n </template>\n\n <!-- The order matters: loading is checked BEFORE emptiness, so that a panel\n waiting for an answer never claims there is no result.\n\n Both are hidden from screen readers, and that is not an oversight. A listbox\n may contain nothing but options and blocks of options, so any other visible\n child would make the panel invalid — where an EMPTY listbox is perfectly\n acceptable. What they say is announced instead by the live region outside the\n panel, which is also the only way a screen reader would ever hear it: plain\n text sitting inside a combobox's popup is not part of what the field walks\n through. -->\n <div v-if=\"loading && filtered.length === 0\" class=\"v-combobox-state\" aria-hidden=\"true\">\n <slot name=\"loading\">\n <VSpinner :label=\"resolvedLoadingText\" />\n <span>{{ resolvedLoadingText }}</span>\n </slot>\n </div>\n <div v-else-if=\"filtered.length === 0\" class=\"v-combobox-state\" aria-hidden=\"true\">\n <slot name=\"empty\" :query=\"searchTerm\">{{ resolvedEmptyText }}</slot>\n </div>\n\n <!-- One element serves as both the marker watched for the end of the list and the\n place the next-page spinner appears. TRAP — it has to stay a SINGLE element:\n rendering the spinner conditionally in its place would destroy the marker and\n silently cancel the observation, and no further page would ever be asked\n for. -->\n <div v-if=\"hasMore\" ref=\"sentinelEl\" class=\"v-combobox-more\" aria-hidden=\"true\">\n <VSpinner v-if=\"loading\" :label=\"resolvedLoadingText\" />\n </div>\n </VPopover>\n\n <!-- The spoken counterpart of the two panel states above. It sits OUTSIDE the\n listbox, being exactly the kind of content a listbox may not contain. -->\n <span class=\"v-visually-hidden\" role=\"status\">{{ stateAnnouncement }}</span>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-combobox {\n /* Confines the anchor name to this instance. It is declared on the root because that\n is the common ancestor of the field and the panel — a panel drawn above the page\n remains a descendant of it in the document. */\n anchor-scope: --combobox-anchor;\n width: 100%;\n font-family: var(--vectis-text-family);\n }\n\n .v-combobox-control {\n display: block;\n }\n\n /* The anchor is the FIELD's box and not this wrapper's, which also holds the label and\n the hint: anchored to the wrapper, the panel opens a hint's height below the field, and\n a label's height above it once there is no room below and `flip-block` turns it over.\n Against the field it covers whichever of the two it lands on, which is what a panel\n belonging to a control is supposed to do. */\n .v-combobox-control .v-input-field {\n anchor-name: --combobox-anchor;\n }\n\n /* The panel comes from VPopover, which brings the floating element, its open state, its\n anchoring, its placement and its surface. It also carries the shared size class, so\n the options and the state rows read their dimensions from it with no size table\n here. The width tied to the field is VPopover's `matchTrigger`, so what stays here\n is the one thing specific to a list: the scrolling. */\n .v-combobox-panel {\n max-block-size: var(--vectis-control-size-combobox-list-max-block);\n overflow: auto;\n }\n\n /* The chevron and the clear cross are pinned to the end of the field by VInput's\n `.v-input-end-pinned`, which also reserves their room. The chevron and the spinner take\n turns in the same place and occupy the same box — the field gives a spinner among its\n direct children the size of an icon — so swapping one for the other shifts nothing. */\n .v-combobox-chevron,\n .v-combobox-spinner {\n color: var(--vectis-color-text-muted);\n }\n\n .v-combobox-chevron {\n transition: rotate var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-combobox[data-open] .v-combobox-chevron {\n rotate: 180deg;\n }\n\n /* Folded away, the input is taken out of the flow entirely rather than merely narrowed:\n even at zero width it would still occupy a line of its own under the chips and leave\n a visible gap.\n\n It remains in the page and remains focusable — clicking the field or tabbing into it\n brings it straight back, the folded state ending as soon as it has the focus. */\n .v-combobox[data-collapsed] .v-input-control {\n position: absolute;\n width: 0;\n height: 0;\n padding: 0;\n }\n\n /* The chosen values as one line of text, cut short with an ellipsis. It shrinks before the\n search input does, the input being `flex: 1` with no floor of its own.\n\n Under the focus the two share the row, and the line is capped at half of it so that a\n long selection cannot squeeze what is being typed down to nothing. Folded, the input is\n out of the flow and the line takes the whole field. */\n .v-combobox-text {\n flex: 0 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .v-combobox:not([data-collapsed]) .v-combobox-text {\n max-inline-size: 50%;\n }\n\n /* The \"+X\" after a line of text never shrinks: the line gives way instead. */\n .v-combobox-overflow {\n flex: none;\n white-space: nowrap;\n color: var(--vectis-color-text-muted);\n }\n\n /* TRAP — an unbroken line has the whole of its text as its min-content width, and that\n width travels up through the field to the root. In a grid track or a flex row sized\n `auto`, the component then widens its container to fit every label instead of cutting\n them short, and the ellipsis never shows. Chips wrap, so they carry no such minimum. */\n .v-combobox:has(.v-combobox-text) {\n min-inline-size: 0;\n }\n\n /* The rows saying \"nothing matches\" or \"loading\" are built like an option: they read\n the same inherited dimensions from the panel, and the spinner follows through the\n same block's icon context. They refuse to shrink, the panel being a bounded column\n that would otherwise squash them. */\n .v-combobox-state {\n display: flex;\n flex: none;\n align-items: center;\n gap: var(--control-gap);\n min-height: var(--control-height);\n padding: var(--vectis-space-1) var(--control-padding-inline);\n font-size: var(--control-font-size);\n color: var(--vectis-color-text-muted);\n }\n\n /* The foot of the list: the marker watched for the end, and the place the next-page\n spinner appears. It is given a real height on purpose — a box of no height at all\n makes the crossing it is watched for unreliable. */\n .v-combobox-more {\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n min-height: var(--vectis-space-6);\n color: var(--vectis-color-text-muted);\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-combobox-chevron {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
@layer vectis.reset, vectis.tokens, vectis.components, vectis.utilities;
|
|
2
|
-
@layer vectis.components{.v-combobox-group{gap:var(--vectis-space-1);flex-direction:column;flex:none;display:flex}
|
|
2
|
+
@layer vectis.components{.v-combobox-group{gap:var(--vectis-space-1);flex-direction:column;flex:none;display:flex}}
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A named block of options inside a VCombobox's list. It is internal: the component
|
|
3
|
-
* renders it from the entries of its own options prop, and a consumer never writes it.
|
|
4
|
-
*
|
|
5
|
-
* A block of this kind is one of the two things a list is allowed to contain — it is the
|
|
6
|
-
* equivalent of the grouping a native list offers. Its name can be neither focused nor
|
|
7
|
-
* chosen: the keyboard counts through the flat list of options and therefore never
|
|
8
|
-
* encounters this element at all.
|
|
9
|
-
*/
|
|
10
1
|
interface ComboboxGroupProps {
|
|
11
2
|
/** The name of the block. It is a heading, not something that can be chosen. */
|
|
12
3
|
label: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VComboboxGroup.js","names":[],"sources":["../../../src/components/VCombobox/VComboboxGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\
|
|
1
|
+
{"version":3,"file":"VComboboxGroup.js","names":[],"sources":["../../../src/components/VCombobox/VComboboxGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * A named block of options inside a VCombobox's list. It is internal: the component\n * renders it from the entries of its own options prop, and a consumer never writes it.\n *\n * A block of this kind is one of the two things a list is allowed to contain — it is the\n * equivalent of the grouping a native list offers. Its name can be neither focused nor\n * chosen: the keyboard counts through the flat list of options and therefore never\n * encounters this element at all.\n */\n\nimport { useId } from 'vue'\n\ninterface ComboboxGroupProps {\n /** The name of the block. It is a heading, not something that can be chosen. */\n label: string\n}\n\ndefineProps<ComboboxGroupProps>()\n\ndefineSlots<{\n /** The options belonging to this block. */\n default(): unknown\n}>()\n\nconst labelId = useId()\n</script>\n\n<template>\n <div role=\"group\" class=\"v-combobox-group\" :aria-labelledby=\"labelId\">\n <span :id=\"labelId\" class=\"v-combobox-group-label v-group-label\">{{ label }}</span>\n <slot />\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /* It refuses to shrink, where a menu group has no need to: this panel is a column of\n bounded height that scrolls, and a block left free to shrink would be squashed to\n make its content fit — the same reason the state rows and the foot of the list refuse\n it too. */\n .v-combobox-group {\n display: flex;\n flex: none;\n flex-direction: column;\n gap: var(--vectis-space-1);\n }\n}\n</style>\n"],"mappings":""}
|
|
@@ -11,7 +11,7 @@ var c = ["aria-labelledby"], l = ["id"], u = /*@__PURE__*/ n({
|
|
|
11
11
|
"aria-labelledby": o(u)
|
|
12
12
|
}, [t("span", {
|
|
13
13
|
id: o(u),
|
|
14
|
-
class: "v-combobox-group-label"
|
|
14
|
+
class: "v-combobox-group-label v-group-label"
|
|
15
15
|
}, a(n.label), 9, l), i(s.$slots, "default")], 8, c));
|
|
16
16
|
}
|
|
17
17
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VComboboxGroup.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VCombobox/VComboboxGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\
|
|
1
|
+
{"version":3,"file":"VComboboxGroup.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VCombobox/VComboboxGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * A named block of options inside a VCombobox's list. It is internal: the component\n * renders it from the entries of its own options prop, and a consumer never writes it.\n *\n * A block of this kind is one of the two things a list is allowed to contain — it is the\n * equivalent of the grouping a native list offers. Its name can be neither focused nor\n * chosen: the keyboard counts through the flat list of options and therefore never\n * encounters this element at all.\n */\n\nimport { useId } from 'vue'\n\ninterface ComboboxGroupProps {\n /** The name of the block. It is a heading, not something that can be chosen. */\n label: string\n}\n\ndefineProps<ComboboxGroupProps>()\n\ndefineSlots<{\n /** The options belonging to this block. */\n default(): unknown\n}>()\n\nconst labelId = useId()\n</script>\n\n<template>\n <div role=\"group\" class=\"v-combobox-group\" :aria-labelledby=\"labelId\">\n <span :id=\"labelId\" class=\"v-combobox-group-label v-group-label\">{{ label }}</span>\n <slot />\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /* It refuses to shrink, where a menu group has no need to: this panel is a column of\n bounded height that scrolls, and a block left free to shrink would be squashed to\n make its content fit — the same reason the state rows and the foot of the list refuse\n it too. */\n .v-combobox-group {\n display: flex;\n flex: none;\n flex-direction: column;\n gap: var(--vectis-space-1);\n }\n}\n</style>\n"],"mappings":";;;;;;EAyBA,IAAM,IAAU,EAAM;yBAIpB,EAGM,OAAA;GAHD,MAAK;GAAQ,OAAM;GAAoB,mBAAiB,EAAA,CAAA;MAC3D,EAAmF,QAAA;GAA5E,IAAI,EAAA,CAAA;GAAS,OAAM;OAA0C,EAAA,KAAK,GAAA,GAAA,CAAA,GACzE,EAAQ,EAAA,QAAA,SAAA,CAAA,GAAA,GAAA,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
@layer vectis.reset, vectis.tokens, vectis.components, vectis.utilities;
|
|
2
|
-
@layer vectis.components{.v-combobox-option{align-items:center;gap:var(--control-gap);width:100%;min-height:var(--control-height);padding:var(--vectis-space-1) var(--control-padding-inline);color:var(--vectis-color-text);border-radius:var(--vectis-radius-
|
|
2
|
+
@layer vectis.components{.v-combobox-option{align-items:center;gap:var(--control-gap);width:100%;min-height:var(--control-height);padding:var(--vectis-space-1) var(--control-padding-inline);color:var(--vectis-color-text);border-radius:min(var(--vectis-radius-interactive), calc(var(--control-height) / 2));font-family:inherit;font-size:var(--control-font-size);line-height:var(--vectis-text-body-md-leading);text-align:start;cursor:pointer;background:0 0;border:none;display:flex}.v-combobox-option-label{flex:1;min-inline-size:0}.v-combobox-option:hover:not([aria-disabled=true]),.v-combobox-option[data-active]{background:var(--vectis-color-surface-muted);outline:none}.v-combobox-option[aria-selected=true]{color:var(--vectis-color-accent-text)}.v-combobox-option[aria-disabled=true]{color:var(--vectis-color-text-subtle);cursor:not-allowed;background:0 0}}
|
|
@@ -1,19 +1,4 @@
|
|
|
1
1
|
import { IconSource } from '../VIcon/types.js';
|
|
2
|
-
/**
|
|
3
|
-
* One row of a VCombobox's list. It is internal to that component and never exported.
|
|
4
|
-
*
|
|
5
|
-
* The focus never comes here: it stays in the text field throughout, so the highlight is
|
|
6
|
-
* told to this row rather than deduced from anything it holds. Choosing it does not close
|
|
7
|
-
* the panel either — the component above decides that.
|
|
8
|
-
*
|
|
9
|
-
* TRAP — a disabled option does NOT carry the native attribute. It has to remain in the
|
|
10
|
-
* accessibility tree the field walks through, and the native attribute would take it out
|
|
11
|
-
* of it; `aria-disabled` says the same thing while leaving it in place.
|
|
12
|
-
*
|
|
13
|
-
* Its surface is deliberately reduced to what VCombobox actually uses. The label comes
|
|
14
|
-
* through the slot rather than a prop, and there is a single icon, at the start: the end
|
|
15
|
-
* of the row is taken by the selection tick.
|
|
16
|
-
*/
|
|
17
2
|
interface ComboboxOptionProps {
|
|
18
3
|
/** An icon before the label: an icon name, or an explicit render. */
|
|
19
4
|
icon?: IconSource;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VComboboxOption.js","names":[],"sources":["../../../src/components/VCombobox/VComboboxOption.vue"],"sourcesContent":["<script setup lang=\"ts\">\
|
|
1
|
+
{"version":3,"file":"VComboboxOption.js","names":[],"sources":["../../../src/components/VCombobox/VComboboxOption.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * One row of a VCombobox's list. Internal to that component, never exported.\n *\n * Focus never comes here — it stays in the field throughout — so the highlight is TOLD to\n * this row through `active` rather than deduced from anything it holds. Choosing it does not\n * close the panel either; the component above decides that.\n *\n * TRAP — a disabled option carries `aria-disabled` and NOT the native attribute. It has to\n * stay in the accessibility tree the field walks with `aria-activedescendant`, and the\n * native attribute would take it out.\n *\n * Its surface is deliberately reduced to what VCombobox uses: the label through the slot,\n * and a single icon at the start, the end of the row being taken by the selection tick.\n */\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { check as checkIcon } from '../VIcon/icons/check'\nimport type { IconSource } from '../VIcon/types'\n\ninterface ComboboxOptionProps {\n /** An icon before the label: an icon name, or an explicit render. */\n icon?: IconSource\n /** Marks the option as one of the chosen ones, which also draws the tick. */\n selected?: boolean\n /** Marks the option as the one currently highlighted. The field decides this. */\n active?: boolean\n /** Shows the option without allowing it to be chosen. */\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<ComboboxOptionProps>(), {\n icon: undefined,\n selected: false,\n active: false,\n disabled: false,\n})\n\nconst emit = defineEmits<{\n /** The option was clicked. What that does to the selection is the field's business. */\n select: []\n}>()\n\ndefineSlots<{\n /** What the row shows. */\n default?(): unknown\n}>()\n\nfunction onClick() {\n if (props.disabled) return\n emit('select')\n}\n</script>\n\n<template>\n <button\n type=\"button\"\n role=\"option\"\n tabindex=\"-1\"\n class=\"v-combobox-option\"\n :aria-selected=\"selected\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :data-active=\"active ? '' : undefined\"\n @click=\"onClick\"\n >\n <VIcon v-if=\"icon\" v-bind=\"iconProps(icon)\" />\n <span class=\"v-combobox-option-label\"><slot /></span>\n <VIcon v-if=\"selected\" :name=\"checkIcon\" class=\"v-combobox-option-check\" />\n </button>\n</template>\n\n<style>\n@layer vectis.components {\n .v-combobox-option {\n /* Every dimension is inherited from the panel, which carries the shared size class,\n and the icons follow through the same inheritance.\n\n The type is composite, exactly as in a menu row: the SIZE comes from the scale, the\n line height stays that of body text — a unitless ratio, so it follows the size —\n and the weight stays regular, since a row may wrap. The corner is a menu row's too,\n capped at half a control height so a wrapped option keeps its neighbours' corner. */\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n width: 100%;\n min-height: var(--control-height);\n padding: var(--vectis-space-1) var(--control-padding-inline);\n border: none;\n background: transparent;\n color: var(--vectis-color-text);\n border-radius: min(var(--vectis-radius-interactive), calc(var(--control-height) / 2));\n font-family: inherit;\n font-size: var(--control-font-size);\n line-height: var(--vectis-text-body-md-leading);\n text-align: start;\n cursor: pointer;\n }\n\n /* The zero minimum is what allows a long label to be compressed at all: a flex item\n otherwise refuses to shrink below its own content. */\n .v-combobox-option-label {\n flex: 1;\n min-inline-size: 0;\n }\n\n /* A row is highlighted by the pointer resting on it, or by being the current one. There\n is deliberately no focus rule: the focus never arrives here, it stays in the field. */\n .v-combobox-option:hover:not([aria-disabled='true']),\n .v-combobox-option[data-active] {\n background: var(--vectis-color-surface-muted);\n outline: none;\n }\n\n .v-combobox-option[aria-selected='true'] {\n color: var(--vectis-color-accent-text);\n }\n\n /* Matched on the ARIA state and not on the native one, which this component\n deliberately never sets — see the introduction. */\n .v-combobox-option[aria-disabled='true'] {\n background: transparent;\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n}\n</style>\n"],"mappings":""}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import e from "../VIcon/
|
|
2
|
-
import
|
|
1
|
+
import { iconProps as e } from "../VIcon/iconProps.js";
|
|
2
|
+
import t from "../VIcon/VIcon.js";
|
|
3
3
|
import { check as n } from "../VIcon/icons/check.js";
|
|
4
4
|
import { createBlock as r, createCommentVNode as i, createElementBlock as a, createElementVNode as o, defineComponent as s, mergeProps as c, normalizeProps as l, openBlock as u, renderSlot as d, unref as f } from "vue";
|
|
5
5
|
//#region src/components/VCombobox/VComboboxOption.vue?vue&type=script&setup=true&lang.ts
|
|
@@ -40,9 +40,9 @@ var p = [
|
|
|
40
40
|
"data-active": s.active ? "" : void 0,
|
|
41
41
|
onClick: v
|
|
42
42
|
}, [
|
|
43
|
-
s.icon ? (u(), r(
|
|
43
|
+
s.icon ? (u(), r(t, l(c({ key: 0 }, f(e)(s.icon))), null, 16)) : i("", !0),
|
|
44
44
|
o("span", m, [d(h.$slots, "default")]),
|
|
45
|
-
s.selected ? (u(), r(
|
|
45
|
+
s.selected ? (u(), r(t, {
|
|
46
46
|
key: 1,
|
|
47
47
|
name: f(n),
|
|
48
48
|
class: "v-combobox-option-check"
|
package/dist/components/VCombobox/VComboboxOption.vue_vue_type_script_setup_true_lang.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VComboboxOption.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VCombobox/VComboboxOption.vue"],"sourcesContent":["<script setup lang=\"ts\">\
|
|
1
|
+
{"version":3,"file":"VComboboxOption.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VCombobox/VComboboxOption.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * One row of a VCombobox's list. Internal to that component, never exported.\n *\n * Focus never comes here — it stays in the field throughout — so the highlight is TOLD to\n * this row through `active` rather than deduced from anything it holds. Choosing it does not\n * close the panel either; the component above decides that.\n *\n * TRAP — a disabled option carries `aria-disabled` and NOT the native attribute. It has to\n * stay in the accessibility tree the field walks with `aria-activedescendant`, and the\n * native attribute would take it out.\n *\n * Its surface is deliberately reduced to what VCombobox uses: the label through the slot,\n * and a single icon at the start, the end of the row being taken by the selection tick.\n */\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { check as checkIcon } from '../VIcon/icons/check'\nimport type { IconSource } from '../VIcon/types'\n\ninterface ComboboxOptionProps {\n /** An icon before the label: an icon name, or an explicit render. */\n icon?: IconSource\n /** Marks the option as one of the chosen ones, which also draws the tick. */\n selected?: boolean\n /** Marks the option as the one currently highlighted. The field decides this. */\n active?: boolean\n /** Shows the option without allowing it to be chosen. */\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<ComboboxOptionProps>(), {\n icon: undefined,\n selected: false,\n active: false,\n disabled: false,\n})\n\nconst emit = defineEmits<{\n /** The option was clicked. What that does to the selection is the field's business. */\n select: []\n}>()\n\ndefineSlots<{\n /** What the row shows. */\n default?(): unknown\n}>()\n\nfunction onClick() {\n if (props.disabled) return\n emit('select')\n}\n</script>\n\n<template>\n <button\n type=\"button\"\n role=\"option\"\n tabindex=\"-1\"\n class=\"v-combobox-option\"\n :aria-selected=\"selected\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :data-active=\"active ? '' : undefined\"\n @click=\"onClick\"\n >\n <VIcon v-if=\"icon\" v-bind=\"iconProps(icon)\" />\n <span class=\"v-combobox-option-label\"><slot /></span>\n <VIcon v-if=\"selected\" :name=\"checkIcon\" class=\"v-combobox-option-check\" />\n </button>\n</template>\n\n<style>\n@layer vectis.components {\n .v-combobox-option {\n /* Every dimension is inherited from the panel, which carries the shared size class,\n and the icons follow through the same inheritance.\n\n The type is composite, exactly as in a menu row: the SIZE comes from the scale, the\n line height stays that of body text — a unitless ratio, so it follows the size —\n and the weight stays regular, since a row may wrap. The corner is a menu row's too,\n capped at half a control height so a wrapped option keeps its neighbours' corner. */\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n width: 100%;\n min-height: var(--control-height);\n padding: var(--vectis-space-1) var(--control-padding-inline);\n border: none;\n background: transparent;\n color: var(--vectis-color-text);\n border-radius: min(var(--vectis-radius-interactive), calc(var(--control-height) / 2));\n font-family: inherit;\n font-size: var(--control-font-size);\n line-height: var(--vectis-text-body-md-leading);\n text-align: start;\n cursor: pointer;\n }\n\n /* The zero minimum is what allows a long label to be compressed at all: a flex item\n otherwise refuses to shrink below its own content. */\n .v-combobox-option-label {\n flex: 1;\n min-inline-size: 0;\n }\n\n /* A row is highlighted by the pointer resting on it, or by being the current one. There\n is deliberately no focus rule: the focus never arrives here, it stays in the field. */\n .v-combobox-option:hover:not([aria-disabled='true']),\n .v-combobox-option[data-active] {\n background: var(--vectis-color-surface-muted);\n outline: none;\n }\n\n .v-combobox-option[aria-selected='true'] {\n color: var(--vectis-color-accent-text);\n }\n\n /* Matched on the ARIA state and not on the native one, which this component\n deliberately never sets — see the introduction. */\n .v-combobox-option[aria-disabled='true'] {\n background: transparent;\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgCA,IAAM,IAAQ,GAOR,IAAO;EAUb,SAAS,IAAU;GACb,EAAM,YACV,EAAK,QAAQ;EACf;yBAIE,EAaS,UAAA;GAZP,MAAK;GACL,MAAK;GACL,UAAS;GACT,OAAM;GACL,iBAAe,EAAA;GACf,iBAAe,EAAA,WAAQ,SAAY,KAAA;GACnC,eAAa,EAAA,SAAM,KAAQ,KAAA;GACpB;;GAEK,EAAA,QAAA,EAAA,GAAb,EAA8C,GAAA,EAAA,EAAA,EAAA,KAAA,EAAA,GAAnB,EAAA,CAAA,CAAS,CAAC,EAAA,IAAI,CAAA,CAAA,GAAA,MAAA,EAAA,KAAA,EAAA,IAAA,EAAA;GACzC,EAAqD,QAArD,GAAqD,CAAf,EAAQ,EAAA,QAAA,SAAA,CAAA,CAAA;GACjC,EAAA,YAAA,EAAA,GAAb,EAA2E,GAAA;;IAAnD,MAAM,EAAA,CAAA;IAAW,OAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VComboboxSeparator.js","names":[],"sources":["../../../src/components/VCombobox/VComboboxSeparator.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * The rule drawn between two blocks of options in a VCombobox's list.\n *\n * TRAP — it
|
|
1
|
+
{"version":3,"file":"VComboboxSeparator.js","names":[],"sources":["../../../src/components/VCombobox/VComboboxSeparator.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * The rule drawn between two blocks of options in a VCombobox's list.\n *\n * TRAP — it carries `role=\"presentation\"`, stripped of the meaning an `<hr>` normally has. A\n * separator is legitimate inside a menu, which VMenuSeparator relies on, but a `listbox` owns\n * nothing but `option` and `group`. Nothing is lost: what the rule suggests visually is\n * already said properly by the named groups around it.\n */\nimport VSeparator from '../VSeparator/VSeparator.vue'\n</script>\n\n<template>\n <VSeparator role=\"presentation\" class=\"v-combobox-separator\" />\n</template>\n\n<style>\n@layer vectis.components {\n /* The rule is bled out to the panel's edges by cancelling its padding. The selector\n compounds both classes because `.v-separator` sets `margin: 0` from a sheet of its\n own: at equal specificity the winner would be whichever of the two the consumer's\n bundler happens to put last. The space above and below comes from the panel's own\n gap, so no margin is added here. */\n .v-separator.v-combobox-separator {\n margin-inline: calc(-1 * var(--vectis-space-1));\n }\n}\n</style>\n"],"mappings":""}
|
package/dist/components/VCombobox/VComboboxSeparator.vue_vue_type_script_setup_true_lang.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VComboboxSeparator.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VCombobox/VComboboxSeparator.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * The rule drawn between two blocks of options in a VCombobox's list.\n *\n * TRAP — it
|
|
1
|
+
{"version":3,"file":"VComboboxSeparator.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VCombobox/VComboboxSeparator.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * The rule drawn between two blocks of options in a VCombobox's list.\n *\n * TRAP — it carries `role=\"presentation\"`, stripped of the meaning an `<hr>` normally has. A\n * separator is legitimate inside a menu, which VMenuSeparator relies on, but a `listbox` owns\n * nothing but `option` and `group`. Nothing is lost: what the rule suggests visually is\n * already said properly by the named groups around it.\n */\nimport VSeparator from '../VSeparator/VSeparator.vue'\n</script>\n\n<template>\n <VSeparator role=\"presentation\" class=\"v-combobox-separator\" />\n</template>\n\n<style>\n@layer vectis.components {\n /* The rule is bled out to the panel's edges by cancelling its padding. The selector\n compounds both classes because `.v-separator` sets `margin: 0` from a sheet of its\n own: at equal specificity the winner would be whichever of the two the consumer's\n bundler happens to put last. The space above and below comes from the panel's own\n gap, so no margin is added here. */\n .v-separator.v-combobox-separator {\n margin-inline: calc(-1 * var(--vectis-space-1));\n }\n}\n</style>\n"],"mappings":";;;;;;yBAaE,EAA+D,GAAA;GAAnD,MAAK;GAAe,OAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"infiniteScroll.js","names":[],"sources":["../../../src/components/VCombobox/infiniteScroll.ts"],"sourcesContent":["// @core\n/**\n * What asks VCombobox for the next page as the reader reaches the end of the list.\n *\n * Nothing in CSS can report that an element has come into view, so this is code by\n * necessity: a marker is rendered at the foot of the panel and watched, with the panel\n * itself — the box that scrolls — as the frame it is watched within.\n *\n * The module lives in the component's folder rather than among the shared ones, for the\n * usual reason: a single consumer, so it does not qualify as shared code, and VCombobox\n * owns the contract. Promote it the day a second panel paginates.\n *\n * It should not be confused with the shared observer between VTabs and VCombobox that\n * was deliberately refused: VTabs watches two markers at the ends of a row to enable or\n * disable buttons, which has nothing in common with paging a list.\n */\nimport { watch, type Ref } from 'vue'\n\nexport interface InfiniteScrollOptions {\n /** The marker at the foot of the list. It is absent when there is nothing left to load. */\n sentinelEl: Ref<HTMLElement | null>\n /** Whether a page may be asked for right now: the panel open, more to come, none in flight. */\n canLoad: () => boolean\n /** How many options are loaded. Its growth is the signal that a page has arrived. */\n loadedCount: () => number\n /** Asks for the next page. */\n onLoadMore: () => void\n}\n\nexport interface InfiniteScroll {\n /**\n * Releases the lock that stops two requests overlapping.\n *\n * It must be called wherever a page that was asked for can no longer arrive — when the\n * panel closes, above all. Without that, a request which failed would leave the paging\n * frozen for the rest of the session.\n */\n reset: () => void\n}\n\nexport function useInfiniteScroll(options: InfiniteScrollOptions): InfiniteScroll {\n let observer: IntersectionObserver | null = null\n /*\n * A lock of our own, set the instant a page is asked for. The consumer's loading flag\n * cannot serve: they raise it when their request starts, which is at best a tick\n * later, and the gap between the two is wide enough for several requests to go out.\n */\n let pending = false\n\n function onIntersect(entries: IntersectionObserverEntry[]) {\n if (!entries.some((entry) => entry.isIntersecting)) return\n if (pending || !options.canLoad()) return\n pending = true\n options.onLoadMore()\n }\n\n watch(\n options.sentinelEl,\n (el, _previous, onCleanup) => {\n // @fallback\n // The observer does not exist in the unit-test environment, so its absence has to\n // be tolerated rather than assumed away; the behaviour is checked in a real\n // browser.\n if (!el || typeof IntersectionObserver === 'undefined') return\n // TRAP — the panel must be named as the frame the marker is watched within, and it\n // is found by its ARIA role, which is public API, rather than by an internal class.\n // Left unspecified, the frame would be the VIEWPORT — and since the panel floats\n // above the page, the marker would count as visible from the very first moment,\n // firing a burst of requests for every page at once.\n const root = el.closest('[role=\"listbox\"]')\n if (!root) return\n observer = new IntersectionObserver(onIntersect, {\n root,\n // The next page is asked for half a panel before the marker is actually reached,\n // so the list is already growing by the time the reader gets there. It is\n // expressed as a proportion of the panel rather than as a number of pixels.\n rootMargin: '0px 0px 50% 0px',\n })\n observer.observe(el)\n onCleanup(() => {\n observer?.disconnect()\n observer = null\n })\n },\n { flush: 'post' },\n )\n\n // TRAP — an observer only reports a CROSSING. If the page that arrives is too short to\n // push the marker out of view, the marker stays visible without ever crossing anything\n // again, and the loading would stop dead at the second page. Watching it afresh after\n // each page forces a new answer.\n //\n // It also gives the stopping condition away for nothing: a source that returns no new\n // option leaves the count unchanged, this never runs, and the loop simply ends.\n watch(options.loadedCount, () => {\n pending = false\n const el = options.sentinelEl.value\n if (!observer || !el) return\n observer.unobserve(el)\n observer.observe(el)\n })\n\n return {\n reset: () => {\n pending = false\n },\n }\n}\n"],"mappings":";;AAwCA,SAAgB,EAAkB,GAAgD;CAChF,IAAI,IAAwC,MAMxC,IAAU;CAEd,SAAS,EAAY,GAAsC;EACpD,EAAQ,MAAM,MAAU,EAAM,cAAc,MAC7C,KAAW,CAAC,EAAQ,QAAQ,MAChC,IAAU,IACV,EAAQ,WAAW;CACrB;
|
|
1
|
+
{"version":3,"file":"infiniteScroll.js","names":[],"sources":["../../../src/components/VCombobox/infiniteScroll.ts"],"sourcesContent":["// @core\n/**\n * What asks VCombobox for the next page as the reader reaches the end of the list.\n *\n * Nothing in CSS can report that an element has come into view, so this is code by\n * necessity: a marker is rendered at the foot of the panel and watched, with the panel\n * itself — the box that scrolls — as the frame it is watched within.\n *\n * The module lives in the component's folder rather than among the shared ones, for the\n * usual reason: a single consumer, so it does not qualify as shared code, and VCombobox\n * owns the contract. Promote it the day a second panel paginates.\n *\n * It should not be confused with the shared observer between VTabs and VCombobox that\n * was deliberately refused: VTabs watches two markers at the ends of a row to enable or\n * disable buttons, which has nothing in common with paging a list.\n */\nimport { watch, type Ref } from 'vue'\n\nexport interface InfiniteScrollOptions {\n /** The marker at the foot of the list. It is absent when there is nothing left to load. */\n sentinelEl: Ref<HTMLElement | null>\n /** Whether a page may be asked for right now: the panel open, more to come, none in flight. */\n canLoad: () => boolean\n /** How many options are loaded. Its growth is the signal that a page has arrived. */\n loadedCount: () => number\n /** Asks for the next page. */\n onLoadMore: () => void\n}\n\nexport interface InfiniteScroll {\n /**\n * Releases the lock that stops two requests overlapping.\n *\n * It must be called wherever a page that was asked for can no longer arrive — when the\n * panel closes, above all. Without that, a request which failed would leave the paging\n * frozen for the rest of the session.\n */\n reset: () => void\n}\n\nexport function useInfiniteScroll(options: InfiniteScrollOptions): InfiniteScroll {\n let observer: IntersectionObserver | null = null\n /*\n * A lock of our own, set the instant a page is asked for. The consumer's loading flag\n * cannot serve: they raise it when their request starts, which is at best a tick\n * later, and the gap between the two is wide enough for several requests to go out.\n */\n let pending = false\n\n function onIntersect(entries: IntersectionObserverEntry[]) {\n if (!entries.some((entry) => entry.isIntersecting)) return\n if (pending || !options.canLoad()) return\n pending = true\n options.onLoadMore()\n }\n\n watch(\n options.sentinelEl,\n (el, _previous, onCleanup) => {\n // @fallback\n // The observer does not exist in the unit-test environment, so its absence has to\n // be tolerated rather than assumed away; the behaviour is checked in a real\n // browser.\n if (!el || typeof IntersectionObserver === 'undefined') return\n // TRAP — the panel must be named as the frame the marker is watched within, and it\n // is found by its ARIA role, which is public API, rather than by an internal class.\n // Left unspecified, the frame would be the VIEWPORT — and since the panel floats\n // above the page, the marker would count as visible from the very first moment,\n // firing a burst of requests for every page at once.\n const root = el.closest('[role=\"listbox\"]')\n if (!root) return\n observer = new IntersectionObserver(onIntersect, {\n root,\n // The next page is asked for half a panel before the marker is actually reached,\n // so the list is already growing by the time the reader gets there. It is\n // expressed as a proportion of the panel rather than as a number of pixels.\n rootMargin: '0px 0px 50% 0px',\n })\n observer.observe(el)\n onCleanup(() => {\n observer?.disconnect()\n observer = null\n })\n },\n // `post` so the sentinel and the panel it is measured against are both in the DOM: in\n // the default timing the refs are still null on the pass that mounts them, and the\n // observer is never built at all.\n { flush: 'post' },\n )\n\n // TRAP — an observer only reports a CROSSING. If the page that arrives is too short to\n // push the marker out of view, the marker stays visible without ever crossing anything\n // again, and the loading would stop dead at the second page. Watching it afresh after\n // each page forces a new answer.\n //\n // It also gives the stopping condition away for nothing: a source that returns no new\n // option leaves the count unchanged, this never runs, and the loop simply ends.\n watch(options.loadedCount, () => {\n pending = false\n const el = options.sentinelEl.value\n if (!observer || !el) return\n observer.unobserve(el)\n observer.observe(el)\n })\n\n return {\n reset: () => {\n pending = false\n },\n }\n}\n"],"mappings":";;AAwCA,SAAgB,EAAkB,GAAgD;CAChF,IAAI,IAAwC,MAMxC,IAAU;CAEd,SAAS,EAAY,GAAsC;EACpD,EAAQ,MAAM,MAAU,EAAM,cAAc,MAC7C,KAAW,CAAC,EAAQ,QAAQ,MAChC,IAAU,IACV,EAAQ,WAAW;CACrB;CAmDA,OAjDA,EACE,EAAQ,aACP,GAAI,GAAW,MAAc;EAK5B,IAAI,CAAC,KAAM,OAAO,uBAAyB,KAAa;EAMxD,IAAM,IAAO,EAAG,QAAQ,oBAAkB;EACrC,MACL,IAAW,IAAI,qBAAqB,GAAa;GAC/C;GAIA,YAAY;EACd,CAAC,GACD,EAAS,QAAQ,CAAE,GACnB,QAAgB;GAEd,AADA,GAAU,WAAW,GACrB,IAAW;EACb,CAAC;CACH,GAIA,EAAE,OAAO,OAAO,CAClB,GASA,EAAM,EAAQ,mBAAmB;EAC/B,IAAU;EACV,IAAM,IAAK,EAAQ,WAAW;EAC1B,CAAC,KAAY,CAAC,MAClB,EAAS,UAAU,CAAE,GACrB,EAAS,QAAQ,CAAE;CACrB,CAAC,GAEM,EACL,aAAa;EACX,IAAU;CACZ,EACF;AACF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
@layer vectis.reset, vectis.tokens, vectis.components, vectis.utilities;
|
|
2
|
-
@layer vectis.components{.v-table
|
|
2
|
+
@layer vectis.components{.v-data-table{--data-table-pad-block:var(--vectis-space-3);--data-table-pad-inline:var(--vectis-space-3);--data-table-head-pad-block:var(--vectis-space-2);--data-table-frame-pad:0px;font-family:var(--vectis-text-family);flex-direction:column;block-size:100%;display:flex;container-type:inline-size}.v-data-table[data-compact]{--data-table-pad-block:var(--vectis-space-2);--data-table-pad-inline:var(--vectis-space-2);--data-table-head-pad-block:var(--vectis-space-1)}.v-data-table[data-variant=outlined]{--data-table-frame-pad:var(--data-table-pad-inline);background:var(--vectis-color-surface-raised);border:1px solid var(--vectis-color-border);border-radius:var(--vectis-radius-surface);overflow:clip}.v-data-table-scroller{flex:auto;min-block-size:0;overflow:auto}.v-data-table-toolbar{justify-content:space-between;align-items:center;gap:var(--vectis-space-3);padding-block-start:var(--data-table-frame-pad);padding-block-end:var(--vectis-space-3);padding-inline:var(--data-table-frame-pad);flex-wrap:wrap;flex:none;display:flex}.v-data-table-title{--typography-color:var(--vectis-color-text)}.v-data-table-toolbar .v-input{inline-size:var(--vectis-control-size-table-search);max-inline-size:100%}.v-data-table-table{border-collapse:collapse;width:100%;font-size:var(--vectis-text-body-md-size);color:var(--vectis-color-text)}.v-data-table-caption{padding-block-end:var(--vectis-space-3);padding-inline:var(--data-table-frame-pad);text-align:start;font-size:var(--vectis-text-body-md-size);color:var(--vectis-color-text-muted)}.v-data-table-table th{padding:var(--data-table-head-pad-block) var(--data-table-pad-inline);text-align:start;font-size:var(--vectis-text-body-md-size);font-weight:var(--vectis-font-weight-semibold);color:var(--vectis-color-text-muted);background-color:var(--vectis-color-surface-muted);border-block-end:1px solid var(--vectis-color-border)}.v-data-table-table td{padding:var(--data-table-pad-block) var(--data-table-pad-inline);border-block-end:1px solid var(--vectis-color-border)}.v-data-table-table tbody tr:last-child td{border-block-end:none}.v-data-table-table [data-align=end]{text-align:end}.v-data-table-table [data-align=center]{text-align:center}.v-data-table-table .v-data-table-select{inline-size:0}.v-data-table[data-striped] tbody tr:nth-child(2n){background-color:var(--vectis-color-surface-sunken)}.v-data-table[data-selectable] tbody tr[data-selected]{background-color:var(--vectis-color-accent-surface)}.v-data-table[data-sticky-header] th{z-index:1;position:sticky;inset-block-start:0}.v-data-table-sort{--vectis-icon-size:var(--vectis-icon-size-md);--vectis-icon-opsz:20;align-items:center;gap:var(--vectis-space-1);color:inherit;font:inherit;cursor:pointer;border-radius:min(var(--vectis-radius-interactive), .5lh);background:0 0;border:none;padding:0;display:inline-flex}.v-data-table-sort:hover{color:var(--vectis-color-text)}.v-data-table-sort:focus-visible{outline:var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);outline-offset:var(--vectis-focus-ring-offset)}.v-data-table-sort-icon{opacity:.35}.v-data-table-sort[data-direction] .v-data-table-sort-icon{opacity:1}.v-data-table-state{padding:var(--vectis-space-6);text-align:center;color:var(--vectis-color-text-muted)}.v-data-table-state-loading{align-items:center;gap:var(--vectis-space-2);display:inline-flex}.v-data-table-footer{justify-content:flex-end;align-items:center;gap:var(--vectis-space-4);padding-block-start:var(--vectis-space-3);padding-block-end:var(--data-table-frame-pad);padding-inline:var(--data-table-frame-pad);flex-wrap:wrap;flex:none;display:flex}.v-data-table-footer-end{justify-content:flex-end;align-items:center;gap:var(--vectis-space-4);flex-wrap:wrap;margin-inline-start:auto;display:flex}.v-data-table-selection,.v-data-table-range,.v-data-table-per-page-label{font-size:var(--vectis-text-body-md-size);color:var(--vectis-color-text-muted)}.v-data-table-per-page{align-items:center;gap:var(--vectis-space-2);display:flex}@container (width<=640px){.v-data-table[data-responsive=stack] .v-data-table-head{clip-path:inset(50%);width:1px;height:1px;margin:-1px;position:absolute;overflow:hidden}.v-data-table[data-responsive=stack] tbody tr{padding-block:var(--vectis-space-2);border-block-end:1px solid var(--vectis-color-border);display:block}.v-data-table[data-responsive=stack] tbody tr:last-child{border-block-end:none}.v-data-table[data-responsive=stack] td{justify-content:space-between;align-items:baseline;gap:var(--vectis-space-4);padding:var(--vectis-space-1) var(--vectis-space-2);text-align:end;border:none;display:flex}.v-data-table[data-responsive=stack] td:before{content:attr(data-label);font-size:var(--vectis-text-overline-size);font-weight:var(--vectis-text-overline-weight);letter-spacing:var(--vectis-text-overline-tracking);color:var(--vectis-color-text-muted)}.v-data-table[data-responsive=stack] td.v-data-table-select{justify-content:flex-start}.v-data-table[data-responsive=stack] td.v-data-table-select:before{content:none}.v-data-table-search{flex:100%}}}
|