vectis-ui 0.14.0 → 0.15.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/dist/components/VAccordion/VAccordion.js.map +1 -1
- package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAccordion/VAccordionItem.js.map +1 -1
- package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAccordion/context.js.map +1 -1
- package/dist/components/VAlert/VAlert.js.map +1 -1
- package/dist/components/VAlert/VAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAvatar/VAvatar.js.map +1 -1
- package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAvatar/VAvatarGroup.js.map +1 -1
- package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAvatar/context.js.map +1 -1
- package/dist/components/VBadge/VBadge.js.map +1 -1
- package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VBreadcrumb/VBreadcrumb.js.map +1 -1
- package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VButton/VButton.js.map +1 -1
- package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VButton/VButtonGroup.js.map +1 -1
- package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VButton/context.js.map +1 -1
- package/dist/components/VCalendar/VCalendar.js.map +1 -1
- package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendarEvent.js.map +1 -1
- package/dist/components/VCalendar/VCalendarEvent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendarMonth.js.map +1 -1
- package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js +5 -17
- package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendarTimeGrid.js.map +1 -1
- package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js +19 -31
- package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendarYear.js.map +1 -1
- package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/color.js.map +1 -1
- package/dist/components/VCalendar/edgeStep.js +1 -5
- package/dist/components/VCalendar/edgeStep.js.map +1 -1
- package/dist/components/VCalendar/gesture.js +4 -12
- package/dist/components/VCalendar/gesture.js.map +1 -1
- package/dist/components/VCalendar/keyboard.js.map +1 -1
- package/dist/components/VCalendar/layout.js +8 -12
- package/dist/components/VCalendar/layout.js.map +1 -1
- package/dist/components/VCalendar/timeGrid.js +5 -5
- package/dist/components/VCalendar/timeGrid.js.map +1 -1
- package/dist/components/VCard/VCard.js.map +1 -1
- package/dist/components/VCard/VCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCarousel/VCarousel.js.map +1 -1
- package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- 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.js.map +1 -1
- package/dist/components/VCheckbox/VCheckMark.js.map +1 -1
- package/dist/components/VCheckbox/VCheckMark.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCheckbox/VCheckbox.js.map +1 -1
- package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VChip/VChip.js.map +1 -1
- package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VColorInput/VColorInput.js.map +1 -1
- package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js +62 -62
- package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VColorPicker/VColorPicker.js.map +1 -1
- package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js +1 -5
- package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VColorPicker/color.js.map +1 -1
- package/dist/components/VCombobox/VCombobox.js.map +1 -1
- package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +114 -122
- package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCommandPalette/VCommandPalette.js.map +1 -1
- package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js +0 -1
- package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCommandPalette/VCommandPaletteItem.js.map +1 -1
- package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js +1 -5
- package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VContextMenu/VContextMenu.js.map +1 -1
- package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js +3 -5
- package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDataTable/VDataTable.js.map +1 -1
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +1 -5
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDateInput/VDateInput.js.map +1 -1
- package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +35 -35
- package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDateInput/mask.js.map +1 -1
- package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
- package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDatePicker/keyboard.js.map +1 -1
- package/dist/components/VDatePicker/names.js.map +1 -1
- package/dist/components/VDialog/VDialog.js.map +1 -1
- package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDialog/VDialogAlert.js.map +1 -1
- package/dist/components/VDialog/VDialogAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDrawer/VDrawer.js.map +1 -1
- package/dist/components/VDrawer/VDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VEmptyState/VEmptyState.js.map +1 -1
- package/dist/components/VEmptyState/VEmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VField/VField.js.map +1 -1
- package/dist/components/VField/VField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VField/VFieldAnnouncer.js.map +1 -1
- package/dist/components/VField/VFieldAnnouncer.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VField/VFieldMessage.js.map +1 -1
- package/dist/components/VField/VFieldMessage.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VFieldset/VFieldset.js.map +1 -1
- package/dist/components/VFieldset/VFieldset.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VFileInput/VFileInput.js.map +1 -1
- package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +85 -85
- package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VFileInput/truncate.js.map +1 -1
- package/dist/components/VFilePicker/VFilePicker.js.map +1 -1
- package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VFilePicker/fileKind.js.map +1 -1
- package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
- package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VHotkeys/platform.js +1 -1
- package/dist/components/VHotkeys/platform.js.map +1 -1
- package/dist/components/VHoverCard/VHoverCard.js.map +1 -1
- package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js +2 -12
- package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VIcon/VIcon.js.map +1 -1
- 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/add.js.map +1 -1
- package/dist/components/VIcon/icons/arrow_downward.js.map +1 -1
- package/dist/components/VIcon/icons/arrow_downward_alt.js.map +1 -1
- package/dist/components/VIcon/icons/arrow_drop_down.js.map +1 -1
- package/dist/components/VIcon/icons/arrow_drop_up.js.map +1 -1
- package/dist/components/VIcon/icons/arrow_left_alt.js.map +1 -1
- package/dist/components/VIcon/icons/arrow_right_alt.js.map +1 -1
- package/dist/components/VIcon/icons/arrow_upward.js.map +1 -1
- package/dist/components/VIcon/icons/arrow_upward_alt.js.map +1 -1
- package/dist/components/VIcon/icons/attach_file.js.map +1 -1
- package/dist/components/VIcon/icons/audio_file.js.map +1 -1
- package/dist/components/VIcon/icons/calendar_today.js.map +1 -1
- package/dist/components/VIcon/icons/check.js.map +1 -1
- package/dist/components/VIcon/icons/check_circle.js.map +1 -1
- package/dist/components/VIcon/icons/chevron_left.js.map +1 -1
- package/dist/components/VIcon/icons/chevron_right.js.map +1 -1
- package/dist/components/VIcon/icons/close.js.map +1 -1
- package/dist/components/VIcon/icons/cloud_upload.js.map +1 -1
- package/dist/components/VIcon/icons/code.js.map +1 -1
- package/dist/components/VIcon/icons/colorize.js.map +1 -1
- package/dist/components/VIcon/icons/description.js.map +1 -1
- package/dist/components/VIcon/icons/error.js.map +1 -1
- package/dist/components/VIcon/icons/expand_less.js.map +1 -1
- package/dist/components/VIcon/icons/expand_more.js.map +1 -1
- package/dist/components/VIcon/icons/first_page.js.map +1 -1
- package/dist/components/VIcon/icons/folder_zip.js.map +1 -1
- package/dist/components/VIcon/icons/image.js.map +1 -1
- package/dist/components/VIcon/icons/inbox.js.map +1 -1
- package/dist/components/VIcon/icons/index.js.map +1 -1
- package/dist/components/VIcon/icons/info.js.map +1 -1
- package/dist/components/VIcon/icons/last_page.js.map +1 -1
- package/dist/components/VIcon/icons/more_horiz.js.map +1 -1
- package/dist/components/VIcon/icons/names.js.map +1 -1
- package/dist/components/VIcon/icons/notifications.js.map +1 -1
- package/dist/components/VIcon/icons/open_in_new.js.map +1 -1
- package/dist/components/VIcon/icons/picture_as_pdf.js.map +1 -1
- package/dist/components/VIcon/icons/priority_high.js.map +1 -1
- package/dist/components/VIcon/icons/remove.js.map +1 -1
- package/dist/components/VIcon/icons/schedule.js.map +1 -1
- package/dist/components/VIcon/icons/search.js.map +1 -1
- package/dist/components/VIcon/icons/search_off.js.map +1 -1
- package/dist/components/VIcon/icons/star.js.map +1 -1
- package/dist/components/VIcon/icons/swap_vert.js.map +1 -1
- package/dist/components/VIcon/icons/table_chart.js.map +1 -1
- package/dist/components/VIcon/icons/video_file.js.map +1 -1
- package/dist/components/VIcon/icons/viewBox.js.map +1 -1
- package/dist/components/VIcon/icons/warning.js.map +1 -1
- package/dist/components/VIcon/resolver.js.map +1 -1
- package/dist/components/VIconButton/VIconButton.js.map +1 -1
- package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/VInput.js.map +1 -1
- package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +20 -20
- package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/VInputGroup.js.map +1 -1
- package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/context.js.map +1 -1
- package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
- package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +2 -8
- package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VLink/VLink.js.map +1 -1
- package/dist/components/VLink/VLink.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VListbox/VListboxChevron.js.map +1 -1
- package/dist/components/VListbox/VListboxChevron.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VListbox/VListboxGroup.js.map +1 -1
- package/dist/components/VListbox/VListboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VListbox/VListboxOption.js.map +1 -1
- package/dist/components/VListbox/VListboxOption.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VListbox/VListboxPanel.js.map +1 -1
- package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VListbox/VListboxSeparator.js.map +1 -1
- package/dist/components/VListbox/VListboxSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VListbox/VListboxValues.js.map +1 -1
- package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenu.js.map +1 -1
- package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenuGroup.js.map +1 -1
- package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenuItem.js.map +1 -1
- package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js +6 -12
- package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenuPanel.js.map +1 -1
- package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +9 -10
- package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenuSeparator.js.map +1 -1
- package/dist/components/VMenu/VMenuSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/context.js.map +1 -1
- package/dist/components/VMeter/VMeter.js.map +1 -1
- package/dist/components/VMeter/VMeter.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VNumberInput/VNumberInput.js.map +1 -1
- package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js +86 -85
- package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VNumberInput/number.js.map +1 -1
- package/dist/components/VPagination/VPagination.js.map +1 -1
- package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VPopover/VPopover.js.map +1 -1
- package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VProgressCircular/VProgressCircular.js.map +1 -1
- package/dist/components/VProgressCircular/VProgressCircular.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
- package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VRadio/VRadio.js.map +1 -1
- package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VRating/VRating.js.map +1 -1
- package/dist/components/VRating/VRating.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VResizable/VResizable.js.map +1 -1
- package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js +8 -6
- package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VResizable/VResizablePanel.js.map +1 -1
- package/dist/components/VResizable/VResizablePanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VResizable/context.js.map +1 -1
- package/dist/components/VResizable/layout.js.map +1 -1
- package/dist/components/VSelect/VSelect.js.map +1 -1
- package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js +124 -125
- package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
- 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.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationGroup.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js +1 -5
- package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationSeparator.js.map +1 -1
- package/dist/components/VSideNavigation/context.js.map +1 -1
- 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.js.map +1 -1
- package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js +22 -22
- package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSnackbar/VSnackbar.js.map +1 -1
- package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSnackbar/state.js.map +1 -1
- package/dist/components/VSpinner/VSpinner.js.map +1 -1
- package/dist/components/VSpinner/VSpinner.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSplitButton/VSplitButton.js.map +1 -1
- package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VStepper/VStepper.js.map +1 -1
- package/dist/components/VStepper/VStepper.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSwitch/VSwitch.js.map +1 -1
- package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/VTab.js.map +1 -1
- package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/VTabPanel.js.map +1 -1
- package/dist/components/VTabs/VTabPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/VTabs.js.map +1 -1
- package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/context.js.map +1 -1
- package/dist/components/VTextarea/VTextarea.js.map +1 -1
- package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
- package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +45 -45
- package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeInput/search.js.map +1 -1
- package/dist/components/VTimePicker/VTimePicker.js.map +1 -1
- package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js +9 -9
- package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimePicker/limits.js.map +1 -1
- package/dist/components/VTimeline/VTimeline.js.map +1 -1
- package/dist/components/VTimeline/VTimeline.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeline/VTimelineItem.js.map +1 -1
- package/dist/components/VTimeline/VTimelineItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeline/context.js.map +1 -1
- package/dist/components/VTimeline/datetime.js.map +1 -1
- package/dist/components/VToast/VToast.js.map +1 -1
- package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToast/VToaster.js.map +1 -1
- package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToast/state.js.map +1 -1
- package/dist/components/VToggle/VToggle.js.map +1 -1
- package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js +1 -7
- package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToggle/VToggleItem.js.map +1 -1
- package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToggle/context.js.map +1 -1
- package/dist/components/VTooltip/VTooltip.js.map +1 -1
- package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTreeView/VTreeView.js.map +1 -1
- package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js +1 -5
- package/dist/components/VTreeView/VTreeView.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/components/VVirtualList/VVirtualList.js.map +1 -1
- package/dist/components/VVirtualList/VVirtualList.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/composables/useAriaLabel.js.map +1 -1
- package/dist/composables/useChoice.js.map +1 -1
- package/dist/composables/useClearable.js.map +1 -1
- package/dist/composables/useControlShape.js.map +1 -1
- package/dist/composables/useDebouncedSearch.js.map +1 -1
- package/dist/composables/useDetailsOpen.js.map +1 -1
- package/dist/composables/useEscapeDismiss.js.map +1 -1
- package/dist/composables/useFieldIds.js.map +1 -1
- package/dist/composables/useFieldPanel.js +3 -7
- package/dist/composables/useFieldPanel.js.map +1 -1
- package/dist/composables/useFileDrop.js.map +1 -1
- package/dist/composables/useFileField.js +2 -2
- package/dist/composables/useFileField.js.map +1 -1
- package/dist/composables/useFocusoutDismiss.js.map +1 -1
- package/dist/composables/useHotkeyListener.js.map +1 -1
- package/dist/composables/useIconClickHandlers.js.map +1 -1
- package/dist/composables/useInertLink.js.map +1 -1
- package/dist/composables/useInfiniteScroll.js +2 -2
- package/dist/composables/useInfiniteScroll.js.map +1 -1
- package/dist/composables/useListbox.js +3 -9
- package/dist/composables/useListbox.js.map +1 -1
- package/dist/composables/useLiveAnnouncer.js.map +1 -1
- package/dist/composables/useMaskedField.js +28 -48
- package/dist/composables/useMaskedField.js.map +1 -1
- package/dist/composables/useModalDialog.js +1 -1
- package/dist/composables/useModalDialog.js.map +1 -1
- package/dist/composables/usePopover.js +1 -1
- package/dist/composables/usePopover.js.map +1 -1
- package/dist/composables/useProgressValue.js.map +1 -1
- package/dist/composables/useRootAttrs.js.map +1 -1
- package/dist/composables/useSlotNodes.js.map +1 -1
- package/dist/composables/useTextLimit.js.map +1 -1
- package/dist/composables/useTimer.js.map +1 -1
- package/dist/composables/useTypeahead.js.map +1 -1
- package/dist/composables/useVirtualList.js.map +1 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/fr.js.map +1 -1
- package/dist/i18n/state.js.map +1 -1
- package/dist/tokens/css.js.map +1 -1
- package/dist/tokens/index.js.map +1 -1
- package/dist/tokens/primitives.js.map +1 -1
- package/dist/tokens/semantic.js.map +1 -1
- package/dist/tokens/themes/dark.js.map +1 -1
- package/dist/tokens/types.js.map +1 -1
- package/dist/utils/array.js.map +1 -1
- package/dist/utils/arrowNav.js.map +1 -1
- package/dist/utils/attrs.js.map +1 -1
- package/dist/utils/chip.js.map +1 -1
- package/dist/utils/clock.js +3 -1
- package/dist/utils/clock.js.map +1 -1
- package/dist/utils/css.js +1 -1
- package/dist/utils/css.js.map +1 -1
- package/dist/utils/date.js.map +1 -1
- package/dist/utils/direction.js.map +1 -1
- package/dist/utils/env.js.map +1 -1
- package/dist/utils/file.js +13 -31
- package/dist/utils/file.js.map +1 -1
- package/dist/utils/focus.js.map +1 -1
- package/dist/utils/hostWarns.js.map +1 -1
- package/dist/utils/ids.js.map +1 -1
- package/dist/utils/matcher.js.map +1 -1
- package/dist/utils/memo.js.map +1 -1
- package/dist/utils/number.js.map +1 -1
- package/dist/utils/text.js.map +1 -1
- package/dist/utils/time.js.map +1 -1
- package/dist/utils/vnode.js.map +1 -1
- package/package.json +19 -19
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VSlider.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VSlider/VSlider.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @core\n/**\n * Native range inputs own thumb interaction. JavaScript keeps range endpoints ordered and\n * synchronizes optional number fields, labels and ARIA values.\n */\n\nimport { computed, inject, ref, watchEffect } from 'vue'\nimport VField from '../VField/VField.vue'\nimport type { FieldControlProps, FieldLabelPosition } from '../VField/VField.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\nimport { inputGroupKey } from '../VInput/context'\nimport VNumberInput from '../VNumberInput/VNumberInput.vue'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { isDev } from '../../utils/env'\nimport { clamp } from '../../utils/number'\nimport { useMessages, useResolvedLocale } from '../../i18n/state'\n\n/**\n * What one step of the track is called: a piece of text, or an icon paired with the words that\n * name it, which a screen reader reads in place of the raw number.\n */\nexport type SliderLabel = string | { icon: IconSource; label: string }\n\n/** The value of a slider: one number, or an ordered pair of them in range mode. */\nexport type SliderValue = number | [number, number]\n\n/** Which way the track runs. */\nexport type SliderOrientation = 'horizontal' | 'vertical'\n\n/** The height of the number fields: 32, 40 or 48 pixels. */\nexport type SliderSize = 'sm' | 'md' | 'lg'\n\n/**\n * Where the number fields sit: none, at the ends of the track, or in a row above or below\n * it. On an upright slider, above and below become the two sides of it.\n */\nexport type SliderInputs = false | 'ends' | 'top' | 'bottom'\n\ninterface SliderProps {\n /** The lowest value the thumb can reach. It is 0 by default. */\n min?: number\n /** The highest value the thumb can reach. It is 100 by default. */\n max?: number\n /**\n * The gap between two values the thumb can stop on, 1 by default. It is also what the\n * arrow keys move by, and what a value typed into the companion field is snapped to.\n */\n step?: number\n /** Offers two thumbs to pick a range, which makes the v-model a pair of values. */\n range?: boolean\n /** Makes the slider unusable. */\n disabled?: boolean\n /**\n * Shows the value without allowing it to be changed. The thumbs can still be focused and\n * are announced as read-only, but neither the pointer nor the keyboard moves them, and the\n * number fields turn read-only with them.\n */\n readonly?: boolean\n /**\n * Marks the value as invalid, which colours the thumbs and tells assistive technology\n * so. It is for a rule the browser cannot check by itself.\n */\n invalid?: boolean\n /**\n * The height of the number fields `inputs` adds. Inside a VInputGroup the group's size\n * wins, as it does for every field of the row.\n */\n size?: SliderSize\n /**\n * The label above the slider, which also names its thumbs: in range mode they are announced\n * as the start and the end of it.\n */\n label?: string\n /** Hides the label visually. It still names the thumbs for assistive technology. */\n hideLabel?: boolean\n /**\n * Where the label sits: above the slider (`top`, the default), or at its start, in a column of\n * its own, moving back above when there is not room for both.\n */\n labelPosition?: FieldLabelPosition\n /**\n * A line of help under the track, stating what the numbers mean or where they may go. It\n * is tied to the slider for assistive technology, so it is read out after the name rather\n * than as part of it.\n */\n hint?: string\n /**\n * A message saying what is wrong with the value, shown in place of the hint so that the field\n * does not grow. It marks the field invalid, is read along with it, and is announced when it\n * appears or changes.\n */\n error?: string\n /** Turns the slider upright, with the lowest value at the bottom. */\n orientation?: SliderOrientation\n /**\n * Adds a number field for setting the value exactly: one, or one per end in range mode. No\n * field is drawn by default.\n */\n inputs?: SliderInputs\n /**\n * Marks each step on the track. Providing labels implies it. Past fifty steps the\n * marks would be an unreadable comb and are not drawn at all.\n */\n ticks?: boolean\n /**\n * A label for every step, in order: a piece of text, or an icon with the words that\n * name it for screen readers. They also become what a screen reader announces in\n * place of the raw number.\n */\n labels?: SliderLabel[]\n /** Shows the value in a bubble above the thumb while it is being moved or focused. */\n tooltip?: boolean\n /**\n * How the value is written in the bubble, the number fields and what the thumbs announce: the\n * options of `Intl.NumberFormat`, for a price, a unit or a percentage. `min`, `max` and `step`\n * stay in the model's unit: with `style: 'percent'`, 0.25 is written 25%.\n */\n formatOptions?: Intl.NumberFormatOptions\n /** The locale the value is written and read in. It defaults to the design system's. */\n locale?: string\n}\n\nconst props = withDefaults(defineProps<SliderProps>(), {\n min: 0,\n max: 100,\n step: 1,\n range: false,\n disabled: false,\n readonly: false,\n invalid: false,\n size: 'md',\n label: undefined,\n hideLabel: false,\n labelPosition: 'top',\n hint: undefined,\n error: undefined,\n orientation: 'horizontal',\n inputs: false,\n ticks: false,\n labels: undefined,\n tooltip: false,\n formatOptions: undefined,\n locale: undefined,\n})\n\n/** The value: a single number, 0 to begin with, or an ordered pair once `range` is set. */\nconst model = defineModel<SliderValue>({ default: 0 })\n\nconst emit = defineEmits<{\n /** The value is BEING changed: every step of a drag, and every key that moves a thumb. */\n input: [value: SliderValue]\n /**\n * The reader has SETTLED on a value: a thumb was released or moved by a key, or a number\n * field was committed.\n */\n change: [value: SliderValue]\n}>()\n\n// The number fields are fields of a VInputGroup row like any other, so the row's size and\n// its disabled state reach them through the same arbitration. A slider has no `compact` of\n// its own: only the row's can apply.\nconst group = inject(inputGroupKey, null)\nconst {\n size: resolvedSize,\n compact: resolvedCompact,\n disabled: resolvedDisabled,\n} = useControlShape(\n {\n get size() {\n return props.size\n },\n compact: false,\n get disabled() {\n return props.disabled\n },\n },\n group,\n)\n\ntype Thumb = 'start' | 'end'\n\nconst startValue = computed(() => (Array.isArray(model.value) ? model.value[0] : props.min))\nconst endValue = computed(() =>\n Array.isArray(model.value) ? model.value[1] : (model.value as number),\n)\n\nconst thumbValue = (which: Thumb) => (which === 'start' ? startValue.value : endValue.value)\n\n/**\n * Where a value sits along the run, as a fraction between 0 and 1. The guard covers a\n * slider whose two bounds are equal, which would otherwise divide by zero.\n */\nconst frac = (v: number) => clamp((v - props.min) / (props.max - props.min || 1), 0, 1)\n\n// @core\n// The thumb being held PUSHES its sibling; it must not stop against it. Two thumbs resting on\n// the same value are one thumb as far as the pointer is concerned, the end one being last in\n// the DOM and therefore the only one it can reach; so a range stopped at `[100, 100]` could\n// only ever be raised, and at the maximum it could not move at all.\nfunction writeThumb(which: Thumb, n: number): SliderValue {\n const next: SliderValue = !props.range\n ? n\n : which === 'start'\n ? [n, Math.max(n, endValue.value)]\n : [Math.min(n, startValue.value), n]\n model.value = next\n return next\n}\n\nconst writtenFor = (value: SliderValue, which: Thumb) =>\n Array.isArray(value) ? value[which === 'start' ? 0 : 1] : value\n\nfunction onThumbInput(which: Thumb, event: Event) {\n const el = event.target as HTMLInputElement\n // @core\n // A drag or a click on the track has ALREADY moved the native thumb when `input` fires,\n // and the event cannot be cancelled: putting the value back is the only refusal there is.\n // It lands before the browser paints, so the thumb never visibly leaves its place, and\n // since the value at release is the one it started from, no `change` follows either.\n if (props.readonly) {\n el.value = String(thumbValue(which))\n return\n }\n const next = writeThumb(which, Number(el.value))\n el.value = String(writtenFor(next, which))\n emit('input', next)\n}\n\n// @keyboard @core\n// The keys that move a range input, cancelled before the browser applies them when the\n// slider is read-only. Tab and every other key pass, so the thumbs stay reachable.\nconst VALUE_KEYS = new Set([\n 'ArrowUp',\n 'ArrowDown',\n 'ArrowLeft',\n 'ArrowRight',\n 'Home',\n 'End',\n 'PageUp',\n 'PageDown',\n])\n\nfunction onThumbKeydown(event: KeyboardEvent) {\n if (props.readonly && VALUE_KEYS.has(event.key)) event.preventDefault()\n}\n\n// @core\n/*\n * The settled value is read off the THUMB, never off the model. A key press fires `input` and\n * `change` back to back in one task, and under a parent `v-model` the model's local copy only\n * catches up when the parent re-renders, so it would still hold the value from before the key.\n */\nfunction onThumbChange(which: Thumb, event: Event) {\n if (props.readonly) return\n const value = Number((event.target as HTMLInputElement).value)\n if (!props.range) emit('change', value)\n else if (which === 'start') emit('change', [value, Math.max(value, endValue.value)])\n else emit('change', [Math.min(value, startValue.value), value])\n}\n\n/** How many whole steps the thumb can actually stop on. */\nconst stepCount = computed(() =>\n props.step > 0 ? Math.floor((props.max - props.min) / props.step + 1e-9) : 0,\n)\n\n/**\n * The highest value a thumb can actually stop on, which is the last whole step and not\n * necessarily the maximum: with `min=0 max=95 step=10` the native control stops at 90.\n */\nconst lastStop = computed(() =>\n props.step > 0 ? props.min + stepCount.value * props.step : props.max,\n)\n\nconst showTicks = computed(\n () =>\n (props.ticks || props.labels !== undefined) && stepCount.value >= 1 && stepCount.value <= 50,\n)\n\n/** Each step's value and the style that places it, shared by the ticks and the labels. */\nconst stepPlaces = computed(() => {\n const length = Math.max(showTicks.value ? stepCount.value + 1 : 0, props.labels?.length ?? 0)\n return Array.from({ length }, (_, i) => {\n const value = props.min + i * props.step\n return { value, style: { '--fill-fraction': String(frac(value)) } }\n })\n})\n\nconst tickPlaces = computed(() =>\n showTicks.value ? stepPlaces.value.slice(0, stepCount.value + 1) : [],\n)\n\nconst isFilled = (value: number) =>\n props.range ? value >= startValue.value && value <= endValue.value : value <= endValue.value\n\n// Resolved once here and handed to the number fields as the result, so the fields and the\n// bubble can never read the value in two different languages.\nconst resolvedLocale = useResolvedLocale(() => props.locale)\nconst numberFormat = computed(\n () => new Intl.NumberFormat(resolvedLocale.value, props.formatOptions),\n)\nconst formatValue = (value: number) => numberFormat.value.format(value)\n\n/** What a value is called, if the consumer named its step; failing that, the number written. */\nfunction labelTextAt(value: number): string {\n const item = props.labels?.[Math.round((value - props.min) / props.step)]\n if (item === undefined) return formatValue(value)\n return typeof item === 'string' ? item : item.label\n}\n\n// @a11y\n/*\n * Resolve the label against consumer ARIA attributes before deriving distinct range-thumb\n * names; an undefined prop must not erase their accessible name.\n */\nconst m = useMessages()\nconst resolvedLabel = useAriaLabel(() => props.label)\nconst startLabel = computed(() =>\n resolvedLabel.value ? m.value.slider.rangeStart(resolvedLabel.value) : m.value.slider.start,\n)\nconst endLabel = computed(() =>\n resolvedLabel.value ? m.value.slider.rangeEnd(resolvedLabel.value) : m.value.slider.end,\n)\nconst thumbEndLabel = computed(() => (props.range ? endLabel.value : resolvedLabel.value))\nconst fieldEndLabel = computed(() =>\n props.range ? endLabel.value : (resolvedLabel.value ?? m.value.slider.value),\n)\n\n// @a11y\n// Said only when it adds something to the bare number a range announces: a step label, or a\n// value written with `formatOptions` (\"120 €\" rather than \"120\").\nconst spokenText = (value: number) =>\n props.labels || props.formatOptions ? labelTextAt(value) : undefined\nconst startValueText = computed(() => (props.range ? spokenText(startValue.value) : undefined))\nconst endValueText = computed(() => spokenText(endValue.value))\n\n// @a11y\n/*\n * The wrapper-root split. The root here is a layout box holding the labels, the track and the\n * optional number fields, so a consumer's `name`, `id`, `required` and `aria-*` have to be\n * redirected onto the real `<input type=\"range\">`; left on the wrapper, a `name` submits\n * nothing and a `<label for>` points at a div.\n */\ndefineOptions({ inheritAttrs: false })\nconst { attrs, rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\n/**\n * What reaches the end thumb from VField: the consumer's attributes and the description. The\n * thumbs are named by `aria-label`, carrying the label's words, so VField's reference to the\n * label text gives way to the consumer's own `aria-labelledby`, if any. A `required` attribute\n * is VField's prop on the way, which group mode keeps for its asterisk: it is handed on here.\n */\nfunction thumbAttrs(fieldProps: FieldControlProps): FieldControlProps {\n return {\n ...fieldProps,\n 'aria-labelledby': attrs['aria-labelledby'] as string | undefined,\n required: attrs.required as true | undefined,\n }\n}\nconst isInvalid = computed(() => props.invalid || !!props.error)\n\n/**\n * Where the fields go, `ends` standing in for any other truthy value: a bare `inputs`\n * attribute reaches the component as `true`, the prop being part boolean.\n */\nconst inputsPlace = computed(() =>\n props.inputs === 'top' || props.inputs === 'bottom' ? props.inputs : props.inputs && 'ends',\n)\n\n// @devwarn\n/*\n * Every guard here describes something that fails SILENTLY. They sit in an effect, so a\n * bound that only becomes wrong on a later render is still caught, and each sentence is\n * given once per instance: the same warning repeated on every keystroke is how a warning\n * stops being read (the `useFileField` arrangement).\n */\nif (isDev) {\n // Keyed by a short id rather than by the sentence: each of these embeds a count, so the\n // same guard reported twice would read as two different messages and warn again.\n const warned = new Set<string>()\n const warn = (id: string, message: string) => {\n if (warned.has(id)) return\n warned.add(id)\n console.warn(`[VSlider] ${message}`)\n }\n watchEffect(() => {\n if (props.step <= 0)\n warn(\n 'step',\n `step=\"${props.step}\" cannot move a thumb: the arrow keys do nothing and a value typed into a field is committed as it stands. Give a positive step.`,\n )\n if ((props.ticks || props.labels) && stepCount.value > 50)\n warn(\n 'ticks',\n `${stepCount.value} steps: no tick is drawn past 50, the comb being unreadable.`,\n )\n if (props.labels && props.labels.length !== stepCount.value + 1)\n warn(\n 'labels',\n `${props.labels.length} labels for ${stepCount.value + 1} steps: one label per step expected.`,\n )\n if (props.inputs !== false && props.inputs !== inputsPlace.value)\n warn(\n 'inputs',\n `inputs=\"${String(props.inputs)}\" is not a placement: the fields are drawn at the ends. Write inputs=\"ends\", \"top\" or \"bottom\".`,\n )\n if (props.range && attrs.name !== undefined)\n warn(\n 'name',\n `name=\"${String(attrs.name)}\" on a range: only the end thumb carries it, so the form receives one value of the two. Bind the model to two inputs of your own instead.`,\n )\n })\n}\n\n// @a11y\n/*\n * The fields are rendered in the order they are SEEN, before or after the track, so that\n * the tab order follows the screen. Laid out by the grid alone from a fixed DOM order, a\n * row of fields above the track would be reached one on each side of the thumbs.\n */\nconst fieldsBefore = computed<Thumb[]>(() => {\n if (inputsPlace.value === 'ends') return props.range ? ['start'] : []\n if (inputsPlace.value === 'top') return props.range ? ['start', 'end'] : ['end']\n return []\n})\nconst fieldsAfter = computed<Thumb[]>(() => {\n if (inputsPlace.value === 'ends') return ['end']\n if (inputsPlace.value === 'bottom') return props.range ? ['start', 'end'] : ['end']\n return []\n})\n\nconst fieldLabel = (which: Thumb) => (which === 'start' ? startLabel.value : fieldEndLabel.value)\n\n// @core\n/**\n * What a number field commits, on leaving it or on Enter. VNumberInput has already parsed and\n * bounded it, and puts its text back to the value it is given when this refuses or changes\n * what was typed; an emptied field is refused.\n */\nfunction commitField(which: Thumb, typed: number | null) {\n if (typed === null) return\n // The ceiling is the last step that FITS, never `max` itself: a native range stops there\n // (what `stepCount` counts), so a value past it would put the thumb, the fill and the number\n // out of step.\n const snapped =\n props.step > 0 ? props.min + Math.round((typed - props.min) / props.step) * props.step : typed\n const value = Math.min(lastStop.value, Math.round(snapped * 1e10) / 1e10)\n const previous = thumbValue(which)\n const next = writeThumb(which, value)\n // Emitted only when the value MOVED, as a native range emits nothing for a key that cannot\n // move its thumb; and the payload is `next`, the model's local copy lagging a parent\n // `v-model` (see `onThumbChange`).\n if (writtenFor(next, which) !== previous) emit('change', next)\n}\n\nconst endThumbEl = ref<HTMLInputElement | null>(null)\n\n// Both members point at the END thumb: it is the one always rendered, and the one the\n// consumer's `id` lands on, so `focus()` goes where a `<label for>` would send it.\ndefineExpose({\n /** Moves the focus to the end thumb, the only thumb outside range mode. */\n focus: (options?: FocusOptions) => endThumbEl.value?.focus(options),\n /** The end thumb's real `<input type=\"range\">`, for what `focus` does not cover. */\n el: endThumbEl,\n})\n</script>\n\n<template>\n <VField\n v-bind=\"forwardedAttrs\"\n class=\"v-slider\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n :label=\"label\"\n :hint=\"hint\"\n :error=\"error\"\n :disabled=\"resolvedDisabled\"\n :hide-label=\"hideLabel\"\n :label-position=\"labelPosition\"\n group\n >\n <!-- The label is text only: the thumbs carry the same words as their accessible names. -->\n <template #default=\"{ fieldProps }\">\n <div\n class=\"v-slider-body\"\n :data-range=\"range ? '' : undefined\"\n :data-disabled=\"resolvedDisabled ? '' : undefined\"\n :data-readonly=\"readonly ? '' : undefined\"\n :data-invalid=\"isInvalid ? '' : undefined\"\n :data-size=\"resolvedSize\"\n :data-orientation=\"orientation\"\n :data-inputs=\"inputsPlace || undefined\"\n :style=\"{\n '--slider-start-fraction': range ? String(frac(startValue)) : undefined,\n '--slider-end-fraction': String(frac(endValue)),\n }\"\n >\n <VNumberInput\n v-for=\"which in fieldsBefore\"\n :key=\"which\"\n :model-value=\"thumbValue(which)\"\n :class=\"['v-slider-field', `v-slider-field-${which}`]\"\n controls=\"none\"\n :format-options=\"formatOptions\"\n :locale=\"resolvedLocale\"\n :size=\"resolvedSize\"\n :compact=\"resolvedCompact\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"resolvedDisabled\"\n :readonly=\"readonly\"\n :invalid=\"isInvalid\"\n :aria-label=\"fieldLabel(which)\"\n @update:model-value=\"commitField(which, $event)\"\n />\n <div class=\"v-slider-rail\">\n <span class=\"v-slider-control\">\n <span class=\"v-slider-track\" aria-hidden=\"true\">\n <span class=\"v-slider-fill\" />\n <span\n v-for=\"(tick, i) in tickPlaces\"\n :key=\"i\"\n class=\"v-slider-tick\"\n :data-filled=\"isFilled(tick.value) ? '' : undefined\"\n :style=\"tick.style\"\n />\n </span>\n <input\n v-if=\"range\"\n type=\"range\"\n class=\"v-slider-input v-slider-input-start\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"resolvedDisabled\"\n :value=\"startValue\"\n :aria-label=\"startLabel\"\n :aria-valuetext=\"startValueText\"\n :aria-invalid=\"isInvalid || undefined\"\n :aria-readonly=\"readonly || undefined\"\n @keydown=\"onThumbKeydown\"\n @input=\"onThumbInput('start', $event)\"\n @change=\"onThumbChange('start', $event)\"\n />\n <!--\n The consumer's attributes come first, so what the component decides for itself; the\n bounds, the value and the disabled state; cannot be overwritten by one of them, and so\n that an ARIA state the consumer sets is not erased by an `undefined` of ours.\n `aria-valuetext` is one of those: the component has one to say only when `labels` or\n `formatOptions` was given.\n -->\n <input\n ref=\"endThumbEl\"\n :aria-invalid=\"isInvalid || undefined\"\n :aria-readonly=\"readonly || undefined\"\n :aria-valuetext=\"endValueText\"\n v-bind=\"thumbAttrs(fieldProps)\"\n type=\"range\"\n class=\"v-slider-input v-slider-input-end\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"resolvedDisabled\"\n :value=\"endValue\"\n :aria-label=\"thumbEndLabel\"\n @keydown=\"onThumbKeydown\"\n @input=\"onThumbInput('end', $event)\"\n @change=\"onThumbChange('end', $event)\"\n />\n </span>\n <span\n v-if=\"tooltip && range\"\n class=\"v-slider-tooltip v-slider-tooltip-start\"\n aria-hidden=\"true\"\n >\n <span class=\"v-slider-tooltip-bubble\">{{ labelTextAt(startValue) }}</span>\n </span>\n <span v-if=\"tooltip\" class=\"v-slider-tooltip v-slider-tooltip-end\" aria-hidden=\"true\">\n <span class=\"v-slider-tooltip-bubble\">{{ labelTextAt(endValue) }}</span>\n </span>\n </div>\n <div v-if=\"labels\" class=\"v-slider-labels\">\n <span\n v-for=\"(item, i) in labels\"\n :key=\"i\"\n class=\"v-slider-label\"\n :style=\"stepPlaces[i]?.style\"\n >\n <template v-if=\"typeof item === 'string'\">{{ item }}</template>\n <VIcon v-else v-bind=\"iconProps(item.icon)\" :label=\"item.label\" />\n </span>\n </div>\n <VNumberInput\n v-for=\"which in fieldsAfter\"\n :key=\"which\"\n :model-value=\"thumbValue(which)\"\n :class=\"['v-slider-field', `v-slider-field-${which}`]\"\n controls=\"none\"\n :format-options=\"formatOptions\"\n :locale=\"resolvedLocale\"\n :size=\"resolvedSize\"\n :compact=\"resolvedCompact\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"resolvedDisabled\"\n :readonly=\"readonly\"\n :invalid=\"isInvalid\"\n :aria-label=\"fieldLabel(which)\"\n @update:model-value=\"commitField(which, $event)\"\n />\n </div>\n </template>\n </VField>\n</template>\n\n<style>\n@layer vectis.components {\n .v-slider-body {\n --slider-thumb: var(--vectis-control-size-slider-thumb);\n --slider-track: var(--vectis-control-size-slider-track);\n display: grid;\n grid-template-areas: 'rail';\n grid-template-columns: minmax(0, 1fr);\n align-items: center;\n column-gap: var(--vectis-space-2);\n row-gap: var(--vectis-space-1);\n inline-size: 100%;\n font-family: var(--vectis-text-family);\n }\n\n /*\n * Each combination is written with exactly the conditions that define it, so a richer one\n * always carries one condition more than the poorer ones it also matches and wins on WEIGHT,\n * never on source order: `[data-inputs='ends'][data-range]:has(.v-slider-labels)` is (0,4,0)\n * against the (0,3,0) of the two it builds on. `:has()` weighs its argument.\n */\n .v-slider-body:has(.v-slider-labels) {\n grid-template-areas: 'rail' 'labels';\n }\n\n .v-slider-body[data-inputs='ends'] {\n grid-template-areas: 'rail field-end';\n grid-template-columns: minmax(0, 1fr) auto;\n }\n\n .v-slider-body[data-inputs='ends']:has(.v-slider-labels) {\n grid-template-areas: 'rail field-end' 'labels .';\n }\n\n .v-slider-body[data-inputs='ends'][data-range] {\n grid-template-areas: 'field-start rail field-end';\n grid-template-columns: auto minmax(0, 1fr) auto;\n }\n\n .v-slider-body[data-inputs='ends'][data-range]:has(.v-slider-labels) {\n grid-template-areas: 'field-start rail field-end' '. labels .';\n }\n\n .v-slider-body[data-inputs='top'] {\n grid-template-areas: 'field-start . field-end' 'rail rail rail';\n grid-template-columns: auto minmax(0, 1fr) auto;\n }\n\n .v-slider-body[data-inputs='top']:has(.v-slider-labels) {\n grid-template-areas: 'field-start . field-end' 'rail rail rail' 'labels labels labels';\n }\n\n .v-slider-body[data-inputs='bottom'] {\n grid-template-areas: 'rail rail rail' 'field-start . field-end';\n grid-template-columns: auto minmax(0, 1fr) auto;\n }\n\n .v-slider-body[data-inputs='bottom']:has(.v-slider-labels) {\n grid-template-areas: 'rail rail rail' 'labels labels labels' 'field-start . field-end';\n }\n\n .v-slider-rail {\n grid-area: rail;\n position: relative;\n block-size: var(--slider-thumb);\n }\n\n .v-slider-control {\n position: absolute;\n inset: 0;\n }\n\n .v-slider-track {\n position: absolute;\n inset-inline: 0;\n inset-block: 0;\n margin-block: auto;\n block-size: var(--slider-track);\n border-radius: var(--vectis-radius-pill);\n background: var(--vectis-color-border);\n overflow: hidden;\n }\n\n /*\n * It must be declared on the element that CARRIES the fraction, never on an ancestor. A\n * custom property's `var()` is resolved where the property is declared, so set on the root it\n * would read a `--fill-fraction` the root does not have and turn invalid, and every tick,\n * label and bubble would drop to its static position at the start of the track, with no\n * error.\n */\n .v-slider-fill,\n .v-slider-tick,\n .v-slider-tooltip,\n .v-slider-label {\n --slider-at: calc(\n var(--slider-thumb) / 2 + (100% - var(--slider-thumb)) * var(--fill-fraction)\n );\n }\n\n .v-slider-fill {\n --fill-fraction: var(--slider-end-fraction);\n position: absolute;\n inset-block: 0;\n inset-inline-start: 0;\n inline-size: var(--slider-at);\n background: var(--vectis-color-accent);\n }\n\n .v-slider-body[data-range] .v-slider-fill {\n --fill-fraction: var(--slider-start-fraction);\n inset-inline-start: var(--slider-at);\n inline-size: calc(\n (100% - var(--slider-thumb)) * (var(--slider-end-fraction) - var(--slider-start-fraction))\n );\n }\n\n .v-slider-tick {\n --slider-tick: calc(var(--slider-track) / 3 * 2);\n position: absolute;\n inset-block: 0;\n margin-block: auto;\n inset-inline-start: calc(var(--slider-at) - var(--slider-tick) / 2);\n inline-size: var(--slider-tick);\n block-size: var(--slider-tick);\n border-radius: var(--vectis-radius-pill);\n background: var(--vectis-color-border-strong);\n }\n\n .v-slider-tick[data-filled] {\n background: var(--vectis-color-text-on-accent);\n }\n\n /*\n * Without that, the one on top would swallow every click and the other thumb could never be\n * grabbed. The thumb's paint lives in variables set here, on the control, and read once by\n * each vendor pseudo-element below.\n */\n .v-slider-input {\n --slider-thumb-bg: var(--vectis-color-text-on-accent);\n --slider-thumb-border: var(--vectis-color-accent);\n --slider-thumb-shadow: var(--vectis-shadow-xs);\n --slider-thumb-outline: none;\n --slider-thumb-cursor: pointer;\n --slider-thumb-transition: background-color var(--vectis-duration-fast)\n var(--vectis-ease-default);\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n appearance: none;\n background: transparent;\n pointer-events: none;\n }\n\n /*\n * With a single thumb there is nothing underneath to protect, so the whole control takes the\n * pointer again; which makes clicking the track jump the value there and start dragging at\n * once, natively and with no code at all. The accepted side effect is that the thumb lights\n * up when the pointer is anywhere over the track.\n */\n .v-slider-body:not([data-range]) .v-slider-input {\n pointer-events: auto;\n cursor: pointer;\n }\n\n /*\n * The two vendor pseudo-elements stay in two separate rules. A selector list holding one a\n * browser does not know is dropped whole, so merged, each engine would throw away the other's\n * thumb and draw its native one.\n */\n .v-slider-input::-webkit-slider-thumb {\n appearance: none;\n pointer-events: auto;\n width: var(--slider-thumb);\n height: var(--slider-thumb);\n border-radius: var(--vectis-radius-pill);\n background: var(--slider-thumb-bg);\n border: var(--vectis-control-border-width) solid var(--slider-thumb-border);\n box-shadow: var(--slider-thumb-shadow);\n outline: var(--slider-thumb-outline);\n outline-offset: var(--vectis-focus-ring-offset);\n cursor: var(--slider-thumb-cursor);\n transition: var(--slider-thumb-transition);\n }\n\n .v-slider-input::-moz-range-thumb {\n pointer-events: auto;\n width: var(--slider-thumb);\n height: var(--slider-thumb);\n border-radius: var(--vectis-radius-pill);\n background: var(--slider-thumb-bg);\n border: var(--vectis-control-border-width) solid var(--slider-thumb-border);\n box-shadow: var(--slider-thumb-shadow);\n outline: var(--slider-thumb-outline);\n outline-offset: var(--vectis-focus-ring-offset);\n cursor: var(--slider-thumb-cursor);\n transition: var(--slider-thumb-transition);\n }\n\n /* The states, all at (0,3,0) but the base, so their ORDER arbitrates them: hover, then\n read-only, invalid and disabled, each overriding what came before on a thumb that is\n several at once.\n\n The hover tint is mixed from the thumb's own white rather than read from\n `accent-surface`: that token turns dark in the dark theme, and a white thumb would\n flash nearly black under the pointer. */\n .v-slider-input:is(:hover, :active):not(:disabled) {\n --slider-thumb-bg: color-mix(\n in oklab,\n var(--vectis-color-text-on-accent),\n var(--vectis-color-accent) 10%\n );\n }\n\n .v-slider-input:focus-visible {\n --slider-thumb-outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline: none;\n }\n\n .v-slider-body[data-readonly] .v-slider-input {\n --slider-thumb-bg: var(--vectis-color-surface);\n --slider-thumb-border: var(--vectis-color-text-muted);\n --slider-thumb-cursor: default;\n cursor: default;\n }\n\n .v-slider-body[data-readonly] .v-slider-fill {\n background: var(--vectis-color-text-muted);\n }\n\n .v-slider-body[data-readonly] .v-slider-tick[data-filled] {\n background: var(--vectis-color-surface);\n }\n\n .v-slider-body[data-invalid] .v-slider-input {\n --slider-thumb-border: var(--vectis-color-danger);\n }\n\n /*\n * It looks like a VTooltip but is placed by arithmetic rather than anchored to the thumb: a\n * native thumb is a pseudo-element, and the browser's anchoring cannot attach anything to\n * one. The fraction it follows is the one the root already carries for the fill.\n */\n .v-slider-tooltip {\n position: absolute;\n inset-block-end: calc(100% + var(--vectis-space-2));\n inset-inline-start: var(--slider-at);\n inline-size: 0;\n display: flex;\n justify-content: center;\n opacity: 0;\n visibility: hidden;\n transition:\n opacity var(--vectis-duration-fast) var(--vectis-ease-default),\n visibility var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-slider-tooltip-start {\n --fill-fraction: var(--slider-start-fraction);\n }\n\n .v-slider-tooltip-end {\n --fill-fraction: var(--slider-end-fraction);\n }\n\n .v-slider-body:has(.v-slider-input-start:active) .v-slider-tooltip-start,\n .v-slider-body:has(.v-slider-input-start:focus-visible) .v-slider-tooltip-start,\n .v-slider-body:has(.v-slider-input-end:active) .v-slider-tooltip-end,\n .v-slider-body:has(.v-slider-input-end:focus-visible) .v-slider-tooltip-end {\n opacity: 1;\n visibility: visible;\n }\n\n .v-slider-tooltip-bubble {\n padding: var(--vectis-space-1) var(--vectis-space-2);\n background: var(--vectis-color-surface-inverse);\n color: var(--vectis-color-text-on-inverse);\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n border-radius: min(var(--vectis-radius-interactive), calc(0.5lh + var(--vectis-space-1)));\n box-shadow: var(--vectis-shadow-sm);\n white-space: nowrap;\n }\n\n .v-slider-labels {\n grid-area: labels;\n position: relative;\n /* The labels are positioned individually and therefore contribute no height of\n their own; this reserves enough room for either kind, a small line of text or an\n icon. */\n min-block-size: var(--vectis-icon-size-md);\n /* The pair travels together, as it does in every `[data-size]` block of\n control-size.css: the size applies to every source, the optical size only to the\n ligature, and setting one without the other draws a 20px glyph cut for 24. */\n --vectis-icon-size: var(--vectis-icon-size-md);\n --vectis-icon-opsz: 20;\n font-size: var(--vectis-text-caption-size);\n color: var(--vectis-color-text-muted);\n }\n\n /* A box of zero width placed exactly on the step: its content then overflows equally\n on both sides, which centres it there. Centring it with a transform would be the\n usual trick, but a transform is physical and would have to be undone for the\n vertical orientation and again for a right-to-left page. */\n .v-slider-label {\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: var(--slider-at);\n inline-size: 0;\n display: flex;\n justify-content: center;\n white-space: nowrap;\n }\n\n .v-slider-field.v-input {\n inline-size: var(--vectis-control-size-slider-field);\n }\n\n /* Qualify number-field selectors so their layout cannot depend on VInput stylesheet order. */\n .v-slider-field-start.v-input {\n grid-area: field-start;\n }\n\n .v-slider-field-end.v-input {\n grid-area: field-end;\n }\n\n .v-slider-body[data-orientation='vertical'] {\n grid-template-areas: 'rail';\n grid-template-columns: none;\n inline-size: fit-content;\n justify-items: center;\n }\n\n .v-slider-body[data-orientation='vertical']:has(.v-slider-labels) {\n grid-template-areas: 'rail labels';\n grid-template-columns: auto auto;\n }\n\n .v-slider-body[data-orientation='vertical'][data-inputs='ends'] {\n grid-template-areas: 'field-end' 'rail';\n grid-template-columns: none;\n }\n\n .v-slider-body[data-orientation='vertical'][data-inputs='ends']:has(.v-slider-labels) {\n grid-template-areas: 'field-end .' 'rail labels';\n grid-template-columns: auto auto;\n }\n\n .v-slider-body[data-orientation='vertical'][data-inputs='ends'][data-range] {\n grid-template-areas: 'field-end' 'rail' 'field-start';\n grid-template-columns: none;\n }\n\n .v-slider-body[data-orientation='vertical'][data-inputs='ends'][data-range]:has(\n .v-slider-labels\n ) {\n grid-template-areas: 'field-end .' 'rail labels' 'field-start .';\n grid-template-columns: auto auto;\n }\n\n /*\n * Upright, `top` and `bottom` become the two SIDES, the inline start and the inline end: the\n * fields stack in a column beside the track, the end field level with its top and the start\n * field with its bottom, where the values they hold sit. The track spans the three rows and\n * gives them their height, the middle one taking whatever the fields leave.\n */\n .v-slider-body[data-orientation='vertical'][data-inputs='top'] {\n grid-template-areas: 'field-end rail' '. rail' 'field-start rail';\n grid-template-columns: auto auto;\n grid-template-rows: auto 1fr auto;\n }\n\n .v-slider-body[data-orientation='vertical'][data-inputs='top']:has(.v-slider-labels) {\n grid-template-areas:\n 'field-end rail labels'\n '. rail labels'\n 'field-start rail labels';\n grid-template-columns: auto auto auto;\n }\n\n .v-slider-body[data-orientation='vertical'][data-inputs='bottom'] {\n grid-template-areas: 'rail field-end' 'rail .' 'rail field-start';\n grid-template-columns: auto auto;\n grid-template-rows: auto 1fr auto;\n }\n\n .v-slider-body[data-orientation='vertical'][data-inputs='bottom']:has(.v-slider-labels) {\n grid-template-areas:\n 'rail labels field-end'\n 'rail labels .'\n 'rail labels field-start';\n grid-template-columns: auto auto auto;\n }\n\n .v-slider-body[data-orientation='vertical'] .v-slider-rail {\n inline-size: var(--slider-thumb);\n block-size: var(--vectis-control-size-slider-length);\n }\n\n /*\n * Rotate only the track box and reverse its direction; labels remain horizontal and the\n * minimum value stays at the bottom.\n */\n .v-slider-body[data-orientation='vertical'] .v-slider-control {\n writing-mode: vertical-lr;\n direction: rtl;\n }\n\n .v-slider-body[data-orientation='vertical'] .v-slider-tooltip {\n inset-block-end: var(--slider-at);\n inset-inline-start: auto;\n inset-inline-end: calc(100% + var(--vectis-space-2));\n inline-size: auto;\n block-size: 0;\n align-items: center;\n justify-content: flex-end;\n }\n\n .v-slider-body[data-orientation='vertical'] .v-slider-labels {\n align-self: stretch;\n min-block-size: auto;\n }\n\n .v-slider-body[data-orientation='vertical'] .v-slider-label {\n inset-block-start: auto;\n inset-block-end: var(--slider-at);\n inset-inline-start: 0;\n inline-size: auto;\n block-size: 0;\n align-items: center;\n justify-content: flex-start;\n }\n\n /*\n * A disabled slider greys out through the colour tokens, the same ones VCheckbox and VSwitch\n * use, and never through opacity. It comes last among the states, so a slider both disabled\n * and read-only or invalid is drawn disabled.\n */\n .v-slider-body[data-disabled] {\n cursor: not-allowed;\n }\n\n .v-slider-body[data-disabled] .v-slider-track {\n background: var(--vectis-color-surface-muted);\n }\n\n .v-slider-body[data-disabled] .v-slider-fill {\n background: var(--vectis-color-text-subtle);\n }\n\n .v-slider-body[data-disabled] .v-slider-tick {\n background: var(--vectis-color-text-subtle);\n }\n\n /*\n * A tick sitting on the greyed fill takes the light colour back, so that it stays visible\n * against it; the same inversion VCheckbox applies to its disabled tick.\n */\n .v-slider-body[data-disabled] .v-slider-tick[data-filled] {\n background: var(--vectis-color-surface-muted);\n }\n\n .v-slider-body[data-disabled] .v-slider-labels {\n color: var(--vectis-color-text-subtle);\n }\n\n .v-slider-body[data-disabled] .v-slider-input {\n --slider-thumb-bg: var(--vectis-color-surface-muted);\n --slider-thumb-border: var(--vectis-color-text-subtle);\n --slider-thumb-shadow: none;\n --slider-thumb-cursor: not-allowed;\n cursor: not-allowed;\n }\n\n /*\n * Forced colours erase background-only tracks and ticks. Restore the track edge and use\n * system Highlight/HighlightText for selected portions.\n */\n @media (forced-colors: active) {\n .v-slider-body .v-slider-track {\n forced-color-adjust: none;\n background: Canvas;\n box-shadow: inset 0 0 0 var(--vectis-control-border-width) CanvasText;\n }\n\n .v-slider-body .v-slider-fill {\n forced-color-adjust: none;\n background: Highlight;\n }\n\n .v-slider-body .v-slider-tick {\n forced-color-adjust: none;\n background: CanvasText;\n }\n\n .v-slider-body .v-slider-tick[data-filled] {\n background: HighlightText;\n }\n\n .v-slider-body .v-slider-input {\n forced-color-adjust: none;\n --slider-thumb-bg: Canvas;\n --slider-thumb-border: CanvasText;\n --slider-thumb-shadow: none;\n }\n\n .v-slider-body[data-disabled] .v-slider-track {\n box-shadow: inset 0 0 0 var(--vectis-control-border-width) GrayText;\n }\n\n .v-slider-body[data-disabled] .v-slider-fill,\n .v-slider-body[data-disabled] .v-slider-tick {\n background: GrayText;\n }\n\n .v-slider-body[data-disabled] .v-slider-input {\n --slider-thumb-border: GrayText;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-slider-input {\n --slider-thumb-transition: none;\n }\n\n .v-slider-tooltip {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VSlider.vue_vue_type_script_setup_true_lang.js","names":["_useModel","_openBlock","_createBlock","VField","_mergeProps","_unref","_withCtx","_createElementVNode","_normalizeStyle","_createElementBlock","_Fragment","_renderList","VNumberInput","_normalizeClass","_createCommentVNode","_toDisplayString","_createTextVNode","VIcon","VIcon_default","VField_default","VNumberInput_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","VSlider_vue_vue_type_script_setup_true_lang_default","\"modelValue\"","\"modelModifiers\"","__expose","__emit","props","__props","model","useModel","emit","group","inject","inputGroupKey","resolvedSize","resolvedCompact","resolvedDisabled","useControlShape","startValue","computed","endValue","thumbValue","which","frac","clamp","v","writeThumb","next","n","writtenFor","value","onThumbInput","el","event","VALUE_KEYS","onThumbKeydown","onThumbChange","stepCount","lastStop","showTicks","stepPlaces","length","i","tickPlaces","isFilled","resolvedLocale","useResolvedLocale","numberFormat","formatValue","labelTextAt","item","m","useMessages","resolvedLabel","useAriaLabel","startLabel","endLabel","thumbEndLabel","fieldEndLabel","spokenText","startValueText","endValueText","useRootAttrs","thumbAttrs","fieldProps","attrs","isInvalid","inputsPlace","isDev","warned","warn","id","message","fieldsBefore","fieldsAfter","fieldLabel","commitField","typed","snapped","previous","endThumbEl","ref","options","openBlock","createBlock","mergeProps","unref","forwardedAttrs","rootClass","rootStyle","withCtx","createElementVNode","normalizeStyle","createElementBlock","Fragment","renderList","normalizeClass","$event","_cache","tick","createCommentVNode","toDisplayString","createTextVNode","iconProps"],"sources":["../../../src/components/VSlider/VSlider.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @core\n/**\n * Native range inputs own thumb interaction. JavaScript keeps range endpoints ordered and\n * synchronizes optional number fields, labels and ARIA values.\n */\n\nimport { computed, inject, ref, watchEffect } from 'vue'\nimport VField from '../VField/VField.vue'\nimport type { FieldControlProps, FieldLabelPosition } from '../VField/VField.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\nimport { inputGroupKey } from '../VInput/context'\nimport VNumberInput from '../VNumberInput/VNumberInput.vue'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { isDev } from '../../utils/env'\nimport { clamp } from '../../utils/number'\nimport { useMessages, useResolvedLocale } from '../../i18n/state'\n\n/**\n * What one step of the track is called: a piece of text, or an icon paired with the words that\n * name it, which a screen reader reads in place of the raw number.\n */\nexport type SliderLabel = string | { icon: IconSource; label: string }\n\n/** The value of a slider: one number, or an ordered pair of them in range mode. */\nexport type SliderValue = number | [number, number]\n\n/** Which way the track runs. */\nexport type SliderOrientation = 'horizontal' | 'vertical'\n\n/** The height of the number fields: 32, 40 or 48 pixels. */\nexport type SliderSize = 'sm' | 'md' | 'lg'\n\n/**\n * Where the number fields sit: none, at the ends of the track, or in a row above or below\n * it. On an upright slider, above and below become the two sides of it.\n */\nexport type SliderInputs = false | 'ends' | 'top' | 'bottom'\n\ninterface SliderProps {\n /** The lowest value the thumb can reach. It is 0 by default. */\n min?: number\n /** The highest value the thumb can reach. It is 100 by default. */\n max?: number\n /**\n * The gap between two values the thumb can stop on, 1 by default. It is also what the\n * arrow keys move by, and what a value typed into the companion field is snapped to.\n */\n step?: number\n /** Offers two thumbs to pick a range, which makes the v-model a pair of values. */\n range?: boolean\n /** Makes the slider unusable. */\n disabled?: boolean\n /**\n * Shows the value without allowing it to be changed. The thumbs can still be focused and\n * are announced as read-only, but neither the pointer nor the keyboard moves them, and the\n * number fields turn read-only with them.\n */\n readonly?: boolean\n /**\n * Marks the value as invalid, which colours the thumbs and tells assistive technology\n * so. It is for a rule the browser cannot check by itself.\n */\n invalid?: boolean\n /**\n * The height of the number fields `inputs` adds. Inside a VInputGroup the group's size\n * wins, as it does for every field of the row.\n */\n size?: SliderSize\n /**\n * The label above the slider, which also names its thumbs: in range mode they are announced\n * as the start and the end of it.\n */\n label?: string\n /** Hides the label visually. It still names the thumbs for assistive technology. */\n hideLabel?: boolean\n /**\n * Where the label sits: above the slider (`top`, the default), or at its start, in a column of\n * its own, moving back above when there is not room for both.\n */\n labelPosition?: FieldLabelPosition\n /**\n * A line of help under the track, stating what the numbers mean or where they may go. It\n * is tied to the slider for assistive technology, so it is read out after the name rather\n * than as part of it.\n */\n hint?: string\n /**\n * A message saying what is wrong with the value, shown in place of the hint so that the field\n * does not grow. It marks the field invalid, is read along with it, and is announced when it\n * appears or changes.\n */\n error?: string\n /** Turns the slider upright, with the lowest value at the bottom. */\n orientation?: SliderOrientation\n /**\n * Adds a number field for setting the value exactly: one, or one per end in range mode. No\n * field is drawn by default.\n */\n inputs?: SliderInputs\n /**\n * Marks each step on the track. Providing labels implies it. Past fifty steps the\n * marks would be an unreadable comb and are not drawn at all.\n */\n ticks?: boolean\n /**\n * A label for every step, in order: a piece of text, or an icon with the words that\n * name it for screen readers. They also become what a screen reader announces in\n * place of the raw number.\n */\n labels?: SliderLabel[]\n /** Shows the value in a bubble above the thumb while it is being moved or focused. */\n tooltip?: boolean\n /**\n * How the value is written in the bubble, the number fields and what the thumbs announce: the\n * options of `Intl.NumberFormat`, for a price, a unit or a percentage. `min`, `max` and `step`\n * stay in the model's unit: with `style: 'percent'`, 0.25 is written 25%.\n */\n formatOptions?: Intl.NumberFormatOptions\n /** The locale the value is written and read in. It defaults to the design system's. */\n locale?: string\n}\n\nconst props = withDefaults(defineProps<SliderProps>(), {\n min: 0,\n max: 100,\n step: 1,\n range: false,\n disabled: false,\n readonly: false,\n invalid: false,\n size: 'md',\n label: undefined,\n hideLabel: false,\n labelPosition: 'top',\n hint: undefined,\n error: undefined,\n orientation: 'horizontal',\n inputs: false,\n ticks: false,\n labels: undefined,\n tooltip: false,\n formatOptions: undefined,\n locale: undefined,\n})\n\n/** The value: a single number, 0 to begin with, or an ordered pair once `range` is set. */\nconst model = defineModel<SliderValue>({ default: 0 })\n\nconst emit = defineEmits<{\n /** The value is BEING changed: every step of a drag, and every key that moves a thumb. */\n input: [value: SliderValue]\n /**\n * The reader has SETTLED on a value: a thumb was released or moved by a key, or a number\n * field was committed.\n */\n change: [value: SliderValue]\n}>()\n\n// The number fields are fields of a VInputGroup row like any other, so the row's size and\n// its disabled state reach them through the same arbitration. A slider has no `compact` of\n// its own: only the row's can apply.\nconst group = inject(inputGroupKey, null)\nconst {\n size: resolvedSize,\n compact: resolvedCompact,\n disabled: resolvedDisabled,\n} = useControlShape(\n {\n get size() {\n return props.size\n },\n compact: false,\n get disabled() {\n return props.disabled\n },\n },\n group,\n)\n\ntype Thumb = 'start' | 'end'\n\nconst startValue = computed(() => (Array.isArray(model.value) ? model.value[0] : props.min))\nconst endValue = computed(() =>\n Array.isArray(model.value) ? model.value[1] : (model.value as number),\n)\n\nconst thumbValue = (which: Thumb) => (which === 'start' ? startValue.value : endValue.value)\n\n/**\n * Where a value sits along the run, as a fraction between 0 and 1. The guard covers a\n * slider whose two bounds are equal, which would otherwise divide by zero.\n */\nconst frac = (v: number) => clamp((v - props.min) / (props.max - props.min || 1), 0, 1)\n\n// @core\n// The thumb being held PUSHES its sibling; it must not stop against it. Two thumbs resting on\n// the same value are one thumb as far as the pointer is concerned, the end one being last in\n// the DOM and therefore the only one it can reach; so a range stopped at `[100, 100]` could\n// only ever be raised, and at the maximum it could not move at all.\nfunction writeThumb(which: Thumb, n: number): SliderValue {\n const next: SliderValue = !props.range\n ? n\n : which === 'start'\n ? [n, Math.max(n, endValue.value)]\n : [Math.min(n, startValue.value), n]\n model.value = next\n return next\n}\n\nconst writtenFor = (value: SliderValue, which: Thumb) =>\n Array.isArray(value) ? value[which === 'start' ? 0 : 1] : value\n\nfunction onThumbInput(which: Thumb, event: Event) {\n const el = event.target as HTMLInputElement\n // @core\n // A drag or a click on the track has ALREADY moved the native thumb when `input` fires,\n // and the event cannot be cancelled: putting the value back is the only refusal there is.\n // It lands before the browser paints, so the thumb never visibly leaves its place, and\n // since the value at release is the one it started from, no `change` follows either.\n if (props.readonly) {\n el.value = String(thumbValue(which))\n return\n }\n const next = writeThumb(which, Number(el.value))\n el.value = String(writtenFor(next, which))\n emit('input', next)\n}\n\n// @keyboard @core\n// The keys that move a range input, cancelled before the browser applies them when the\n// slider is read-only. Tab and every other key pass, so the thumbs stay reachable.\nconst VALUE_KEYS = new Set([\n 'ArrowUp',\n 'ArrowDown',\n 'ArrowLeft',\n 'ArrowRight',\n 'Home',\n 'End',\n 'PageUp',\n 'PageDown',\n])\n\nfunction onThumbKeydown(event: KeyboardEvent) {\n if (props.readonly && VALUE_KEYS.has(event.key)) event.preventDefault()\n}\n\n// @core\n/*\n * The settled value is read off the THUMB, never off the model. A key press fires `input` and\n * `change` back to back in one task, and under a parent `v-model` the model's local copy only\n * catches up when the parent re-renders, so it would still hold the value from before the key.\n */\nfunction onThumbChange(which: Thumb, event: Event) {\n if (props.readonly) return\n const value = Number((event.target as HTMLInputElement).value)\n if (!props.range) emit('change', value)\n else if (which === 'start') emit('change', [value, Math.max(value, endValue.value)])\n else emit('change', [Math.min(value, startValue.value), value])\n}\n\n/** How many whole steps the thumb can actually stop on. */\nconst stepCount = computed(() =>\n props.step > 0 ? Math.floor((props.max - props.min) / props.step + 1e-9) : 0,\n)\n\n/**\n * The highest value a thumb can actually stop on, which is the last whole step and not\n * necessarily the maximum: with `min=0 max=95 step=10` the native control stops at 90.\n */\nconst lastStop = computed(() =>\n props.step > 0 ? props.min + stepCount.value * props.step : props.max,\n)\n\nconst showTicks = computed(\n () =>\n (props.ticks || props.labels !== undefined) && stepCount.value >= 1 && stepCount.value <= 50,\n)\n\n/** Each step's value and the style that places it, shared by the ticks and the labels. */\nconst stepPlaces = computed(() => {\n const length = Math.max(showTicks.value ? stepCount.value + 1 : 0, props.labels?.length ?? 0)\n return Array.from({ length }, (_, i) => {\n const value = props.min + i * props.step\n return { value, style: { '--fill-fraction': String(frac(value)) } }\n })\n})\n\nconst tickPlaces = computed(() =>\n showTicks.value ? stepPlaces.value.slice(0, stepCount.value + 1) : [],\n)\n\nconst isFilled = (value: number) =>\n props.range ? value >= startValue.value && value <= endValue.value : value <= endValue.value\n\n// Resolved once here and handed to the number fields as the result, so the fields and the\n// bubble can never read the value in two different languages.\nconst resolvedLocale = useResolvedLocale(() => props.locale)\nconst numberFormat = computed(\n () => new Intl.NumberFormat(resolvedLocale.value, props.formatOptions),\n)\nconst formatValue = (value: number) => numberFormat.value.format(value)\n\n/** What a value is called, if the consumer named its step; failing that, the number written. */\nfunction labelTextAt(value: number): string {\n const item = props.labels?.[Math.round((value - props.min) / props.step)]\n if (item === undefined) return formatValue(value)\n return typeof item === 'string' ? item : item.label\n}\n\n// @a11y\n/*\n * Resolve the label against consumer ARIA attributes before deriving distinct range-thumb\n * names; an undefined prop must not erase their accessible name.\n */\nconst m = useMessages()\nconst resolvedLabel = useAriaLabel(() => props.label)\nconst startLabel = computed(() =>\n resolvedLabel.value ? m.value.slider.rangeStart(resolvedLabel.value) : m.value.slider.start,\n)\nconst endLabel = computed(() =>\n resolvedLabel.value ? m.value.slider.rangeEnd(resolvedLabel.value) : m.value.slider.end,\n)\nconst thumbEndLabel = computed(() => (props.range ? endLabel.value : resolvedLabel.value))\nconst fieldEndLabel = computed(() =>\n props.range ? endLabel.value : (resolvedLabel.value ?? m.value.slider.value),\n)\n\n// @a11y\n// Said only when it adds something to the bare number a range announces: a step label, or a\n// value written with `formatOptions` (\"120 €\" rather than \"120\").\nconst spokenText = (value: number) =>\n props.labels || props.formatOptions ? labelTextAt(value) : undefined\nconst startValueText = computed(() => (props.range ? spokenText(startValue.value) : undefined))\nconst endValueText = computed(() => spokenText(endValue.value))\n\n// @a11y\n/*\n * The wrapper-root split. The root here is a layout box holding the labels, the track and the\n * optional number fields, so a consumer's `name`, `id`, `required` and `aria-*` have to be\n * redirected onto the real `<input type=\"range\">`; left on the wrapper, a `name` submits\n * nothing and a `<label for>` points at a div.\n */\ndefineOptions({ inheritAttrs: false })\nconst { attrs, rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\n/**\n * What reaches the end thumb from VField: the consumer's attributes and the description. The\n * thumbs are named by `aria-label`, carrying the label's words, so VField's reference to the\n * label text gives way to the consumer's own `aria-labelledby`, if any. A `required` attribute\n * is VField's prop on the way, which group mode keeps for its asterisk: it is handed on here.\n */\nfunction thumbAttrs(fieldProps: FieldControlProps): FieldControlProps {\n return {\n ...fieldProps,\n 'aria-labelledby': attrs['aria-labelledby'] as string | undefined,\n required: attrs.required as true | undefined,\n }\n}\nconst isInvalid = computed(() => props.invalid || !!props.error)\n\n/**\n * Where the fields go, `ends` standing in for any other truthy value: a bare `inputs`\n * attribute reaches the component as `true`, the prop being part boolean.\n */\nconst inputsPlace = computed(() =>\n props.inputs === 'top' || props.inputs === 'bottom' ? props.inputs : props.inputs && 'ends',\n)\n\n// @devwarn\n/*\n * Every guard here describes something that fails SILENTLY. They sit in an effect, so a\n * bound that only becomes wrong on a later render is still caught, and each sentence is\n * given once per instance: the same warning repeated on every keystroke is how a warning\n * stops being read (the `useFileField` arrangement).\n */\nif (isDev) {\n // Keyed by a short id rather than by the sentence: each of these embeds a count, so the\n // same guard reported twice would read as two different messages and warn again.\n const warned = new Set<string>()\n const warn = (id: string, message: string) => {\n if (warned.has(id)) return\n warned.add(id)\n console.warn(`[VSlider] ${message}`)\n }\n watchEffect(() => {\n if (props.step <= 0)\n warn(\n 'step',\n `step=\"${props.step}\" cannot move a thumb: the arrow keys do nothing and a value typed into a field is committed as it stands. Give a positive step.`,\n )\n if ((props.ticks || props.labels) && stepCount.value > 50)\n warn(\n 'ticks',\n `${stepCount.value} steps: no tick is drawn past 50, the comb being unreadable.`,\n )\n if (props.labels && props.labels.length !== stepCount.value + 1)\n warn(\n 'labels',\n `${props.labels.length} labels for ${stepCount.value + 1} steps: one label per step expected.`,\n )\n if (props.inputs !== false && props.inputs !== inputsPlace.value)\n warn(\n 'inputs',\n `inputs=\"${String(props.inputs)}\" is not a placement: the fields are drawn at the ends. Write inputs=\"ends\", \"top\" or \"bottom\".`,\n )\n if (props.range && attrs.name !== undefined)\n warn(\n 'name',\n `name=\"${String(attrs.name)}\" on a range: only the end thumb carries it, so the form receives one value of the two. Bind the model to two inputs of your own instead.`,\n )\n })\n}\n\n// @a11y\n/*\n * The fields are rendered in the order they are SEEN, before or after the track, so that\n * the tab order follows the screen. Laid out by the grid alone from a fixed DOM order, a\n * row of fields above the track would be reached one on each side of the thumbs.\n */\nconst fieldsBefore = computed<Thumb[]>(() => {\n if (inputsPlace.value === 'ends') return props.range ? ['start'] : []\n if (inputsPlace.value === 'top') return props.range ? ['start', 'end'] : ['end']\n return []\n})\nconst fieldsAfter = computed<Thumb[]>(() => {\n if (inputsPlace.value === 'ends') return ['end']\n if (inputsPlace.value === 'bottom') return props.range ? ['start', 'end'] : ['end']\n return []\n})\n\nconst fieldLabel = (which: Thumb) => (which === 'start' ? startLabel.value : fieldEndLabel.value)\n\n// @core\n/**\n * What a number field commits, on leaving it or on Enter. VNumberInput has already parsed and\n * bounded it, and puts its text back to the value it is given when this refuses or changes\n * what was typed; an emptied field is refused.\n */\nfunction commitField(which: Thumb, typed: number | null) {\n if (typed === null) return\n // The ceiling is the last step that FITS, never `max` itself: a native range stops there\n // (what `stepCount` counts), so a value past it would put the thumb, the fill and the number\n // out of step.\n const snapped =\n props.step > 0 ? props.min + Math.round((typed - props.min) / props.step) * props.step : typed\n const value = Math.min(lastStop.value, Math.round(snapped * 1e10) / 1e10)\n const previous = thumbValue(which)\n const next = writeThumb(which, value)\n // Emitted only when the value MOVED, as a native range emits nothing for a key that cannot\n // move its thumb; and the payload is `next`, the model's local copy lagging a parent\n // `v-model` (see `onThumbChange`).\n if (writtenFor(next, which) !== previous) emit('change', next)\n}\n\nconst endThumbEl = ref<HTMLInputElement | null>(null)\n\n// Both members point at the END thumb: it is the one always rendered, and the one the\n// consumer's `id` lands on, so `focus()` goes where a `<label for>` would send it.\ndefineExpose({\n /** Moves the focus to the end thumb, the only thumb outside range mode. */\n focus: (options?: FocusOptions) => endThumbEl.value?.focus(options),\n /** The end thumb's real `<input type=\"range\">`, for what `focus` does not cover. */\n el: endThumbEl,\n})\n</script>\n\n<template>\n <VField\n v-bind=\"forwardedAttrs\"\n class=\"v-slider\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n :label=\"label\"\n :hint=\"hint\"\n :error=\"error\"\n :disabled=\"resolvedDisabled\"\n :hide-label=\"hideLabel\"\n :label-position=\"labelPosition\"\n group\n >\n <!-- The label is text only: the thumbs carry the same words as their accessible names. -->\n <template #default=\"{ fieldProps }\">\n <div\n class=\"v-slider-body\"\n :data-range=\"range ? '' : undefined\"\n :data-disabled=\"resolvedDisabled ? '' : undefined\"\n :data-readonly=\"readonly ? '' : undefined\"\n :data-invalid=\"isInvalid ? '' : undefined\"\n :data-size=\"resolvedSize\"\n :data-orientation=\"orientation\"\n :data-inputs=\"inputsPlace || undefined\"\n :style=\"{\n '--slider-start-fraction': range ? String(frac(startValue)) : undefined,\n '--slider-end-fraction': String(frac(endValue)),\n }\"\n >\n <VNumberInput\n v-for=\"which in fieldsBefore\"\n :key=\"which\"\n :model-value=\"thumbValue(which)\"\n :class=\"['v-slider-field', `v-slider-field-${which}`]\"\n controls=\"none\"\n :format-options=\"formatOptions\"\n :locale=\"resolvedLocale\"\n :size=\"resolvedSize\"\n :compact=\"resolvedCompact\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"resolvedDisabled\"\n :readonly=\"readonly\"\n :invalid=\"isInvalid\"\n :aria-label=\"fieldLabel(which)\"\n @update:model-value=\"commitField(which, $event)\"\n />\n <div class=\"v-slider-rail\">\n <span class=\"v-slider-control\">\n <span class=\"v-slider-track\" aria-hidden=\"true\">\n <span class=\"v-slider-fill\" />\n <span\n v-for=\"(tick, i) in tickPlaces\"\n :key=\"i\"\n class=\"v-slider-tick\"\n :data-filled=\"isFilled(tick.value) ? '' : undefined\"\n :style=\"tick.style\"\n />\n </span>\n <input\n v-if=\"range\"\n type=\"range\"\n class=\"v-slider-input v-slider-input-start\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"resolvedDisabled\"\n :value=\"startValue\"\n :aria-label=\"startLabel\"\n :aria-valuetext=\"startValueText\"\n :aria-invalid=\"isInvalid || undefined\"\n :aria-readonly=\"readonly || undefined\"\n @keydown=\"onThumbKeydown\"\n @input=\"onThumbInput('start', $event)\"\n @change=\"onThumbChange('start', $event)\"\n />\n <!--\n The consumer's attributes come first, so what the component decides for itself; the\n bounds, the value and the disabled state; cannot be overwritten by one of them, and so\n that an ARIA state the consumer sets is not erased by an `undefined` of ours.\n `aria-valuetext` is one of those: the component has one to say only when `labels` or\n `formatOptions` was given.\n -->\n <input\n ref=\"endThumbEl\"\n :aria-invalid=\"isInvalid || undefined\"\n :aria-readonly=\"readonly || undefined\"\n :aria-valuetext=\"endValueText\"\n v-bind=\"thumbAttrs(fieldProps)\"\n type=\"range\"\n class=\"v-slider-input v-slider-input-end\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"resolvedDisabled\"\n :value=\"endValue\"\n :aria-label=\"thumbEndLabel\"\n @keydown=\"onThumbKeydown\"\n @input=\"onThumbInput('end', $event)\"\n @change=\"onThumbChange('end', $event)\"\n />\n </span>\n <span\n v-if=\"tooltip && range\"\n class=\"v-slider-tooltip v-slider-tooltip-start\"\n aria-hidden=\"true\"\n >\n <span class=\"v-slider-tooltip-bubble\">{{ labelTextAt(startValue) }}</span>\n </span>\n <span v-if=\"tooltip\" class=\"v-slider-tooltip v-slider-tooltip-end\" aria-hidden=\"true\">\n <span class=\"v-slider-tooltip-bubble\">{{ labelTextAt(endValue) }}</span>\n </span>\n </div>\n <div v-if=\"labels\" class=\"v-slider-labels\">\n <span\n v-for=\"(item, i) in labels\"\n :key=\"i\"\n class=\"v-slider-label\"\n :style=\"stepPlaces[i]?.style\"\n >\n <template v-if=\"typeof item === 'string'\">{{ item }}</template>\n <VIcon v-else v-bind=\"iconProps(item.icon)\" :label=\"item.label\" />\n </span>\n </div>\n <VNumberInput\n v-for=\"which in fieldsAfter\"\n :key=\"which\"\n :model-value=\"thumbValue(which)\"\n :class=\"['v-slider-field', `v-slider-field-${which}`]\"\n controls=\"none\"\n :format-options=\"formatOptions\"\n :locale=\"resolvedLocale\"\n :size=\"resolvedSize\"\n :compact=\"resolvedCompact\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"resolvedDisabled\"\n :readonly=\"readonly\"\n :invalid=\"isInvalid\"\n :aria-label=\"fieldLabel(which)\"\n @update:model-value=\"commitField(which, $event)\"\n />\n </div>\n </template>\n </VField>\n</template>\n\n<style>\n@layer vectis.components {\n .v-slider-body {\n --slider-thumb: var(--vectis-control-size-slider-thumb);\n --slider-track: var(--vectis-control-size-slider-track);\n display: grid;\n grid-template-areas: 'rail';\n grid-template-columns: minmax(0, 1fr);\n align-items: center;\n column-gap: var(--vectis-space-2);\n row-gap: var(--vectis-space-1);\n inline-size: 100%;\n font-family: var(--vectis-text-family);\n }\n\n /*\n * Each combination is written with exactly the conditions that define it, so a richer one\n * always carries one condition more than the poorer ones it also matches and wins on WEIGHT,\n * never on source order: `[data-inputs='ends'][data-range]:has(.v-slider-labels)` is (0,4,0)\n * against the (0,3,0) of the two it builds on. `:has()` weighs its argument.\n */\n .v-slider-body:has(.v-slider-labels) {\n grid-template-areas: 'rail' 'labels';\n }\n\n .v-slider-body[data-inputs='ends'] {\n grid-template-areas: 'rail field-end';\n grid-template-columns: minmax(0, 1fr) auto;\n }\n\n .v-slider-body[data-inputs='ends']:has(.v-slider-labels) {\n grid-template-areas: 'rail field-end' 'labels .';\n }\n\n .v-slider-body[data-inputs='ends'][data-range] {\n grid-template-areas: 'field-start rail field-end';\n grid-template-columns: auto minmax(0, 1fr) auto;\n }\n\n .v-slider-body[data-inputs='ends'][data-range]:has(.v-slider-labels) {\n grid-template-areas: 'field-start rail field-end' '. labels .';\n }\n\n .v-slider-body[data-inputs='top'] {\n grid-template-areas: 'field-start . field-end' 'rail rail rail';\n grid-template-columns: auto minmax(0, 1fr) auto;\n }\n\n .v-slider-body[data-inputs='top']:has(.v-slider-labels) {\n grid-template-areas: 'field-start . field-end' 'rail rail rail' 'labels labels labels';\n }\n\n .v-slider-body[data-inputs='bottom'] {\n grid-template-areas: 'rail rail rail' 'field-start . field-end';\n grid-template-columns: auto minmax(0, 1fr) auto;\n }\n\n .v-slider-body[data-inputs='bottom']:has(.v-slider-labels) {\n grid-template-areas: 'rail rail rail' 'labels labels labels' 'field-start . field-end';\n }\n\n .v-slider-rail {\n grid-area: rail;\n position: relative;\n block-size: var(--slider-thumb);\n }\n\n .v-slider-control {\n position: absolute;\n inset: 0;\n }\n\n .v-slider-track {\n position: absolute;\n inset-inline: 0;\n inset-block: 0;\n margin-block: auto;\n block-size: var(--slider-track);\n border-radius: var(--vectis-radius-pill);\n background: var(--vectis-color-border);\n overflow: hidden;\n }\n\n /*\n * It must be declared on the element that CARRIES the fraction, never on an ancestor. A\n * custom property's `var()` is resolved where the property is declared, so set on the root it\n * would read a `--fill-fraction` the root does not have and turn invalid, and every tick,\n * label and bubble would drop to its static position at the start of the track, with no\n * error.\n */\n .v-slider-fill,\n .v-slider-tick,\n .v-slider-tooltip,\n .v-slider-label {\n --slider-at: calc(\n var(--slider-thumb) / 2 + (100% - var(--slider-thumb)) * var(--fill-fraction)\n );\n }\n\n .v-slider-fill {\n --fill-fraction: var(--slider-end-fraction);\n position: absolute;\n inset-block: 0;\n inset-inline-start: 0;\n inline-size: var(--slider-at);\n background: var(--vectis-color-accent);\n }\n\n .v-slider-body[data-range] .v-slider-fill {\n --fill-fraction: var(--slider-start-fraction);\n inset-inline-start: var(--slider-at);\n inline-size: calc(\n (100% - var(--slider-thumb)) * (var(--slider-end-fraction) - var(--slider-start-fraction))\n );\n }\n\n .v-slider-tick {\n --slider-tick: calc(var(--slider-track) / 3 * 2);\n position: absolute;\n inset-block: 0;\n margin-block: auto;\n inset-inline-start: calc(var(--slider-at) - var(--slider-tick) / 2);\n inline-size: var(--slider-tick);\n block-size: var(--slider-tick);\n border-radius: var(--vectis-radius-pill);\n background: var(--vectis-color-border-strong);\n }\n\n .v-slider-tick[data-filled] {\n background: var(--vectis-color-text-on-accent);\n }\n\n /*\n * Without that, the one on top would swallow every click and the other thumb could never be\n * grabbed. The thumb's paint lives in variables set here, on the control, and read once by\n * each vendor pseudo-element below.\n */\n .v-slider-input {\n --slider-thumb-bg: var(--vectis-color-text-on-accent);\n --slider-thumb-border: var(--vectis-color-accent);\n --slider-thumb-shadow: var(--vectis-shadow-xs);\n --slider-thumb-outline: none;\n --slider-thumb-cursor: pointer;\n --slider-thumb-transition: background-color var(--vectis-duration-fast)\n var(--vectis-ease-default);\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n appearance: none;\n background: transparent;\n pointer-events: none;\n }\n\n /*\n * With a single thumb there is nothing underneath to protect, so the whole control takes the\n * pointer again; which makes clicking the track jump the value there and start dragging at\n * once, natively and with no code at all. The accepted side effect is that the thumb lights\n * up when the pointer is anywhere over the track.\n */\n .v-slider-body:not([data-range]) .v-slider-input {\n pointer-events: auto;\n cursor: pointer;\n }\n\n /*\n * The two vendor pseudo-elements stay in two separate rules. A selector list holding one a\n * browser does not know is dropped whole, so merged, each engine would throw away the other's\n * thumb and draw its native one.\n */\n .v-slider-input::-webkit-slider-thumb {\n appearance: none;\n pointer-events: auto;\n width: var(--slider-thumb);\n height: var(--slider-thumb);\n border-radius: var(--vectis-radius-pill);\n background: var(--slider-thumb-bg);\n border: var(--vectis-control-border-width) solid var(--slider-thumb-border);\n box-shadow: var(--slider-thumb-shadow);\n outline: var(--slider-thumb-outline);\n outline-offset: var(--vectis-focus-ring-offset);\n cursor: var(--slider-thumb-cursor);\n transition: var(--slider-thumb-transition);\n }\n\n .v-slider-input::-moz-range-thumb {\n pointer-events: auto;\n width: var(--slider-thumb);\n height: var(--slider-thumb);\n border-radius: var(--vectis-radius-pill);\n background: var(--slider-thumb-bg);\n border: var(--vectis-control-border-width) solid var(--slider-thumb-border);\n box-shadow: var(--slider-thumb-shadow);\n outline: var(--slider-thumb-outline);\n outline-offset: var(--vectis-focus-ring-offset);\n cursor: var(--slider-thumb-cursor);\n transition: var(--slider-thumb-transition);\n }\n\n /* The states, all at (0,3,0) but the base, so their ORDER arbitrates them: hover, then\n read-only, invalid and disabled, each overriding what came before on a thumb that is\n several at once.\n\n The hover tint is mixed from the thumb's own white rather than read from\n `accent-surface`: that token turns dark in the dark theme, and a white thumb would\n flash nearly black under the pointer. */\n .v-slider-input:is(:hover, :active):not(:disabled) {\n --slider-thumb-bg: color-mix(\n in oklab,\n var(--vectis-color-text-on-accent),\n var(--vectis-color-accent) 10%\n );\n }\n\n .v-slider-input:focus-visible {\n --slider-thumb-outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline: none;\n }\n\n .v-slider-body[data-readonly] .v-slider-input {\n --slider-thumb-bg: var(--vectis-color-surface);\n --slider-thumb-border: var(--vectis-color-text-muted);\n --slider-thumb-cursor: default;\n cursor: default;\n }\n\n .v-slider-body[data-readonly] .v-slider-fill {\n background: var(--vectis-color-text-muted);\n }\n\n .v-slider-body[data-readonly] .v-slider-tick[data-filled] {\n background: var(--vectis-color-surface);\n }\n\n .v-slider-body[data-invalid] .v-slider-input {\n --slider-thumb-border: var(--vectis-color-danger);\n }\n\n /*\n * It looks like a VTooltip but is placed by arithmetic rather than anchored to the thumb: a\n * native thumb is a pseudo-element, and the browser's anchoring cannot attach anything to\n * one. The fraction it follows is the one the root already carries for the fill.\n */\n .v-slider-tooltip {\n position: absolute;\n inset-block-end: calc(100% + var(--vectis-space-2));\n inset-inline-start: var(--slider-at);\n inline-size: 0;\n display: flex;\n justify-content: center;\n opacity: 0;\n visibility: hidden;\n transition:\n opacity var(--vectis-duration-fast) var(--vectis-ease-default),\n visibility var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-slider-tooltip-start {\n --fill-fraction: var(--slider-start-fraction);\n }\n\n .v-slider-tooltip-end {\n --fill-fraction: var(--slider-end-fraction);\n }\n\n .v-slider-body:has(.v-slider-input-start:active) .v-slider-tooltip-start,\n .v-slider-body:has(.v-slider-input-start:focus-visible) .v-slider-tooltip-start,\n .v-slider-body:has(.v-slider-input-end:active) .v-slider-tooltip-end,\n .v-slider-body:has(.v-slider-input-end:focus-visible) .v-slider-tooltip-end {\n opacity: 1;\n visibility: visible;\n }\n\n .v-slider-tooltip-bubble {\n padding: var(--vectis-space-1) var(--vectis-space-2);\n background: var(--vectis-color-surface-inverse);\n color: var(--vectis-color-text-on-inverse);\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n border-radius: min(var(--vectis-radius-interactive), calc(0.5lh + var(--vectis-space-1)));\n box-shadow: var(--vectis-shadow-sm);\n white-space: nowrap;\n }\n\n .v-slider-labels {\n grid-area: labels;\n position: relative;\n /* The labels are positioned individually and therefore contribute no height of\n their own; this reserves enough room for either kind, a small line of text or an\n icon. */\n min-block-size: var(--vectis-icon-size-md);\n /* The pair travels together, as it does in every `[data-size]` block of\n control-size.css: the size applies to every source, the optical size only to the\n ligature, and setting one without the other draws a 20px glyph cut for 24. */\n --vectis-icon-size: var(--vectis-icon-size-md);\n --vectis-icon-opsz: 20;\n font-size: var(--vectis-text-caption-size);\n color: var(--vectis-color-text-muted);\n }\n\n /* A box of zero width placed exactly on the step: its content then overflows equally\n on both sides, which centres it there. Centring it with a transform would be the\n usual trick, but a transform is physical and would have to be undone for the\n vertical orientation and again for a right-to-left page. */\n .v-slider-label {\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: var(--slider-at);\n inline-size: 0;\n display: flex;\n justify-content: center;\n white-space: nowrap;\n }\n\n .v-slider-field.v-input {\n inline-size: var(--vectis-control-size-slider-field);\n }\n\n /* Qualify number-field selectors so their layout cannot depend on VInput stylesheet order. */\n .v-slider-field-start.v-input {\n grid-area: field-start;\n }\n\n .v-slider-field-end.v-input {\n grid-area: field-end;\n }\n\n .v-slider-body[data-orientation='vertical'] {\n grid-template-areas: 'rail';\n grid-template-columns: none;\n inline-size: fit-content;\n justify-items: center;\n }\n\n .v-slider-body[data-orientation='vertical']:has(.v-slider-labels) {\n grid-template-areas: 'rail labels';\n grid-template-columns: auto auto;\n }\n\n .v-slider-body[data-orientation='vertical'][data-inputs='ends'] {\n grid-template-areas: 'field-end' 'rail';\n grid-template-columns: none;\n }\n\n .v-slider-body[data-orientation='vertical'][data-inputs='ends']:has(.v-slider-labels) {\n grid-template-areas: 'field-end .' 'rail labels';\n grid-template-columns: auto auto;\n }\n\n .v-slider-body[data-orientation='vertical'][data-inputs='ends'][data-range] {\n grid-template-areas: 'field-end' 'rail' 'field-start';\n grid-template-columns: none;\n }\n\n .v-slider-body[data-orientation='vertical'][data-inputs='ends'][data-range]:has(\n .v-slider-labels\n ) {\n grid-template-areas: 'field-end .' 'rail labels' 'field-start .';\n grid-template-columns: auto auto;\n }\n\n /*\n * Upright, `top` and `bottom` become the two SIDES, the inline start and the inline end: the\n * fields stack in a column beside the track, the end field level with its top and the start\n * field with its bottom, where the values they hold sit. The track spans the three rows and\n * gives them their height, the middle one taking whatever the fields leave.\n */\n .v-slider-body[data-orientation='vertical'][data-inputs='top'] {\n grid-template-areas: 'field-end rail' '. rail' 'field-start rail';\n grid-template-columns: auto auto;\n grid-template-rows: auto 1fr auto;\n }\n\n .v-slider-body[data-orientation='vertical'][data-inputs='top']:has(.v-slider-labels) {\n grid-template-areas:\n 'field-end rail labels'\n '. rail labels'\n 'field-start rail labels';\n grid-template-columns: auto auto auto;\n }\n\n .v-slider-body[data-orientation='vertical'][data-inputs='bottom'] {\n grid-template-areas: 'rail field-end' 'rail .' 'rail field-start';\n grid-template-columns: auto auto;\n grid-template-rows: auto 1fr auto;\n }\n\n .v-slider-body[data-orientation='vertical'][data-inputs='bottom']:has(.v-slider-labels) {\n grid-template-areas:\n 'rail labels field-end'\n 'rail labels .'\n 'rail labels field-start';\n grid-template-columns: auto auto auto;\n }\n\n .v-slider-body[data-orientation='vertical'] .v-slider-rail {\n inline-size: var(--slider-thumb);\n block-size: var(--vectis-control-size-slider-length);\n }\n\n /*\n * Rotate only the track box and reverse its direction; labels remain horizontal and the\n * minimum value stays at the bottom.\n */\n .v-slider-body[data-orientation='vertical'] .v-slider-control {\n writing-mode: vertical-lr;\n direction: rtl;\n }\n\n .v-slider-body[data-orientation='vertical'] .v-slider-tooltip {\n inset-block-end: var(--slider-at);\n inset-inline-start: auto;\n inset-inline-end: calc(100% + var(--vectis-space-2));\n inline-size: auto;\n block-size: 0;\n align-items: center;\n justify-content: flex-end;\n }\n\n .v-slider-body[data-orientation='vertical'] .v-slider-labels {\n align-self: stretch;\n min-block-size: auto;\n }\n\n .v-slider-body[data-orientation='vertical'] .v-slider-label {\n inset-block-start: auto;\n inset-block-end: var(--slider-at);\n inset-inline-start: 0;\n inline-size: auto;\n block-size: 0;\n align-items: center;\n justify-content: flex-start;\n }\n\n /*\n * A disabled slider greys out through the colour tokens, the same ones VCheckbox and VSwitch\n * use, and never through opacity. It comes last among the states, so a slider both disabled\n * and read-only or invalid is drawn disabled.\n */\n .v-slider-body[data-disabled] {\n cursor: not-allowed;\n }\n\n .v-slider-body[data-disabled] .v-slider-track {\n background: var(--vectis-color-surface-muted);\n }\n\n .v-slider-body[data-disabled] .v-slider-fill {\n background: var(--vectis-color-text-subtle);\n }\n\n .v-slider-body[data-disabled] .v-slider-tick {\n background: var(--vectis-color-text-subtle);\n }\n\n /*\n * A tick sitting on the greyed fill takes the light colour back, so that it stays visible\n * against it; the same inversion VCheckbox applies to its disabled tick.\n */\n .v-slider-body[data-disabled] .v-slider-tick[data-filled] {\n background: var(--vectis-color-surface-muted);\n }\n\n .v-slider-body[data-disabled] .v-slider-labels {\n color: var(--vectis-color-text-subtle);\n }\n\n .v-slider-body[data-disabled] .v-slider-input {\n --slider-thumb-bg: var(--vectis-color-surface-muted);\n --slider-thumb-border: var(--vectis-color-text-subtle);\n --slider-thumb-shadow: none;\n --slider-thumb-cursor: not-allowed;\n cursor: not-allowed;\n }\n\n /*\n * Forced colours erase background-only tracks and ticks. Restore the track edge and use\n * system Highlight/HighlightText for selected portions.\n */\n @media (forced-colors: active) {\n .v-slider-body .v-slider-track {\n forced-color-adjust: none;\n background: Canvas;\n box-shadow: inset 0 0 0 var(--vectis-control-border-width) CanvasText;\n }\n\n .v-slider-body .v-slider-fill {\n forced-color-adjust: none;\n background: Highlight;\n }\n\n .v-slider-body .v-slider-tick {\n forced-color-adjust: none;\n background: CanvasText;\n }\n\n .v-slider-body .v-slider-tick[data-filled] {\n background: HighlightText;\n }\n\n .v-slider-body .v-slider-input {\n forced-color-adjust: none;\n --slider-thumb-bg: Canvas;\n --slider-thumb-border: CanvasText;\n --slider-thumb-shadow: none;\n }\n\n .v-slider-body[data-disabled] .v-slider-track {\n box-shadow: inset 0 0 0 var(--vectis-control-border-width) GrayText;\n }\n\n .v-slider-body[data-disabled] .v-slider-fill,\n .v-slider-body[data-disabled] .v-slider-tick {\n background: GrayText;\n }\n\n .v-slider-body[data-disabled] .v-slider-input {\n --slider-thumb-border: GrayText;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-slider-input {\n --slider-thumb-transition: none;\n }\n\n .v-slider-tooltip {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VSnackbar.js","names":[],"sources":["../../../src/components/VSnackbar/VSnackbar.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * A manual popover hosts the current confirmation. JavaScript bridges imperative visibility,\n * announcements and a dismissal timer paused during hover or focus.\n */\n\nimport { computed, nextTick, onMounted, ref, watch } from 'vue'\n\nimport { usePopover } from '../../composables/usePopover'\nimport { useTimer } from '../../composables/useTimer'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useLiveAnnouncer } from '../../composables/useLiveAnnouncer'\nimport { useMessages } from '../../i18n/state'\nimport VButton from '../VButton/VButton.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { current, dismissSnackbar, type SnackbarPlacement } from './state'\n\ninterface SnackbarProps {\n /** Which end of the bottom edge confirmations appear at, unless one of them asks for another. */\n placement?: SnackbarPlacement\n /**\n * How long a confirmation stays, in milliseconds, unless it asks for something else. A\n * confirmation given 0 stays until it is replaced or taken away by hand.\n */\n duration?: number\n /**\n * The word drawn on the single action, when the confirmation does not give one. It is\n * also the button's accessible name, and falls back to the design system dictionary.\n */\n actionText?: string\n /**\n * What screen readers announce for the confirmation area itself, which is a landmark of\n * the page. It falls back to the design system dictionary.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<SnackbarProps>(), {\n placement: 'bottom-center',\n /* Shorter than a notification's five seconds. A confirmation is one short sentence\n about something the reader has just done, so they already know what it says. */\n duration: 4000,\n actionText: undefined,\n label: undefined,\n})\n\ndefineOptions({ inheritAttrs: false })\n\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.snackbar.label)\n\nconst placement = computed(() => current.value?.placement ?? props.placement)\nconst actionText = computed(\n () => current.value?.actionText ?? props.actionText ?? m.value.snackbar.action,\n)\nconst icon = computed(() => (current.value?.icon ? iconProps(current.value.icon) : undefined))\n\nconst hostEl = ref<HTMLElement | null>(null)\nconst { syncShown, show, hide } = usePopover(hostEl)\nconst { start, cancel } = useTimer()\n\n/*\n * They are two separate flags rather than one counter because they can be true at the same time\n * and end independently: a reader tabs to the action button, then moves the mouse over the bar,\n * then moves it away; and the bar must not leave while the button still has focus.\n */\nlet hovered = false\nlet focused = false\n\nfunction arm() {\n const bar = current.value\n if (!bar || hovered || focused) return\n const duration = bar.duration ?? props.duration\n /*\n * GUARD, not a default: `useTimer` runs a delay of 0 synchronously, which is the design\n * system's convention for \"no deferral at all\". Without this test a confirmation asking to be\n * permanent would be taken away in the same tick it was raised.\n */\n if (duration > 0) start(() => dismissSnackbar(bar.id), duration)\n}\n\n// @core\n/**\n * Brings the page into line with the state: it shows the container when there is a confirmation\n * and hides it when there is none, and restarts the countdown from the top.\n */\nconst { polite, assertive, announce } = useLiveAnnouncer()\nlet lastAnnounced: number | undefined\n\nfunction sync() {\n cancel()\n // Clear hover/focus flags when removing the bar; a removed focused element may emit no\n // focusout and leave future timers permanently held.\n if (!current.value) hovered = focused = false\n // A REPLACEMENT removes the focused action too (the card is keyed on the bar), and the same\n // engine leaves `focused` standing: the new bar then never left. The toaster's test, where\n // the focus actually is, is the one that holds in both cases.\n else if (!hostEl.value?.contains(document.activeElement)) focused = false\n // @a11y\n // Said through the live regions rendered once beside the host: the card is created WITH\n // its message, and a region inserted along with its text is not reliably announced. A\n // failure interrupts; a plain confirmation waits for a pause.\n if (current.value && current.value.id !== lastAnnounced) {\n lastAnnounced = current.value.id\n announce(current.value.message, current.value.tone === 'danger')\n }\n // Showing and hiding are safe to call on a container already in that state, the guards living\n // in the popover plumbing; so there is no need to remember which it is in.\n if (current.value) show()\n else hide()\n arm()\n}\n\n/*\n * Watching the resolved bar covers a change of confirmation and its disappearance. The `post`\n * timing is load-bearing: the card must already be in the page before its container is told to\n * show itself.\n */\nwatch(current, sync, { flush: 'post' })\n// @ssr\n// A watcher does not run during the server render, so a confirmation raised before\n// this component mounted would never be picked up. Running the same synchronization on\n// mount is what brings it in.\nonMounted(sync)\n\n// @a11y\n// WCAG 2.2.1: something that disappears on a clock has to be holdable, or a slow reader simply\n// never finishes it; and here they would also never reach the button.\n/*\n * Resting the pointer on the bar suspends its countdown, and so does moving the keyboard into\n * it: the action is a real button, so a reader tabbing towards it would otherwise watch it\n * vanish from under the focus ring. VToaster holds its stacks on the same two reasons, with the\n * same verbs, for its close crosses.\n */\nfunction hold(which: 'pointer' | 'focus') {\n if (which === 'pointer') hovered = true\n else focused = true\n cancel()\n}\n\n// Where the focus came from when it entered the bar, to hand it back once the action has\n// taken the bar, and the focused button with it, away.\nlet cameFrom: HTMLElement | null = null\nfunction onFocusIn(event: FocusEvent) {\n const from = event.relatedTarget as HTMLElement | null\n if (from && !hostEl.value?.contains(from)) cameFrom = from\n hold('focus')\n}\n\nfunction release(which: 'pointer' | 'focus') {\n if (which === 'pointer') hovered = false\n else focused = false\n arm()\n}\n\n/* The action always takes the bar away: it answers the confirmation, so leaving it on\n screen would invite the reader to answer it twice. */\nfunction runAction() {\n const bar = current.value\n if (!bar) return\n const hadFocus = !!hostEl.value?.contains(document.activeElement)\n try {\n bar.action?.()\n } finally {\n // Even when the action throws: the error reaches the application, and the bar it\n // answered does not stay on screen, a permanent one for good.\n dismissSnackbar(bar.id)\n // @a11y\n // The focused button leaves with the bar; the focus goes back where it came from\n // rather than falling to `<body>`.\n if (hadFocus)\n void nextTick(() => {\n if (cameFrom?.isConnected && !current.value) cameFrom.focus()\n })\n }\n}\n</script>\n\n<template>\n <div\n ref=\"hostEl\"\n v-bind=\"$attrs\"\n class=\"v-overlay v-snackbar-host\"\n popover=\"manual\"\n :data-placement=\"placement\"\n role=\"region\"\n :aria-label=\"ariaLabel\"\n @pointerenter=\"hold('pointer')\"\n @pointerleave=\"release('pointer')\"\n @focusin=\"onFocusIn\"\n @focusout=\"release('focus')\"\n @beforetoggle=\"syncShown\"\n @toggle=\"syncShown\"\n >\n <div\n v-if=\"current\"\n :key=\"current.id\"\n class=\"v-banner v-snackbar v-tone\"\n :data-tone=\"current.tone\"\n >\n <VIcon v-if=\"icon\" class=\"v-snackbar-icon\" v-bind=\"icon\" />\n <p class=\"v-banner-text v-snackbar-message\">{{ current.message }}</p>\n <VButton\n v-if=\"current.action\"\n class=\"v-banner-control v-snackbar-action\"\n variant=\"ghost\"\n tone=\"neutral\"\n size=\"sm\"\n @click=\"runAction\"\n >\n {{ actionText }}\n </VButton>\n </div>\n </div>\n <span class=\"v-visually-hidden\" role=\"status\">{{ polite }}</span>\n <span class=\"v-visually-hidden\" role=\"alert\">{{ assertive }}</span>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * They are not factored into styles/banner.css because that sheet is paid for by every\n * consumer, and these rules cost more than the size gate allows; change one, change the\n * other. The fixed positioning and the guard hiding a closed container come from the shared\n * `.v-overlay` class, set on this same element.\n */\n .v-snackbar-host {\n margin: 0;\n border: none;\n padding: 0;\n background: transparent;\n overflow: visible;\n width: fit-content;\n }\n\n /*\n * Keep screen placement physical in RTL. The host supplies the entry direction because it\n * knows its screen edge.\n */\n .v-snackbar-host[data-placement^='bottom-'] {\n bottom: var(--vectis-space-4);\n --banner-enter-y: var(--vectis-space-4);\n }\n\n .v-snackbar-host[data-placement$='-left'] {\n left: var(--vectis-space-4);\n }\n\n .v-snackbar-host[data-placement$='-right'] {\n right: var(--vectis-space-4);\n }\n\n .v-snackbar-host[data-placement$='-center'] {\n left: 0;\n right: 0;\n margin-inline: auto;\n }\n\n /* The container fades in and out. Animating an element that is being added to or removed\n from the page needs the two `allow-discrete` declarations and the starting values\n below; a browser missing either simply shows and hides it at once. */\n .v-snackbar-host {\n opacity: 1;\n transition:\n opacity var(--vectis-duration-base) var(--vectis-ease-default),\n overlay var(--vectis-duration-base) allow-discrete,\n display var(--vectis-duration-base) allow-discrete;\n }\n\n .v-snackbar-host:not(:popover-open) {\n opacity: 0;\n }\n\n @starting-style {\n .v-snackbar-host:popover-open {\n opacity: 0;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-snackbar-host {\n transition: none;\n }\n }\n\n /*\n * Shared banner chrome supplies decoration and motion; snackbar owns alignment, dimensions\n * and tone painting.\n */\n .v-snackbar {\n /*\n * Centred, where the notification hooks to its first line. A confirmation is ONE short\n * sentence and it carries a real button: on a message that wraps, aligning to the start\n * would park that button in the top corner, away from the text it answers.\n */\n align-items: center;\n padding-block: var(--vectis-space-3);\n /* The action pulls back into this gutter with a negative margin (`.v-banner-control`),\n rather than the bar declaring a smaller padding at its end. */\n padding-inline: var(--vectis-space-4);\n min-inline-size: var(--vectis-control-size-snackbar-min);\n /* Wide enough for a sentence, never wider than the viewport with the container's own\n margins deducted. */\n max-inline-size: min(\n var(--vectis-control-size-snackbar-max),\n calc(100dvi - 2 * var(--vectis-space-4))\n );\n }\n\n /*\n * Both tones are painted SOLID, and there is deliberately no soft variant: a confirmation is\n * a short-lived object laid over arbitrary content, so it has to read at a glance rather than\n * tint into the page. The tone table itself lives in styles/tones.css, in a layer below the\n * components, and is shared with VButton, VChip and VToast.\n */\n .v-snackbar {\n background: var(--tone-bg-solid);\n color: var(--tone-text-solid);\n }\n\n /*\n * No alignment margin here, unlike the notification: `align-items: center` above already puts\n * the icon on the row's centre line, and an equal margin on a centred item changes nothing at\n * all; it would be a declaration that does no work.\n */\n .v-snackbar-icon {\n --vectis-icon-size: var(--vectis-icon-size-md);\n }\n\n /* An outline draws one without moving the layout by a pixel. */\n @media (forced-colors: active) {\n .v-snackbar {\n outline: 1px solid CanvasText;\n }\n }\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"VSnackbar.js","names":["_sfc_main","VSnackbar_vue_vue_type_script_setup_true_lang_default","VSnackbar_default"],"sources":["../../../src/components/VSnackbar/VSnackbar.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * A manual popover hosts the current confirmation. JavaScript bridges imperative visibility,\n * announcements and a dismissal timer paused during hover or focus.\n */\n\nimport { computed, nextTick, onMounted, ref, watch } from 'vue'\n\nimport { usePopover } from '../../composables/usePopover'\nimport { useTimer } from '../../composables/useTimer'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useLiveAnnouncer } from '../../composables/useLiveAnnouncer'\nimport { useMessages } from '../../i18n/state'\nimport VButton from '../VButton/VButton.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { current, dismissSnackbar, type SnackbarPlacement } from './state'\n\ninterface SnackbarProps {\n /** Which end of the bottom edge confirmations appear at, unless one of them asks for another. */\n placement?: SnackbarPlacement\n /**\n * How long a confirmation stays, in milliseconds, unless it asks for something else. A\n * confirmation given 0 stays until it is replaced or taken away by hand.\n */\n duration?: number\n /**\n * The word drawn on the single action, when the confirmation does not give one. It is\n * also the button's accessible name, and falls back to the design system dictionary.\n */\n actionText?: string\n /**\n * What screen readers announce for the confirmation area itself, which is a landmark of\n * the page. It falls back to the design system dictionary.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<SnackbarProps>(), {\n placement: 'bottom-center',\n /* Shorter than a notification's five seconds. A confirmation is one short sentence\n about something the reader has just done, so they already know what it says. */\n duration: 4000,\n actionText: undefined,\n label: undefined,\n})\n\ndefineOptions({ inheritAttrs: false })\n\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.snackbar.label)\n\nconst placement = computed(() => current.value?.placement ?? props.placement)\nconst actionText = computed(\n () => current.value?.actionText ?? props.actionText ?? m.value.snackbar.action,\n)\nconst icon = computed(() => (current.value?.icon ? iconProps(current.value.icon) : undefined))\n\nconst hostEl = ref<HTMLElement | null>(null)\nconst { syncShown, show, hide } = usePopover(hostEl)\nconst { start, cancel } = useTimer()\n\n/*\n * They are two separate flags rather than one counter because they can be true at the same time\n * and end independently: a reader tabs to the action button, then moves the mouse over the bar,\n * then moves it away; and the bar must not leave while the button still has focus.\n */\nlet hovered = false\nlet focused = false\n\nfunction arm() {\n const bar = current.value\n if (!bar || hovered || focused) return\n const duration = bar.duration ?? props.duration\n /*\n * GUARD, not a default: `useTimer` runs a delay of 0 synchronously, which is the design\n * system's convention for \"no deferral at all\". Without this test a confirmation asking to be\n * permanent would be taken away in the same tick it was raised.\n */\n if (duration > 0) start(() => dismissSnackbar(bar.id), duration)\n}\n\n// @core\n/**\n * Brings the page into line with the state: it shows the container when there is a confirmation\n * and hides it when there is none, and restarts the countdown from the top.\n */\nconst { polite, assertive, announce } = useLiveAnnouncer()\nlet lastAnnounced: number | undefined\n\nfunction sync() {\n cancel()\n // Clear hover/focus flags when removing the bar; a removed focused element may emit no\n // focusout and leave future timers permanently held.\n if (!current.value) hovered = focused = false\n // A REPLACEMENT removes the focused action too (the card is keyed on the bar), and the same\n // engine leaves `focused` standing: the new bar then never left. The toaster's test, where\n // the focus actually is, is the one that holds in both cases.\n else if (!hostEl.value?.contains(document.activeElement)) focused = false\n // @a11y\n // Said through the live regions rendered once beside the host: the card is created WITH\n // its message, and a region inserted along with its text is not reliably announced. A\n // failure interrupts; a plain confirmation waits for a pause.\n if (current.value && current.value.id !== lastAnnounced) {\n lastAnnounced = current.value.id\n announce(current.value.message, current.value.tone === 'danger')\n }\n // Showing and hiding are safe to call on a container already in that state, the guards living\n // in the popover plumbing; so there is no need to remember which it is in.\n if (current.value) show()\n else hide()\n arm()\n}\n\n/*\n * Watching the resolved bar covers a change of confirmation and its disappearance. The `post`\n * timing is load-bearing: the card must already be in the page before its container is told to\n * show itself.\n */\nwatch(current, sync, { flush: 'post' })\n// @ssr\n// A watcher does not run during the server render, so a confirmation raised before\n// this component mounted would never be picked up. Running the same synchronization on\n// mount is what brings it in.\nonMounted(sync)\n\n// @a11y\n// WCAG 2.2.1: something that disappears on a clock has to be holdable, or a slow reader simply\n// never finishes it; and here they would also never reach the button.\n/*\n * Resting the pointer on the bar suspends its countdown, and so does moving the keyboard into\n * it: the action is a real button, so a reader tabbing towards it would otherwise watch it\n * vanish from under the focus ring. VToaster holds its stacks on the same two reasons, with the\n * same verbs, for its close crosses.\n */\nfunction hold(which: 'pointer' | 'focus') {\n if (which === 'pointer') hovered = true\n else focused = true\n cancel()\n}\n\n// Where the focus came from when it entered the bar, to hand it back once the action has\n// taken the bar, and the focused button with it, away.\nlet cameFrom: HTMLElement | null = null\nfunction onFocusIn(event: FocusEvent) {\n const from = event.relatedTarget as HTMLElement | null\n if (from && !hostEl.value?.contains(from)) cameFrom = from\n hold('focus')\n}\n\nfunction release(which: 'pointer' | 'focus') {\n if (which === 'pointer') hovered = false\n else focused = false\n arm()\n}\n\n/* The action always takes the bar away: it answers the confirmation, so leaving it on\n screen would invite the reader to answer it twice. */\nfunction runAction() {\n const bar = current.value\n if (!bar) return\n const hadFocus = !!hostEl.value?.contains(document.activeElement)\n try {\n bar.action?.()\n } finally {\n // Even when the action throws: the error reaches the application, and the bar it\n // answered does not stay on screen, a permanent one for good.\n dismissSnackbar(bar.id)\n // @a11y\n // The focused button leaves with the bar; the focus goes back where it came from\n // rather than falling to `<body>`.\n if (hadFocus)\n void nextTick(() => {\n if (cameFrom?.isConnected && !current.value) cameFrom.focus()\n })\n }\n}\n</script>\n\n<template>\n <div\n ref=\"hostEl\"\n v-bind=\"$attrs\"\n class=\"v-overlay v-snackbar-host\"\n popover=\"manual\"\n :data-placement=\"placement\"\n role=\"region\"\n :aria-label=\"ariaLabel\"\n @pointerenter=\"hold('pointer')\"\n @pointerleave=\"release('pointer')\"\n @focusin=\"onFocusIn\"\n @focusout=\"release('focus')\"\n @beforetoggle=\"syncShown\"\n @toggle=\"syncShown\"\n >\n <div\n v-if=\"current\"\n :key=\"current.id\"\n class=\"v-banner v-snackbar v-tone\"\n :data-tone=\"current.tone\"\n >\n <VIcon v-if=\"icon\" class=\"v-snackbar-icon\" v-bind=\"icon\" />\n <p class=\"v-banner-text v-snackbar-message\">{{ current.message }}</p>\n <VButton\n v-if=\"current.action\"\n class=\"v-banner-control v-snackbar-action\"\n variant=\"ghost\"\n tone=\"neutral\"\n size=\"sm\"\n @click=\"runAction\"\n >\n {{ actionText }}\n </VButton>\n </div>\n </div>\n <span class=\"v-visually-hidden\" role=\"status\">{{ polite }}</span>\n <span class=\"v-visually-hidden\" role=\"alert\">{{ assertive }}</span>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * They are not factored into styles/banner.css because that sheet is paid for by every\n * consumer, and these rules cost more than the size gate allows; change one, change the\n * other. The fixed positioning and the guard hiding a closed container come from the shared\n * `.v-overlay` class, set on this same element.\n */\n .v-snackbar-host {\n margin: 0;\n border: none;\n padding: 0;\n background: transparent;\n overflow: visible;\n width: fit-content;\n }\n\n /*\n * Keep screen placement physical in RTL. The host supplies the entry direction because it\n * knows its screen edge.\n */\n .v-snackbar-host[data-placement^='bottom-'] {\n bottom: var(--vectis-space-4);\n --banner-enter-y: var(--vectis-space-4);\n }\n\n .v-snackbar-host[data-placement$='-left'] {\n left: var(--vectis-space-4);\n }\n\n .v-snackbar-host[data-placement$='-right'] {\n right: var(--vectis-space-4);\n }\n\n .v-snackbar-host[data-placement$='-center'] {\n left: 0;\n right: 0;\n margin-inline: auto;\n }\n\n /* The container fades in and out. Animating an element that is being added to or removed\n from the page needs the two `allow-discrete` declarations and the starting values\n below; a browser missing either simply shows and hides it at once. */\n .v-snackbar-host {\n opacity: 1;\n transition:\n opacity var(--vectis-duration-base) var(--vectis-ease-default),\n overlay var(--vectis-duration-base) allow-discrete,\n display var(--vectis-duration-base) allow-discrete;\n }\n\n .v-snackbar-host:not(:popover-open) {\n opacity: 0;\n }\n\n @starting-style {\n .v-snackbar-host:popover-open {\n opacity: 0;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-snackbar-host {\n transition: none;\n }\n }\n\n /*\n * Shared banner chrome supplies decoration and motion; snackbar owns alignment, dimensions\n * and tone painting.\n */\n .v-snackbar {\n /*\n * Centred, where the notification hooks to its first line. A confirmation is ONE short\n * sentence and it carries a real button: on a message that wraps, aligning to the start\n * would park that button in the top corner, away from the text it answers.\n */\n align-items: center;\n padding-block: var(--vectis-space-3);\n /* The action pulls back into this gutter with a negative margin (`.v-banner-control`),\n rather than the bar declaring a smaller padding at its end. */\n padding-inline: var(--vectis-space-4);\n min-inline-size: var(--vectis-control-size-snackbar-min);\n /* Wide enough for a sentence, never wider than the viewport with the container's own\n margins deducted. */\n max-inline-size: min(\n var(--vectis-control-size-snackbar-max),\n calc(100dvi - 2 * var(--vectis-space-4))\n );\n }\n\n /*\n * Both tones are painted SOLID, and there is deliberately no soft variant: a confirmation is\n * a short-lived object laid over arbitrary content, so it has to read at a glance rather than\n * tint into the page. The tone table itself lives in styles/tones.css, in a layer below the\n * components, and is shared with VButton, VChip and VToast.\n */\n .v-snackbar {\n background: var(--tone-bg-solid);\n color: var(--tone-text-solid);\n }\n\n /*\n * No alignment margin here, unlike the notification: `align-items: center` above already puts\n * the icon on the row's centre line, and an equal margin on a centred item changes nothing at\n * all; it would be a declaration that does no work.\n */\n .v-snackbar-icon {\n --vectis-icon-size: var(--vectis-icon-size-md);\n }\n\n /* An outline draws one without moving the layout by a pixel. */\n @media (forced-colors: active) {\n .v-snackbar {\n outline: 1px solid CanvasText;\n }\n }\n}\n</style>\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VSnackbar.vue_vue_type_script_setup_true_lang.js","names":["$attrs"],"sources":["../../../src/components/VSnackbar/VSnackbar.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * A manual popover hosts the current confirmation. JavaScript bridges imperative visibility,\n * announcements and a dismissal timer paused during hover or focus.\n */\n\nimport { computed, nextTick, onMounted, ref, watch } from 'vue'\n\nimport { usePopover } from '../../composables/usePopover'\nimport { useTimer } from '../../composables/useTimer'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useLiveAnnouncer } from '../../composables/useLiveAnnouncer'\nimport { useMessages } from '../../i18n/state'\nimport VButton from '../VButton/VButton.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { current, dismissSnackbar, type SnackbarPlacement } from './state'\n\ninterface SnackbarProps {\n /** Which end of the bottom edge confirmations appear at, unless one of them asks for another. */\n placement?: SnackbarPlacement\n /**\n * How long a confirmation stays, in milliseconds, unless it asks for something else. A\n * confirmation given 0 stays until it is replaced or taken away by hand.\n */\n duration?: number\n /**\n * The word drawn on the single action, when the confirmation does not give one. It is\n * also the button's accessible name, and falls back to the design system dictionary.\n */\n actionText?: string\n /**\n * What screen readers announce for the confirmation area itself, which is a landmark of\n * the page. It falls back to the design system dictionary.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<SnackbarProps>(), {\n placement: 'bottom-center',\n /* Shorter than a notification's five seconds. A confirmation is one short sentence\n about something the reader has just done, so they already know what it says. */\n duration: 4000,\n actionText: undefined,\n label: undefined,\n})\n\ndefineOptions({ inheritAttrs: false })\n\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.snackbar.label)\n\nconst placement = computed(() => current.value?.placement ?? props.placement)\nconst actionText = computed(\n () => current.value?.actionText ?? props.actionText ?? m.value.snackbar.action,\n)\nconst icon = computed(() => (current.value?.icon ? iconProps(current.value.icon) : undefined))\n\nconst hostEl = ref<HTMLElement | null>(null)\nconst { syncShown, show, hide } = usePopover(hostEl)\nconst { start, cancel } = useTimer()\n\n/*\n * They are two separate flags rather than one counter because they can be true at the same time\n * and end independently: a reader tabs to the action button, then moves the mouse over the bar,\n * then moves it away; and the bar must not leave while the button still has focus.\n */\nlet hovered = false\nlet focused = false\n\nfunction arm() {\n const bar = current.value\n if (!bar || hovered || focused) return\n const duration = bar.duration ?? props.duration\n /*\n * GUARD, not a default: `useTimer` runs a delay of 0 synchronously, which is the design\n * system's convention for \"no deferral at all\". Without this test a confirmation asking to be\n * permanent would be taken away in the same tick it was raised.\n */\n if (duration > 0) start(() => dismissSnackbar(bar.id), duration)\n}\n\n// @core\n/**\n * Brings the page into line with the state: it shows the container when there is a confirmation\n * and hides it when there is none, and restarts the countdown from the top.\n */\nconst { polite, assertive, announce } = useLiveAnnouncer()\nlet lastAnnounced: number | undefined\n\nfunction sync() {\n cancel()\n // Clear hover/focus flags when removing the bar; a removed focused element may emit no\n // focusout and leave future timers permanently held.\n if (!current.value) hovered = focused = false\n // A REPLACEMENT removes the focused action too (the card is keyed on the bar), and the same\n // engine leaves `focused` standing: the new bar then never left. The toaster's test, where\n // the focus actually is, is the one that holds in both cases.\n else if (!hostEl.value?.contains(document.activeElement)) focused = false\n // @a11y\n // Said through the live regions rendered once beside the host: the card is created WITH\n // its message, and a region inserted along with its text is not reliably announced. A\n // failure interrupts; a plain confirmation waits for a pause.\n if (current.value && current.value.id !== lastAnnounced) {\n lastAnnounced = current.value.id\n announce(current.value.message, current.value.tone === 'danger')\n }\n // Showing and hiding are safe to call on a container already in that state, the guards living\n // in the popover plumbing; so there is no need to remember which it is in.\n if (current.value) show()\n else hide()\n arm()\n}\n\n/*\n * Watching the resolved bar covers a change of confirmation and its disappearance. The `post`\n * timing is load-bearing: the card must already be in the page before its container is told to\n * show itself.\n */\nwatch(current, sync, { flush: 'post' })\n// @ssr\n// A watcher does not run during the server render, so a confirmation raised before\n// this component mounted would never be picked up. Running the same synchronization on\n// mount is what brings it in.\nonMounted(sync)\n\n// @a11y\n// WCAG 2.2.1: something that disappears on a clock has to be holdable, or a slow reader simply\n// never finishes it; and here they would also never reach the button.\n/*\n * Resting the pointer on the bar suspends its countdown, and so does moving the keyboard into\n * it: the action is a real button, so a reader tabbing towards it would otherwise watch it\n * vanish from under the focus ring. VToaster holds its stacks on the same two reasons, with the\n * same verbs, for its close crosses.\n */\nfunction hold(which: 'pointer' | 'focus') {\n if (which === 'pointer') hovered = true\n else focused = true\n cancel()\n}\n\n// Where the focus came from when it entered the bar, to hand it back once the action has\n// taken the bar, and the focused button with it, away.\nlet cameFrom: HTMLElement | null = null\nfunction onFocusIn(event: FocusEvent) {\n const from = event.relatedTarget as HTMLElement | null\n if (from && !hostEl.value?.contains(from)) cameFrom = from\n hold('focus')\n}\n\nfunction release(which: 'pointer' | 'focus') {\n if (which === 'pointer') hovered = false\n else focused = false\n arm()\n}\n\n/* The action always takes the bar away: it answers the confirmation, so leaving it on\n screen would invite the reader to answer it twice. */\nfunction runAction() {\n const bar = current.value\n if (!bar) return\n const hadFocus = !!hostEl.value?.contains(document.activeElement)\n try {\n bar.action?.()\n } finally {\n // Even when the action throws: the error reaches the application, and the bar it\n // answered does not stay on screen, a permanent one for good.\n dismissSnackbar(bar.id)\n // @a11y\n // The focused button leaves with the bar; the focus goes back where it came from\n // rather than falling to `<body>`.\n if (hadFocus)\n void nextTick(() => {\n if (cameFrom?.isConnected && !current.value) cameFrom.focus()\n })\n }\n}\n</script>\n\n<template>\n <div\n ref=\"hostEl\"\n v-bind=\"$attrs\"\n class=\"v-overlay v-snackbar-host\"\n popover=\"manual\"\n :data-placement=\"placement\"\n role=\"region\"\n :aria-label=\"ariaLabel\"\n @pointerenter=\"hold('pointer')\"\n @pointerleave=\"release('pointer')\"\n @focusin=\"onFocusIn\"\n @focusout=\"release('focus')\"\n @beforetoggle=\"syncShown\"\n @toggle=\"syncShown\"\n >\n <div\n v-if=\"current\"\n :key=\"current.id\"\n class=\"v-banner v-snackbar v-tone\"\n :data-tone=\"current.tone\"\n >\n <VIcon v-if=\"icon\" class=\"v-snackbar-icon\" v-bind=\"icon\" />\n <p class=\"v-banner-text v-snackbar-message\">{{ current.message }}</p>\n <VButton\n v-if=\"current.action\"\n class=\"v-banner-control v-snackbar-action\"\n variant=\"ghost\"\n tone=\"neutral\"\n size=\"sm\"\n @click=\"runAction\"\n >\n {{ actionText }}\n </VButton>\n </div>\n </div>\n <span class=\"v-visually-hidden\" role=\"status\">{{ polite }}</span>\n <span class=\"v-visually-hidden\" role=\"alert\">{{ assertive }}</span>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * They are not factored into styles/banner.css because that sheet is paid for by every\n * consumer, and these rules cost more than the size gate allows; change one, change the\n * other. The fixed positioning and the guard hiding a closed container come from the shared\n * `.v-overlay` class, set on this same element.\n */\n .v-snackbar-host {\n margin: 0;\n border: none;\n padding: 0;\n background: transparent;\n overflow: visible;\n width: fit-content;\n }\n\n /*\n * Keep screen placement physical in RTL. The host supplies the entry direction because it\n * knows its screen edge.\n */\n .v-snackbar-host[data-placement^='bottom-'] {\n bottom: var(--vectis-space-4);\n --banner-enter-y: var(--vectis-space-4);\n }\n\n .v-snackbar-host[data-placement$='-left'] {\n left: var(--vectis-space-4);\n }\n\n .v-snackbar-host[data-placement$='-right'] {\n right: var(--vectis-space-4);\n }\n\n .v-snackbar-host[data-placement$='-center'] {\n left: 0;\n right: 0;\n margin-inline: auto;\n }\n\n /* The container fades in and out. Animating an element that is being added to or removed\n from the page needs the two `allow-discrete` declarations and the starting values\n below; a browser missing either simply shows and hides it at once. */\n .v-snackbar-host {\n opacity: 1;\n transition:\n opacity var(--vectis-duration-base) var(--vectis-ease-default),\n overlay var(--vectis-duration-base) allow-discrete,\n display var(--vectis-duration-base) allow-discrete;\n }\n\n .v-snackbar-host:not(:popover-open) {\n opacity: 0;\n }\n\n @starting-style {\n .v-snackbar-host:popover-open {\n opacity: 0;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-snackbar-host {\n transition: none;\n }\n }\n\n /*\n * Shared banner chrome supplies decoration and motion; snackbar owns alignment, dimensions\n * and tone painting.\n */\n .v-snackbar {\n /*\n * Centred, where the notification hooks to its first line. A confirmation is ONE short\n * sentence and it carries a real button: on a message that wraps, aligning to the start\n * would park that button in the top corner, away from the text it answers.\n */\n align-items: center;\n padding-block: var(--vectis-space-3);\n /* The action pulls back into this gutter with a negative margin (`.v-banner-control`),\n rather than the bar declaring a smaller padding at its end. */\n padding-inline: var(--vectis-space-4);\n min-inline-size: var(--vectis-control-size-snackbar-min);\n /* Wide enough for a sentence, never wider than the viewport with the container's own\n margins deducted. */\n max-inline-size: min(\n var(--vectis-control-size-snackbar-max),\n calc(100dvi - 2 * var(--vectis-space-4))\n );\n }\n\n /*\n * Both tones are painted SOLID, and there is deliberately no soft variant: a confirmation is\n * a short-lived object laid over arbitrary content, so it has to read at a glance rather than\n * tint into the page. The tone table itself lives in styles/tones.css, in a layer below the\n * components, and is shared with VButton, VChip and VToast.\n */\n .v-snackbar {\n background: var(--tone-bg-solid);\n color: var(--tone-text-solid);\n }\n\n /*\n * No alignment margin here, unlike the notification: `align-items: center` above already puts\n * the icon on the row's centre line, and an equal margin on a centred item changes nothing at\n * all; it would be a declaration that does no work.\n */\n .v-snackbar-icon {\n --vectis-icon-size: var(--vectis-icon-size-md);\n }\n\n /* An outline draws one without moving the layout by a pixel. */\n @media (forced-colors: active) {\n .v-snackbar {\n outline: 1px solid CanvasText;\n }\n }\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VSnackbar.vue_vue_type_script_setup_true_lang.js","names":["$attrs","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_mergeProps","_unref","_createBlock","VIcon","_createCommentVNode","_toDisplayString","VButton","_withCtx","_createTextVNode","VIcon_default","VButton_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","VSnackbar_vue_vue_type_script_setup_true_lang_default","props","__props","m","useMessages","ariaLabel","useAriaLabel","placement","computed","current","actionText","icon","iconProps","hostEl","ref","usePopover","useTimer","hovered","focused","arm","bar","duration","start","dismissSnackbar","useLiveAnnouncer","lastAnnounced","sync","cancel","announce","show","hide","watch","onMounted","hold","which","cameFrom","onFocusIn","from","event","release","runAction","hadFocus","nextTick","openBlock","createElementBlock","Fragment","createElementVNode","mergeProps","_ctx","unref","_cache","args","syncShown","createBlock","createCommentVNode","toDisplayString","withCtx","createTextVNode","polite","assertive"],"sources":["../../../src/components/VSnackbar/VSnackbar.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * A manual popover hosts the current confirmation. JavaScript bridges imperative visibility,\n * announcements and a dismissal timer paused during hover or focus.\n */\n\nimport { computed, nextTick, onMounted, ref, watch } from 'vue'\n\nimport { usePopover } from '../../composables/usePopover'\nimport { useTimer } from '../../composables/useTimer'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useLiveAnnouncer } from '../../composables/useLiveAnnouncer'\nimport { useMessages } from '../../i18n/state'\nimport VButton from '../VButton/VButton.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { current, dismissSnackbar, type SnackbarPlacement } from './state'\n\ninterface SnackbarProps {\n /** Which end of the bottom edge confirmations appear at, unless one of them asks for another. */\n placement?: SnackbarPlacement\n /**\n * How long a confirmation stays, in milliseconds, unless it asks for something else. A\n * confirmation given 0 stays until it is replaced or taken away by hand.\n */\n duration?: number\n /**\n * The word drawn on the single action, when the confirmation does not give one. It is\n * also the button's accessible name, and falls back to the design system dictionary.\n */\n actionText?: string\n /**\n * What screen readers announce for the confirmation area itself, which is a landmark of\n * the page. It falls back to the design system dictionary.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<SnackbarProps>(), {\n placement: 'bottom-center',\n /* Shorter than a notification's five seconds. A confirmation is one short sentence\n about something the reader has just done, so they already know what it says. */\n duration: 4000,\n actionText: undefined,\n label: undefined,\n})\n\ndefineOptions({ inheritAttrs: false })\n\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.snackbar.label)\n\nconst placement = computed(() => current.value?.placement ?? props.placement)\nconst actionText = computed(\n () => current.value?.actionText ?? props.actionText ?? m.value.snackbar.action,\n)\nconst icon = computed(() => (current.value?.icon ? iconProps(current.value.icon) : undefined))\n\nconst hostEl = ref<HTMLElement | null>(null)\nconst { syncShown, show, hide } = usePopover(hostEl)\nconst { start, cancel } = useTimer()\n\n/*\n * They are two separate flags rather than one counter because they can be true at the same time\n * and end independently: a reader tabs to the action button, then moves the mouse over the bar,\n * then moves it away; and the bar must not leave while the button still has focus.\n */\nlet hovered = false\nlet focused = false\n\nfunction arm() {\n const bar = current.value\n if (!bar || hovered || focused) return\n const duration = bar.duration ?? props.duration\n /*\n * GUARD, not a default: `useTimer` runs a delay of 0 synchronously, which is the design\n * system's convention for \"no deferral at all\". Without this test a confirmation asking to be\n * permanent would be taken away in the same tick it was raised.\n */\n if (duration > 0) start(() => dismissSnackbar(bar.id), duration)\n}\n\n// @core\n/**\n * Brings the page into line with the state: it shows the container when there is a confirmation\n * and hides it when there is none, and restarts the countdown from the top.\n */\nconst { polite, assertive, announce } = useLiveAnnouncer()\nlet lastAnnounced: number | undefined\n\nfunction sync() {\n cancel()\n // Clear hover/focus flags when removing the bar; a removed focused element may emit no\n // focusout and leave future timers permanently held.\n if (!current.value) hovered = focused = false\n // A REPLACEMENT removes the focused action too (the card is keyed on the bar), and the same\n // engine leaves `focused` standing: the new bar then never left. The toaster's test, where\n // the focus actually is, is the one that holds in both cases.\n else if (!hostEl.value?.contains(document.activeElement)) focused = false\n // @a11y\n // Said through the live regions rendered once beside the host: the card is created WITH\n // its message, and a region inserted along with its text is not reliably announced. A\n // failure interrupts; a plain confirmation waits for a pause.\n if (current.value && current.value.id !== lastAnnounced) {\n lastAnnounced = current.value.id\n announce(current.value.message, current.value.tone === 'danger')\n }\n // Showing and hiding are safe to call on a container already in that state, the guards living\n // in the popover plumbing; so there is no need to remember which it is in.\n if (current.value) show()\n else hide()\n arm()\n}\n\n/*\n * Watching the resolved bar covers a change of confirmation and its disappearance. The `post`\n * timing is load-bearing: the card must already be in the page before its container is told to\n * show itself.\n */\nwatch(current, sync, { flush: 'post' })\n// @ssr\n// A watcher does not run during the server render, so a confirmation raised before\n// this component mounted would never be picked up. Running the same synchronization on\n// mount is what brings it in.\nonMounted(sync)\n\n// @a11y\n// WCAG 2.2.1: something that disappears on a clock has to be holdable, or a slow reader simply\n// never finishes it; and here they would also never reach the button.\n/*\n * Resting the pointer on the bar suspends its countdown, and so does moving the keyboard into\n * it: the action is a real button, so a reader tabbing towards it would otherwise watch it\n * vanish from under the focus ring. VToaster holds its stacks on the same two reasons, with the\n * same verbs, for its close crosses.\n */\nfunction hold(which: 'pointer' | 'focus') {\n if (which === 'pointer') hovered = true\n else focused = true\n cancel()\n}\n\n// Where the focus came from when it entered the bar, to hand it back once the action has\n// taken the bar, and the focused button with it, away.\nlet cameFrom: HTMLElement | null = null\nfunction onFocusIn(event: FocusEvent) {\n const from = event.relatedTarget as HTMLElement | null\n if (from && !hostEl.value?.contains(from)) cameFrom = from\n hold('focus')\n}\n\nfunction release(which: 'pointer' | 'focus') {\n if (which === 'pointer') hovered = false\n else focused = false\n arm()\n}\n\n/* The action always takes the bar away: it answers the confirmation, so leaving it on\n screen would invite the reader to answer it twice. */\nfunction runAction() {\n const bar = current.value\n if (!bar) return\n const hadFocus = !!hostEl.value?.contains(document.activeElement)\n try {\n bar.action?.()\n } finally {\n // Even when the action throws: the error reaches the application, and the bar it\n // answered does not stay on screen, a permanent one for good.\n dismissSnackbar(bar.id)\n // @a11y\n // The focused button leaves with the bar; the focus goes back where it came from\n // rather than falling to `<body>`.\n if (hadFocus)\n void nextTick(() => {\n if (cameFrom?.isConnected && !current.value) cameFrom.focus()\n })\n }\n}\n</script>\n\n<template>\n <div\n ref=\"hostEl\"\n v-bind=\"$attrs\"\n class=\"v-overlay v-snackbar-host\"\n popover=\"manual\"\n :data-placement=\"placement\"\n role=\"region\"\n :aria-label=\"ariaLabel\"\n @pointerenter=\"hold('pointer')\"\n @pointerleave=\"release('pointer')\"\n @focusin=\"onFocusIn\"\n @focusout=\"release('focus')\"\n @beforetoggle=\"syncShown\"\n @toggle=\"syncShown\"\n >\n <div\n v-if=\"current\"\n :key=\"current.id\"\n class=\"v-banner v-snackbar v-tone\"\n :data-tone=\"current.tone\"\n >\n <VIcon v-if=\"icon\" class=\"v-snackbar-icon\" v-bind=\"icon\" />\n <p class=\"v-banner-text v-snackbar-message\">{{ current.message }}</p>\n <VButton\n v-if=\"current.action\"\n class=\"v-banner-control v-snackbar-action\"\n variant=\"ghost\"\n tone=\"neutral\"\n size=\"sm\"\n @click=\"runAction\"\n >\n {{ actionText }}\n </VButton>\n </div>\n </div>\n <span class=\"v-visually-hidden\" role=\"status\">{{ polite }}</span>\n <span class=\"v-visually-hidden\" role=\"alert\">{{ assertive }}</span>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * They are not factored into styles/banner.css because that sheet is paid for by every\n * consumer, and these rules cost more than the size gate allows; change one, change the\n * other. The fixed positioning and the guard hiding a closed container come from the shared\n * `.v-overlay` class, set on this same element.\n */\n .v-snackbar-host {\n margin: 0;\n border: none;\n padding: 0;\n background: transparent;\n overflow: visible;\n width: fit-content;\n }\n\n /*\n * Keep screen placement physical in RTL. The host supplies the entry direction because it\n * knows its screen edge.\n */\n .v-snackbar-host[data-placement^='bottom-'] {\n bottom: var(--vectis-space-4);\n --banner-enter-y: var(--vectis-space-4);\n }\n\n .v-snackbar-host[data-placement$='-left'] {\n left: var(--vectis-space-4);\n }\n\n .v-snackbar-host[data-placement$='-right'] {\n right: var(--vectis-space-4);\n }\n\n .v-snackbar-host[data-placement$='-center'] {\n left: 0;\n right: 0;\n margin-inline: auto;\n }\n\n /* The container fades in and out. Animating an element that is being added to or removed\n from the page needs the two `allow-discrete` declarations and the starting values\n below; a browser missing either simply shows and hides it at once. */\n .v-snackbar-host {\n opacity: 1;\n transition:\n opacity var(--vectis-duration-base) var(--vectis-ease-default),\n overlay var(--vectis-duration-base) allow-discrete,\n display var(--vectis-duration-base) allow-discrete;\n }\n\n .v-snackbar-host:not(:popover-open) {\n opacity: 0;\n }\n\n @starting-style {\n .v-snackbar-host:popover-open {\n opacity: 0;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-snackbar-host {\n transition: none;\n }\n }\n\n /*\n * Shared banner chrome supplies decoration and motion; snackbar owns alignment, dimensions\n * and tone painting.\n */\n .v-snackbar {\n /*\n * Centred, where the notification hooks to its first line. A confirmation is ONE short\n * sentence and it carries a real button: on a message that wraps, aligning to the start\n * would park that button in the top corner, away from the text it answers.\n */\n align-items: center;\n padding-block: var(--vectis-space-3);\n /* The action pulls back into this gutter with a negative margin (`.v-banner-control`),\n rather than the bar declaring a smaller padding at its end. */\n padding-inline: var(--vectis-space-4);\n min-inline-size: var(--vectis-control-size-snackbar-min);\n /* Wide enough for a sentence, never wider than the viewport with the container's own\n margins deducted. */\n max-inline-size: min(\n var(--vectis-control-size-snackbar-max),\n calc(100dvi - 2 * var(--vectis-space-4))\n );\n }\n\n /*\n * Both tones are painted SOLID, and there is deliberately no soft variant: a confirmation is\n * a short-lived object laid over arbitrary content, so it has to read at a glance rather than\n * tint into the page. The tone table itself lives in styles/tones.css, in a layer below the\n * components, and is shared with VButton, VChip and VToast.\n */\n .v-snackbar {\n background: var(--tone-bg-solid);\n color: var(--tone-text-solid);\n }\n\n /*\n * No alignment margin here, unlike the notification: `align-items: center` above already puts\n * the icon on the row's centre line, and an equal margin on a centred item changes nothing at\n * all; it would be a declaration that does no work.\n */\n .v-snackbar-icon {\n --vectis-icon-size: var(--vectis-icon-size-md);\n }\n\n /* An outline draws one without moving the layout by a pixel. */\n @media (forced-colors: active) {\n .v-snackbar {\n outline: 1px solid CanvasText;\n }\n }\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","names":[],"sources":["../../../src/components/VSnackbar/state.ts"],"sourcesContent":["// @ssr @core\n/**\n * A snackbar confirms the last action a reader took, which separates it from a notification\n * reporting a state: notifications stack, because two things can be true at once, whereas only\n * the last action is worth offering to undo. So there is at most one snackbar at a time, and\n * raising a new one replaces the one before it on the spot.\n */\n\nimport { shallowRef } from 'vue'\n\nimport type { IconSource } from '../VIcon/types'\nimport { isDev } from '../../utils/env'\n\n/** What a confirmation can mean. */\nexport type SnackbarTone = 'neutral' | 'danger'\n\n/** Where the bar appears. Along the bottom edge only, where it is out of the content's way. */\nexport type SnackbarPlacement = 'bottom-left' | 'bottom-center' | 'bottom-right'\n\n/** Everything one can say when raising a confirmation. */\nexport interface SnackbarOptions {\n /** What the confirmation says. One short sentence: there is no title to frame it. */\n message: string\n /** What it means, expressed as a colour: the plain inversion, or the failure colour. */\n tone?: SnackbarTone\n /**\n * An icon before the message. There is none by default, and none is deduced from the\n * tone: a confirmation is read, not scanned.\n */\n icon?: IconSource\n /**\n * How long it stays, in milliseconds. Setting it to 0 keeps it until it is replaced or\n * taken away by hand, and leaving it out takes the duration set on the VSnackbar.\n */\n duration?: number\n /** Which end of the bottom edge it appears at. Left out, it takes the one set on the VSnackbar. */\n placement?: SnackbarPlacement\n /**\n * What the single action does. Left out, the bar carries no button at all. Running it\n * always takes the bar away, so nothing has to be dismissed by hand afterwards.\n */\n action?: () => void\n /** The word drawn on that action. It falls back to the design system dictionary (\"Undo\"). */\n actionText?: string\n}\n\n/**\n * A confirmation once it is showing, with the choices the caller left out filled in and\n * an id of its own. Internal: it is what VSnackbar renders.\n */\nexport interface SnackbarItem extends SnackbarOptions {\n id: number\n tone: SnackbarTone\n}\n\n/** The bar currently showing, if any. Internal, read only by VSnackbar, and not public API. */\nexport const current = shallowRef<SnackbarItem | null>(null)\n\nlet nextId = 0\n\n/**\n * Raises a confirmation, replacing whatever was showing, and hands back an id that\n * `dismissSnackbar` can use to take it away again before its time.\n */\nexport function snackbar(options: SnackbarOptions): number {\n // @devwarn\n // The queue is module state, shared by every request a server renders: raised there, it\n // would reach another reader's page and differ from what the browser hydrates.\n if (isDev && typeof document === 'undefined')\n console.warn(\n '[snackbar] called during a server render: call it from a handler or a lifecycle hook that runs in the browser.',\n )\n const id = nextId++\n current.value = { tone: 'neutral', ...options, id }\n return id\n}\n\n/**\n * Takes a confirmation away, by its id or, called with no argument, whichever one is showing.\n * Passing an id is not a formality: it is checked against the bar actually on screen, and a\n * stale one is ignored.\n */\nexport function dismissSnackbar(id?: number): void {\n if (id === undefined || current.value?.id === id) current.value = null\n}\n"],"mappings":";;;AAwDA,
|
|
1
|
+
{"version":3,"file":"state.js","names":["current","shallowRef","nextId","snackbar","isDev","id","options","dismissSnackbar"],"sources":["../../../src/components/VSnackbar/state.ts"],"sourcesContent":["// @ssr @core\n/**\n * A snackbar confirms the last action a reader took, which separates it from a notification\n * reporting a state: notifications stack, because two things can be true at once, whereas only\n * the last action is worth offering to undo. So there is at most one snackbar at a time, and\n * raising a new one replaces the one before it on the spot.\n */\n\nimport { shallowRef } from 'vue'\n\nimport type { IconSource } from '../VIcon/types'\nimport { isDev } from '../../utils/env'\n\n/** What a confirmation can mean. */\nexport type SnackbarTone = 'neutral' | 'danger'\n\n/** Where the bar appears. Along the bottom edge only, where it is out of the content's way. */\nexport type SnackbarPlacement = 'bottom-left' | 'bottom-center' | 'bottom-right'\n\n/** Everything one can say when raising a confirmation. */\nexport interface SnackbarOptions {\n /** What the confirmation says. One short sentence: there is no title to frame it. */\n message: string\n /** What it means, expressed as a colour: the plain inversion, or the failure colour. */\n tone?: SnackbarTone\n /**\n * An icon before the message. There is none by default, and none is deduced from the\n * tone: a confirmation is read, not scanned.\n */\n icon?: IconSource\n /**\n * How long it stays, in milliseconds. Setting it to 0 keeps it until it is replaced or\n * taken away by hand, and leaving it out takes the duration set on the VSnackbar.\n */\n duration?: number\n /** Which end of the bottom edge it appears at. Left out, it takes the one set on the VSnackbar. */\n placement?: SnackbarPlacement\n /**\n * What the single action does. Left out, the bar carries no button at all. Running it\n * always takes the bar away, so nothing has to be dismissed by hand afterwards.\n */\n action?: () => void\n /** The word drawn on that action. It falls back to the design system dictionary (\"Undo\"). */\n actionText?: string\n}\n\n/**\n * A confirmation once it is showing, with the choices the caller left out filled in and\n * an id of its own. Internal: it is what VSnackbar renders.\n */\nexport interface SnackbarItem extends SnackbarOptions {\n id: number\n tone: SnackbarTone\n}\n\n/** The bar currently showing, if any. Internal, read only by VSnackbar, and not public API. */\nexport const current = shallowRef<SnackbarItem | null>(null)\n\nlet nextId = 0\n\n/**\n * Raises a confirmation, replacing whatever was showing, and hands back an id that\n * `dismissSnackbar` can use to take it away again before its time.\n */\nexport function snackbar(options: SnackbarOptions): number {\n // @devwarn\n // The queue is module state, shared by every request a server renders: raised there, it\n // would reach another reader's page and differ from what the browser hydrates.\n if (isDev && typeof document === 'undefined')\n console.warn(\n '[snackbar] called during a server render: call it from a handler or a lifecycle hook that runs in the browser.',\n )\n const id = nextId++\n current.value = { tone: 'neutral', ...options, id }\n return id\n}\n\n/**\n * Takes a confirmation away, by its id or, called with no argument, whichever one is showing.\n * Passing an id is not a formality: it is checked against the bar actually on screen, and a\n * stale one is ignored.\n */\nexport function dismissSnackbar(id?: number): void {\n if (id === undefined || current.value?.id === id) current.value = null\n}\n"],"mappings":";;;AAwDA,IAAaA,IAAUC,EAAgC,IAAI,GAEvDC,IAAS;AAMb,SAAgBC,EAAS,GAAkC;CAIzD,AAAIC,KAAS,OAAO,WAAa,OAC/B,QAAQ,KACN,gHACF;CACF,IAAMC,IAAK;CAEX,OADA,EAAQ,QAAQ;EAAE,MAAM;EAAW,GAAGC;EAAS;CAAG,GAC3CD;AACT;AAOA,SAAgBE,EAAgB,GAAmB;CACjD,CAAIF,MAAO,KAAA,KAAaL,EAAQ,OAAO,OAAOK,OAAI,EAAQ,QAAQ;AACpE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VSpinner.js","names":[],"sources":["../../../src/components/VSpinner/VSpinner.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Animate the ring in CSS; JavaScript resolves its accessible status label and normalizes\n * icon-compatible dimensions.\n */\nimport { computed } from 'vue'\n\nimport { useMessages } from '../../i18n/state'\nimport { px } from '../../utils/css'\n\ninterface SpinnerProps {\n /**\n * A size in pixels, as a number or a numeric string, understood exactly as VIcon's: it is the\n * BOX the spinner occupies, not the diameter of the ring, which is drawn slightly smaller\n * inside it.\n */\n size?: number | string\n /**\n * What screen readers announce while it turns. It falls back to the wording of the\n * design system dictionary, in the current language.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<SpinnerProps>(), {\n size: undefined,\n label: undefined,\n})\n\nconst m = useMessages()\nconst resolvedLabel = computed(() => props.label ?? m.value.common.loading)\n</script>\n\n<template>\n <span\n class=\"v-spinner\"\n :style=\"px(size) !== undefined ? { '--spinner-size': px(size) } : undefined\"\n role=\"status\"\n >\n <span class=\"v-spinner-circle\" aria-hidden=\"true\" />\n <span class=\"v-visually-hidden\">{{ resolvedLabel }}</span>\n </span>\n</template>\n\n<style>\n@layer vectis.components {\n .v-spinner {\n /* Draw at five sixths of the icon box to match the Material Symbols spinner's visible size. */\n --spinner-size: 1em;\n --spinner-ring: calc(var(--spinner-size) * 5 / 6);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: var(--spinner-size);\n height: var(--spinner-size);\n }\n\n .v-spinner-circle {\n width: var(--spinner-ring);\n height: var(--spinner-ring);\n /*\n * The stroke is a tenth of the ring's diameter, the same source again: the glyph's outer\n * radius is 400 and its inner one 320, so its stroke is 80 of those 800. It stays in\n * proportion at every size, with a one-pixel floor below which the ring would disappear; a\n * floor that only bites under a 12px box, which no size of the design system reaches.\n */\n border: max(1px, calc(var(--spinner-ring) / 10)) solid\n color-mix(in oklab, currentcolor, transparent 75%);\n border-block-start-color: currentcolor;\n border-radius: var(--vectis-radius-pill);\n animation: v-spinner-spin var(--vectis-duration-1000) linear infinite;\n }\n\n /* Written here and not shared with VProgressCircular's identical turn: in the core sheet\n it cost every consumer the bytes, and the two copies only meet in an application that\n loads both components. The name carries the component, since keyframe names are global. */\n @keyframes v-spinner-spin {\n to {\n transform: rotate(1turn);\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-spinner-circle {\n animation-duration: var(--vectis-duration-3000);\n }\n }\n\n /*\n * The track is drawn in GrayText and the moving quarter keeps the forced text colour, which\n * `currentcolor` still reads here since only the circle opts out of the forcing.\n */\n @media (forced-colors: active) {\n .v-spinner-circle {\n forced-color-adjust: none;\n border-color: GrayText;\n border-block-start-color: currentcolor;\n }\n }\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"VSpinner.js","names":["_sfc_main","VSpinner_vue_vue_type_script_setup_true_lang_default","VSpinner_default"],"sources":["../../../src/components/VSpinner/VSpinner.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Animate the ring in CSS; JavaScript resolves its accessible status label and normalizes\n * icon-compatible dimensions.\n */\nimport { computed } from 'vue'\n\nimport { useMessages } from '../../i18n/state'\nimport { px } from '../../utils/css'\n\ninterface SpinnerProps {\n /**\n * A size in pixels, as a number or a numeric string, understood exactly as VIcon's: it is the\n * BOX the spinner occupies, not the diameter of the ring, which is drawn slightly smaller\n * inside it.\n */\n size?: number | string\n /**\n * What screen readers announce while it turns. It falls back to the wording of the\n * design system dictionary, in the current language.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<SpinnerProps>(), {\n size: undefined,\n label: undefined,\n})\n\nconst m = useMessages()\nconst resolvedLabel = computed(() => props.label ?? m.value.common.loading)\n</script>\n\n<template>\n <span\n class=\"v-spinner\"\n :style=\"px(size) !== undefined ? { '--spinner-size': px(size) } : undefined\"\n role=\"status\"\n >\n <span class=\"v-spinner-circle\" aria-hidden=\"true\" />\n <span class=\"v-visually-hidden\">{{ resolvedLabel }}</span>\n </span>\n</template>\n\n<style>\n@layer vectis.components {\n .v-spinner {\n /* Draw at five sixths of the icon box to match the Material Symbols spinner's visible size. */\n --spinner-size: 1em;\n --spinner-ring: calc(var(--spinner-size) * 5 / 6);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: var(--spinner-size);\n height: var(--spinner-size);\n }\n\n .v-spinner-circle {\n width: var(--spinner-ring);\n height: var(--spinner-ring);\n /*\n * The stroke is a tenth of the ring's diameter, the same source again: the glyph's outer\n * radius is 400 and its inner one 320, so its stroke is 80 of those 800. It stays in\n * proportion at every size, with a one-pixel floor below which the ring would disappear; a\n * floor that only bites under a 12px box, which no size of the design system reaches.\n */\n border: max(1px, calc(var(--spinner-ring) / 10)) solid\n color-mix(in oklab, currentcolor, transparent 75%);\n border-block-start-color: currentcolor;\n border-radius: var(--vectis-radius-pill);\n animation: v-spinner-spin var(--vectis-duration-1000) linear infinite;\n }\n\n /* Written here and not shared with VProgressCircular's identical turn: in the core sheet\n it cost every consumer the bytes, and the two copies only meet in an application that\n loads both components. The name carries the component, since keyframe names are global. */\n @keyframes v-spinner-spin {\n to {\n transform: rotate(1turn);\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-spinner-circle {\n animation-duration: var(--vectis-duration-3000);\n }\n }\n\n /*\n * The track is drawn in GrayText and the moving quarter keeps the forced text colour, which\n * `currentcolor` still reads here since only the circle opts out of the forcing.\n */\n @media (forced-colors: active) {\n .v-spinner-circle {\n forced-color-adjust: none;\n border-color: GrayText;\n border-block-start-color: currentcolor;\n }\n }\n}\n</style>\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VSpinner.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VSpinner/VSpinner.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Animate the ring in CSS; JavaScript resolves its accessible status label and normalizes\n * icon-compatible dimensions.\n */\nimport { computed } from 'vue'\n\nimport { useMessages } from '../../i18n/state'\nimport { px } from '../../utils/css'\n\ninterface SpinnerProps {\n /**\n * A size in pixels, as a number or a numeric string, understood exactly as VIcon's: it is the\n * BOX the spinner occupies, not the diameter of the ring, which is drawn slightly smaller\n * inside it.\n */\n size?: number | string\n /**\n * What screen readers announce while it turns. It falls back to the wording of the\n * design system dictionary, in the current language.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<SpinnerProps>(), {\n size: undefined,\n label: undefined,\n})\n\nconst m = useMessages()\nconst resolvedLabel = computed(() => props.label ?? m.value.common.loading)\n</script>\n\n<template>\n <span\n class=\"v-spinner\"\n :style=\"px(size) !== undefined ? { '--spinner-size': px(size) } : undefined\"\n role=\"status\"\n >\n <span class=\"v-spinner-circle\" aria-hidden=\"true\" />\n <span class=\"v-visually-hidden\">{{ resolvedLabel }}</span>\n </span>\n</template>\n\n<style>\n@layer vectis.components {\n .v-spinner {\n /* Draw at five sixths of the icon box to match the Material Symbols spinner's visible size. */\n --spinner-size: 1em;\n --spinner-ring: calc(var(--spinner-size) * 5 / 6);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: var(--spinner-size);\n height: var(--spinner-size);\n }\n\n .v-spinner-circle {\n width: var(--spinner-ring);\n height: var(--spinner-ring);\n /*\n * The stroke is a tenth of the ring's diameter, the same source again: the glyph's outer\n * radius is 400 and its inner one 320, so its stroke is 80 of those 800. It stays in\n * proportion at every size, with a one-pixel floor below which the ring would disappear; a\n * floor that only bites under a 12px box, which no size of the design system reaches.\n */\n border: max(1px, calc(var(--spinner-ring) / 10)) solid\n color-mix(in oklab, currentcolor, transparent 75%);\n border-block-start-color: currentcolor;\n border-radius: var(--vectis-radius-pill);\n animation: v-spinner-spin var(--vectis-duration-1000) linear infinite;\n }\n\n /* Written here and not shared with VProgressCircular's identical turn: in the core sheet\n it cost every consumer the bytes, and the two copies only meet in an application that\n loads both components. The name carries the component, since keyframe names are global. */\n @keyframes v-spinner-spin {\n to {\n transform: rotate(1turn);\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-spinner-circle {\n animation-duration: var(--vectis-duration-3000);\n }\n }\n\n /*\n * The track is drawn in GrayText and the moving quarter keeps the forced text colour, which\n * `currentcolor` still reads here since only the circle opts out of the forcing.\n */\n @media (forced-colors: active) {\n .v-spinner-circle {\n forced-color-adjust: none;\n border-color: GrayText;\n border-block-start-color: currentcolor;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;EAyBA,
|
|
1
|
+
{"version":3,"file":"VSpinner.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_normalizeStyle","_unref","_createElementVNode","_toDisplayString","_hoisted_1","VSpinner_vue_vue_type_script_setup_true_lang_default","props","__props","m","useMessages","resolvedLabel","computed","openBlock","createElementBlock","normalizeStyle","unref","px","_cache","createElementVNode","toDisplayString"],"sources":["../../../src/components/VSpinner/VSpinner.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Animate the ring in CSS; JavaScript resolves its accessible status label and normalizes\n * icon-compatible dimensions.\n */\nimport { computed } from 'vue'\n\nimport { useMessages } from '../../i18n/state'\nimport { px } from '../../utils/css'\n\ninterface SpinnerProps {\n /**\n * A size in pixels, as a number or a numeric string, understood exactly as VIcon's: it is the\n * BOX the spinner occupies, not the diameter of the ring, which is drawn slightly smaller\n * inside it.\n */\n size?: number | string\n /**\n * What screen readers announce while it turns. It falls back to the wording of the\n * design system dictionary, in the current language.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<SpinnerProps>(), {\n size: undefined,\n label: undefined,\n})\n\nconst m = useMessages()\nconst resolvedLabel = computed(() => props.label ?? m.value.common.loading)\n</script>\n\n<template>\n <span\n class=\"v-spinner\"\n :style=\"px(size) !== undefined ? { '--spinner-size': px(size) } : undefined\"\n role=\"status\"\n >\n <span class=\"v-spinner-circle\" aria-hidden=\"true\" />\n <span class=\"v-visually-hidden\">{{ resolvedLabel }}</span>\n </span>\n</template>\n\n<style>\n@layer vectis.components {\n .v-spinner {\n /* Draw at five sixths of the icon box to match the Material Symbols spinner's visible size. */\n --spinner-size: 1em;\n --spinner-ring: calc(var(--spinner-size) * 5 / 6);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: var(--spinner-size);\n height: var(--spinner-size);\n }\n\n .v-spinner-circle {\n width: var(--spinner-ring);\n height: var(--spinner-ring);\n /*\n * The stroke is a tenth of the ring's diameter, the same source again: the glyph's outer\n * radius is 400 and its inner one 320, so its stroke is 80 of those 800. It stays in\n * proportion at every size, with a one-pixel floor below which the ring would disappear; a\n * floor that only bites under a 12px box, which no size of the design system reaches.\n */\n border: max(1px, calc(var(--spinner-ring) / 10)) solid\n color-mix(in oklab, currentcolor, transparent 75%);\n border-block-start-color: currentcolor;\n border-radius: var(--vectis-radius-pill);\n animation: v-spinner-spin var(--vectis-duration-1000) linear infinite;\n }\n\n /* Written here and not shared with VProgressCircular's identical turn: in the core sheet\n it cost every consumer the bytes, and the two copies only meet in an application that\n loads both components. The name carries the component, since keyframe names are global. */\n @keyframes v-spinner-spin {\n to {\n transform: rotate(1turn);\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-spinner-circle {\n animation-duration: var(--vectis-duration-3000);\n }\n }\n\n /*\n * The track is drawn in GrayText and the moving quarter keeps the forced text colour, which\n * `currentcolor` still reads here since only the circle opts out of the forcing.\n */\n @media (forced-colors: active) {\n .v-spinner-circle {\n forced-color-adjust: none;\n border-color: GrayText;\n border-block-start-color: currentcolor;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;EAyBA,IAAMQ,IAAQC,GAKRC,IAAIC,EAAY,GAChBC,IAAgBC,QAAeL,EAAM,SAASE,EAAE,MAAM,OAAO,OAAO;oBAIxEV,EAAA,GAAAC,EAOO,QAAA;GANL,OAAM;GACL,OAAKC,EAAEC,EAAAe,CAAA,CAAE,CAACT,EAAA,IAAI,MAAM,KAAA,IAA6C,KAAA,IAApC,EAAA,kBAAuBN,EAAAe,CAAA,CAAE,CAACT,EAAA,IAAI,EAAA,CAAe;GAC3E,MAAK;EAEL,GAAA,CAAA,AAAAU,EAAA,OAAAf,EAAoD,QAAA;GAA9C,OAAM;GAAmB,eAAY;EAC3CA,GAAAA,MAAAA,EAAAA,GAAAA,EAA0D,QAA1DE,GAA0DD,EAAvBO,EAAA,KAAa,GAAA,CAAA,CAAA,GAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VSplitButton.js","names":[],"sources":["../../../src/components/VSplitButton/VSplitButton.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * A VButtonGroup joining a VButton to the VIconButton that opens a VMenu. The group shares the\n * shape and the disabled state; the menu is anchored to the whole control rather than to its\n * trigger, so it can line up with either edge and match the full width.\n */\nimport { computed, ref } from 'vue'\nimport type { ButtonHTMLAttributes } from 'vue'\n\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useMessages } from '../../i18n/state'\n\nimport VButton from '../VButton/VButton.vue'\nimport type { ButtonSize, ButtonTone, ButtonVariant } from '../VButton/VButton.vue'\nimport VButtonGroup from '../VButton/VButtonGroup.vue'\nimport VIconButton from '../VIconButton/VIconButton.vue'\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport type { IconSource } from '../VIcon/types'\nimport VMenu from '../VMenu/VMenu.vue'\nimport type { MenuPlacement, MenuSize } from '../VMenu/context'\n\ninterface SplitButtonProps {\n /** The visible text of the main action. */\n label: string\n /**\n * How much visual weight both halves carry, on the values of VButton's own `variant`:\n * `solid`, `outline`, `ghost` or `soft`.\n */\n variant?: ButtonVariant\n /** What the actions mean, in colour: `accent`, `neutral` or `danger`. */\n tone?: ButtonTone\n /** The height of both halves, from the size scale shared by every control. */\n size?: ButtonSize\n /** Takes 4px off the height of both halves. */\n compact?: boolean\n /** Raises the control off the page with a shadow, on the terms of VButton's `elevated`. */\n elevated?: boolean\n /**\n * Stretches the control to the whole inline size of its parent. The main action takes the\n * extra room; the menu button stays square.\n */\n fullWidth?: boolean\n /**\n * Turns the main action into an `<a>` pointing at this address. A disabled or loading link\n * becomes inert: the address is dropped, so it can be neither focused nor followed.\n */\n href?: string\n /** The native type of the main button. It is ignored as soon as `href` makes it a link. */\n type?: ButtonHTMLAttributes['type']\n /** Makes both halves unusable. */\n disabled?: boolean\n /**\n * Shows a spinner in the main action and announces it as busy. The menu button is disabled\n * meanwhile: an action is already under way.\n */\n loading?: boolean\n /** An icon before the label of the main action. */\n iconStart?: IconSource\n /** An icon after the label of the main action. */\n iconEnd?: IconSource\n /** Renders `iconStart` and `iconEnd` in their filled form. */\n iconFilled?: boolean\n /** What screen readers announce for the menu button. The dictionary's \"More options\" otherwise. */\n menuLabel?: string\n /** The icon of the menu button, the built-in `expand_more` otherwise. */\n menuIcon?: IconSource\n /**\n * Where the menu opens relative to the whole control. The browser moves it to another side by\n * itself when there is not enough room.\n */\n placement?: MenuPlacement\n /** How tall the rows of the menu are: 32, 40 or 48 pixels. */\n menuSize?: MenuSize\n /**\n * A width for the menu: a number is read as pixels, a string as any CSS length or keyword\n * (`16rem`, `max-content`).\n */\n menuWidth?: number | string\n /** Stops the menu from being narrower than the whole control, while leaving it free to grow. */\n matchTrigger?: boolean\n}\n\nconst props = withDefaults(defineProps<SplitButtonProps>(), {\n variant: 'solid',\n tone: 'accent',\n size: 'md',\n compact: false,\n elevated: false,\n fullWidth: false,\n href: undefined,\n type: 'button',\n disabled: false,\n loading: false,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n menuLabel: undefined,\n menuIcon: () => expandMoreIcon,\n placement: 'bottom-end',\n menuSize: 'sm',\n menuWidth: undefined,\n matchTrigger: false,\n})\n\nconst emit = defineEmits<{\n /** The main action was activated. A disabled or loading control emits nothing. */\n click: [event: MouseEvent]\n}>()\n\n/** Whether the menu is showing. The browser's own dismissal writes back to it. */\nconst open = defineModel<boolean>('open', { default: false })\n\ndefineSlots<{\n /** The other actions: VMenuItem, VMenuGroup and VMenuSeparator, submenus included. */\n default(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\nconst m = useMessages()\nconst menuText = computed(() => props.menuLabel ?? m.value.splitButton.menu)\n\nconst mainRef = ref<InstanceType<typeof VButton> | null>(null)\nconst el = computed(() => (mainRef.value?.$el as HTMLElement | undefined) ?? null)\n\ndefineExpose({\n /** Moves the focus to the main action. */\n focus: (options?: FocusOptions) => el.value?.focus(options),\n /** The main action's `<button>` or `<a>`. */\n el,\n})\n</script>\n\n<template>\n <VButtonGroup\n bordered\n class=\"v-split-button\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n :variant=\"variant\"\n :tone=\"tone\"\n :size=\"size\"\n :compact=\"compact\"\n :elevated=\"elevated\"\n :disabled=\"disabled\"\n :full-width=\"fullWidth\"\n :data-loading=\"loading ? '' : undefined\"\n >\n <VButton\n ref=\"mainRef\"\n v-bind=\"forwardedAttrs\"\n class=\"v-split-button-main\"\n :href=\"href\"\n :type=\"type\"\n :loading=\"loading\"\n :icon-start=\"iconStart\"\n :icon-end=\"iconEnd\"\n :icon-filled=\"iconFilled\"\n @click=\"emit('click', $event)\"\n >\n {{ label }}\n </VButton>\n <VMenu\n v-model:open=\"open\"\n :placement=\"placement\"\n :size=\"menuSize\"\n :width=\"menuWidth\"\n :match-trigger=\"matchTrigger\"\n >\n <template #trigger=\"{ triggerProps }\">\n <VIconButton\n v-bind=\"triggerProps\"\n class=\"v-split-button-menu\"\n :label=\"menuText\"\n :icon=\"menuIcon\"\n :disabled=\"loading\"\n />\n </template>\n <slot />\n </VMenu>\n </VButtonGroup>\n</template>\n\n<style>\n@layer vectis.components {\n /* The panel stays a descendant of the group in the document, so the scope covers it while\n keeping every split button on the page from answering to the same name. */\n .v-split-button {\n anchor-name: --split-button-anchor;\n anchor-scope: --split-button-anchor;\n }\n\n /* The whole control instead of the menu button VMenu anchors to. (0,2,0) beats the panel's\n own `position-anchor` at (0,1,0) whatever the sheet order, and the child combinator leaves\n submenus, nested inside the panel, on their parent item. */\n .v-split-button > .v-menu {\n position-anchor: --split-button-anchor;\n }\n\n /* The group's full width shares the row equally between its segments; here only the main\n action grows, and the menu button keeps its square. */\n .v-split-button[data-full-width] {\n grid-template-columns: 1fr auto;\n }\n\n /*\n * While the main action loads, the disabled menu button keeps the paint of the main one, half\n * transparent, instead of VButton's grey disabled state, which would split the control in two\n * colours. At (0,5,0) these beat VButton's (0,4,0) disabled rules whatever the sheet order.\n */\n .v-split-button[data-loading] > .v-split-button-menu.v-button {\n opacity: 0.5;\n }\n\n .v-split-button[data-loading] > .v-split-button-menu.v-button[data-variant='solid'] {\n background: var(--tone-bg-solid);\n color: var(--tone-text-solid);\n }\n\n .v-split-button[data-loading] > .v-split-button-menu.v-button[data-variant='soft'] {\n background: var(--tone-bg-soft);\n color: var(--tone-text-tinted);\n }\n\n .v-split-button[data-loading] > .v-split-button-menu.v-button[data-variant='outline'] {\n color: var(--tone-text-tinted);\n border-color: var(--tone-border-soft);\n }\n\n .v-split-button[data-loading] > .v-split-button-menu.v-button[data-variant='ghost'] {\n color: var(--tone-text-tinted);\n }\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"VSplitButton.js","names":["_sfc_main","VSplitButton_vue_vue_type_script_setup_true_lang_default","VSplitButton_default"],"sources":["../../../src/components/VSplitButton/VSplitButton.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * A VButtonGroup joining a VButton to the VIconButton that opens a VMenu. The group shares the\n * shape and the disabled state; the menu is anchored to the whole control rather than to its\n * trigger, so it can line up with either edge and match the full width.\n */\nimport { computed, ref } from 'vue'\nimport type { ButtonHTMLAttributes } from 'vue'\n\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useMessages } from '../../i18n/state'\n\nimport VButton from '../VButton/VButton.vue'\nimport type { ButtonSize, ButtonTone, ButtonVariant } from '../VButton/VButton.vue'\nimport VButtonGroup from '../VButton/VButtonGroup.vue'\nimport VIconButton from '../VIconButton/VIconButton.vue'\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport type { IconSource } from '../VIcon/types'\nimport VMenu from '../VMenu/VMenu.vue'\nimport type { MenuPlacement, MenuSize } from '../VMenu/context'\n\ninterface SplitButtonProps {\n /** The visible text of the main action. */\n label: string\n /**\n * How much visual weight both halves carry, on the values of VButton's own `variant`:\n * `solid`, `outline`, `ghost` or `soft`.\n */\n variant?: ButtonVariant\n /** What the actions mean, in colour: `accent`, `neutral` or `danger`. */\n tone?: ButtonTone\n /** The height of both halves, from the size scale shared by every control. */\n size?: ButtonSize\n /** Takes 4px off the height of both halves. */\n compact?: boolean\n /** Raises the control off the page with a shadow, on the terms of VButton's `elevated`. */\n elevated?: boolean\n /**\n * Stretches the control to the whole inline size of its parent. The main action takes the\n * extra room; the menu button stays square.\n */\n fullWidth?: boolean\n /**\n * Turns the main action into an `<a>` pointing at this address. A disabled or loading link\n * becomes inert: the address is dropped, so it can be neither focused nor followed.\n */\n href?: string\n /** The native type of the main button. It is ignored as soon as `href` makes it a link. */\n type?: ButtonHTMLAttributes['type']\n /** Makes both halves unusable. */\n disabled?: boolean\n /**\n * Shows a spinner in the main action and announces it as busy. The menu button is disabled\n * meanwhile: an action is already under way.\n */\n loading?: boolean\n /** An icon before the label of the main action. */\n iconStart?: IconSource\n /** An icon after the label of the main action. */\n iconEnd?: IconSource\n /** Renders `iconStart` and `iconEnd` in their filled form. */\n iconFilled?: boolean\n /** What screen readers announce for the menu button. The dictionary's \"More options\" otherwise. */\n menuLabel?: string\n /** The icon of the menu button, the built-in `expand_more` otherwise. */\n menuIcon?: IconSource\n /**\n * Where the menu opens relative to the whole control. The browser moves it to another side by\n * itself when there is not enough room.\n */\n placement?: MenuPlacement\n /** How tall the rows of the menu are: 32, 40 or 48 pixels. */\n menuSize?: MenuSize\n /**\n * A width for the menu: a number is read as pixels, a string as any CSS length or keyword\n * (`16rem`, `max-content`).\n */\n menuWidth?: number | string\n /** Stops the menu from being narrower than the whole control, while leaving it free to grow. */\n matchTrigger?: boolean\n}\n\nconst props = withDefaults(defineProps<SplitButtonProps>(), {\n variant: 'solid',\n tone: 'accent',\n size: 'md',\n compact: false,\n elevated: false,\n fullWidth: false,\n href: undefined,\n type: 'button',\n disabled: false,\n loading: false,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n menuLabel: undefined,\n menuIcon: () => expandMoreIcon,\n placement: 'bottom-end',\n menuSize: 'sm',\n menuWidth: undefined,\n matchTrigger: false,\n})\n\nconst emit = defineEmits<{\n /** The main action was activated. A disabled or loading control emits nothing. */\n click: [event: MouseEvent]\n}>()\n\n/** Whether the menu is showing. The browser's own dismissal writes back to it. */\nconst open = defineModel<boolean>('open', { default: false })\n\ndefineSlots<{\n /** The other actions: VMenuItem, VMenuGroup and VMenuSeparator, submenus included. */\n default(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\nconst m = useMessages()\nconst menuText = computed(() => props.menuLabel ?? m.value.splitButton.menu)\n\nconst mainRef = ref<InstanceType<typeof VButton> | null>(null)\nconst el = computed(() => (mainRef.value?.$el as HTMLElement | undefined) ?? null)\n\ndefineExpose({\n /** Moves the focus to the main action. */\n focus: (options?: FocusOptions) => el.value?.focus(options),\n /** The main action's `<button>` or `<a>`. */\n el,\n})\n</script>\n\n<template>\n <VButtonGroup\n bordered\n class=\"v-split-button\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n :variant=\"variant\"\n :tone=\"tone\"\n :size=\"size\"\n :compact=\"compact\"\n :elevated=\"elevated\"\n :disabled=\"disabled\"\n :full-width=\"fullWidth\"\n :data-loading=\"loading ? '' : undefined\"\n >\n <VButton\n ref=\"mainRef\"\n v-bind=\"forwardedAttrs\"\n class=\"v-split-button-main\"\n :href=\"href\"\n :type=\"type\"\n :loading=\"loading\"\n :icon-start=\"iconStart\"\n :icon-end=\"iconEnd\"\n :icon-filled=\"iconFilled\"\n @click=\"emit('click', $event)\"\n >\n {{ label }}\n </VButton>\n <VMenu\n v-model:open=\"open\"\n :placement=\"placement\"\n :size=\"menuSize\"\n :width=\"menuWidth\"\n :match-trigger=\"matchTrigger\"\n >\n <template #trigger=\"{ triggerProps }\">\n <VIconButton\n v-bind=\"triggerProps\"\n class=\"v-split-button-menu\"\n :label=\"menuText\"\n :icon=\"menuIcon\"\n :disabled=\"loading\"\n />\n </template>\n <slot />\n </VMenu>\n </VButtonGroup>\n</template>\n\n<style>\n@layer vectis.components {\n /* The panel stays a descendant of the group in the document, so the scope covers it while\n keeping every split button on the page from answering to the same name. */\n .v-split-button {\n anchor-name: --split-button-anchor;\n anchor-scope: --split-button-anchor;\n }\n\n /* The whole control instead of the menu button VMenu anchors to. (0,2,0) beats the panel's\n own `position-anchor` at (0,1,0) whatever the sheet order, and the child combinator leaves\n submenus, nested inside the panel, on their parent item. */\n .v-split-button > .v-menu {\n position-anchor: --split-button-anchor;\n }\n\n /* The group's full width shares the row equally between its segments; here only the main\n action grows, and the menu button keeps its square. */\n .v-split-button[data-full-width] {\n grid-template-columns: 1fr auto;\n }\n\n /*\n * While the main action loads, the disabled menu button keeps the paint of the main one, half\n * transparent, instead of VButton's grey disabled state, which would split the control in two\n * colours. At (0,5,0) these beat VButton's (0,4,0) disabled rules whatever the sheet order.\n */\n .v-split-button[data-loading] > .v-split-button-menu.v-button {\n opacity: 0.5;\n }\n\n .v-split-button[data-loading] > .v-split-button-menu.v-button[data-variant='solid'] {\n background: var(--tone-bg-solid);\n color: var(--tone-text-solid);\n }\n\n .v-split-button[data-loading] > .v-split-button-menu.v-button[data-variant='soft'] {\n background: var(--tone-bg-soft);\n color: var(--tone-text-tinted);\n }\n\n .v-split-button[data-loading] > .v-split-button-menu.v-button[data-variant='outline'] {\n color: var(--tone-text-tinted);\n border-color: var(--tone-border-soft);\n }\n\n .v-split-button[data-loading] > .v-split-button-menu.v-button[data-variant='ghost'] {\n color: var(--tone-text-tinted);\n }\n}\n</style>\n"],"mappings":""}
|