vectis-ui 0.14.0 → 0.16.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.css +1 -1
- package/dist/components/VAccordion/VAccordion.d.ts +0 -6
- package/dist/components/VAccordion/VAccordion.js.map +1 -1
- package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js +3 -8
- 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.d.ts +0 -3
- package/dist/components/VAvatar/VAvatar.js.map +1 -1
- package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js +21 -27
- package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAvatar/VAvatarGroup.d.ts +0 -6
- package/dist/components/VAvatar/VAvatarGroup.js.map +1 -1
- package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js +5 -19
- package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAvatar/context.d.ts +0 -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.d.ts +0 -6
- package/dist/components/VButton/VButton.js.map +1 -1
- package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js +11 -17
- package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VButton/VButtonGroup.d.ts +0 -6
- package/dist/components/VButton/VButtonGroup.js.map +1 -1
- package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js +0 -7
- package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VButton/context.d.ts +0 -2
- 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.d.ts +0 -3
- package/dist/components/VChip/VChip.js.map +1 -1
- package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VColorInput/VColorInput.d.ts +0 -3
- package/dist/components/VColorInput/VColorInput.js.map +1 -1
- package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js +3 -9
- 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.d.ts +0 -3
- package/dist/components/VCombobox/VCombobox.js.map +1 -1
- package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +100 -119
- 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.d.ts +0 -3
- package/dist/components/VContextMenu/VContextMenu.js.map +1 -1
- package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js +3 -11
- package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDataTable/VDataTable.css +1 -1
- package/dist/components/VDataTable/VDataTable.d.ts +0 -2
- package/dist/components/VDataTable/VDataTable.js.map +1 -1
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +69 -88
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDateInput/VDateInput.d.ts +0 -3
- package/dist/components/VDateInput/VDateInput.js.map +1 -1
- package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +5 -10
- 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.d.ts +0 -5
- package/dist/components/VFileInput/VFileInput.js.map +1 -1
- package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +54 -65
- 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.d.ts +0 -3
- package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
- package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js +0 -6
- 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.d.ts +0 -3
- package/dist/components/VIconButton/VIconButton.js.map +1 -1
- package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/VInput.css +1 -1
- package/dist/components/VInput/VInput.d.ts +0 -3
- package/dist/components/VInput/VInput.js.map +1 -1
- package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +2 -8
- package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/VInputGroup.d.ts +0 -3
- package/dist/components/VInput/VInputGroup.js.map +1 -1
- package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js +0 -7
- package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/context.d.ts +0 -2
- package/dist/components/VInput/context.js.map +1 -1
- package/dist/components/VInputOTP/VInputOTP.d.ts +0 -3
- package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
- package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +2 -14
- 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.d.ts +0 -3
- package/dist/components/VListbox/VListboxPanel.js.map +1 -1
- package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js +0 -6
- 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 +4 -14
- package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenu.d.ts +0 -3
- package/dist/components/VMenu/VMenu.js.map +1 -1
- package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js +0 -6
- 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.d.ts +0 -3
- package/dist/components/VMenu/VMenuPanel.js.map +1 -1
- package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +9 -16
- 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.d.ts +0 -3
- package/dist/components/VNumberInput/VNumberInput.js.map +1 -1
- package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js +85 -90
- 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.d.ts +0 -3
- package/dist/components/VPagination/VPagination.js.map +1 -1
- package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js +0 -6
- 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.d.ts +0 -3
- package/dist/components/VSelect/VSelect.js.map +1 -1
- package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js +80 -90
- 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.d.ts +0 -6
- package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +2 -11
- 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.d.ts +0 -3
- package/dist/components/VSkeletonLoader/VSkeletonLoader.js.map +1 -1
- package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js +0 -6
- 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 +107 -112
- 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.d.ts +0 -3
- package/dist/components/VSplitButton/VSplitButton.js.map +1 -1
- package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js +0 -6
- 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 +0 -2
- 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.css +1 -1
- package/dist/components/VTabs/VTabs.d.ts +0 -3
- package/dist/components/VTabs/VTabs.js.map +1 -1
- package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js +2 -15
- package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/context.d.ts +0 -2
- package/dist/components/VTabs/context.js.map +1 -1
- package/dist/components/VTextarea/VTextarea.d.ts +0 -6
- package/dist/components/VTextarea/VTextarea.js.map +1 -1
- package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeInput/VTimeInput.d.ts +0 -3
- package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
- package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +197 -204
- 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.d.ts +0 -3
- package/dist/components/VToggle/VToggle.js.map +1 -1
- package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js +1 -13
- 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.d.ts +1 -5
- package/dist/composables/useControlShape.js +0 -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.d.ts +0 -1
- package/dist/composables/useListbox.js +4 -10
- 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/styles.css +1 -1
- package/dist/tokens/css.js.map +1 -1
- package/dist/tokens/index.d.ts +3 -0
- package/dist/tokens/index.js.map +1 -1
- package/dist/tokens/primitives.js.map +1 -1
- package/dist/tokens/semantic.d.ts +3 -0
- package/dist/tokens/semantic.js +6 -5
- 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/tokens.json +12 -5
- package/dist/types.d.ts +0 -2
- 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.d.ts +4 -6
- package/dist/utils/chip.js +8 -5
- 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":"VChip.js","names":[],"sources":["../../../src/components/VChip/VChip.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Choose native links or buttons for chip actions. Dismissal is a sibling button because nested\n * buttons are invalid and inaccessible; inert links need a JS bridge.\n */\n\nimport { computed, onMounted, ref, useAttrs } from 'vue'\nimport type { StyleValue, VNode } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\n// Neither import may be named after a prop (`check`, `checkIcon`): every top-level binding of a\n// `<script setup>` reaches the template, where it shadows the prop of that name.\nimport { check as checkMark } from '../VIcon/icons/check'\nimport { close as closeIcon } from '../VIcon/icons/close'\nimport type { IconSource } from '../VIcon/types'\nimport { useMessages } from '../../i18n/state'\n\nimport { useInertLink } from '../../composables/useInertLink'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { customColorStyle } from '../../utils/css'\nimport { isDev } from '../../utils/env'\nimport { flattenSlot } from '../../utils/vnode'\n\n/** How much visual weight the chip carries. */\nexport type ChipVariant = 'soft' | 'solid' | 'outline'\n\n/** What the chip means, in colour. */\nexport type ChipTone = 'neutral' | 'accent' | 'danger' | 'success' | 'warning'\n\n/** The silhouette: softly rounded, or a full pill. */\nexport type ChipShape = 'chip' | 'pill'\n\n/** The height of the chip: 24 or 32 pixels. */\nexport type ChipSize = 'xs' | 'sm'\n\ninterface ChipProps {\n /**\n * How strongly the chip is painted: a tinted background, the full colour, or a\n * border alone.\n */\n variant?: ChipVariant\n /**\n * What the chip means, expressed as a colour: `neutral` by default, then `accent` for\n * something singled out and `danger`/ `success`/ `warning` for a state being reported.\n */\n tone?: ChipTone\n /** A colour of your own (hex, CSS name or `oklch()`), which REPLACES the tone. */\n color?: string\n /** The silhouette: softly rounded corners, or a full pill. */\n shape?: ChipShape\n /** The height of the chip. */\n size?: ChipSize\n /** Takes 4px off the height, leaving the padding, the text and the icons as they are. */\n compact?: boolean\n /** Makes the chip a button that reacts to clicks, without holding a state. */\n clickable?: boolean\n /**\n * Where the chip leads, which makes it a link. A disabled link is made inert by\n * hand.\n */\n href?: string\n /**\n * Makes the chip something that stays chosen, bound to `v-model:selected`. It wins\n * over `href` and `clickable`.\n */\n selectable?: boolean\n /**\n * Shows a tick before the label while the chip is selected. It REPLACES whatever\n * start icon was given, so the two are never shown together.\n */\n check?: boolean\n /** The icon of that tick, a built-in check mark by default. `iconFilled` does not reach it. */\n checkIcon?: IconSource\n /** An icon before the label. The `#start` slot replaces it. */\n iconStart?: IconSource\n /** An icon after the label. The `#end` slot replaces it. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis). It has\n * no effect on the `#start`/`#end` slots, whose icons the consumer builds, nor on the tick\n * or the removal cross.\n */\n iconFilled?: boolean\n /**\n * Adds a button that asks for the chip to be removed. It only EMITS that request:\n * actually taking the chip away is the consumer's decision.\n */\n dismissible?: boolean\n /** The icon of that removal button. */\n dismissIcon?: IconSource\n /**\n * What the removal button does, in words. It falls back to the design system\n * dictionary.\n */\n dismissLabel?: string\n /** Makes the chip unusable, greyed out through the colour tokens. */\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<ChipProps>(), {\n variant: 'soft',\n tone: 'neutral',\n color: undefined,\n shape: 'chip',\n size: 'xs',\n compact: false,\n clickable: false,\n href: undefined,\n selectable: false,\n check: false,\n checkIcon: () => checkMark,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n dismissible: false,\n dismissIcon: () => closeIcon,\n dismissLabel: undefined,\n disabled: false,\n})\n\nconst m = useMessages()\nconst resolvedDismissLabel = computed(() => props.dismissLabel ?? m.value.common.dismiss)\n\n/**\n * Whether the chip is selected, which is also what makes it selectable at all: binding this\n * turns it into a toggle button and takes precedence over `href` and `clickable`. It starts\n * unselected.\n */\nconst selected = defineModel<boolean>('selected', { default: false })\n\ndefineEmits<{\n /** The removal button was pressed. The chip is still there: removing it is up to you. */\n dismiss: []\n}>()\n\nconst slots = defineSlots<{\n /** The label. It may be left out entirely, which gives a chip made of icons alone. */\n default?(): unknown\n /** Content before the label, which takes the place of `iconStart`. */\n start?(): unknown\n /** Content after the label, which takes the place of `iconEnd`. */\n end?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\n// The wrapper-root split: the pill is the box a consumer positions, and the ACTION is the\n// element that is focused, named and clicked, so everything else goes there.\nconst { rootClass, rootStyle: consumerStyle, forwardedAttrs } = useRootAttrs()\n\n/* A selectable chip is therefore never a link, whatever `href` says. */\nconst {\n isLink,\n isInertLink,\n linkHref,\n attrs: actionAttrs,\n} = useInertLink({\n href: () => (props.selectable ? undefined : props.href),\n inert: () => props.disabled,\n attrs: () => forwardedAttrs.value,\n})\nconst actionTag = computed(() =>\n props.selectable ? 'button' : isLink.value ? 'a' : props.clickable ? 'button' : 'span',\n)\n\nconst rootStyle = computed<StyleValue>(() => [customColorStyle(props.color), consumerStyle.value])\n\nconst actionEl = ref<HTMLElement | null>(null)\n\nconst showCheck = computed(() => props.check && props.selectable && selected.value)\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\n/**\n * An icon and no label at all: the chip becomes a square, as wide as it is tall. The default\n * slot is asked what it RENDERS, not whether it was passed: a wrapper forwarding an empty\n * label still hands one down.\n */\nfunction iconOnly() {\n return (\n flattenSlot(slots.default?.() as VNode[] | undefined).length === 0 &&\n !!(slots.start || slots.end || props.iconStart || props.iconEnd)\n )\n}\n\n// @devwarn\n// A chip drawn with an icon alone has no accessible name: the icon is decorative, so a\n// clickable, selectable or linked one is announced as a bare button or link. Nothing fails\n// visibly, hence the message.\nif (isDev) {\n const attrs = useAttrs()\n onMounted(() => {\n if (\n actionTag.value !== 'span' &&\n actionEl.value?.parentElement?.hasAttribute('data-icon-only') &&\n attrs['aria-label'] === undefined &&\n attrs['aria-labelledby'] === undefined\n ) {\n console.warn(\n '[VChip] a chip made of an icon alone has no accessible name: set aria-label or aria-labelledby on it.',\n )\n }\n })\n}\n\n// A template ref on the chip reaches the pill, a layout box; the element that is focused,\n// named and clicked is the action inside it.\ndefineExpose({\n /** Moves the focus to the chip's action: its button or link. */\n focus: (options?: FocusOptions) => actionEl.value?.focus(options),\n /** The action element, which is also where the consumer's attributes land. */\n el: actionEl,\n})\n</script>\n\n<template>\n <span\n :class=\"['v-chip v-control v-tone v-variant', rootClass]\"\n :style=\"rootStyle\"\n :data-variant=\"variant\"\n :data-tone=\"tone\"\n :data-custom=\"color !== undefined ? '' : undefined\"\n :data-shape=\"shape\"\n :data-size=\"size\"\n :data-compact=\"compact ? '' : undefined\"\n :data-selected=\"selectable && selected ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n :data-icon-only=\"iconOnly() ? '' : undefined\"\n >\n <component\n :is=\"actionTag\"\n ref=\"actionEl\"\n :aria-disabled=\"isInertLink ? 'true' : undefined\"\n :aria-pressed=\"selectable ? selected : undefined\"\n v-bind=\"actionAttrs\"\n class=\"v-chip-action\"\n :type=\"actionTag === 'button' ? 'button' : undefined\"\n :href=\"linkHref\"\n :disabled=\"actionTag === 'button' ? disabled : undefined\"\n @click=\"selectable && !disabled && (selected = !selected)\"\n >\n <VIcon v-if=\"showCheck\" v-bind=\"iconProps(checkIcon)\" />\n <slot v-else name=\"start\">\n <VIcon v-if=\"iconStart\" v-bind=\"iconProps(iconStart)\" :filled=\"iconFilled\" />\n </slot>\n <slot />\n <slot name=\"end\">\n <VIcon v-if=\"iconEnd\" v-bind=\"iconProps(iconEnd)\" :filled=\"iconFilled\" />\n </slot>\n </component>\n <button\n v-if=\"dismissible\"\n type=\"button\"\n class=\"v-chip-dismiss\"\n :aria-label=\"resolvedDismissLabel\"\n :disabled=\"disabled\"\n @click=\"$emit('dismiss')\"\n >\n <VIcon v-bind=\"iconProps(dismissIcon)\" />\n </button>\n </span>\n</template>\n\n<style>\n@layer vectis.components {\n /* The height comes from the shared size class set on this element. The scale itself\n holds five steps; the component's type restricts it to the two smallest, a chip\n larger than that being a button. */\n .v-chip {\n display: inline-flex;\n align-items: center;\n block-size: var(--control-height);\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-chip);\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n transition:\n background-color var(--vectis-duration-fast) var(--vectis-ease-default),\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n color var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-chip[data-shape='pill'] {\n border-radius: var(--vectis-radius-pill);\n }\n\n /* The tone table itself lives in styles/tones.css, in a layer below the components.\n All five tones are taken exactly as they come; the only values redefined here are\n those of a custom colour, and they win over the shared table by LAYER rather than\n by order, so no bundling can change the outcome. */\n\n /* A custom colour replaces the tone entirely. The shared table derives the four values\n every adopter reads (styles/tones.css, `.v-tone[data-custom]`); the chip adds the\n three only it paints, its hover and active steps and its outline, from the same colour\n mixed with the theme's own text and surface. */\n .v-chip[data-custom] {\n --tone-bg-solid-hover: color-mix(in oklab, var(--custom-color), var(--vectis-color-text) 8%);\n --tone-bg-solid-active: color-mix(in oklab, var(--custom-color), var(--vectis-color-text) 14%);\n --tone-border-soft: color-mix(in oklab, var(--custom-color), var(--vectis-color-surface) 60%);\n }\n\n /*\n * It COMPOUNDS `.v-variant`: the variant table weighs (0,2,0) like this rule and lives in\n * another sheet, so at equal specificity the winner would be whichever one the consumer's\n * bundler put last. At (0,3,0) it wins by construction.\n */\n .v-variant.v-chip[data-selected] {\n background: var(--tone-bg-solid);\n color: var(--tone-text-solid);\n border-color: transparent;\n }\n\n /* Hovering and pressing are conditioned on the ACTION being a real control, which\n buys two things at once: a chip that merely displays a value never lights up, and\n hovering the removal button does not repaint the whole chip as though it were\n about to be activated. */\n /* The same steps as VButton's, rule for rule: an outline sits on the soft surface while\n hovered and one tint further when pressed, and that tint is where a soft chip starts\n on hover. Held in each sheet rather than in `variants.css`, which every consumer\n downloads whether or not it renders a single button or chip. */\n .v-chip[data-variant='outline']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:hover\n ) {\n background: var(--tone-bg-soft);\n }\n\n .v-chip[data-variant='outline']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:active\n ),\n .v-chip[data-variant='soft']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:hover\n ) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 8%);\n }\n\n .v-chip[data-variant='soft']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:active\n ) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 14%);\n }\n\n .v-chip[data-variant='solid']:not([data-disabled]):has(:is(button, a).v-chip-action:hover),\n .v-chip[data-selected]:not([data-disabled]):has(:is(button, a).v-chip-action:hover) {\n background: var(--tone-bg-solid-hover);\n }\n\n .v-chip[data-variant='solid']:not([data-disabled]):has(:is(button, a).v-chip-action:active),\n .v-chip[data-selected]:not([data-disabled]):has(:is(button, a).v-chip-action:active) {\n background: var(--tone-bg-solid-active);\n }\n\n .v-chip-action {\n display: inline-flex;\n align-items: center;\n gap: var(--control-gap);\n block-size: 100%;\n padding-block: 0;\n padding-inline: var(--control-padding-inline);\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n border-radius: inherit;\n text-decoration: none;\n cursor: default;\n }\n\n /* With an icon and no label the action becomes square, its width taken from its own\n height through the ratio rather than from a new measurement to keep in step. */\n .v-chip[data-icon-only] .v-chip-action {\n aspect-ratio: 1;\n justify-content: center;\n padding-inline: 0;\n }\n\n :is(button, a).v-chip-action {\n cursor: pointer;\n }\n\n :is(button, a).v-chip-action:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /*\n * The cross inherits the chip's text colour rather than a muted one: drawn from the icon font,\n * it is real text, to which the text contrast rule applies.\n */\n .v-chip-dismiss {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n inline-size: var(--control-action-size);\n block-size: var(--control-action-size);\n margin-inline: calc(var(--vectis-space-1) * -1) var(--vectis-space-1);\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n border-radius: var(--vectis-radius-pill);\n cursor: pointer;\n flex: none;\n }\n\n .v-chip-dismiss:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n /*\n * A disabled chip greys out through the colour tokens, which makes it follow the dark theme\n * without a rule of its own.\n */\n .v-variant.v-chip[data-disabled] {\n background: var(--vectis-color-surface-muted);\n color: var(--vectis-color-text-subtle);\n border-color: transparent;\n }\n\n .v-variant.v-chip[data-disabled][data-variant='outline'] {\n background: transparent;\n border-color: var(--vectis-color-border);\n }\n\n .v-chip[data-disabled] :is(:is(button, a).v-chip-action, .v-chip-dismiss) {\n cursor: not-allowed;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-chip {\n transition: none;\n }\n }\n\n /*\n * The class is repeated to reach (0,6,0). The variant, hover and active rules reach (0,5,0),\n * and with the forcing turned off any of them that still won would paint its tone over the\n * selection, with HighlightText on top of it.\n */\n @media (forced-colors: active) {\n .v-chip.v-chip.v-chip.v-chip[data-selected]:not([data-disabled]) {\n forced-color-adjust: none;\n background-color: Highlight;\n color: HighlightText;\n border-color: Highlight;\n outline-color: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"VChip.js","names":["_sfc_main","VChip_vue_vue_type_script_setup_true_lang_default","VChip_default"],"sources":["../../../src/components/VChip/VChip.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Choose native links or buttons for chip actions. Dismissal is a sibling button because nested\n * buttons are invalid and inaccessible; inert links need a JS bridge.\n */\n\nimport { computed, onMounted, ref, useAttrs } from 'vue'\nimport type { StyleValue, VNode } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\n// Neither import may be named after a prop (`check`, `checkIcon`): every top-level binding of a\n// `<script setup>` reaches the template, where it shadows the prop of that name.\nimport { check as checkMark } from '../VIcon/icons/check'\nimport { close as closeIcon } from '../VIcon/icons/close'\nimport type { IconSource } from '../VIcon/types'\nimport { useMessages } from '../../i18n/state'\n\nimport { useInertLink } from '../../composables/useInertLink'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { customColorStyle } from '../../utils/css'\nimport { isDev } from '../../utils/env'\nimport { flattenSlot } from '../../utils/vnode'\n\n/** How much visual weight the chip carries. */\nexport type ChipVariant = 'soft' | 'solid' | 'outline'\n\n/** What the chip means, in colour. */\nexport type ChipTone = 'neutral' | 'accent' | 'danger' | 'success' | 'warning'\n\n/** The silhouette: softly rounded, or a full pill. */\nexport type ChipShape = 'chip' | 'pill'\n\n/** The height of the chip: 24 or 32 pixels. */\nexport type ChipSize = 'xs' | 'sm'\n\ninterface ChipProps {\n /**\n * How strongly the chip is painted: a tinted background, the full colour, or a\n * border alone.\n */\n variant?: ChipVariant\n /**\n * What the chip means, expressed as a colour: `neutral` by default, then `accent` for\n * something singled out and `danger`/ `success`/ `warning` for a state being reported.\n */\n tone?: ChipTone\n /** A colour of your own (hex, CSS name or `oklch()`), which REPLACES the tone. */\n color?: string\n /** The silhouette: softly rounded corners, or a full pill. */\n shape?: ChipShape\n /** The height of the chip. */\n size?: ChipSize\n /** Makes the chip a button that reacts to clicks, without holding a state. */\n clickable?: boolean\n /**\n * Where the chip leads, which makes it a link. A disabled link is made inert by\n * hand.\n */\n href?: string\n /**\n * Makes the chip something that stays chosen, bound to `v-model:selected`. It wins\n * over `href` and `clickable`.\n */\n selectable?: boolean\n /**\n * Shows a tick before the label while the chip is selected. It REPLACES whatever\n * start icon was given, so the two are never shown together.\n */\n check?: boolean\n /** The icon of that tick, a built-in check mark by default. `iconFilled` does not reach it. */\n checkIcon?: IconSource\n /** An icon before the label. The `#start` slot replaces it. */\n iconStart?: IconSource\n /** An icon after the label. The `#end` slot replaces it. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis). It has\n * no effect on the `#start`/`#end` slots, whose icons the consumer builds, nor on the tick\n * or the removal cross.\n */\n iconFilled?: boolean\n /**\n * Adds a button that asks for the chip to be removed. It only EMITS that request:\n * actually taking the chip away is the consumer's decision.\n */\n dismissible?: boolean\n /** The icon of that removal button. */\n dismissIcon?: IconSource\n /**\n * What the removal button does, in words. It falls back to the design system\n * dictionary.\n */\n dismissLabel?: string\n /** Makes the chip unusable, greyed out through the colour tokens. */\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<ChipProps>(), {\n variant: 'soft',\n tone: 'neutral',\n color: undefined,\n shape: 'chip',\n size: 'xs',\n clickable: false,\n href: undefined,\n selectable: false,\n check: false,\n checkIcon: () => checkMark,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n dismissible: false,\n dismissIcon: () => closeIcon,\n dismissLabel: undefined,\n disabled: false,\n})\n\nconst m = useMessages()\nconst resolvedDismissLabel = computed(() => props.dismissLabel ?? m.value.common.dismiss)\n\n/**\n * Whether the chip is selected, which is also what makes it selectable at all: binding this\n * turns it into a toggle button and takes precedence over `href` and `clickable`. It starts\n * unselected.\n */\nconst selected = defineModel<boolean>('selected', { default: false })\n\ndefineEmits<{\n /** The removal button was pressed. The chip is still there: removing it is up to you. */\n dismiss: []\n}>()\n\nconst slots = defineSlots<{\n /** The label. It may be left out entirely, which gives a chip made of icons alone. */\n default?(): unknown\n /** Content before the label, which takes the place of `iconStart`. */\n start?(): unknown\n /** Content after the label, which takes the place of `iconEnd`. */\n end?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\n// The wrapper-root split: the pill is the box a consumer positions, and the ACTION is the\n// element that is focused, named and clicked, so everything else goes there.\nconst { rootClass, rootStyle: consumerStyle, forwardedAttrs } = useRootAttrs()\n\n/* A selectable chip is therefore never a link, whatever `href` says. */\nconst {\n isLink,\n isInertLink,\n linkHref,\n attrs: actionAttrs,\n} = useInertLink({\n href: () => (props.selectable ? undefined : props.href),\n inert: () => props.disabled,\n attrs: () => forwardedAttrs.value,\n})\nconst actionTag = computed(() =>\n props.selectable ? 'button' : isLink.value ? 'a' : props.clickable ? 'button' : 'span',\n)\n\nconst rootStyle = computed<StyleValue>(() => [customColorStyle(props.color), consumerStyle.value])\n\nconst actionEl = ref<HTMLElement | null>(null)\n\nconst showCheck = computed(() => props.check && props.selectable && selected.value)\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\n/**\n * An icon and no label at all: the chip becomes a square, as wide as it is tall. The default\n * slot is asked what it RENDERS, not whether it was passed: a wrapper forwarding an empty\n * label still hands one down.\n */\nfunction iconOnly() {\n return (\n flattenSlot(slots.default?.() as VNode[] | undefined).length === 0 &&\n !!(slots.start || slots.end || props.iconStart || props.iconEnd)\n )\n}\n\n// @devwarn\n// A chip drawn with an icon alone has no accessible name: the icon is decorative, so a\n// clickable, selectable or linked one is announced as a bare button or link. Nothing fails\n// visibly, hence the message.\nif (isDev) {\n const attrs = useAttrs()\n onMounted(() => {\n if (\n actionTag.value !== 'span' &&\n actionEl.value?.parentElement?.hasAttribute('data-icon-only') &&\n attrs['aria-label'] === undefined &&\n attrs['aria-labelledby'] === undefined\n ) {\n console.warn(\n '[VChip] a chip made of an icon alone has no accessible name: set aria-label or aria-labelledby on it.',\n )\n }\n })\n}\n\n// A template ref on the chip reaches the pill, a layout box; the element that is focused,\n// named and clicked is the action inside it.\ndefineExpose({\n /** Moves the focus to the chip's action: its button or link. */\n focus: (options?: FocusOptions) => actionEl.value?.focus(options),\n /** The action element, which is also where the consumer's attributes land. */\n el: actionEl,\n})\n</script>\n\n<template>\n <span\n :class=\"['v-chip v-control v-tone v-variant', rootClass]\"\n :style=\"rootStyle\"\n :data-variant=\"variant\"\n :data-tone=\"tone\"\n :data-custom=\"color !== undefined ? '' : undefined\"\n :data-shape=\"shape\"\n :data-size=\"size\"\n :data-selected=\"selectable && selected ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n :data-icon-only=\"iconOnly() ? '' : undefined\"\n >\n <component\n :is=\"actionTag\"\n ref=\"actionEl\"\n :aria-disabled=\"isInertLink ? 'true' : undefined\"\n :aria-pressed=\"selectable ? selected : undefined\"\n v-bind=\"actionAttrs\"\n class=\"v-chip-action\"\n :type=\"actionTag === 'button' ? 'button' : undefined\"\n :href=\"linkHref\"\n :disabled=\"actionTag === 'button' ? disabled : undefined\"\n @click=\"selectable && !disabled && (selected = !selected)\"\n >\n <VIcon v-if=\"showCheck\" v-bind=\"iconProps(checkIcon)\" />\n <slot v-else name=\"start\">\n <VIcon v-if=\"iconStart\" v-bind=\"iconProps(iconStart)\" :filled=\"iconFilled\" />\n </slot>\n <slot />\n <slot name=\"end\">\n <VIcon v-if=\"iconEnd\" v-bind=\"iconProps(iconEnd)\" :filled=\"iconFilled\" />\n </slot>\n </component>\n <button\n v-if=\"dismissible\"\n type=\"button\"\n class=\"v-chip-dismiss\"\n :aria-label=\"resolvedDismissLabel\"\n :disabled=\"disabled\"\n @click=\"$emit('dismiss')\"\n >\n <VIcon v-bind=\"iconProps(dismissIcon)\" />\n </button>\n </span>\n</template>\n\n<style>\n@layer vectis.components {\n /* The height comes from the shared size class set on this element. The scale itself\n holds five steps; the component's type restricts it to the two smallest, a chip\n larger than that being a button. */\n .v-chip {\n display: inline-flex;\n align-items: center;\n block-size: var(--control-height);\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-chip);\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n transition:\n background-color var(--vectis-duration-fast) var(--vectis-ease-default),\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n color var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-chip[data-shape='pill'] {\n border-radius: var(--vectis-radius-pill);\n }\n\n /* The tone table itself lives in styles/tones.css, in a layer below the components.\n All five tones are taken exactly as they come; the only values redefined here are\n those of a custom colour, and they win over the shared table by LAYER rather than\n by order, so no bundling can change the outcome. */\n\n /* A custom colour replaces the tone entirely. The shared table derives the four values\n every adopter reads (styles/tones.css, `.v-tone[data-custom]`); the chip adds the\n three only it paints, its hover and active steps and its outline, from the same colour\n mixed with the theme's own text and surface. */\n .v-chip[data-custom] {\n --tone-bg-solid-hover: color-mix(in oklab, var(--custom-color), var(--vectis-color-text) 8%);\n --tone-bg-solid-active: color-mix(in oklab, var(--custom-color), var(--vectis-color-text) 14%);\n --tone-border-soft: color-mix(in oklab, var(--custom-color), var(--vectis-color-surface) 60%);\n }\n\n /*\n * It COMPOUNDS `.v-variant`: the variant table weighs (0,2,0) like this rule and lives in\n * another sheet, so at equal specificity the winner would be whichever one the consumer's\n * bundler put last. At (0,3,0) it wins by construction.\n */\n .v-variant.v-chip[data-selected] {\n background: var(--tone-bg-solid);\n color: var(--tone-text-solid);\n border-color: transparent;\n }\n\n /* Hovering and pressing are conditioned on the ACTION being a real control, which\n buys two things at once: a chip that merely displays a value never lights up, and\n hovering the removal button does not repaint the whole chip as though it were\n about to be activated. */\n /* The same steps as VButton's, rule for rule: an outline sits on the soft surface while\n hovered and one tint further when pressed, and that tint is where a soft chip starts\n on hover. Held in each sheet rather than in `variants.css`, which every consumer\n downloads whether or not it renders a single button or chip. */\n .v-chip[data-variant='outline']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:hover\n ) {\n background: var(--tone-bg-soft);\n }\n\n .v-chip[data-variant='outline']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:active\n ),\n .v-chip[data-variant='soft']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:hover\n ) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 8%);\n }\n\n .v-chip[data-variant='soft']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:active\n ) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 14%);\n }\n\n .v-chip[data-variant='solid']:not([data-disabled]):has(:is(button, a).v-chip-action:hover),\n .v-chip[data-selected]:not([data-disabled]):has(:is(button, a).v-chip-action:hover) {\n background: var(--tone-bg-solid-hover);\n }\n\n .v-chip[data-variant='solid']:not([data-disabled]):has(:is(button, a).v-chip-action:active),\n .v-chip[data-selected]:not([data-disabled]):has(:is(button, a).v-chip-action:active) {\n background: var(--tone-bg-solid-active);\n }\n\n .v-chip-action {\n display: inline-flex;\n align-items: center;\n gap: var(--control-gap);\n block-size: 100%;\n padding-block: 0;\n padding-inline: var(--control-padding-inline);\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n border-radius: inherit;\n text-decoration: none;\n cursor: default;\n }\n\n /* With an icon and no label the action becomes square, its width taken from its own\n height through the ratio rather than from a new measurement to keep in step. */\n .v-chip[data-icon-only] .v-chip-action {\n aspect-ratio: 1;\n justify-content: center;\n padding-inline: 0;\n }\n\n :is(button, a).v-chip-action {\n cursor: pointer;\n }\n\n :is(button, a).v-chip-action:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /*\n * The cross inherits the chip's text colour rather than a muted one: drawn from the icon font,\n * it is real text, to which the text contrast rule applies.\n */\n .v-chip-dismiss {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n inline-size: var(--control-action-size);\n block-size: var(--control-action-size);\n margin-inline: calc(var(--vectis-space-1) * -1) var(--vectis-space-1);\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n border-radius: var(--vectis-radius-pill);\n cursor: pointer;\n flex: none;\n }\n\n .v-chip-dismiss:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n /*\n * A disabled chip greys out through the colour tokens, which makes it follow the dark theme\n * without a rule of its own.\n */\n .v-variant.v-chip[data-disabled] {\n background: var(--vectis-color-surface-muted);\n color: var(--vectis-color-text-subtle);\n border-color: transparent;\n }\n\n .v-variant.v-chip[data-disabled][data-variant='outline'] {\n background: transparent;\n border-color: var(--vectis-color-border);\n }\n\n .v-chip[data-disabled] :is(:is(button, a).v-chip-action, .v-chip-dismiss) {\n cursor: not-allowed;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-chip {\n transition: none;\n }\n }\n\n /*\n * The class is repeated to reach (0,6,0). The variant, hover and active rules reach (0,5,0),\n * and with the forcing turned off any of them that still won would paint its tone over the\n * selection, with HighlightText on top of it.\n */\n @media (forced-colors: active) {\n .v-chip.v-chip.v-chip.v-chip[data-selected]:not([data-disabled]) {\n forced-color-adjust: none;\n background-color: Highlight;\n color: HighlightText;\n border-color: Highlight;\n outline-color: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":""}
|
|
@@ -16,7 +16,6 @@ var M = [
|
|
|
16
16
|
"data-custom",
|
|
17
17
|
"data-shape",
|
|
18
18
|
"data-size",
|
|
19
|
-
"data-compact",
|
|
20
19
|
"data-selected",
|
|
21
20
|
"data-disabled",
|
|
22
21
|
"data-icon-only"
|
|
@@ -29,10 +28,6 @@ var M = [
|
|
|
29
28
|
color: { default: void 0 },
|
|
30
29
|
shape: { default: "chip" },
|
|
31
30
|
size: { default: "xs" },
|
|
32
|
-
compact: {
|
|
33
|
-
type: Boolean,
|
|
34
|
-
default: !1
|
|
35
|
-
},
|
|
36
31
|
clickable: {
|
|
37
32
|
type: Boolean,
|
|
38
33
|
default: !1
|
|
@@ -97,7 +92,6 @@ var M = [
|
|
|
97
92
|
"data-custom": c.color === void 0 ? void 0 : "",
|
|
98
93
|
"data-shape": c.shape,
|
|
99
94
|
"data-size": c.size,
|
|
100
|
-
"data-compact": c.compact ? "" : void 0,
|
|
101
95
|
"data-selected": c.selectable && F.value ? "" : void 0,
|
|
102
96
|
"data-disabled": c.disabled ? "" : void 0,
|
|
103
97
|
"data-icon-only": J() ? "" : void 0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VChip.vue_vue_type_script_setup_true_lang.js","names":["$emit"],"sources":["../../../src/components/VChip/VChip.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Choose native links or buttons for chip actions. Dismissal is a sibling button because nested\n * buttons are invalid and inaccessible; inert links need a JS bridge.\n */\n\nimport { computed, onMounted, ref, useAttrs } from 'vue'\nimport type { StyleValue, VNode } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\n// Neither import may be named after a prop (`check`, `checkIcon`): every top-level binding of a\n// `<script setup>` reaches the template, where it shadows the prop of that name.\nimport { check as checkMark } from '../VIcon/icons/check'\nimport { close as closeIcon } from '../VIcon/icons/close'\nimport type { IconSource } from '../VIcon/types'\nimport { useMessages } from '../../i18n/state'\n\nimport { useInertLink } from '../../composables/useInertLink'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { customColorStyle } from '../../utils/css'\nimport { isDev } from '../../utils/env'\nimport { flattenSlot } from '../../utils/vnode'\n\n/** How much visual weight the chip carries. */\nexport type ChipVariant = 'soft' | 'solid' | 'outline'\n\n/** What the chip means, in colour. */\nexport type ChipTone = 'neutral' | 'accent' | 'danger' | 'success' | 'warning'\n\n/** The silhouette: softly rounded, or a full pill. */\nexport type ChipShape = 'chip' | 'pill'\n\n/** The height of the chip: 24 or 32 pixels. */\nexport type ChipSize = 'xs' | 'sm'\n\ninterface ChipProps {\n /**\n * How strongly the chip is painted: a tinted background, the full colour, or a\n * border alone.\n */\n variant?: ChipVariant\n /**\n * What the chip means, expressed as a colour: `neutral` by default, then `accent` for\n * something singled out and `danger`/ `success`/ `warning` for a state being reported.\n */\n tone?: ChipTone\n /** A colour of your own (hex, CSS name or `oklch()`), which REPLACES the tone. */\n color?: string\n /** The silhouette: softly rounded corners, or a full pill. */\n shape?: ChipShape\n /** The height of the chip. */\n size?: ChipSize\n /** Takes 4px off the height, leaving the padding, the text and the icons as they are. */\n compact?: boolean\n /** Makes the chip a button that reacts to clicks, without holding a state. */\n clickable?: boolean\n /**\n * Where the chip leads, which makes it a link. A disabled link is made inert by\n * hand.\n */\n href?: string\n /**\n * Makes the chip something that stays chosen, bound to `v-model:selected`. It wins\n * over `href` and `clickable`.\n */\n selectable?: boolean\n /**\n * Shows a tick before the label while the chip is selected. It REPLACES whatever\n * start icon was given, so the two are never shown together.\n */\n check?: boolean\n /** The icon of that tick, a built-in check mark by default. `iconFilled` does not reach it. */\n checkIcon?: IconSource\n /** An icon before the label. The `#start` slot replaces it. */\n iconStart?: IconSource\n /** An icon after the label. The `#end` slot replaces it. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis). It has\n * no effect on the `#start`/`#end` slots, whose icons the consumer builds, nor on the tick\n * or the removal cross.\n */\n iconFilled?: boolean\n /**\n * Adds a button that asks for the chip to be removed. It only EMITS that request:\n * actually taking the chip away is the consumer's decision.\n */\n dismissible?: boolean\n /** The icon of that removal button. */\n dismissIcon?: IconSource\n /**\n * What the removal button does, in words. It falls back to the design system\n * dictionary.\n */\n dismissLabel?: string\n /** Makes the chip unusable, greyed out through the colour tokens. */\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<ChipProps>(), {\n variant: 'soft',\n tone: 'neutral',\n color: undefined,\n shape: 'chip',\n size: 'xs',\n compact: false,\n clickable: false,\n href: undefined,\n selectable: false,\n check: false,\n checkIcon: () => checkMark,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n dismissible: false,\n dismissIcon: () => closeIcon,\n dismissLabel: undefined,\n disabled: false,\n})\n\nconst m = useMessages()\nconst resolvedDismissLabel = computed(() => props.dismissLabel ?? m.value.common.dismiss)\n\n/**\n * Whether the chip is selected, which is also what makes it selectable at all: binding this\n * turns it into a toggle button and takes precedence over `href` and `clickable`. It starts\n * unselected.\n */\nconst selected = defineModel<boolean>('selected', { default: false })\n\ndefineEmits<{\n /** The removal button was pressed. The chip is still there: removing it is up to you. */\n dismiss: []\n}>()\n\nconst slots = defineSlots<{\n /** The label. It may be left out entirely, which gives a chip made of icons alone. */\n default?(): unknown\n /** Content before the label, which takes the place of `iconStart`. */\n start?(): unknown\n /** Content after the label, which takes the place of `iconEnd`. */\n end?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\n// The wrapper-root split: the pill is the box a consumer positions, and the ACTION is the\n// element that is focused, named and clicked, so everything else goes there.\nconst { rootClass, rootStyle: consumerStyle, forwardedAttrs } = useRootAttrs()\n\n/* A selectable chip is therefore never a link, whatever `href` says. */\nconst {\n isLink,\n isInertLink,\n linkHref,\n attrs: actionAttrs,\n} = useInertLink({\n href: () => (props.selectable ? undefined : props.href),\n inert: () => props.disabled,\n attrs: () => forwardedAttrs.value,\n})\nconst actionTag = computed(() =>\n props.selectable ? 'button' : isLink.value ? 'a' : props.clickable ? 'button' : 'span',\n)\n\nconst rootStyle = computed<StyleValue>(() => [customColorStyle(props.color), consumerStyle.value])\n\nconst actionEl = ref<HTMLElement | null>(null)\n\nconst showCheck = computed(() => props.check && props.selectable && selected.value)\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\n/**\n * An icon and no label at all: the chip becomes a square, as wide as it is tall. The default\n * slot is asked what it RENDERS, not whether it was passed: a wrapper forwarding an empty\n * label still hands one down.\n */\nfunction iconOnly() {\n return (\n flattenSlot(slots.default?.() as VNode[] | undefined).length === 0 &&\n !!(slots.start || slots.end || props.iconStart || props.iconEnd)\n )\n}\n\n// @devwarn\n// A chip drawn with an icon alone has no accessible name: the icon is decorative, so a\n// clickable, selectable or linked one is announced as a bare button or link. Nothing fails\n// visibly, hence the message.\nif (isDev) {\n const attrs = useAttrs()\n onMounted(() => {\n if (\n actionTag.value !== 'span' &&\n actionEl.value?.parentElement?.hasAttribute('data-icon-only') &&\n attrs['aria-label'] === undefined &&\n attrs['aria-labelledby'] === undefined\n ) {\n console.warn(\n '[VChip] a chip made of an icon alone has no accessible name: set aria-label or aria-labelledby on it.',\n )\n }\n })\n}\n\n// A template ref on the chip reaches the pill, a layout box; the element that is focused,\n// named and clicked is the action inside it.\ndefineExpose({\n /** Moves the focus to the chip's action: its button or link. */\n focus: (options?: FocusOptions) => actionEl.value?.focus(options),\n /** The action element, which is also where the consumer's attributes land. */\n el: actionEl,\n})\n</script>\n\n<template>\n <span\n :class=\"['v-chip v-control v-tone v-variant', rootClass]\"\n :style=\"rootStyle\"\n :data-variant=\"variant\"\n :data-tone=\"tone\"\n :data-custom=\"color !== undefined ? '' : undefined\"\n :data-shape=\"shape\"\n :data-size=\"size\"\n :data-compact=\"compact ? '' : undefined\"\n :data-selected=\"selectable && selected ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n :data-icon-only=\"iconOnly() ? '' : undefined\"\n >\n <component\n :is=\"actionTag\"\n ref=\"actionEl\"\n :aria-disabled=\"isInertLink ? 'true' : undefined\"\n :aria-pressed=\"selectable ? selected : undefined\"\n v-bind=\"actionAttrs\"\n class=\"v-chip-action\"\n :type=\"actionTag === 'button' ? 'button' : undefined\"\n :href=\"linkHref\"\n :disabled=\"actionTag === 'button' ? disabled : undefined\"\n @click=\"selectable && !disabled && (selected = !selected)\"\n >\n <VIcon v-if=\"showCheck\" v-bind=\"iconProps(checkIcon)\" />\n <slot v-else name=\"start\">\n <VIcon v-if=\"iconStart\" v-bind=\"iconProps(iconStart)\" :filled=\"iconFilled\" />\n </slot>\n <slot />\n <slot name=\"end\">\n <VIcon v-if=\"iconEnd\" v-bind=\"iconProps(iconEnd)\" :filled=\"iconFilled\" />\n </slot>\n </component>\n <button\n v-if=\"dismissible\"\n type=\"button\"\n class=\"v-chip-dismiss\"\n :aria-label=\"resolvedDismissLabel\"\n :disabled=\"disabled\"\n @click=\"$emit('dismiss')\"\n >\n <VIcon v-bind=\"iconProps(dismissIcon)\" />\n </button>\n </span>\n</template>\n\n<style>\n@layer vectis.components {\n /* The height comes from the shared size class set on this element. The scale itself\n holds five steps; the component's type restricts it to the two smallest, a chip\n larger than that being a button. */\n .v-chip {\n display: inline-flex;\n align-items: center;\n block-size: var(--control-height);\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-chip);\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n transition:\n background-color var(--vectis-duration-fast) var(--vectis-ease-default),\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n color var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-chip[data-shape='pill'] {\n border-radius: var(--vectis-radius-pill);\n }\n\n /* The tone table itself lives in styles/tones.css, in a layer below the components.\n All five tones are taken exactly as they come; the only values redefined here are\n those of a custom colour, and they win over the shared table by LAYER rather than\n by order, so no bundling can change the outcome. */\n\n /* A custom colour replaces the tone entirely. The shared table derives the four values\n every adopter reads (styles/tones.css, `.v-tone[data-custom]`); the chip adds the\n three only it paints, its hover and active steps and its outline, from the same colour\n mixed with the theme's own text and surface. */\n .v-chip[data-custom] {\n --tone-bg-solid-hover: color-mix(in oklab, var(--custom-color), var(--vectis-color-text) 8%);\n --tone-bg-solid-active: color-mix(in oklab, var(--custom-color), var(--vectis-color-text) 14%);\n --tone-border-soft: color-mix(in oklab, var(--custom-color), var(--vectis-color-surface) 60%);\n }\n\n /*\n * It COMPOUNDS `.v-variant`: the variant table weighs (0,2,0) like this rule and lives in\n * another sheet, so at equal specificity the winner would be whichever one the consumer's\n * bundler put last. At (0,3,0) it wins by construction.\n */\n .v-variant.v-chip[data-selected] {\n background: var(--tone-bg-solid);\n color: var(--tone-text-solid);\n border-color: transparent;\n }\n\n /* Hovering and pressing are conditioned on the ACTION being a real control, which\n buys two things at once: a chip that merely displays a value never lights up, and\n hovering the removal button does not repaint the whole chip as though it were\n about to be activated. */\n /* The same steps as VButton's, rule for rule: an outline sits on the soft surface while\n hovered and one tint further when pressed, and that tint is where a soft chip starts\n on hover. Held in each sheet rather than in `variants.css`, which every consumer\n downloads whether or not it renders a single button or chip. */\n .v-chip[data-variant='outline']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:hover\n ) {\n background: var(--tone-bg-soft);\n }\n\n .v-chip[data-variant='outline']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:active\n ),\n .v-chip[data-variant='soft']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:hover\n ) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 8%);\n }\n\n .v-chip[data-variant='soft']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:active\n ) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 14%);\n }\n\n .v-chip[data-variant='solid']:not([data-disabled]):has(:is(button, a).v-chip-action:hover),\n .v-chip[data-selected]:not([data-disabled]):has(:is(button, a).v-chip-action:hover) {\n background: var(--tone-bg-solid-hover);\n }\n\n .v-chip[data-variant='solid']:not([data-disabled]):has(:is(button, a).v-chip-action:active),\n .v-chip[data-selected]:not([data-disabled]):has(:is(button, a).v-chip-action:active) {\n background: var(--tone-bg-solid-active);\n }\n\n .v-chip-action {\n display: inline-flex;\n align-items: center;\n gap: var(--control-gap);\n block-size: 100%;\n padding-block: 0;\n padding-inline: var(--control-padding-inline);\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n border-radius: inherit;\n text-decoration: none;\n cursor: default;\n }\n\n /* With an icon and no label the action becomes square, its width taken from its own\n height through the ratio rather than from a new measurement to keep in step. */\n .v-chip[data-icon-only] .v-chip-action {\n aspect-ratio: 1;\n justify-content: center;\n padding-inline: 0;\n }\n\n :is(button, a).v-chip-action {\n cursor: pointer;\n }\n\n :is(button, a).v-chip-action:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /*\n * The cross inherits the chip's text colour rather than a muted one: drawn from the icon font,\n * it is real text, to which the text contrast rule applies.\n */\n .v-chip-dismiss {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n inline-size: var(--control-action-size);\n block-size: var(--control-action-size);\n margin-inline: calc(var(--vectis-space-1) * -1) var(--vectis-space-1);\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n border-radius: var(--vectis-radius-pill);\n cursor: pointer;\n flex: none;\n }\n\n .v-chip-dismiss:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n /*\n * A disabled chip greys out through the colour tokens, which makes it follow the dark theme\n * without a rule of its own.\n */\n .v-variant.v-chip[data-disabled] {\n background: var(--vectis-color-surface-muted);\n color: var(--vectis-color-text-subtle);\n border-color: transparent;\n }\n\n .v-variant.v-chip[data-disabled][data-variant='outline'] {\n background: transparent;\n border-color: var(--vectis-color-border);\n }\n\n .v-chip[data-disabled] :is(:is(button, a).v-chip-action, .v-chip-dismiss) {\n cursor: not-allowed;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-chip {\n transition: none;\n }\n }\n\n /*\n * The class is repeated to reach (0,6,0). The variant, hover and active rules reach (0,5,0),\n * and with the forcing turned off any of them that still won would paint its tone over the\n * selection, with HighlightText on top of it.\n */\n @media (forced-colors: active) {\n .v-chip.v-chip.v-chip.v-chip[data-selected]:not([data-disabled]) {\n forced-color-adjust: none;\n background-color: Highlight;\n color: HighlightText;\n border-color: Highlight;\n outline-color: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VChip.vue_vue_type_script_setup_true_lang.js","names":["$emit","checkMark","closeIcon","_useModel","_useSlots","_openBlock","_createElementBlock","_normalizeClass","_unref","_normalizeStyle","_createBlock","_resolveDynamicComponent","_mergeProps","_withCtx","VIcon","_normalizeProps","_renderSlot","_createCommentVNode","_createVNode","_guardReactiveProps","VIcon_default","_hoisted_1","_hoisted_2","VChip_vue_vue_type_script_setup_true_lang_default","check","close","\"selected\"","\"selectedModifiers\"","__expose","props","__props","m","useMessages","resolvedDismissLabel","computed","selected","useModel","slots","useSlots","consumerStyle","useRootAttrs","actionAttrs","useInertLink","forwardedAttrs","actionTag","isLink","rootStyle","customColorStyle","actionEl","ref","showCheck","iconOnly","flattenSlot","isDev","attrs","useAttrs","options","openBlock","createElementBlock","normalizeClass","unref","rootClass","normalizeStyle","createBlock","resolveDynamicComponent","mergeProps","isInertLink","linkHref","_cache","withCtx","normalizeProps","iconProps","renderSlot","_ctx","createCommentVNode","createVNode","guardReactiveProps"],"sources":["../../../src/components/VChip/VChip.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Choose native links or buttons for chip actions. Dismissal is a sibling button because nested\n * buttons are invalid and inaccessible; inert links need a JS bridge.\n */\n\nimport { computed, onMounted, ref, useAttrs } from 'vue'\nimport type { StyleValue, VNode } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\n// Neither import may be named after a prop (`check`, `checkIcon`): every top-level binding of a\n// `<script setup>` reaches the template, where it shadows the prop of that name.\nimport { check as checkMark } from '../VIcon/icons/check'\nimport { close as closeIcon } from '../VIcon/icons/close'\nimport type { IconSource } from '../VIcon/types'\nimport { useMessages } from '../../i18n/state'\n\nimport { useInertLink } from '../../composables/useInertLink'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { customColorStyle } from '../../utils/css'\nimport { isDev } from '../../utils/env'\nimport { flattenSlot } from '../../utils/vnode'\n\n/** How much visual weight the chip carries. */\nexport type ChipVariant = 'soft' | 'solid' | 'outline'\n\n/** What the chip means, in colour. */\nexport type ChipTone = 'neutral' | 'accent' | 'danger' | 'success' | 'warning'\n\n/** The silhouette: softly rounded, or a full pill. */\nexport type ChipShape = 'chip' | 'pill'\n\n/** The height of the chip: 24 or 32 pixels. */\nexport type ChipSize = 'xs' | 'sm'\n\ninterface ChipProps {\n /**\n * How strongly the chip is painted: a tinted background, the full colour, or a\n * border alone.\n */\n variant?: ChipVariant\n /**\n * What the chip means, expressed as a colour: `neutral` by default, then `accent` for\n * something singled out and `danger`/ `success`/ `warning` for a state being reported.\n */\n tone?: ChipTone\n /** A colour of your own (hex, CSS name or `oklch()`), which REPLACES the tone. */\n color?: string\n /** The silhouette: softly rounded corners, or a full pill. */\n shape?: ChipShape\n /** The height of the chip. */\n size?: ChipSize\n /** Makes the chip a button that reacts to clicks, without holding a state. */\n clickable?: boolean\n /**\n * Where the chip leads, which makes it a link. A disabled link is made inert by\n * hand.\n */\n href?: string\n /**\n * Makes the chip something that stays chosen, bound to `v-model:selected`. It wins\n * over `href` and `clickable`.\n */\n selectable?: boolean\n /**\n * Shows a tick before the label while the chip is selected. It REPLACES whatever\n * start icon was given, so the two are never shown together.\n */\n check?: boolean\n /** The icon of that tick, a built-in check mark by default. `iconFilled` does not reach it. */\n checkIcon?: IconSource\n /** An icon before the label. The `#start` slot replaces it. */\n iconStart?: IconSource\n /** An icon after the label. The `#end` slot replaces it. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis). It has\n * no effect on the `#start`/`#end` slots, whose icons the consumer builds, nor on the tick\n * or the removal cross.\n */\n iconFilled?: boolean\n /**\n * Adds a button that asks for the chip to be removed. It only EMITS that request:\n * actually taking the chip away is the consumer's decision.\n */\n dismissible?: boolean\n /** The icon of that removal button. */\n dismissIcon?: IconSource\n /**\n * What the removal button does, in words. It falls back to the design system\n * dictionary.\n */\n dismissLabel?: string\n /** Makes the chip unusable, greyed out through the colour tokens. */\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<ChipProps>(), {\n variant: 'soft',\n tone: 'neutral',\n color: undefined,\n shape: 'chip',\n size: 'xs',\n clickable: false,\n href: undefined,\n selectable: false,\n check: false,\n checkIcon: () => checkMark,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n dismissible: false,\n dismissIcon: () => closeIcon,\n dismissLabel: undefined,\n disabled: false,\n})\n\nconst m = useMessages()\nconst resolvedDismissLabel = computed(() => props.dismissLabel ?? m.value.common.dismiss)\n\n/**\n * Whether the chip is selected, which is also what makes it selectable at all: binding this\n * turns it into a toggle button and takes precedence over `href` and `clickable`. It starts\n * unselected.\n */\nconst selected = defineModel<boolean>('selected', { default: false })\n\ndefineEmits<{\n /** The removal button was pressed. The chip is still there: removing it is up to you. */\n dismiss: []\n}>()\n\nconst slots = defineSlots<{\n /** The label. It may be left out entirely, which gives a chip made of icons alone. */\n default?(): unknown\n /** Content before the label, which takes the place of `iconStart`. */\n start?(): unknown\n /** Content after the label, which takes the place of `iconEnd`. */\n end?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\n// The wrapper-root split: the pill is the box a consumer positions, and the ACTION is the\n// element that is focused, named and clicked, so everything else goes there.\nconst { rootClass, rootStyle: consumerStyle, forwardedAttrs } = useRootAttrs()\n\n/* A selectable chip is therefore never a link, whatever `href` says. */\nconst {\n isLink,\n isInertLink,\n linkHref,\n attrs: actionAttrs,\n} = useInertLink({\n href: () => (props.selectable ? undefined : props.href),\n inert: () => props.disabled,\n attrs: () => forwardedAttrs.value,\n})\nconst actionTag = computed(() =>\n props.selectable ? 'button' : isLink.value ? 'a' : props.clickable ? 'button' : 'span',\n)\n\nconst rootStyle = computed<StyleValue>(() => [customColorStyle(props.color), consumerStyle.value])\n\nconst actionEl = ref<HTMLElement | null>(null)\n\nconst showCheck = computed(() => props.check && props.selectable && selected.value)\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\n/**\n * An icon and no label at all: the chip becomes a square, as wide as it is tall. The default\n * slot is asked what it RENDERS, not whether it was passed: a wrapper forwarding an empty\n * label still hands one down.\n */\nfunction iconOnly() {\n return (\n flattenSlot(slots.default?.() as VNode[] | undefined).length === 0 &&\n !!(slots.start || slots.end || props.iconStart || props.iconEnd)\n )\n}\n\n// @devwarn\n// A chip drawn with an icon alone has no accessible name: the icon is decorative, so a\n// clickable, selectable or linked one is announced as a bare button or link. Nothing fails\n// visibly, hence the message.\nif (isDev) {\n const attrs = useAttrs()\n onMounted(() => {\n if (\n actionTag.value !== 'span' &&\n actionEl.value?.parentElement?.hasAttribute('data-icon-only') &&\n attrs['aria-label'] === undefined &&\n attrs['aria-labelledby'] === undefined\n ) {\n console.warn(\n '[VChip] a chip made of an icon alone has no accessible name: set aria-label or aria-labelledby on it.',\n )\n }\n })\n}\n\n// A template ref on the chip reaches the pill, a layout box; the element that is focused,\n// named and clicked is the action inside it.\ndefineExpose({\n /** Moves the focus to the chip's action: its button or link. */\n focus: (options?: FocusOptions) => actionEl.value?.focus(options),\n /** The action element, which is also where the consumer's attributes land. */\n el: actionEl,\n})\n</script>\n\n<template>\n <span\n :class=\"['v-chip v-control v-tone v-variant', rootClass]\"\n :style=\"rootStyle\"\n :data-variant=\"variant\"\n :data-tone=\"tone\"\n :data-custom=\"color !== undefined ? '' : undefined\"\n :data-shape=\"shape\"\n :data-size=\"size\"\n :data-selected=\"selectable && selected ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n :data-icon-only=\"iconOnly() ? '' : undefined\"\n >\n <component\n :is=\"actionTag\"\n ref=\"actionEl\"\n :aria-disabled=\"isInertLink ? 'true' : undefined\"\n :aria-pressed=\"selectable ? selected : undefined\"\n v-bind=\"actionAttrs\"\n class=\"v-chip-action\"\n :type=\"actionTag === 'button' ? 'button' : undefined\"\n :href=\"linkHref\"\n :disabled=\"actionTag === 'button' ? disabled : undefined\"\n @click=\"selectable && !disabled && (selected = !selected)\"\n >\n <VIcon v-if=\"showCheck\" v-bind=\"iconProps(checkIcon)\" />\n <slot v-else name=\"start\">\n <VIcon v-if=\"iconStart\" v-bind=\"iconProps(iconStart)\" :filled=\"iconFilled\" />\n </slot>\n <slot />\n <slot name=\"end\">\n <VIcon v-if=\"iconEnd\" v-bind=\"iconProps(iconEnd)\" :filled=\"iconFilled\" />\n </slot>\n </component>\n <button\n v-if=\"dismissible\"\n type=\"button\"\n class=\"v-chip-dismiss\"\n :aria-label=\"resolvedDismissLabel\"\n :disabled=\"disabled\"\n @click=\"$emit('dismiss')\"\n >\n <VIcon v-bind=\"iconProps(dismissIcon)\" />\n </button>\n </span>\n</template>\n\n<style>\n@layer vectis.components {\n /* The height comes from the shared size class set on this element. The scale itself\n holds five steps; the component's type restricts it to the two smallest, a chip\n larger than that being a button. */\n .v-chip {\n display: inline-flex;\n align-items: center;\n block-size: var(--control-height);\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-chip);\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n transition:\n background-color var(--vectis-duration-fast) var(--vectis-ease-default),\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n color var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-chip[data-shape='pill'] {\n border-radius: var(--vectis-radius-pill);\n }\n\n /* The tone table itself lives in styles/tones.css, in a layer below the components.\n All five tones are taken exactly as they come; the only values redefined here are\n those of a custom colour, and they win over the shared table by LAYER rather than\n by order, so no bundling can change the outcome. */\n\n /* A custom colour replaces the tone entirely. The shared table derives the four values\n every adopter reads (styles/tones.css, `.v-tone[data-custom]`); the chip adds the\n three only it paints, its hover and active steps and its outline, from the same colour\n mixed with the theme's own text and surface. */\n .v-chip[data-custom] {\n --tone-bg-solid-hover: color-mix(in oklab, var(--custom-color), var(--vectis-color-text) 8%);\n --tone-bg-solid-active: color-mix(in oklab, var(--custom-color), var(--vectis-color-text) 14%);\n --tone-border-soft: color-mix(in oklab, var(--custom-color), var(--vectis-color-surface) 60%);\n }\n\n /*\n * It COMPOUNDS `.v-variant`: the variant table weighs (0,2,0) like this rule and lives in\n * another sheet, so at equal specificity the winner would be whichever one the consumer's\n * bundler put last. At (0,3,0) it wins by construction.\n */\n .v-variant.v-chip[data-selected] {\n background: var(--tone-bg-solid);\n color: var(--tone-text-solid);\n border-color: transparent;\n }\n\n /* Hovering and pressing are conditioned on the ACTION being a real control, which\n buys two things at once: a chip that merely displays a value never lights up, and\n hovering the removal button does not repaint the whole chip as though it were\n about to be activated. */\n /* The same steps as VButton's, rule for rule: an outline sits on the soft surface while\n hovered and one tint further when pressed, and that tint is where a soft chip starts\n on hover. Held in each sheet rather than in `variants.css`, which every consumer\n downloads whether or not it renders a single button or chip. */\n .v-chip[data-variant='outline']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:hover\n ) {\n background: var(--tone-bg-soft);\n }\n\n .v-chip[data-variant='outline']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:active\n ),\n .v-chip[data-variant='soft']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:hover\n ) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 8%);\n }\n\n .v-chip[data-variant='soft']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:active\n ) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 14%);\n }\n\n .v-chip[data-variant='solid']:not([data-disabled]):has(:is(button, a).v-chip-action:hover),\n .v-chip[data-selected]:not([data-disabled]):has(:is(button, a).v-chip-action:hover) {\n background: var(--tone-bg-solid-hover);\n }\n\n .v-chip[data-variant='solid']:not([data-disabled]):has(:is(button, a).v-chip-action:active),\n .v-chip[data-selected]:not([data-disabled]):has(:is(button, a).v-chip-action:active) {\n background: var(--tone-bg-solid-active);\n }\n\n .v-chip-action {\n display: inline-flex;\n align-items: center;\n gap: var(--control-gap);\n block-size: 100%;\n padding-block: 0;\n padding-inline: var(--control-padding-inline);\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n border-radius: inherit;\n text-decoration: none;\n cursor: default;\n }\n\n /* With an icon and no label the action becomes square, its width taken from its own\n height through the ratio rather than from a new measurement to keep in step. */\n .v-chip[data-icon-only] .v-chip-action {\n aspect-ratio: 1;\n justify-content: center;\n padding-inline: 0;\n }\n\n :is(button, a).v-chip-action {\n cursor: pointer;\n }\n\n :is(button, a).v-chip-action:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /*\n * The cross inherits the chip's text colour rather than a muted one: drawn from the icon font,\n * it is real text, to which the text contrast rule applies.\n */\n .v-chip-dismiss {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n inline-size: var(--control-action-size);\n block-size: var(--control-action-size);\n margin-inline: calc(var(--vectis-space-1) * -1) var(--vectis-space-1);\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n border-radius: var(--vectis-radius-pill);\n cursor: pointer;\n flex: none;\n }\n\n .v-chip-dismiss:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n /*\n * A disabled chip greys out through the colour tokens, which makes it follow the dark theme\n * without a rule of its own.\n */\n .v-variant.v-chip[data-disabled] {\n background: var(--vectis-color-surface-muted);\n color: var(--vectis-color-text-subtle);\n border-color: transparent;\n }\n\n .v-variant.v-chip[data-disabled][data-variant='outline'] {\n background: transparent;\n border-color: var(--vectis-color-border);\n }\n\n .v-chip[data-disabled] :is(:is(button, a).v-chip-action, .v-chip-dismiss) {\n cursor: not-allowed;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-chip {\n transition: none;\n }\n }\n\n /*\n * The class is repeated to reach (0,6,0). The variant, hover and active rules reach (0,5,0),\n * and with the forcing turned off any of them that still won would paint its tone over the\n * selection, with HighlightText on top of it.\n */\n @media (forced-colors: active) {\n .v-chip.v-chip.v-chip.v-chip[data-selected]:not([data-disabled]) {\n forced-color-adjust: none;\n background-color: Highlight;\n color: HighlightText;\n border-color: Highlight;\n outline-color: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -38,8 +38,6 @@ interface ColorInputProps {
|
|
|
38
38
|
placeholder?: string;
|
|
39
39
|
/** The height of the field: 32, 40 or 48 pixels. */
|
|
40
40
|
size?: ColorInputSize;
|
|
41
|
-
/** Takes 4px off the height. */
|
|
42
|
-
compact?: boolean;
|
|
43
41
|
/** Makes the field unusable. */
|
|
44
42
|
disabled?: boolean;
|
|
45
43
|
/** Shows the colour without letting it change: nothing can be typed, and there is no picker. */
|
|
@@ -90,7 +88,6 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_PublicProps, {
|
|
|
90
88
|
pickerLabel: string;
|
|
91
89
|
error: string;
|
|
92
90
|
size: ColorInputSize;
|
|
93
|
-
compact: boolean;
|
|
94
91
|
disabled: boolean;
|
|
95
92
|
required: boolean;
|
|
96
93
|
placement: ColorInputPlacement;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VColorInput.js","names":[],"sources":["../../../src/components/VColorInput/VColorInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * Compose VInput with VColorPicker. The colour is typed into the field in any format, and the\n * swatch at its start opens the picker in a panel; a text input cannot use popovertarget, so the\n * manual panel bridge handles opening, dismissal and focus transfer into the picker.\n */\nimport { computed, inject, ref, watch } from 'vue'\n\nimport VColorPicker from '../VColorPicker/VColorPicker.vue'\nimport type { ColorFormat, ColorSwatch } from '../VColorPicker/VColorPicker.vue'\nimport { formatColor, parseColor } from '../VColorPicker/color'\nimport type { FieldLabelPosition } from '../VField/VField.vue'\nimport VInput from '../VInput/VInput.vue'\nimport { inputGroupKey } from '../VInput/context'\nimport VPopover from '../VPopover/VPopover.vue'\nimport { canClear } from '../../composables/useClearable'\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useFieldPanel } from '../../composables/useFieldPanel'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useMessages } from '../../i18n/state'\n\n/** The height of the field: 32, 40 or 48 pixels. */\nexport type ColorInputSize = 'sm' | 'md' | 'lg'\n\n/** Where the picker opens relative to the field. */\nexport type ColorInputPlacement =\n 'bottom' | 'bottom-start' | 'bottom-end' | 'top' | 'top-start' | 'top-end'\n\ninterface ColorInputProps {\n /**\n * How the value is written: `#rrggbb`, `rgb()`, `hsl()` or `oklch()`. The field accepts all\n * four, and rewrites what was typed in this one when the reader leaves it.\n */\n format?: ColorFormat\n /** Offers an opacity track, and writes the alpha into the value when it is below 1. */\n alpha?: boolean\n /** Preset colours, offered as swatches in the picker. */\n swatches?: ColorSwatch[]\n /** Hides the picker's eyedropper button. */\n hideEyeDropper?: boolean\n /** The label above the field. */\n label?: string\n /** A line of help under the field. */\n hint?: string\n /**\n * A message saying what is wrong with the value, shown in place of the hint. It marks the\n * field invalid, is read along with it, and is announced when it appears or changes.\n */\n error?: string\n /** Marks the field as required: an asterisk follows the label, and the field is `required`. */\n required?: boolean\n /** Hides the label visually. It still names the field for assistive technology. */\n hideLabel?: boolean\n /**\n * Where the label sits: above the field (`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 /** What the field says while empty. */\n placeholder?: string\n /** The height of the field: 32, 40 or 48 pixels. */\n size?: ColorInputSize\n /** Takes 4px off the height. */\n compact?: boolean\n /** Makes the field unusable. */\n disabled?: boolean\n /** Shows the colour without letting it change: nothing can be typed, and there is no picker. */\n readonly?: boolean\n /** Marks the field as invalid, for a rule of your own. */\n invalid?: boolean\n /** Offers a cross that empties the value. */\n clearable?: boolean\n /** What that cross does, in words. It falls back to the design system dictionary. */\n clearLabel?: string\n /**\n * What the swatch at the start of the field does, in words. It falls back to the design\n * system dictionary.\n */\n pickerLabel?: string\n /** Where the picker opens relative to the field. */\n placement?: ColorInputPlacement\n}\n\nconst props = withDefaults(defineProps<ColorInputProps>(), {\n format: 'hex',\n alpha: false,\n swatches: undefined,\n hideEyeDropper: false,\n label: undefined,\n hint: undefined,\n error: undefined,\n required: false,\n hideLabel: false,\n labelPosition: 'top',\n placeholder: undefined,\n size: 'md',\n compact: false,\n disabled: false,\n readonly: false,\n invalid: false,\n clearable: false,\n clearLabel: undefined,\n pickerLabel: undefined,\n placement: 'bottom-start',\n})\n\n/** The colour, written in `format`, or `null` when the field is empty. */\nconst model = defineModel<string | null>({ default: null })\n\nconst emit = defineEmits<{\n /** The clear cross emptied the field. The value has already been reset. */\n clear: []\n}>()\n\ndefineSlots<{\n /** Controls of your own inside the field, placed before the clear cross. */\n 'value-end'?(): unknown\n}>()\n\n// `class` and `style` stay on the wrapper; everything else goes down to the text field, which a\n// consumer's label points at and what assistive technology deals with.\ndefineOptions({ inheritAttrs: false })\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\nconst m = useMessages()\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelRef = ref<InstanceType<typeof VPopover> | null>(null)\nconst inputRef = ref<InstanceType<typeof VInput> | null>(null)\nconst pickerRef = ref<InstanceType<typeof VColorPicker> | null>(null)\n\n// A VInputGroup joins several controls into one object, so the shape of this field is the\n// row's decision rather than its own (VInput/context.ts).\nconst group = inject(inputGroupKey, null)\nconst {\n size: resolvedSize,\n compact: resolvedCompact,\n disabled: resolvedDisabled,\n} = useControlShape(props, group)\n\nconst hasPanel = computed(() => !props.readonly)\n\nconst {\n open,\n panelId,\n popupAttrs,\n closeAndFocus,\n focusField,\n toggleFromIcon,\n onFocusout,\n onKeydown,\n onPanelMousedown,\n} = useFieldPanel({\n rootEl,\n panelRef,\n field: inputRef,\n disabled: () => resolvedDisabled.value || !hasPanel.value,\n focusInPanel: () => pickerRef.value?.focus(),\n})\n\nconst inputAttrs = popupAttrs(\n () => forwardedAttrs.value,\n () => hasPanel.value,\n)\n\n/** What the field shows: the value, or what is being typed until the reader leaves. */\nconst fieldText = ref('')\nwatch(model, (value) => (fieldText.value = value ?? ''), { immediate: true })\n\nconst parsed = computed(() => parseColor(model.value))\n\n/** The colour the swatch shows, with the opacity it has. */\nconst swatchColor = computed(() =>\n parsed.value ? formatColor(parsed.value, 'rgb', props.alpha) : undefined,\n)\n\n/**\n * Takes what was typed, in any of the four formats, and rewrites it in `format`. Anything that\n * is not a colour puts the field back as it was.\n */\nfunction commitField() {\n const text = fieldText.value.trim()\n if (!text) {\n model.value = null\n return\n }\n const color = parseColor(text)\n if (!color) {\n fieldText.value = model.value ?? ''\n return\n }\n const next = formatColor(color, props.format, props.alpha)\n model.value = next\n // The same value written again changes nothing, so the watcher would leave the text as typed.\n fieldText.value = next\n}\n\n// @keyboard\n// Enter takes the value and stays in the field; without it, the panel bridge would open the\n// picker. The down arrow remains the way into the picker.\nfunction onFieldKeydown(event: KeyboardEvent) {\n if (event.key !== 'Enter') return\n event.preventDefault()\n commitField()\n}\n\n// @keyboard\n// Enter on a track or a swatch is done with choosing: the panel closes and the focus returns to\n// the field. A button's Enter is left alone, being its click.\nfunction onPickerKeydown(event: KeyboardEvent) {\n if (event.key !== 'Enter' || !(event.target as HTMLElement).matches('input')) return\n event.preventDefault()\n closeAndFocus()\n}\n\nconst clearVisible = computed(() =>\n canClear(props, resolvedDisabled.value, !!model.value || !!fieldText.value),\n)\n\nfunction clearValue() {\n model.value = null\n fieldText.value = ''\n focusField()\n emit('clear')\n}\n\ndefineExpose({\n /** Moves the focus to the text field. */\n focus: (options?: FocusOptions) => inputRef.value?.focus(options),\n /** Selects what the field is showing. */\n select: () => inputRef.value?.select(),\n /** The real `<input>` behind the field. */\n el: computed(() => inputRef.value?.el ?? null),\n})\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n class=\"v-color-input\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n :data-open=\"open ? '' : undefined\"\n :data-disabled=\"resolvedDisabled ? '' : undefined\"\n @focusout=\"onFocusout\"\n @keydown=\"onKeydown\"\n >\n <VInput\n ref=\"inputRef\"\n v-model=\"fieldText\"\n class=\"v-color-input-field\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n v-bind=\"inputAttrs\"\n :readonly=\"readonly\"\n :label=\"label\"\n :hint=\"hint\"\n :error=\"error\"\n :required=\"required\"\n :hide-label=\"hideLabel\"\n :label-position=\"labelPosition\"\n :placeholder=\"placeholder\"\n :size=\"resolvedSize\"\n :compact=\"resolvedCompact\"\n :disabled=\"resolvedDisabled\"\n :invalid=\"invalid\"\n :clearable=\"clearable\"\n :clear-visible=\"clearVisible\"\n :clear-label=\"clearLabel ?? m.colorInput.clear\"\n @clear=\"clearValue\"\n @change=\"commitField\"\n @keydown=\"onFieldKeydown\"\n >\n <template #start>\n <button\n v-if=\"hasPanel\"\n type=\"button\"\n class=\"v-field-action v-color-input-swatch\"\n :aria-label=\"pickerLabel ?? m.colorInput.openPicker\"\n :disabled=\"resolvedDisabled\"\n @click=\"toggleFromIcon\"\n >\n <span class=\"v-color-input-chip\" :style=\"{ '--color-input-chip': swatchColor }\" />\n </button>\n <span v-else class=\"v-color-input-swatch\" aria-hidden=\"true\">\n <span class=\"v-color-input-chip\" :style=\"{ '--color-input-chip': swatchColor }\" />\n </span>\n </template>\n <template v-if=\"$slots['value-end']\" #value-end><slot name=\"value-end\" /></template>\n </VInput>\n\n <VPopover\n v-if=\"hasPanel\"\n :id=\"panelId\"\n ref=\"panelRef\"\n v-model:open=\"open\"\n mode=\"manual\"\n anchor=\"--color-input-anchor\"\n :placement=\"placement\"\n role=\"dialog\"\n :aria-label=\"label ?? m.colorInput.pickerLabel\"\n class=\"v-color-input-panel\"\n @mousedown=\"onPanelMousedown\"\n >\n <VColorPicker\n ref=\"pickerRef\"\n v-model=\"model\"\n :format=\"format\"\n :alpha=\"alpha\"\n :swatches=\"swatches\"\n :hide-eye-dropper=\"hideEyeDropper\"\n hide-input\n @keydown=\"onPickerKeydown\"\n />\n </VPopover>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-color-input {\n anchor-scope: --color-input-anchor;\n display: block;\n inline-size: 100%;\n font-family: var(--vectis-text-family);\n }\n\n /*\n * Anchored to the field's box rather than the wrapper, which also holds the label and the\n * hint: the panel then covers whichever of the two it lands on.\n */\n .v-color-input .v-input-field {\n anchor-name: --color-input-anchor;\n }\n\n /* A colour is written left to right in every language, as in VColorPicker's own field. */\n .v-color-input .v-input-control {\n direction: ltr;\n font-variant-numeric: tabular-nums;\n }\n\n .v-color-input .v-input-control:dir(rtl) {\n text-align: right;\n }\n\n /* The read-only swatch, which is no button and takes none of `.v-field-action`. */\n span.v-color-input-swatch {\n display: inline-flex;\n flex: none;\n }\n\n /*\n * The checkerboard shows through a translucent colour, drawn in the theme's own surface and\n * border colours; an empty field shows it alone. Forced colours leave the chip untouched: it\n * is the value, not the theme's paint. As on VColorPicker's swatches, the edge is a\n * translucent border shading the colour that runs under it, the checkerboard stopping inside:\n * an opaque ring would leave a pale fringe on the antialiased pixels.\n */\n .v-color-input-chip {\n inline-size: var(--vectis-icon-size);\n block-size: var(--vectis-icon-size);\n border: 1px solid var(--vectis-color-border-on-fill);\n border-radius: var(--vectis-radius-interactive);\n background:\n linear-gradient(var(--color-input-chip, transparent), var(--color-input-chip, transparent))\n border-box,\n repeating-conic-gradient(var(--vectis-color-border) 0 25%, var(--vectis-color-surface) 0 50%)\n 0 0 / var(--vectis-control-size-color-picker-checker)\n var(--vectis-control-size-color-picker-checker) padding-box;\n forced-color-adjust: none;\n }\n\n .v-color-input[data-disabled] .v-color-input-chip {\n opacity: 0.5;\n }\n\n /*\n * `position-anchor` and the chrome come from VPopover; the panel's own padding is cancelled,\n * VColorPicker padding itself as the date and time pickers do.\n */\n .v-popover-panel.v-color-input-panel {\n inline-size: max-content;\n padding: 0;\n }\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"VColorInput.js","names":["_sfc_main","VColorInput_vue_vue_type_script_setup_true_lang_default","VColorInput_default"],"sources":["../../../src/components/VColorInput/VColorInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * Compose VInput with VColorPicker. The colour is typed into the field in any format, and the\n * swatch at its start opens the picker in a panel; a text input cannot use popovertarget, so the\n * manual panel bridge handles opening, dismissal and focus transfer into the picker.\n */\nimport { computed, inject, ref, watch } from 'vue'\n\nimport VColorPicker from '../VColorPicker/VColorPicker.vue'\nimport type { ColorFormat, ColorSwatch } from '../VColorPicker/VColorPicker.vue'\nimport { formatColor, parseColor } from '../VColorPicker/color'\nimport type { FieldLabelPosition } from '../VField/VField.vue'\nimport VInput from '../VInput/VInput.vue'\nimport { inputGroupKey } from '../VInput/context'\nimport VPopover from '../VPopover/VPopover.vue'\nimport { canClear } from '../../composables/useClearable'\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useFieldPanel } from '../../composables/useFieldPanel'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useMessages } from '../../i18n/state'\n\n/** The height of the field: 32, 40 or 48 pixels. */\nexport type ColorInputSize = 'sm' | 'md' | 'lg'\n\n/** Where the picker opens relative to the field. */\nexport type ColorInputPlacement =\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'top'\n | 'top-start'\n | 'top-end'\n\ninterface ColorInputProps {\n /**\n * How the value is written: `#rrggbb`, `rgb()`, `hsl()` or `oklch()`. The field accepts all\n * four, and rewrites what was typed in this one when the reader leaves it.\n */\n format?: ColorFormat\n /** Offers an opacity track, and writes the alpha into the value when it is below 1. */\n alpha?: boolean\n /** Preset colours, offered as swatches in the picker. */\n swatches?: ColorSwatch[]\n /** Hides the picker's eyedropper button. */\n hideEyeDropper?: boolean\n /** The label above the field. */\n label?: string\n /** A line of help under the field. */\n hint?: string\n /**\n * A message saying what is wrong with the value, shown in place of the hint. It marks the\n * field invalid, is read along with it, and is announced when it appears or changes.\n */\n error?: string\n /** Marks the field as required: an asterisk follows the label, and the field is `required`. */\n required?: boolean\n /** Hides the label visually. It still names the field for assistive technology. */\n hideLabel?: boolean\n /**\n * Where the label sits: above the field (`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 /** What the field says while empty. */\n placeholder?: string\n /** The height of the field: 32, 40 or 48 pixels. */\n size?: ColorInputSize\n /** Makes the field unusable. */\n disabled?: boolean\n /** Shows the colour without letting it change: nothing can be typed, and there is no picker. */\n readonly?: boolean\n /** Marks the field as invalid, for a rule of your own. */\n invalid?: boolean\n /** Offers a cross that empties the value. */\n clearable?: boolean\n /** What that cross does, in words. It falls back to the design system dictionary. */\n clearLabel?: string\n /**\n * What the swatch at the start of the field does, in words. It falls back to the design\n * system dictionary.\n */\n pickerLabel?: string\n /** Where the picker opens relative to the field. */\n placement?: ColorInputPlacement\n}\n\nconst props = withDefaults(defineProps<ColorInputProps>(), {\n format: 'hex',\n alpha: false,\n swatches: undefined,\n hideEyeDropper: false,\n label: undefined,\n hint: undefined,\n error: undefined,\n required: false,\n hideLabel: false,\n labelPosition: 'top',\n placeholder: undefined,\n size: 'md',\n disabled: false,\n readonly: false,\n invalid: false,\n clearable: false,\n clearLabel: undefined,\n pickerLabel: undefined,\n placement: 'bottom-start',\n})\n\n/** The colour, written in `format`, or `null` when the field is empty. */\nconst model = defineModel<string | null>({ default: null })\n\nconst emit = defineEmits<{\n /** The clear cross emptied the field. The value has already been reset. */\n clear: []\n}>()\n\ndefineSlots<{\n /** Controls of your own inside the field, placed before the clear cross. */\n 'value-end'?(): unknown\n}>()\n\n// `class` and `style` stay on the wrapper; everything else goes down to the text field, which a\n// consumer's label points at and what assistive technology deals with.\ndefineOptions({ inheritAttrs: false })\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\nconst m = useMessages()\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelRef = ref<InstanceType<typeof VPopover> | null>(null)\nconst inputRef = ref<InstanceType<typeof VInput> | null>(null)\nconst pickerRef = ref<InstanceType<typeof VColorPicker> | null>(null)\n\n// A VInputGroup joins several controls into one object, so the shape of this field is the\n// row's decision rather than its own (VInput/context.ts).\nconst group = inject(inputGroupKey, null)\nconst { size: resolvedSize, disabled: resolvedDisabled } = useControlShape(props, group)\n\nconst hasPanel = computed(() => !props.readonly)\n\nconst {\n open,\n panelId,\n popupAttrs,\n closeAndFocus,\n focusField,\n toggleFromIcon,\n onFocusout,\n onKeydown,\n onPanelMousedown,\n} = useFieldPanel({\n rootEl,\n panelRef,\n field: inputRef,\n disabled: () => resolvedDisabled.value || !hasPanel.value,\n focusInPanel: () => pickerRef.value?.focus(),\n})\n\nconst inputAttrs = popupAttrs(\n () => forwardedAttrs.value,\n () => hasPanel.value,\n)\n\n/** What the field shows: the value, or what is being typed until the reader leaves. */\nconst fieldText = ref('')\nwatch(model, (value) => (fieldText.value = value ?? ''), { immediate: true })\n\nconst parsed = computed(() => parseColor(model.value))\n\n/** The colour the swatch shows, with the opacity it has. */\nconst swatchColor = computed(() =>\n parsed.value ? formatColor(parsed.value, 'rgb', props.alpha) : undefined,\n)\n\n/**\n * Takes what was typed, in any of the four formats, and rewrites it in `format`. Anything that\n * is not a colour puts the field back as it was.\n */\nfunction commitField() {\n const text = fieldText.value.trim()\n if (!text) {\n model.value = null\n return\n }\n const color = parseColor(text)\n if (!color) {\n fieldText.value = model.value ?? ''\n return\n }\n const next = formatColor(color, props.format, props.alpha)\n model.value = next\n // The same value written again changes nothing, so the watcher would leave the text as typed.\n fieldText.value = next\n}\n\n// @keyboard\n// Enter takes the value and stays in the field; without it, the panel bridge would open the\n// picker. The down arrow remains the way into the picker.\nfunction onFieldKeydown(event: KeyboardEvent) {\n if (event.key !== 'Enter') return\n event.preventDefault()\n commitField()\n}\n\n// @keyboard\n// Enter on a track or a swatch is done with choosing: the panel closes and the focus returns to\n// the field. A button's Enter is left alone, being its click.\nfunction onPickerKeydown(event: KeyboardEvent) {\n if (event.key !== 'Enter' || !(event.target as HTMLElement).matches('input')) return\n event.preventDefault()\n closeAndFocus()\n}\n\nconst clearVisible = computed(() =>\n canClear(props, resolvedDisabled.value, !!model.value || !!fieldText.value),\n)\n\nfunction clearValue() {\n model.value = null\n fieldText.value = ''\n focusField()\n emit('clear')\n}\n\ndefineExpose({\n /** Moves the focus to the text field. */\n focus: (options?: FocusOptions) => inputRef.value?.focus(options),\n /** Selects what the field is showing. */\n select: () => inputRef.value?.select(),\n /** The real `<input>` behind the field. */\n el: computed(() => inputRef.value?.el ?? null),\n})\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n class=\"v-color-input\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n :data-open=\"open ? '' : undefined\"\n :data-disabled=\"resolvedDisabled ? '' : undefined\"\n @focusout=\"onFocusout\"\n @keydown=\"onKeydown\"\n >\n <VInput\n ref=\"inputRef\"\n v-model=\"fieldText\"\n class=\"v-color-input-field\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n v-bind=\"inputAttrs\"\n :readonly=\"readonly\"\n :label=\"label\"\n :hint=\"hint\"\n :error=\"error\"\n :required=\"required\"\n :hide-label=\"hideLabel\"\n :label-position=\"labelPosition\"\n :placeholder=\"placeholder\"\n :size=\"resolvedSize\"\n :disabled=\"resolvedDisabled\"\n :invalid=\"invalid\"\n :clearable=\"clearable\"\n :clear-visible=\"clearVisible\"\n :clear-label=\"clearLabel ?? m.colorInput.clear\"\n @clear=\"clearValue\"\n @change=\"commitField\"\n @keydown=\"onFieldKeydown\"\n >\n <template #start>\n <button\n v-if=\"hasPanel\"\n type=\"button\"\n class=\"v-field-action v-color-input-swatch\"\n :aria-label=\"pickerLabel ?? m.colorInput.openPicker\"\n :disabled=\"resolvedDisabled\"\n @click=\"toggleFromIcon\"\n >\n <span class=\"v-color-input-chip\" :style=\"{ '--color-input-chip': swatchColor }\" />\n </button>\n <span v-else class=\"v-color-input-swatch\" aria-hidden=\"true\">\n <span class=\"v-color-input-chip\" :style=\"{ '--color-input-chip': swatchColor }\" />\n </span>\n </template>\n <template v-if=\"$slots['value-end']\" #value-end><slot name=\"value-end\" /></template>\n </VInput>\n\n <VPopover\n v-if=\"hasPanel\"\n :id=\"panelId\"\n ref=\"panelRef\"\n v-model:open=\"open\"\n mode=\"manual\"\n anchor=\"--color-input-anchor\"\n :placement=\"placement\"\n role=\"dialog\"\n :aria-label=\"label ?? m.colorInput.pickerLabel\"\n class=\"v-color-input-panel\"\n @mousedown=\"onPanelMousedown\"\n >\n <VColorPicker\n ref=\"pickerRef\"\n v-model=\"model\"\n :format=\"format\"\n :alpha=\"alpha\"\n :swatches=\"swatches\"\n :hide-eye-dropper=\"hideEyeDropper\"\n hide-input\n @keydown=\"onPickerKeydown\"\n />\n </VPopover>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-color-input {\n anchor-scope: --color-input-anchor;\n display: block;\n inline-size: 100%;\n font-family: var(--vectis-text-family);\n }\n\n /*\n * Anchored to the field's box rather than the wrapper, which also holds the label and the\n * hint: the panel then covers whichever of the two it lands on.\n */\n .v-color-input .v-input-field {\n anchor-name: --color-input-anchor;\n }\n\n /* A colour is written left to right in every language, as in VColorPicker's own field. */\n .v-color-input .v-input-control {\n direction: ltr;\n font-variant-numeric: tabular-nums;\n }\n\n .v-color-input .v-input-control:dir(rtl) {\n text-align: right;\n }\n\n /* The read-only swatch, which is no button and takes none of `.v-field-action`. */\n span.v-color-input-swatch {\n display: inline-flex;\n flex: none;\n }\n\n /*\n * The checkerboard shows through a translucent colour, drawn in the theme's own surface and\n * border colours; an empty field shows it alone. Forced colours leave the chip untouched: it\n * is the value, not the theme's paint. As on VColorPicker's swatches, the edge is a\n * translucent border shading the colour that runs under it, the checkerboard stopping inside:\n * an opaque ring would leave a pale fringe on the antialiased pixels.\n */\n .v-color-input-chip {\n inline-size: var(--vectis-icon-size);\n block-size: var(--vectis-icon-size);\n border: 1px solid var(--vectis-color-border-on-fill);\n border-radius: var(--vectis-radius-interactive);\n background:\n linear-gradient(var(--color-input-chip, transparent), var(--color-input-chip, transparent))\n border-box,\n repeating-conic-gradient(var(--vectis-color-border) 0 25%, var(--vectis-color-surface) 0 50%)\n 0 0 / var(--vectis-control-size-color-picker-checker)\n var(--vectis-control-size-color-picker-checker) padding-box;\n forced-color-adjust: none;\n }\n\n .v-color-input[data-disabled] .v-color-input-chip {\n opacity: 0.5;\n }\n\n /*\n * `position-anchor` and the chrome come from VPopover; the panel's own padding is cancelled,\n * VColorPicker padding itself as the date and time pickers do.\n */\n .v-popover-panel.v-color-input-panel {\n inline-size: max-content;\n padding: 0;\n }\n}\n</style>\n"],"mappings":""}
|
|
@@ -42,10 +42,6 @@ var ae = ["data-open", "data-disabled"], oe = ["aria-label", "disabled"], se = {
|
|
|
42
42
|
labelPosition: { default: "top" },
|
|
43
43
|
placeholder: { default: void 0 },
|
|
44
44
|
size: { default: "md" },
|
|
45
|
-
compact: {
|
|
46
|
-
type: Boolean,
|
|
47
|
-
default: !1
|
|
48
|
-
},
|
|
49
45
|
disabled: {
|
|
50
46
|
type: Boolean,
|
|
51
47
|
default: !1
|
|
@@ -71,7 +67,7 @@ var ae = ["data-open", "data-disabled"], oe = ["aria-label", "disabled"], se = {
|
|
|
71
67
|
}),
|
|
72
68
|
emits: /*@__PURE__*/ y(["clear"], ["update:modelValue"]),
|
|
73
69
|
setup(g, { expose: y, emit: D }) {
|
|
74
|
-
let O = g, k = re(g, "modelValue"), A = D, { rootClass: j, rootStyle: M, forwardedAttrs: N } = r(), P = e(), F = w(null), I = w(null), L = w(null), R = w(null),
|
|
70
|
+
let O = g, k = re(g, "modelValue"), A = D, { rootClass: j, rootStyle: M, forwardedAttrs: N } = r(), P = e(), F = w(null), I = w(null), L = w(null), R = w(null), z = _(a, null), { size: B, disabled: V } = n(O, z), H = l(() => !O.readonly), { open: U, panelId: W, popupAttrs: G, closeAndFocus: K, focusField: ce, toggleFromIcon: q, onFocusout: J, onKeydown: Y, onPanelMousedown: le } = te({
|
|
75
71
|
rootEl: F,
|
|
76
72
|
panelRef: I,
|
|
77
73
|
field: L,
|
|
@@ -98,7 +94,7 @@ var ae = ["data-open", "data-disabled"], oe = ["aria-label", "disabled"], se = {
|
|
|
98
94
|
e.key === "Enter" && (e.preventDefault(), $());
|
|
99
95
|
}
|
|
100
96
|
function fe(e) {
|
|
101
|
-
e.key
|
|
97
|
+
e.key === "Enter" && e.target.matches("input") && (e.preventDefault(), K());
|
|
102
98
|
}
|
|
103
99
|
let pe = l(() => i(O, V.value, !!k.value || !!X.value));
|
|
104
100
|
function me() {
|
|
@@ -134,8 +130,7 @@ var ae = ["data-open", "data-disabled"], oe = ["aria-label", "disabled"], se = {
|
|
|
134
130
|
"hide-label": g.hideLabel,
|
|
135
131
|
"label-position": g.labelPosition,
|
|
136
132
|
placeholder: g.placeholder,
|
|
137
|
-
size: T(
|
|
138
|
-
compact: T(B),
|
|
133
|
+
size: T(B),
|
|
139
134
|
disabled: T(V),
|
|
140
135
|
invalid: g.invalid,
|
|
141
136
|
clearable: g.clearable,
|
|
@@ -175,7 +170,6 @@ var ae = ["data-open", "data-disabled"], oe = ["aria-label", "disabled"], se = {
|
|
|
175
170
|
"label-position",
|
|
176
171
|
"placeholder",
|
|
177
172
|
"size",
|
|
178
|
-
"compact",
|
|
179
173
|
"disabled",
|
|
180
174
|
"invalid",
|
|
181
175
|
"clearable",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VColorInput.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../src/components/VColorInput/VColorInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * Compose VInput with VColorPicker. The colour is typed into the field in any format, and the\n * swatch at its start opens the picker in a panel; a text input cannot use popovertarget, so the\n * manual panel bridge handles opening, dismissal and focus transfer into the picker.\n */\nimport { computed, inject, ref, watch } from 'vue'\n\nimport VColorPicker from '../VColorPicker/VColorPicker.vue'\nimport type { ColorFormat, ColorSwatch } from '../VColorPicker/VColorPicker.vue'\nimport { formatColor, parseColor } from '../VColorPicker/color'\nimport type { FieldLabelPosition } from '../VField/VField.vue'\nimport VInput from '../VInput/VInput.vue'\nimport { inputGroupKey } from '../VInput/context'\nimport VPopover from '../VPopover/VPopover.vue'\nimport { canClear } from '../../composables/useClearable'\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useFieldPanel } from '../../composables/useFieldPanel'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useMessages } from '../../i18n/state'\n\n/** The height of the field: 32, 40 or 48 pixels. */\nexport type ColorInputSize = 'sm' | 'md' | 'lg'\n\n/** Where the picker opens relative to the field. */\nexport type ColorInputPlacement =\n 'bottom' | 'bottom-start' | 'bottom-end' | 'top' | 'top-start' | 'top-end'\n\ninterface ColorInputProps {\n /**\n * How the value is written: `#rrggbb`, `rgb()`, `hsl()` or `oklch()`. The field accepts all\n * four, and rewrites what was typed in this one when the reader leaves it.\n */\n format?: ColorFormat\n /** Offers an opacity track, and writes the alpha into the value when it is below 1. */\n alpha?: boolean\n /** Preset colours, offered as swatches in the picker. */\n swatches?: ColorSwatch[]\n /** Hides the picker's eyedropper button. */\n hideEyeDropper?: boolean\n /** The label above the field. */\n label?: string\n /** A line of help under the field. */\n hint?: string\n /**\n * A message saying what is wrong with the value, shown in place of the hint. It marks the\n * field invalid, is read along with it, and is announced when it appears or changes.\n */\n error?: string\n /** Marks the field as required: an asterisk follows the label, and the field is `required`. */\n required?: boolean\n /** Hides the label visually. It still names the field for assistive technology. */\n hideLabel?: boolean\n /**\n * Where the label sits: above the field (`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 /** What the field says while empty. */\n placeholder?: string\n /** The height of the field: 32, 40 or 48 pixels. */\n size?: ColorInputSize\n /** Takes 4px off the height. */\n compact?: boolean\n /** Makes the field unusable. */\n disabled?: boolean\n /** Shows the colour without letting it change: nothing can be typed, and there is no picker. */\n readonly?: boolean\n /** Marks the field as invalid, for a rule of your own. */\n invalid?: boolean\n /** Offers a cross that empties the value. */\n clearable?: boolean\n /** What that cross does, in words. It falls back to the design system dictionary. */\n clearLabel?: string\n /**\n * What the swatch at the start of the field does, in words. It falls back to the design\n * system dictionary.\n */\n pickerLabel?: string\n /** Where the picker opens relative to the field. */\n placement?: ColorInputPlacement\n}\n\nconst props = withDefaults(defineProps<ColorInputProps>(), {\n format: 'hex',\n alpha: false,\n swatches: undefined,\n hideEyeDropper: false,\n label: undefined,\n hint: undefined,\n error: undefined,\n required: false,\n hideLabel: false,\n labelPosition: 'top',\n placeholder: undefined,\n size: 'md',\n compact: false,\n disabled: false,\n readonly: false,\n invalid: false,\n clearable: false,\n clearLabel: undefined,\n pickerLabel: undefined,\n placement: 'bottom-start',\n})\n\n/** The colour, written in `format`, or `null` when the field is empty. */\nconst model = defineModel<string | null>({ default: null })\n\nconst emit = defineEmits<{\n /** The clear cross emptied the field. The value has already been reset. */\n clear: []\n}>()\n\ndefineSlots<{\n /** Controls of your own inside the field, placed before the clear cross. */\n 'value-end'?(): unknown\n}>()\n\n// `class` and `style` stay on the wrapper; everything else goes down to the text field, which a\n// consumer's label points at and what assistive technology deals with.\ndefineOptions({ inheritAttrs: false })\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\nconst m = useMessages()\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelRef = ref<InstanceType<typeof VPopover> | null>(null)\nconst inputRef = ref<InstanceType<typeof VInput> | null>(null)\nconst pickerRef = ref<InstanceType<typeof VColorPicker> | null>(null)\n\n// A VInputGroup joins several controls into one object, so the shape of this field is the\n// row's decision rather than its own (VInput/context.ts).\nconst group = inject(inputGroupKey, null)\nconst {\n size: resolvedSize,\n compact: resolvedCompact,\n disabled: resolvedDisabled,\n} = useControlShape(props, group)\n\nconst hasPanel = computed(() => !props.readonly)\n\nconst {\n open,\n panelId,\n popupAttrs,\n closeAndFocus,\n focusField,\n toggleFromIcon,\n onFocusout,\n onKeydown,\n onPanelMousedown,\n} = useFieldPanel({\n rootEl,\n panelRef,\n field: inputRef,\n disabled: () => resolvedDisabled.value || !hasPanel.value,\n focusInPanel: () => pickerRef.value?.focus(),\n})\n\nconst inputAttrs = popupAttrs(\n () => forwardedAttrs.value,\n () => hasPanel.value,\n)\n\n/** What the field shows: the value, or what is being typed until the reader leaves. */\nconst fieldText = ref('')\nwatch(model, (value) => (fieldText.value = value ?? ''), { immediate: true })\n\nconst parsed = computed(() => parseColor(model.value))\n\n/** The colour the swatch shows, with the opacity it has. */\nconst swatchColor = computed(() =>\n parsed.value ? formatColor(parsed.value, 'rgb', props.alpha) : undefined,\n)\n\n/**\n * Takes what was typed, in any of the four formats, and rewrites it in `format`. Anything that\n * is not a colour puts the field back as it was.\n */\nfunction commitField() {\n const text = fieldText.value.trim()\n if (!text) {\n model.value = null\n return\n }\n const color = parseColor(text)\n if (!color) {\n fieldText.value = model.value ?? ''\n return\n }\n const next = formatColor(color, props.format, props.alpha)\n model.value = next\n // The same value written again changes nothing, so the watcher would leave the text as typed.\n fieldText.value = next\n}\n\n// @keyboard\n// Enter takes the value and stays in the field; without it, the panel bridge would open the\n// picker. The down arrow remains the way into the picker.\nfunction onFieldKeydown(event: KeyboardEvent) {\n if (event.key !== 'Enter') return\n event.preventDefault()\n commitField()\n}\n\n// @keyboard\n// Enter on a track or a swatch is done with choosing: the panel closes and the focus returns to\n// the field. A button's Enter is left alone, being its click.\nfunction onPickerKeydown(event: KeyboardEvent) {\n if (event.key !== 'Enter' || !(event.target as HTMLElement).matches('input')) return\n event.preventDefault()\n closeAndFocus()\n}\n\nconst clearVisible = computed(() =>\n canClear(props, resolvedDisabled.value, !!model.value || !!fieldText.value),\n)\n\nfunction clearValue() {\n model.value = null\n fieldText.value = ''\n focusField()\n emit('clear')\n}\n\ndefineExpose({\n /** Moves the focus to the text field. */\n focus: (options?: FocusOptions) => inputRef.value?.focus(options),\n /** Selects what the field is showing. */\n select: () => inputRef.value?.select(),\n /** The real `<input>` behind the field. */\n el: computed(() => inputRef.value?.el ?? null),\n})\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n class=\"v-color-input\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n :data-open=\"open ? '' : undefined\"\n :data-disabled=\"resolvedDisabled ? '' : undefined\"\n @focusout=\"onFocusout\"\n @keydown=\"onKeydown\"\n >\n <VInput\n ref=\"inputRef\"\n v-model=\"fieldText\"\n class=\"v-color-input-field\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n v-bind=\"inputAttrs\"\n :readonly=\"readonly\"\n :label=\"label\"\n :hint=\"hint\"\n :error=\"error\"\n :required=\"required\"\n :hide-label=\"hideLabel\"\n :label-position=\"labelPosition\"\n :placeholder=\"placeholder\"\n :size=\"resolvedSize\"\n :compact=\"resolvedCompact\"\n :disabled=\"resolvedDisabled\"\n :invalid=\"invalid\"\n :clearable=\"clearable\"\n :clear-visible=\"clearVisible\"\n :clear-label=\"clearLabel ?? m.colorInput.clear\"\n @clear=\"clearValue\"\n @change=\"commitField\"\n @keydown=\"onFieldKeydown\"\n >\n <template #start>\n <button\n v-if=\"hasPanel\"\n type=\"button\"\n class=\"v-field-action v-color-input-swatch\"\n :aria-label=\"pickerLabel ?? m.colorInput.openPicker\"\n :disabled=\"resolvedDisabled\"\n @click=\"toggleFromIcon\"\n >\n <span class=\"v-color-input-chip\" :style=\"{ '--color-input-chip': swatchColor }\" />\n </button>\n <span v-else class=\"v-color-input-swatch\" aria-hidden=\"true\">\n <span class=\"v-color-input-chip\" :style=\"{ '--color-input-chip': swatchColor }\" />\n </span>\n </template>\n <template v-if=\"$slots['value-end']\" #value-end><slot name=\"value-end\" /></template>\n </VInput>\n\n <VPopover\n v-if=\"hasPanel\"\n :id=\"panelId\"\n ref=\"panelRef\"\n v-model:open=\"open\"\n mode=\"manual\"\n anchor=\"--color-input-anchor\"\n :placement=\"placement\"\n role=\"dialog\"\n :aria-label=\"label ?? m.colorInput.pickerLabel\"\n class=\"v-color-input-panel\"\n @mousedown=\"onPanelMousedown\"\n >\n <VColorPicker\n ref=\"pickerRef\"\n v-model=\"model\"\n :format=\"format\"\n :alpha=\"alpha\"\n :swatches=\"swatches\"\n :hide-eye-dropper=\"hideEyeDropper\"\n hide-input\n @keydown=\"onPickerKeydown\"\n />\n </VPopover>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-color-input {\n anchor-scope: --color-input-anchor;\n display: block;\n inline-size: 100%;\n font-family: var(--vectis-text-family);\n }\n\n /*\n * Anchored to the field's box rather than the wrapper, which also holds the label and the\n * hint: the panel then covers whichever of the two it lands on.\n */\n .v-color-input .v-input-field {\n anchor-name: --color-input-anchor;\n }\n\n /* A colour is written left to right in every language, as in VColorPicker's own field. */\n .v-color-input .v-input-control {\n direction: ltr;\n font-variant-numeric: tabular-nums;\n }\n\n .v-color-input .v-input-control:dir(rtl) {\n text-align: right;\n }\n\n /* The read-only swatch, which is no button and takes none of `.v-field-action`. */\n span.v-color-input-swatch {\n display: inline-flex;\n flex: none;\n }\n\n /*\n * The checkerboard shows through a translucent colour, drawn in the theme's own surface and\n * border colours; an empty field shows it alone. Forced colours leave the chip untouched: it\n * is the value, not the theme's paint. As on VColorPicker's swatches, the edge is a\n * translucent border shading the colour that runs under it, the checkerboard stopping inside:\n * an opaque ring would leave a pale fringe on the antialiased pixels.\n */\n .v-color-input-chip {\n inline-size: var(--vectis-icon-size);\n block-size: var(--vectis-icon-size);\n border: 1px solid var(--vectis-color-border-on-fill);\n border-radius: var(--vectis-radius-interactive);\n background:\n linear-gradient(var(--color-input-chip, transparent), var(--color-input-chip, transparent))\n border-box,\n repeating-conic-gradient(var(--vectis-color-border) 0 25%, var(--vectis-color-surface) 0 50%)\n 0 0 / var(--vectis-control-size-color-picker-checker)\n var(--vectis-control-size-color-picker-checker) padding-box;\n forced-color-adjust: none;\n }\n\n .v-color-input[data-disabled] .v-color-input-chip {\n opacity: 0.5;\n }\n\n /*\n * `position-anchor` and the chrome come from VPopover; the panel's own padding is cancelled,\n * VColorPicker padding itself as the date and time pickers do.\n */\n .v-popover-panel.v-color-input-panel {\n inline-size: max-content;\n padding: 0;\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoFA,IAAM,IAAQ,GAwBR,IAAQ,GAA0B,GAAA,YAAkB,GAEpD,IAAO,GAaP,EAAE,cAAW,cAAW,sBAAmB,EAAa,GAExD,IAAI,EAAY,GAEhB,IAAS,EAAwB,IAAI,GACrC,IAAW,EAA0C,IAAI,GACzD,IAAW,EAAwC,IAAI,GACvD,IAAY,EAA8C,IAAI,GAK9D,EACJ,MAAM,GACN,SAAS,GACT,UAAU,MACR,EAAgB,GALN,EAAO,GAAe,IAKT,CAAK,GAE1B,IAAW,QAAe,CAAC,EAAM,QAAQ,GAEzC,EACJ,SACA,YACA,eACA,kBACA,gBACA,mBACA,eACA,cACA,yBACE,GAAc;GAChB;GACA;GACA,OAAO;GACP,gBAAgB,EAAiB,SAAS,CAAC,EAAS;GACpD,oBAAoB,EAAU,OAAO,MAAM;EAC7C,CAAC,GAEK,KAAa,QACX,EAAe,aACf,EAAS,KACjB,GAGM,IAAY,EAAI,EAAE;EACxB,GAAM,IAAQ,MAAW,EAAU,QAAQ,KAAS,IAAK,EAAE,WAAW,GAAK,CAAC;EAE5E,IAAM,IAAS,QAAe,EAAW,EAAM,KAAK,CAAC,GAG/C,IAAc,QAClB,EAAO,QAAQ,EAAY,EAAO,OAAO,OAAO,EAAM,KAAK,IAAI,KAAA,CACjE;EAMA,SAAS,IAAc;GACrB,IAAM,IAAO,EAAU,MAAM,KAAK;GAClC,IAAI,CAAC,GAAM;IACT,EAAM,QAAQ;IACd;GACF;GACA,IAAM,IAAQ,EAAW,CAAI;GAC7B,IAAI,CAAC,GAAO;IACV,EAAU,QAAQ,EAAM,SAAS;IACjC;GACF;GACA,IAAM,IAAO,EAAY,GAAO,EAAM,QAAQ,EAAM,KAAK;GAGzD,AAFA,EAAM,QAAQ,GAEd,EAAU,QAAQ;EACpB;EAKA,SAAS,GAAe,GAAsB;GACxC,EAAM,QAAQ,YAClB,EAAM,eAAe,GACrB,EAAY;EACd;EAKA,SAAS,GAAgB,GAAsB;GACzC,EAAM,QAAQ,WAAW,CAAE,EAAM,OAAuB,QAAQ,OAAO,MAC3E,EAAM,eAAe,GACrB,EAAc;EAChB;EAEA,IAAM,KAAe,QACnB,EAAS,GAAO,EAAiB,OAAO,CAAC,CAAC,EAAM,SAAS,CAAC,CAAC,EAAU,KAAK,CAC5E;EAEA,SAAS,KAAa;GAIpB,AAHA,EAAM,QAAQ,MACd,EAAU,QAAQ,IAClB,GAAW,GACX,EAAK,OAAO;EACd;SAEA,EAAa;GAEX,QAAQ,MAA2B,EAAS,OAAO,MAAM,CAAO;GAEhE,cAAc,EAAS,OAAO,OAAO;GAErC,IAAI,QAAe,EAAS,OAAO,MAAM,IAAI;EAC/C,CAAC,mBAIC,EA8EM,OAAA;YA7EA;GAAJ,KAAI;GACJ,OAAK,EAAA,CAAC,iBACE,EAAA,CAAA,CAAS,CAAA;GAChB,OAAK,EAAE,EAAA,CAAA,CAAS;GAChB,aAAW,EAAA,CAAA,IAAI,KAAQ,KAAA;GACvB,iBAAe,EAAA,CAAA,IAAgB,KAAQ,KAAA;GACvC,YAAQ,AAAA,EAAA,QAAA,GAAA,MAAE,EAAA,CAAA,KAAA,EAAA,CAAA,CAAA,CAAA,GAAA,CAAA;GACV,WAAO,AAAA,EAAA,QAAA,GAAA,MAAE,EAAA,CAAA,KAAA,EAAA,CAAA,CAAA,CAAA,GAAA,CAAA;MAEV,EA0CS,IA1CT,EA0CS;YAzCH;GAAJ,KAAI;eACK,EAAA;4CAAS,QAAA;GAClB,OAAM;GACN,cAAa;GACb,YAAW;KACH,EAAA,EAAA,GAAU;GACjB,UAAU,EAAA;GACV,OAAO,EAAA;GACP,MAAM,EAAA;GACN,OAAO,EAAA;GACP,UAAU,EAAA;GACV,cAAY,EAAA;GACZ,kBAAgB,EAAA;GAChB,aAAa,EAAA;GACb,MAAM,EAAA,CAAA;GACN,SAAS,EAAA,CAAA;GACT,UAAU,EAAA,CAAA;GACV,SAAS,EAAA;GACT,WAAW,EAAA;GACX,iBAAe,GAAA;GACf,eAAa,EAAA,cAAc,EAAA,CAAA,CAAC,CAAC,WAAW;GACxC,SAAO;GACP,UAAQ;GACR,WAAS;;GAEC,OAAK,QAMpB,CAJc,EAAA,SAAA,EAAA,GADR,EASS,UAAA;;IAPP,MAAK;IACL,OAAM;IACL,cAAY,EAAA,eAAe,EAAA,CAAA,CAAC,CAAC,WAAW;IACxC,UAAU,EAAA,CAAA;IACV,SAAK,AAAA,EAAA,QAAA,GAAA,MAAE,EAAA,CAAA,KAAA,EAAA,CAAA,CAAA,CAAA,GAAA,CAAA;OAER,EAAkF,QAAA;IAA5E,OAAM;IAAsB,OAAK,EAAA,EAAA,sBAA0B,EAAA,MAAW,CAAA;iCAE9E,EAEO,QAFP,IAEO,CADL,EAAkF,QAAA;IAA5E,OAAM;IAAsB,OAAK,EAAA,EAAA,sBAA0B,EAAA,MAAW,CAAA;;;MAGhEA,EAAAA,OAAM,eAAA;SAAgB;eAAmC,CAAzB,GAAyB,EAAA,QAAA,WAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;MAInE,EAAA,SAAA,EAAA,GADR,EAuBW,GAAA;;GArBR,IAAI,EAAA,CAAA;YACD;GAAJ,KAAI;GACI,MAAM,EAAA,CAAA;6CAAI,QAAA,IAAA;GAClB,MAAK;GACL,QAAO;GACN,WAAW,EAAA;GACZ,MAAK;GACJ,cAAY,EAAA,SAAS,EAAA,CAAA,CAAC,CAAC,WAAW;GACnC,OAAM;GACL,aAAW,EAAA,EAAA;;oBAWV,CATF,EASE,GAAA;aARI;IAAJ,KAAI;gBACK,EAAA;6CAAK,QAAA;IACb,QAAQ,EAAA;IACR,OAAO,EAAA;IACP,UAAU,EAAA;IACV,oBAAkB,EAAA;IACnB,cAAA;IACC,WAAS"}
|
|
1
|
+
{"version":3,"file":"VColorInput.vue_vue_type_script_setup_true_lang.js","names":["$slots","_useModel","_openBlock","_createElementBlock","_normalizeClass","_unref","_normalizeStyle","_createVNode","VInput","_mergeProps","_createSlots","_withCtx","_createElementVNode","_renderSlot","_createBlock","VPopover","_isRef","VColorPicker","_createCommentVNode","VPopover_default","VInput_default","VColorPicker_default","_hoisted_1","_hoisted_2","_hoisted_3","VColorInput_vue_vue_type_script_setup_true_lang_default","\"modelValue\"","\"modelModifiers\"","__expose","__emit","props","__props","model","useModel","emit","useRootAttrs","m","useMessages","rootEl","ref","panelRef","inputRef","pickerRef","group","inject","inputGroupKey","resolvedSize","resolvedDisabled","useControlShape","hasPanel","computed","useFieldPanel","inputAttrs","popupAttrs","forwardedAttrs","fieldText","value","parsed","parseColor","swatchColor","formatColor","commitField","text","color","next","onFieldKeydown","event","onPickerKeydown","closeAndFocus","clearVisible","canClear","clearValue","focusField","options","openBlock","createElementBlock","normalizeClass","unref","rootClass","normalizeStyle","rootStyle","open","_cache","args","onFocusout","onKeydown","createVNode","mergeProps","$event","createSlots","withCtx","toggleFromIcon","createElementVNode","_ctx","renderSlot","createBlock","panelId","isRef","onPanelMousedown","createCommentVNode"],"sources":["../../../src/components/VColorInput/VColorInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * Compose VInput with VColorPicker. The colour is typed into the field in any format, and the\n * swatch at its start opens the picker in a panel; a text input cannot use popovertarget, so the\n * manual panel bridge handles opening, dismissal and focus transfer into the picker.\n */\nimport { computed, inject, ref, watch } from 'vue'\n\nimport VColorPicker from '../VColorPicker/VColorPicker.vue'\nimport type { ColorFormat, ColorSwatch } from '../VColorPicker/VColorPicker.vue'\nimport { formatColor, parseColor } from '../VColorPicker/color'\nimport type { FieldLabelPosition } from '../VField/VField.vue'\nimport VInput from '../VInput/VInput.vue'\nimport { inputGroupKey } from '../VInput/context'\nimport VPopover from '../VPopover/VPopover.vue'\nimport { canClear } from '../../composables/useClearable'\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useFieldPanel } from '../../composables/useFieldPanel'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useMessages } from '../../i18n/state'\n\n/** The height of the field: 32, 40 or 48 pixels. */\nexport type ColorInputSize = 'sm' | 'md' | 'lg'\n\n/** Where the picker opens relative to the field. */\nexport type ColorInputPlacement =\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'top'\n | 'top-start'\n | 'top-end'\n\ninterface ColorInputProps {\n /**\n * How the value is written: `#rrggbb`, `rgb()`, `hsl()` or `oklch()`. The field accepts all\n * four, and rewrites what was typed in this one when the reader leaves it.\n */\n format?: ColorFormat\n /** Offers an opacity track, and writes the alpha into the value when it is below 1. */\n alpha?: boolean\n /** Preset colours, offered as swatches in the picker. */\n swatches?: ColorSwatch[]\n /** Hides the picker's eyedropper button. */\n hideEyeDropper?: boolean\n /** The label above the field. */\n label?: string\n /** A line of help under the field. */\n hint?: string\n /**\n * A message saying what is wrong with the value, shown in place of the hint. It marks the\n * field invalid, is read along with it, and is announced when it appears or changes.\n */\n error?: string\n /** Marks the field as required: an asterisk follows the label, and the field is `required`. */\n required?: boolean\n /** Hides the label visually. It still names the field for assistive technology. */\n hideLabel?: boolean\n /**\n * Where the label sits: above the field (`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 /** What the field says while empty. */\n placeholder?: string\n /** The height of the field: 32, 40 or 48 pixels. */\n size?: ColorInputSize\n /** Makes the field unusable. */\n disabled?: boolean\n /** Shows the colour without letting it change: nothing can be typed, and there is no picker. */\n readonly?: boolean\n /** Marks the field as invalid, for a rule of your own. */\n invalid?: boolean\n /** Offers a cross that empties the value. */\n clearable?: boolean\n /** What that cross does, in words. It falls back to the design system dictionary. */\n clearLabel?: string\n /**\n * What the swatch at the start of the field does, in words. It falls back to the design\n * system dictionary.\n */\n pickerLabel?: string\n /** Where the picker opens relative to the field. */\n placement?: ColorInputPlacement\n}\n\nconst props = withDefaults(defineProps<ColorInputProps>(), {\n format: 'hex',\n alpha: false,\n swatches: undefined,\n hideEyeDropper: false,\n label: undefined,\n hint: undefined,\n error: undefined,\n required: false,\n hideLabel: false,\n labelPosition: 'top',\n placeholder: undefined,\n size: 'md',\n disabled: false,\n readonly: false,\n invalid: false,\n clearable: false,\n clearLabel: undefined,\n pickerLabel: undefined,\n placement: 'bottom-start',\n})\n\n/** The colour, written in `format`, or `null` when the field is empty. */\nconst model = defineModel<string | null>({ default: null })\n\nconst emit = defineEmits<{\n /** The clear cross emptied the field. The value has already been reset. */\n clear: []\n}>()\n\ndefineSlots<{\n /** Controls of your own inside the field, placed before the clear cross. */\n 'value-end'?(): unknown\n}>()\n\n// `class` and `style` stay on the wrapper; everything else goes down to the text field, which a\n// consumer's label points at and what assistive technology deals with.\ndefineOptions({ inheritAttrs: false })\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\nconst m = useMessages()\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelRef = ref<InstanceType<typeof VPopover> | null>(null)\nconst inputRef = ref<InstanceType<typeof VInput> | null>(null)\nconst pickerRef = ref<InstanceType<typeof VColorPicker> | null>(null)\n\n// A VInputGroup joins several controls into one object, so the shape of this field is the\n// row's decision rather than its own (VInput/context.ts).\nconst group = inject(inputGroupKey, null)\nconst { size: resolvedSize, disabled: resolvedDisabled } = useControlShape(props, group)\n\nconst hasPanel = computed(() => !props.readonly)\n\nconst {\n open,\n panelId,\n popupAttrs,\n closeAndFocus,\n focusField,\n toggleFromIcon,\n onFocusout,\n onKeydown,\n onPanelMousedown,\n} = useFieldPanel({\n rootEl,\n panelRef,\n field: inputRef,\n disabled: () => resolvedDisabled.value || !hasPanel.value,\n focusInPanel: () => pickerRef.value?.focus(),\n})\n\nconst inputAttrs = popupAttrs(\n () => forwardedAttrs.value,\n () => hasPanel.value,\n)\n\n/** What the field shows: the value, or what is being typed until the reader leaves. */\nconst fieldText = ref('')\nwatch(model, (value) => (fieldText.value = value ?? ''), { immediate: true })\n\nconst parsed = computed(() => parseColor(model.value))\n\n/** The colour the swatch shows, with the opacity it has. */\nconst swatchColor = computed(() =>\n parsed.value ? formatColor(parsed.value, 'rgb', props.alpha) : undefined,\n)\n\n/**\n * Takes what was typed, in any of the four formats, and rewrites it in `format`. Anything that\n * is not a colour puts the field back as it was.\n */\nfunction commitField() {\n const text = fieldText.value.trim()\n if (!text) {\n model.value = null\n return\n }\n const color = parseColor(text)\n if (!color) {\n fieldText.value = model.value ?? ''\n return\n }\n const next = formatColor(color, props.format, props.alpha)\n model.value = next\n // The same value written again changes nothing, so the watcher would leave the text as typed.\n fieldText.value = next\n}\n\n// @keyboard\n// Enter takes the value and stays in the field; without it, the panel bridge would open the\n// picker. The down arrow remains the way into the picker.\nfunction onFieldKeydown(event: KeyboardEvent) {\n if (event.key !== 'Enter') return\n event.preventDefault()\n commitField()\n}\n\n// @keyboard\n// Enter on a track or a swatch is done with choosing: the panel closes and the focus returns to\n// the field. A button's Enter is left alone, being its click.\nfunction onPickerKeydown(event: KeyboardEvent) {\n if (event.key !== 'Enter' || !(event.target as HTMLElement).matches('input')) return\n event.preventDefault()\n closeAndFocus()\n}\n\nconst clearVisible = computed(() =>\n canClear(props, resolvedDisabled.value, !!model.value || !!fieldText.value),\n)\n\nfunction clearValue() {\n model.value = null\n fieldText.value = ''\n focusField()\n emit('clear')\n}\n\ndefineExpose({\n /** Moves the focus to the text field. */\n focus: (options?: FocusOptions) => inputRef.value?.focus(options),\n /** Selects what the field is showing. */\n select: () => inputRef.value?.select(),\n /** The real `<input>` behind the field. */\n el: computed(() => inputRef.value?.el ?? null),\n})\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n class=\"v-color-input\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n :data-open=\"open ? '' : undefined\"\n :data-disabled=\"resolvedDisabled ? '' : undefined\"\n @focusout=\"onFocusout\"\n @keydown=\"onKeydown\"\n >\n <VInput\n ref=\"inputRef\"\n v-model=\"fieldText\"\n class=\"v-color-input-field\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n v-bind=\"inputAttrs\"\n :readonly=\"readonly\"\n :label=\"label\"\n :hint=\"hint\"\n :error=\"error\"\n :required=\"required\"\n :hide-label=\"hideLabel\"\n :label-position=\"labelPosition\"\n :placeholder=\"placeholder\"\n :size=\"resolvedSize\"\n :disabled=\"resolvedDisabled\"\n :invalid=\"invalid\"\n :clearable=\"clearable\"\n :clear-visible=\"clearVisible\"\n :clear-label=\"clearLabel ?? m.colorInput.clear\"\n @clear=\"clearValue\"\n @change=\"commitField\"\n @keydown=\"onFieldKeydown\"\n >\n <template #start>\n <button\n v-if=\"hasPanel\"\n type=\"button\"\n class=\"v-field-action v-color-input-swatch\"\n :aria-label=\"pickerLabel ?? m.colorInput.openPicker\"\n :disabled=\"resolvedDisabled\"\n @click=\"toggleFromIcon\"\n >\n <span class=\"v-color-input-chip\" :style=\"{ '--color-input-chip': swatchColor }\" />\n </button>\n <span v-else class=\"v-color-input-swatch\" aria-hidden=\"true\">\n <span class=\"v-color-input-chip\" :style=\"{ '--color-input-chip': swatchColor }\" />\n </span>\n </template>\n <template v-if=\"$slots['value-end']\" #value-end><slot name=\"value-end\" /></template>\n </VInput>\n\n <VPopover\n v-if=\"hasPanel\"\n :id=\"panelId\"\n ref=\"panelRef\"\n v-model:open=\"open\"\n mode=\"manual\"\n anchor=\"--color-input-anchor\"\n :placement=\"placement\"\n role=\"dialog\"\n :aria-label=\"label ?? m.colorInput.pickerLabel\"\n class=\"v-color-input-panel\"\n @mousedown=\"onPanelMousedown\"\n >\n <VColorPicker\n ref=\"pickerRef\"\n v-model=\"model\"\n :format=\"format\"\n :alpha=\"alpha\"\n :swatches=\"swatches\"\n :hide-eye-dropper=\"hideEyeDropper\"\n hide-input\n @keydown=\"onPickerKeydown\"\n />\n </VPopover>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-color-input {\n anchor-scope: --color-input-anchor;\n display: block;\n inline-size: 100%;\n font-family: var(--vectis-text-family);\n }\n\n /*\n * Anchored to the field's box rather than the wrapper, which also holds the label and the\n * hint: the panel then covers whichever of the two it lands on.\n */\n .v-color-input .v-input-field {\n anchor-name: --color-input-anchor;\n }\n\n /* A colour is written left to right in every language, as in VColorPicker's own field. */\n .v-color-input .v-input-control {\n direction: ltr;\n font-variant-numeric: tabular-nums;\n }\n\n .v-color-input .v-input-control:dir(rtl) {\n text-align: right;\n }\n\n /* The read-only swatch, which is no button and takes none of `.v-field-action`. */\n span.v-color-input-swatch {\n display: inline-flex;\n flex: none;\n }\n\n /*\n * The checkerboard shows through a translucent colour, drawn in the theme's own surface and\n * border colours; an empty field shows it alone. Forced colours leave the chip untouched: it\n * is the value, not the theme's paint. As on VColorPicker's swatches, the edge is a\n * translucent border shading the colour that runs under it, the checkerboard stopping inside:\n * an opaque ring would leave a pale fringe on the antialiased pixels.\n */\n .v-color-input-chip {\n inline-size: var(--vectis-icon-size);\n block-size: var(--vectis-icon-size);\n border: 1px solid var(--vectis-color-border-on-fill);\n border-radius: var(--vectis-radius-interactive);\n background:\n linear-gradient(var(--color-input-chip, transparent), var(--color-input-chip, transparent))\n border-box,\n repeating-conic-gradient(var(--vectis-color-border) 0 25%, var(--vectis-color-surface) 0 50%)\n 0 0 / var(--vectis-control-size-color-picker-checker)\n var(--vectis-control-size-color-picker-checker) padding-box;\n forced-color-adjust: none;\n }\n\n .v-color-input[data-disabled] .v-color-input-chip {\n opacity: 0.5;\n }\n\n /*\n * `position-anchor` and the chrome come from VPopover; the panel's own padding is cancelled,\n * VColorPicker padding itself as the date and time pickers do.\n */\n .v-popover-panel.v-color-input-panel {\n inline-size: max-content;\n padding: 0;\n }\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VColorPicker.js","names":[],"sources":["../../../src/components/VColorPicker/VColorPicker.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @a11y @core\n/**\n * A colour picker built from native ranges: the hue and alpha tracks are `<input type=\"range\">`,\n * and the saturation-brightness area holds two hidden ones, one per axis, which assistive\n * technology adjusts on its own. JavaScript adds the pointer on the area and its two-axis arrow\n * keys, which no native control offers.\n */\nimport { computed, onMounted, ref, useId, watch } from 'vue'\n\nimport VIconButton from '../VIconButton/VIconButton.vue'\nimport VInput from '../VInput/VInput.vue'\nimport VSelect from '../VSelect/VSelect.vue'\nimport { colorize as colorizeIcon } from '../VIcon/icons/colorize'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useLocale, useMessages } from '../../i18n/state'\nimport { clamp } from '../../utils/number'\nimport { COLOR_FORMATS, formatColor, hsvToRgb, parseColor, rgbToHsv, sameColor } from './color'\nimport type { ColorFormat, Hsva, Rgba } from './color'\n\nexport type { ColorFormat } from './color'\n\n/** A preset colour: the colour alone, or the colour and the words that name it. */\nexport type ColorSwatch = string | { color: string; label: string }\n\ninterface ColorPickerProps {\n /**\n * How the value is written: `#rrggbb`, `rgb()`, `hsl()` or `oklch()`. The text field accepts\n * all four whatever this says.\n */\n format?: ColorFormat\n /** Offers an opacity track, and writes the alpha into the value when it is below 1. */\n alpha?: boolean\n /** Preset colours, offered as a row of swatches under the picker. */\n swatches?: ColorSwatch[]\n /**\n * Hides the text field and its format menu. VColorInput does, its own field playing that\n * part.\n */\n hideInput?: boolean\n /**\n * Hides the eyedropper button, shown otherwise wherever the browser offers the `EyeDropper`\n * API.\n */\n hideEyeDropper?: boolean\n /** Makes the picker unusable. */\n disabled?: boolean\n /**\n * Shows the colour without letting it be changed. The controls stay focusable and announce\n * their values, which separates it from `disabled`, and the value is still submitted.\n */\n readonly?: boolean\n /** What the picker is called. It falls back to the design system dictionary. */\n label?: string\n /** The name the value is sent under in a form. */\n name?: string\n}\n\nconst props = withDefaults(defineProps<ColorPickerProps>(), {\n format: 'hex',\n alpha: false,\n swatches: undefined,\n hideInput: false,\n hideEyeDropper: false,\n disabled: false,\n readonly: false,\n label: undefined,\n name: undefined,\n})\n\n/** The colour, written in `format`, or `null` when none has been chosen. */\nconst model = defineModel<string | null>({ default: null })\n\nconst m = useMessages()\nconst locale = useLocale()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.colorPicker.label)\n\n/** What the picker shows. With no colour yet, it rests on black, which writes nothing. */\nconst hsva = ref<Hsva>({ h: 0, s: 0, v: 0, a: 1 })\nconst rgba = computed(() => hsvToRgb(hsva.value))\n\n// @core\n// The value last written, so that its echo through the model is not parsed back: hex rounds the\n// channels, and a grey has no hue, so reading it back would move the thumbs under the pointer.\nlet written: string | null = null\n\nwatch(\n model,\n (value) => {\n if (value === written) return\n const parsed = parseColor(value)\n if (parsed) hsva.value = rgbToHsv(props.alpha ? parsed : { ...parsed, a: 1 }, hsva.value.h)\n },\n { immediate: true },\n)\n\nfunction write(next: Hsva) {\n hsva.value = next\n written = formatColor(hsvToRgb(next), props.format, props.alpha)\n model.value = written\n}\n\nfunction writeRgba(next: Rgba) {\n write(rgbToHsv(props.alpha ? next : { ...next, a: 1 }, hsva.value.h))\n}\n\nconst percent = computed(() => new Intl.NumberFormat(locale.value, { style: 'percent' }))\nconst degrees = computed(\n () =>\n new Intl.NumberFormat(locale.value, { style: 'unit', unit: 'degree', unitDisplay: 'narrow' }),\n)\n\n/** Both axes in words: read on either of the area's inputs, whichever one has the focus. */\nconst areaValueText = computed(() =>\n m.value.colorPicker.areaValue(\n percent.value.format(Math.round(hsva.value.s * 100) / 100),\n percent.value.format(Math.round(hsva.value.v * 100) / 100),\n ),\n)\n\nconst areaEl = ref<HTMLElement | null>(null)\nconst saturationEl = ref<HTMLInputElement | null>(null)\n\nfunction moveTo(event: PointerEvent) {\n const rect = areaEl.value!.getBoundingClientRect()\n write({\n ...hsva.value,\n s: clamp((event.clientX - rect.left) / rect.width, 0, 1),\n v: clamp(1 - (event.clientY - rect.top) / rect.height, 0, 1),\n })\n}\n\n/** The pointer dragging the area's thumb, if one is. */\nlet dragging: number | null = null\n\nfunction onAreaPointerdown(event: PointerEvent) {\n if (props.disabled || event.button !== 0) return\n // The focus is moved by hand, without the scroll a default focus on mousedown could cause.\n event.preventDefault()\n saturationEl.value?.focus({ preventScroll: true })\n if (props.readonly) return\n dragging = event.pointerId\n // @fallback\n // Capture keeps the drag going outside the area; synthetic events may not reference a\n // capturable pointer.\n try {\n areaEl.value!.setPointerCapture(event.pointerId)\n } catch {\n /* Synthetic pointers cannot be captured. */\n }\n moveTo(event)\n}\n\nfunction onAreaPointermove(event: PointerEvent) {\n if (event.pointerId === dragging) moveTo(event)\n}\n\nfunction onAreaPointerup(event: PointerEvent) {\n if (event.pointerId === dragging) dragging = null\n}\n\n// @keyboard\n/**\n * Both inputs of the area answer the same keys: the horizontal arrows and Home/End move the\n * saturation, the vertical arrows and PageUp/PageDown the brightness. Natively each input would\n * move its own axis on every arrow.\n */\nfunction onAreaKeydown(event: KeyboardEvent) {\n if (props.readonly) return\n const step = event.shiftKey ? 10 : 1\n const moves: Record<string, [axis: 's' | 'v', delta: number]> = {\n ArrowLeft: ['s', -step],\n ArrowRight: ['s', step],\n ArrowDown: ['v', -step],\n ArrowUp: ['v', step],\n PageDown: ['v', -10],\n PageUp: ['v', 10],\n Home: ['s', -100],\n End: ['s', 100],\n }\n const move = moves[event.key]\n if (!move) return\n event.preventDefault()\n const [axis, delta] = move\n write({ ...hsva.value, [axis]: clamp((Math.round(hsva.value[axis] * 100) + delta) / 100, 0, 1) })\n}\n\n/** What a native range reports on its own: a drag on a track, or assistive technology. */\nfunction onRangeInput(channel: keyof Hsva, scale: number, event: Event) {\n const el = event.target as HTMLInputElement\n // @core\n // A range has no native `readonly`: the move it has already made is put back.\n if (props.readonly) {\n el.value = String(Math.round(hsva.value[channel] * scale))\n return\n }\n write({ ...hsva.value, [channel]: Number(el.value) / scale })\n}\n\n/** A read-only swatch refuses the click, which is what checks a radio, arrows included. */\nfunction onSwatchClick(event: MouseEvent) {\n if (props.readonly) event.preventDefault()\n}\n\nconst shownFormat = ref<ColorFormat>(props.format)\nwatch(\n () => props.format,\n (format) => (shownFormat.value = format),\n)\n\nconst formatOptions = COLOR_FORMATS.map((format) => ({\n value: format,\n label: format.toUpperCase(),\n}))\n\n/** The text field's own copy, rewritten whenever the colour changes from elsewhere. */\nconst fieldText = ref('')\nwatch(\n [rgba, shownFormat, () => props.alpha, () => model.value === null],\n () => {\n fieldText.value =\n model.value === null ? '' : formatColor(rgba.value, shownFormat.value, props.alpha)\n },\n { immediate: true },\n)\n\n/** Takes what was typed, in any of the four formats, or puts the field back as it was. */\nfunction commitField() {\n const text = fieldText.value.trim()\n if (!text) {\n written = null\n model.value = null\n return\n }\n const parsed = parseColor(text)\n if (parsed) writeRgba(parsed)\n else fieldText.value = formatColor(rgba.value, shownFormat.value, props.alpha)\n}\n\nfunction onFieldKeydown(event: KeyboardEvent) {\n if (event.key !== 'Enter') return\n event.preventDefault()\n commitField()\n}\n\nconst items = computed(() =>\n (props.swatches ?? []).map((swatch) => {\n const color = typeof swatch === 'string' ? swatch : swatch.color\n return {\n color,\n label: typeof swatch === 'string' ? swatch : swatch.label,\n rgba: parseColor(color),\n }\n }),\n)\n\n// @core\n// The swatches are native radios, so the arrow keys move between them. Their name only groups\n// them: they are tied to a form that does not exist so that no enclosing form sends them, the\n// picker's value going through its own hidden input.\nconst swatchGroup = useId()\n\ninterface EyeDropperResult {\n sRGBHex: string\n}\n\n/** The EyeDropper API, which TypeScript's DOM library does not describe yet. */\ntype EyeDropperConstructor = new () => { open: () => Promise<EyeDropperResult> }\n\n// @fallback\n// Detected once mounted, never on the server: the button simply does not exist elsewhere.\nconst eyeDropper = ref<EyeDropperConstructor | null>(null)\nonMounted(() => {\n eyeDropper.value =\n (window as unknown as { EyeDropper?: EyeDropperConstructor }).EyeDropper ?? null\n})\n\nasync function pickFromScreen() {\n if (!eyeDropper.value) return\n try {\n const { sRGBHex } = await new eyeDropper.value().open()\n const picked = parseColor(sRGBHex)\n // The screen has no transparency: the opacity already chosen is kept.\n if (picked) writeRgba({ ...picked, a: hsva.value.a })\n } catch {\n // Escape cancels the eyedropper by rejecting the promise: nothing to report.\n }\n}\n\nconst opaque = computed(() => formatColor({ ...rgba.value, a: 1 }, 'rgb'))\n\nconst rootStyle = computed(() => ({\n '--color-picker-hue': String(hsva.value.h),\n '--color-picker-opaque': opaque.value,\n '--color-picker-color': formatColor(rgba.value, 'rgb', props.alpha),\n '--color-picker-s': String(hsva.value.s),\n '--color-picker-v': String(hsva.value.v),\n}))\n\nconst rootEl = ref<HTMLElement | null>(null)\n\ndefineExpose({\n /** Moves the focus to the saturation-brightness area. */\n focus: (options?: FocusOptions) => saturationEl.value?.focus(options),\n /** The root element. */\n el: rootEl,\n})\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n class=\"v-color-picker\"\n role=\"group\"\n :aria-label=\"ariaLabel\"\n :data-disabled=\"disabled ? '' : undefined\"\n :data-readonly=\"readonly ? '' : undefined\"\n :style=\"rootStyle\"\n >\n <div\n ref=\"areaEl\"\n class=\"v-color-picker-area\"\n @pointerdown=\"onAreaPointerdown\"\n @pointermove=\"onAreaPointermove\"\n @pointerup=\"onAreaPointerup\"\n @pointercancel=\"onAreaPointerup\"\n >\n <input\n ref=\"saturationEl\"\n type=\"range\"\n class=\"v-hidden-input\"\n min=\"0\"\n max=\"100\"\n :value=\"Math.round(hsva.s * 100)\"\n :aria-label=\"m.colorPicker.saturation\"\n :aria-valuetext=\"areaValueText\"\n :aria-readonly=\"readonly || undefined\"\n :disabled=\"disabled\"\n @input=\"onRangeInput('s', 100, $event)\"\n @keydown=\"onAreaKeydown\"\n />\n <!-- Out of the tab order: one stop for the area, whose arrows move both axes. -->\n <input\n type=\"range\"\n class=\"v-hidden-input\"\n tabindex=\"-1\"\n min=\"0\"\n max=\"100\"\n :value=\"Math.round(hsva.v * 100)\"\n :aria-label=\"m.colorPicker.brightness\"\n :aria-valuetext=\"areaValueText\"\n :aria-readonly=\"readonly || undefined\"\n :disabled=\"disabled\"\n @input=\"onRangeInput('v', 100, $event)\"\n @keydown=\"onAreaKeydown\"\n />\n <span class=\"v-color-picker-thumb\" aria-hidden=\"true\" />\n </div>\n\n <div class=\"v-color-picker-controls\">\n <VIconButton\n v-if=\"eyeDropper && !hideEyeDropper\"\n class=\"v-color-picker-eye-dropper\"\n :icon=\"colorizeIcon\"\n :label=\"m.colorPicker.eyeDropper\"\n size=\"sm\"\n :disabled=\"disabled || readonly\"\n @click=\"pickFromScreen\"\n />\n <span class=\"v-color-picker-preview\" aria-hidden=\"true\" />\n <div class=\"v-color-picker-tracks\">\n <input\n type=\"range\"\n class=\"v-color-picker-track v-color-picker-hue\"\n min=\"0\"\n max=\"360\"\n :value=\"Math.round(hsva.h)\"\n :aria-label=\"m.colorPicker.hue\"\n :aria-valuetext=\"degrees.format(Math.round(hsva.h))\"\n :aria-readonly=\"readonly || undefined\"\n :disabled=\"disabled\"\n @input=\"onRangeInput('h', 1, $event)\"\n />\n <input\n v-if=\"alpha\"\n type=\"range\"\n class=\"v-color-picker-track v-color-picker-alpha\"\n min=\"0\"\n max=\"100\"\n :value=\"Math.round(hsva.a * 100)\"\n :aria-label=\"m.colorPicker.alpha\"\n :aria-valuetext=\"percent.format(Math.round(hsva.a * 100) / 100)\"\n :aria-readonly=\"readonly || undefined\"\n :disabled=\"disabled\"\n @input=\"onRangeInput('a', 100, $event)\"\n />\n </div>\n </div>\n\n <div v-if=\"!hideInput\" class=\"v-color-picker-fields\">\n <VSelect\n :model-value=\"shownFormat\"\n class=\"v-color-picker-format\"\n :options=\"formatOptions\"\n size=\"sm\"\n :aria-label=\"m.colorPicker.format\"\n :disabled=\"disabled\"\n @update:model-value=\"shownFormat = $event as ColorFormat\"\n />\n <VInput\n v-model=\"fieldText\"\n class=\"v-color-picker-value\"\n size=\"sm\"\n :aria-label=\"m.colorPicker.value\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n @change=\"commitField\"\n @keydown=\"onFieldKeydown\"\n />\n </div>\n\n <fieldset\n v-if=\"items.length\"\n class=\"v-color-picker-swatches\"\n :aria-label=\"m.colorPicker.swatches\"\n :disabled=\"disabled\"\n >\n <label\n v-for=\"item in items\"\n :key=\"item.color\"\n class=\"v-color-picker-swatch\"\n :style=\"{ '--color-picker-swatch': item.color }\"\n >\n <input\n type=\"radio\"\n class=\"v-hidden-input\"\n :name=\"swatchGroup\"\n :form=\"`${swatchGroup}-none`\"\n :value=\"item.color\"\n :checked=\"model !== null && sameColor(item.rgba, rgba, alpha)\"\n :aria-disabled=\"readonly || undefined\"\n @click=\"onSwatchClick\"\n @change=\"item.rgba && writeRgba(item.rgba)\"\n />\n <span class=\"v-color-picker-chip\" />\n <span class=\"v-visually-hidden\">{{ item.label }}</span>\n </label>\n </fieldset>\n\n <input v-if=\"name && model\" type=\"hidden\" :name=\"name\" :value=\"model\" />\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-color-picker {\n /*\n * The checkerboard that shows through a translucent colour. It is drawn in the theme's own\n * surface and border colours, so it reads as \"transparent\" in both themes.\n */\n --color-picker-checker: repeating-conic-gradient(\n var(--vectis-color-border) 0 25%,\n var(--vectis-color-surface) 0 50%\n )\n 0 0 / var(--vectis-control-size-color-picker-checker)\n var(--vectis-control-size-color-picker-checker);\n --color-picker-thumb: var(--vectis-control-size-color-picker-thumb);\n\n display: inline-flex;\n flex-direction: column;\n gap: var(--vectis-space-3);\n inline-size: var(--vectis-control-size-color-picker-width);\n max-inline-size: 100%;\n padding: var(--vectis-space-3);\n color: var(--vectis-color-text);\n font-family: var(--vectis-text-family);\n }\n\n /*\n * The colour content (area, tracks, preview, swatches) is the value itself, not the theme's\n * paint: it is never mirrored in right-to-left text, and forced colours leave it untouched.\n * Its gradients run between literal black and white, the ends of the colour model.\n */\n\n /*\n * The edge of the preview and the swatches is a translucent border over the colour, which it\n * shades rather than covers: an opaque grey ring left a pale fringe on the antialiased pixels.\n * The colour runs under the border and the checkerboard stops inside it, so no square of the\n * checkerboard shows through those pixels either.\n */\n .v-color-picker-area,\n .v-color-picker-track,\n .v-color-picker-preview,\n .v-color-picker-chip {\n direction: ltr;\n forced-color-adjust: none;\n }\n\n .v-color-picker-area {\n position: relative;\n block-size: var(--vectis-control-size-color-picker-area);\n /* Capped like VTextarea so a pill theme rounds the area as much as a md VInput, not into a stadium. */\n border-radius: min(var(--vectis-radius-interactive), calc(var(--vectis-control-height-md) / 2));\n background:\n linear-gradient(to top, black, transparent), linear-gradient(to right, white, transparent),\n hsl(var(--color-picker-hue) 100% 50%);\n cursor: crosshair;\n touch-action: none;\n }\n\n /*\n * Every thumb carries a light ring and a dark one, whatever the theme: it sits on colours of\n * any lightness, against which a single themed ring would vanish.\n */\n .v-color-picker-thumb {\n position: absolute;\n inset-inline-start: calc(var(--color-picker-s) * 100%);\n inset-block-start: calc((1 - var(--color-picker-v)) * 100%);\n inline-size: var(--color-picker-thumb);\n block-size: var(--color-picker-thumb);\n border: 2px solid white;\n border-radius: var(--vectis-radius-pill);\n background: var(--color-picker-opaque);\n box-shadow:\n 0 0 0 1px rgb(0 0 0 / 0.4),\n inset 0 0 0 1px rgb(0 0 0 / 0.4);\n translate: -50% -50%;\n pointer-events: none;\n }\n\n .v-color-picker-area:has(.v-hidden-input:focus-visible) .v-color-picker-thumb {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n .v-color-picker-controls {\n display: flex;\n align-items: center;\n gap: var(--vectis-space-2);\n }\n\n .v-color-picker-preview {\n flex: none;\n inline-size: var(--vectis-control-size-color-picker-preview);\n block-size: var(--vectis-control-size-color-picker-preview);\n border-radius: var(--vectis-radius-pill);\n border: 1px solid var(--vectis-color-border-on-fill);\n background:\n linear-gradient(var(--color-picker-color), var(--color-picker-color)) border-box,\n var(--color-picker-checker) padding-box;\n }\n\n .v-color-picker-tracks {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--vectis-space-3);\n min-inline-size: 0;\n }\n\n .v-color-picker-track {\n display: block;\n inline-size: 100%;\n block-size: var(--vectis-control-size-color-picker-track);\n margin: 0;\n border-radius: var(--vectis-radius-pill);\n appearance: none;\n cursor: pointer;\n }\n\n .v-color-picker-hue {\n --color-picker-track-thumb: hsl(var(--color-picker-hue) 100% 50%);\n background: linear-gradient(\n to right,\n hsl(0 100% 50%),\n hsl(60 100% 50%),\n hsl(120 100% 50%),\n hsl(180 100% 50%),\n hsl(240 100% 50%),\n hsl(300 100% 50%),\n hsl(360 100% 50%)\n );\n }\n\n .v-color-picker-alpha {\n --color-picker-track-thumb: var(--color-picker-color);\n background:\n linear-gradient(to right, transparent, var(--color-picker-opaque)),\n var(--color-picker-checker);\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.\n */\n .v-color-picker-track::-webkit-slider-thumb {\n appearance: none;\n inline-size: var(--color-picker-thumb);\n block-size: var(--color-picker-thumb);\n border: 2px solid white;\n border-radius: var(--vectis-radius-pill);\n background:\n linear-gradient(var(--color-picker-track-thumb), var(--color-picker-track-thumb)),\n var(--color-picker-checker);\n box-shadow:\n 0 0 0 1px rgb(0 0 0 / 0.4),\n inset 0 0 0 1px rgb(0 0 0 / 0.4);\n }\n\n .v-color-picker-track::-moz-range-thumb {\n box-sizing: border-box;\n inline-size: var(--color-picker-thumb);\n block-size: var(--color-picker-thumb);\n border: 2px solid white;\n border-radius: var(--vectis-radius-pill);\n background:\n linear-gradient(var(--color-picker-track-thumb), var(--color-picker-track-thumb)),\n var(--color-picker-checker);\n box-shadow:\n 0 0 0 1px rgb(0 0 0 / 0.4),\n inset 0 0 0 1px rgb(0 0 0 / 0.4);\n }\n\n .v-color-picker-track:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n .v-color-picker-fields {\n display: flex;\n gap: var(--vectis-space-2);\n }\n\n /* Qualified to outweigh the full width VSelect takes by default. */\n .v-color-picker-fields .v-color-picker-format {\n flex: none;\n inline-size: var(--vectis-control-size-color-picker-format);\n }\n\n .v-color-picker-value {\n flex: 1;\n min-inline-size: 0;\n }\n\n /*\n * A colour is written left to right in every language: in right-to-left text the bidi\n * algorithm would move the `#` of `#f59e0b` to the end. The field keeps its right alignment.\n */\n .v-color-picker-value .v-input-control {\n direction: ltr;\n font-variant-numeric: tabular-nums;\n }\n\n .v-color-picker-value .v-input-control:dir(rtl) {\n text-align: right;\n }\n\n .v-color-picker-swatches {\n display: flex;\n flex-wrap: wrap;\n gap: var(--vectis-space-2);\n min-inline-size: 0;\n margin: 0;\n padding: 0;\n border: 0;\n }\n\n .v-color-picker-swatch {\n position: relative;\n display: inline-flex;\n cursor: pointer;\n }\n\n .v-color-picker-chip {\n inline-size: var(--vectis-control-size-color-picker-swatch);\n block-size: var(--vectis-control-size-color-picker-swatch);\n border-radius: var(--vectis-radius-interactive);\n border: 1px solid var(--vectis-color-border-on-fill);\n background:\n linear-gradient(var(--color-picker-swatch), var(--color-picker-swatch)) border-box,\n var(--color-picker-checker) padding-box;\n }\n\n /* An outline rather than a shadow marks the chosen swatch: forced colours keep outlines. */\n .v-color-picker-swatch .v-hidden-input:checked + .v-color-picker-chip {\n outline: 2px solid var(--vectis-color-text);\n outline-offset: 1px;\n }\n\n .v-color-picker-swatch .v-hidden-input:focus-visible + .v-color-picker-chip {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /* Colour content is media, which may fade; the text controls take the disabled tokens. */\n .v-color-picker[data-disabled]\n :is(\n .v-color-picker-area,\n .v-color-picker-preview,\n .v-color-picker-track,\n .v-color-picker-chip\n ) {\n opacity: 0.5;\n }\n\n .v-color-picker[data-disabled]\n :is(.v-color-picker-area, .v-color-picker-track, .v-color-picker-swatch) {\n cursor: not-allowed;\n }\n\n /* A read-only picker is still read, so it keeps the ordinary cursor, as VTimePicker does. */\n .v-color-picker[data-readonly]\n :is(.v-color-picker-area, .v-color-picker-track, .v-color-picker-swatch) {\n cursor: default;\n }\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"VColorPicker.js","names":["_sfc_main","VColorPicker_vue_vue_type_script_setup_true_lang_default","VColorPicker_default"],"sources":["../../../src/components/VColorPicker/VColorPicker.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @a11y @core\n/**\n * A colour picker built from native ranges: the hue and alpha tracks are `<input type=\"range\">`,\n * and the saturation-brightness area holds two hidden ones, one per axis, which assistive\n * technology adjusts on its own. JavaScript adds the pointer on the area and its two-axis arrow\n * keys, which no native control offers.\n */\nimport { computed, onMounted, ref, useId, watch } from 'vue'\n\nimport VIconButton from '../VIconButton/VIconButton.vue'\nimport VInput from '../VInput/VInput.vue'\nimport VSelect from '../VSelect/VSelect.vue'\nimport { colorize as colorizeIcon } from '../VIcon/icons/colorize'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useLocale, useMessages } from '../../i18n/state'\nimport { clamp } from '../../utils/number'\nimport { COLOR_FORMATS, formatColor, hsvToRgb, parseColor, rgbToHsv, sameColor } from './color'\nimport type { ColorFormat, Hsva, Rgba } from './color'\n\nexport type { ColorFormat } from './color'\n\n/** A preset colour: the colour alone, or the colour and the words that name it. */\nexport type ColorSwatch = string | { color: string; label: string }\n\ninterface ColorPickerProps {\n /**\n * How the value is written: `#rrggbb`, `rgb()`, `hsl()` or `oklch()`. The text field accepts\n * all four whatever this says.\n */\n format?: ColorFormat\n /** Offers an opacity track, and writes the alpha into the value when it is below 1. */\n alpha?: boolean\n /** Preset colours, offered as a row of swatches under the picker. */\n swatches?: ColorSwatch[]\n /**\n * Hides the text field and its format menu. VColorInput does, its own field playing that\n * part.\n */\n hideInput?: boolean\n /**\n * Hides the eyedropper button, shown otherwise wherever the browser offers the `EyeDropper`\n * API.\n */\n hideEyeDropper?: boolean\n /** Makes the picker unusable. */\n disabled?: boolean\n /**\n * Shows the colour without letting it be changed. The controls stay focusable and announce\n * their values, which separates it from `disabled`, and the value is still submitted.\n */\n readonly?: boolean\n /** What the picker is called. It falls back to the design system dictionary. */\n label?: string\n /** The name the value is sent under in a form. */\n name?: string\n}\n\nconst props = withDefaults(defineProps<ColorPickerProps>(), {\n format: 'hex',\n alpha: false,\n swatches: undefined,\n hideInput: false,\n hideEyeDropper: false,\n disabled: false,\n readonly: false,\n label: undefined,\n name: undefined,\n})\n\n/** The colour, written in `format`, or `null` when none has been chosen. */\nconst model = defineModel<string | null>({ default: null })\n\nconst m = useMessages()\nconst locale = useLocale()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.colorPicker.label)\n\n/** What the picker shows. With no colour yet, it rests on black, which writes nothing. */\nconst hsva = ref<Hsva>({ h: 0, s: 0, v: 0, a: 1 })\nconst rgba = computed(() => hsvToRgb(hsva.value))\n\n// @core\n// The value last written, so that its echo through the model is not parsed back: hex rounds the\n// channels, and a grey has no hue, so reading it back would move the thumbs under the pointer.\nlet written: string | null = null\n\nwatch(\n model,\n (value) => {\n if (value === written) return\n const parsed = parseColor(value)\n if (parsed) hsva.value = rgbToHsv(props.alpha ? parsed : { ...parsed, a: 1 }, hsva.value.h)\n },\n { immediate: true },\n)\n\nfunction write(next: Hsva) {\n hsva.value = next\n written = formatColor(hsvToRgb(next), props.format, props.alpha)\n model.value = written\n}\n\nfunction writeRgba(next: Rgba) {\n write(rgbToHsv(props.alpha ? next : { ...next, a: 1 }, hsva.value.h))\n}\n\nconst percent = computed(() => new Intl.NumberFormat(locale.value, { style: 'percent' }))\nconst degrees = computed(\n () =>\n new Intl.NumberFormat(locale.value, { style: 'unit', unit: 'degree', unitDisplay: 'narrow' }),\n)\n\n/** Both axes in words: read on either of the area's inputs, whichever one has the focus. */\nconst areaValueText = computed(() =>\n m.value.colorPicker.areaValue(\n percent.value.format(Math.round(hsva.value.s * 100) / 100),\n percent.value.format(Math.round(hsva.value.v * 100) / 100),\n ),\n)\n\nconst areaEl = ref<HTMLElement | null>(null)\nconst saturationEl = ref<HTMLInputElement | null>(null)\n\nfunction moveTo(event: PointerEvent) {\n const rect = areaEl.value!.getBoundingClientRect()\n write({\n ...hsva.value,\n s: clamp((event.clientX - rect.left) / rect.width, 0, 1),\n v: clamp(1 - (event.clientY - rect.top) / rect.height, 0, 1),\n })\n}\n\n/** The pointer dragging the area's thumb, if one is. */\nlet dragging: number | null = null\n\nfunction onAreaPointerdown(event: PointerEvent) {\n if (props.disabled || event.button !== 0) return\n // The focus is moved by hand, without the scroll a default focus on mousedown could cause.\n event.preventDefault()\n saturationEl.value?.focus({ preventScroll: true })\n if (props.readonly) return\n dragging = event.pointerId\n // @fallback\n // Capture keeps the drag going outside the area; synthetic events may not reference a\n // capturable pointer.\n try {\n areaEl.value!.setPointerCapture(event.pointerId)\n } catch {\n /* Synthetic pointers cannot be captured. */\n }\n moveTo(event)\n}\n\nfunction onAreaPointermove(event: PointerEvent) {\n if (event.pointerId === dragging) moveTo(event)\n}\n\nfunction onAreaPointerup(event: PointerEvent) {\n if (event.pointerId === dragging) dragging = null\n}\n\n// @keyboard\n/**\n * Both inputs of the area answer the same keys: the horizontal arrows and Home/End move the\n * saturation, the vertical arrows and PageUp/PageDown the brightness. Natively each input would\n * move its own axis on every arrow.\n */\nfunction onAreaKeydown(event: KeyboardEvent) {\n if (props.readonly) return\n const step = event.shiftKey ? 10 : 1\n const moves: Record<string, [axis: 's' | 'v', delta: number]> = {\n ArrowLeft: ['s', -step],\n ArrowRight: ['s', step],\n ArrowDown: ['v', -step],\n ArrowUp: ['v', step],\n PageDown: ['v', -10],\n PageUp: ['v', 10],\n Home: ['s', -100],\n End: ['s', 100],\n }\n const move = moves[event.key]\n if (!move) return\n event.preventDefault()\n const [axis, delta] = move\n write({ ...hsva.value, [axis]: clamp((Math.round(hsva.value[axis] * 100) + delta) / 100, 0, 1) })\n}\n\n/** What a native range reports on its own: a drag on a track, or assistive technology. */\nfunction onRangeInput(channel: keyof Hsva, scale: number, event: Event) {\n const el = event.target as HTMLInputElement\n // @core\n // A range has no native `readonly`: the move it has already made is put back.\n if (props.readonly) {\n el.value = String(Math.round(hsva.value[channel] * scale))\n return\n }\n write({ ...hsva.value, [channel]: Number(el.value) / scale })\n}\n\n/** A read-only swatch refuses the click, which is what checks a radio, arrows included. */\nfunction onSwatchClick(event: MouseEvent) {\n if (props.readonly) event.preventDefault()\n}\n\nconst shownFormat = ref<ColorFormat>(props.format)\nwatch(\n () => props.format,\n (format) => (shownFormat.value = format),\n)\n\nconst formatOptions = COLOR_FORMATS.map((format) => ({\n value: format,\n label: format.toUpperCase(),\n}))\n\n/** The text field's own copy, rewritten whenever the colour changes from elsewhere. */\nconst fieldText = ref('')\nwatch(\n [rgba, shownFormat, () => props.alpha, () => model.value === null],\n () => {\n fieldText.value =\n model.value === null ? '' : formatColor(rgba.value, shownFormat.value, props.alpha)\n },\n { immediate: true },\n)\n\n/** Takes what was typed, in any of the four formats, or puts the field back as it was. */\nfunction commitField() {\n const text = fieldText.value.trim()\n if (!text) {\n written = null\n model.value = null\n return\n }\n const parsed = parseColor(text)\n if (parsed) writeRgba(parsed)\n else fieldText.value = formatColor(rgba.value, shownFormat.value, props.alpha)\n}\n\nfunction onFieldKeydown(event: KeyboardEvent) {\n if (event.key !== 'Enter') return\n event.preventDefault()\n commitField()\n}\n\nconst items = computed(() =>\n (props.swatches ?? []).map((swatch) => {\n const color = typeof swatch === 'string' ? swatch : swatch.color\n return {\n color,\n label: typeof swatch === 'string' ? swatch : swatch.label,\n rgba: parseColor(color),\n }\n }),\n)\n\n// @core\n// The swatches are native radios, so the arrow keys move between them. Their name only groups\n// them: they are tied to a form that does not exist so that no enclosing form sends them, the\n// picker's value going through its own hidden input.\nconst swatchGroup = useId()\n\ninterface EyeDropperResult {\n sRGBHex: string\n}\n\n/** The EyeDropper API, which TypeScript's DOM library does not describe yet. */\ntype EyeDropperConstructor = new () => { open: () => Promise<EyeDropperResult> }\n\n// @fallback\n// Detected once mounted, never on the server: the button simply does not exist elsewhere.\nconst eyeDropper = ref<EyeDropperConstructor | null>(null)\nonMounted(() => {\n eyeDropper.value =\n (window as unknown as { EyeDropper?: EyeDropperConstructor }).EyeDropper ?? null\n})\n\nasync function pickFromScreen() {\n if (!eyeDropper.value) return\n try {\n const { sRGBHex } = await new eyeDropper.value().open()\n const picked = parseColor(sRGBHex)\n // The screen has no transparency: the opacity already chosen is kept.\n if (picked) writeRgba({ ...picked, a: hsva.value.a })\n } catch {\n // Escape cancels the eyedropper by rejecting the promise: nothing to report.\n }\n}\n\nconst opaque = computed(() => formatColor({ ...rgba.value, a: 1 }, 'rgb'))\n\nconst rootStyle = computed(() => ({\n '--color-picker-hue': String(hsva.value.h),\n '--color-picker-opaque': opaque.value,\n '--color-picker-color': formatColor(rgba.value, 'rgb', props.alpha),\n '--color-picker-s': String(hsva.value.s),\n '--color-picker-v': String(hsva.value.v),\n}))\n\nconst rootEl = ref<HTMLElement | null>(null)\n\ndefineExpose({\n /** Moves the focus to the saturation-brightness area. */\n focus: (options?: FocusOptions) => saturationEl.value?.focus(options),\n /** The root element. */\n el: rootEl,\n})\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n class=\"v-color-picker\"\n role=\"group\"\n :aria-label=\"ariaLabel\"\n :data-disabled=\"disabled ? '' : undefined\"\n :data-readonly=\"readonly ? '' : undefined\"\n :style=\"rootStyle\"\n >\n <div\n ref=\"areaEl\"\n class=\"v-color-picker-area\"\n @pointerdown=\"onAreaPointerdown\"\n @pointermove=\"onAreaPointermove\"\n @pointerup=\"onAreaPointerup\"\n @pointercancel=\"onAreaPointerup\"\n >\n <input\n ref=\"saturationEl\"\n type=\"range\"\n class=\"v-hidden-input\"\n min=\"0\"\n max=\"100\"\n :value=\"Math.round(hsva.s * 100)\"\n :aria-label=\"m.colorPicker.saturation\"\n :aria-valuetext=\"areaValueText\"\n :aria-readonly=\"readonly || undefined\"\n :disabled=\"disabled\"\n @input=\"onRangeInput('s', 100, $event)\"\n @keydown=\"onAreaKeydown\"\n />\n <!-- Out of the tab order: one stop for the area, whose arrows move both axes. -->\n <input\n type=\"range\"\n class=\"v-hidden-input\"\n tabindex=\"-1\"\n min=\"0\"\n max=\"100\"\n :value=\"Math.round(hsva.v * 100)\"\n :aria-label=\"m.colorPicker.brightness\"\n :aria-valuetext=\"areaValueText\"\n :aria-readonly=\"readonly || undefined\"\n :disabled=\"disabled\"\n @input=\"onRangeInput('v', 100, $event)\"\n @keydown=\"onAreaKeydown\"\n />\n <span class=\"v-color-picker-thumb\" aria-hidden=\"true\" />\n </div>\n\n <div class=\"v-color-picker-controls\">\n <VIconButton\n v-if=\"eyeDropper && !hideEyeDropper\"\n class=\"v-color-picker-eye-dropper\"\n :icon=\"colorizeIcon\"\n :label=\"m.colorPicker.eyeDropper\"\n size=\"sm\"\n :disabled=\"disabled || readonly\"\n @click=\"pickFromScreen\"\n />\n <span class=\"v-color-picker-preview\" aria-hidden=\"true\" />\n <div class=\"v-color-picker-tracks\">\n <input\n type=\"range\"\n class=\"v-color-picker-track v-color-picker-hue\"\n min=\"0\"\n max=\"360\"\n :value=\"Math.round(hsva.h)\"\n :aria-label=\"m.colorPicker.hue\"\n :aria-valuetext=\"degrees.format(Math.round(hsva.h))\"\n :aria-readonly=\"readonly || undefined\"\n :disabled=\"disabled\"\n @input=\"onRangeInput('h', 1, $event)\"\n />\n <input\n v-if=\"alpha\"\n type=\"range\"\n class=\"v-color-picker-track v-color-picker-alpha\"\n min=\"0\"\n max=\"100\"\n :value=\"Math.round(hsva.a * 100)\"\n :aria-label=\"m.colorPicker.alpha\"\n :aria-valuetext=\"percent.format(Math.round(hsva.a * 100) / 100)\"\n :aria-readonly=\"readonly || undefined\"\n :disabled=\"disabled\"\n @input=\"onRangeInput('a', 100, $event)\"\n />\n </div>\n </div>\n\n <div v-if=\"!hideInput\" class=\"v-color-picker-fields\">\n <VSelect\n :model-value=\"shownFormat\"\n class=\"v-color-picker-format\"\n :options=\"formatOptions\"\n size=\"sm\"\n :aria-label=\"m.colorPicker.format\"\n :disabled=\"disabled\"\n @update:model-value=\"shownFormat = $event as ColorFormat\"\n />\n <VInput\n v-model=\"fieldText\"\n class=\"v-color-picker-value\"\n size=\"sm\"\n :aria-label=\"m.colorPicker.value\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n @change=\"commitField\"\n @keydown=\"onFieldKeydown\"\n />\n </div>\n\n <fieldset\n v-if=\"items.length\"\n class=\"v-color-picker-swatches\"\n :aria-label=\"m.colorPicker.swatches\"\n :disabled=\"disabled\"\n >\n <label\n v-for=\"item in items\"\n :key=\"item.color\"\n class=\"v-color-picker-swatch\"\n :style=\"{ '--color-picker-swatch': item.color }\"\n >\n <input\n type=\"radio\"\n class=\"v-hidden-input\"\n :name=\"swatchGroup\"\n :form=\"`${swatchGroup}-none`\"\n :value=\"item.color\"\n :checked=\"model !== null && sameColor(item.rgba, rgba, alpha)\"\n :aria-disabled=\"readonly || undefined\"\n @click=\"onSwatchClick\"\n @change=\"item.rgba && writeRgba(item.rgba)\"\n />\n <span class=\"v-color-picker-chip\" />\n <span class=\"v-visually-hidden\">{{ item.label }}</span>\n </label>\n </fieldset>\n\n <input v-if=\"name && model\" type=\"hidden\" :name=\"name\" :value=\"model\" />\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-color-picker {\n /*\n * The checkerboard that shows through a translucent colour. It is drawn in the theme's own\n * surface and border colours, so it reads as \"transparent\" in both themes.\n */\n --color-picker-checker: repeating-conic-gradient(\n var(--vectis-color-border) 0 25%,\n var(--vectis-color-surface) 0 50%\n )\n 0 0 / var(--vectis-control-size-color-picker-checker)\n var(--vectis-control-size-color-picker-checker);\n --color-picker-thumb: var(--vectis-control-size-color-picker-thumb);\n\n display: inline-flex;\n flex-direction: column;\n gap: var(--vectis-space-3);\n inline-size: var(--vectis-control-size-color-picker-width);\n max-inline-size: 100%;\n padding: var(--vectis-space-3);\n color: var(--vectis-color-text);\n font-family: var(--vectis-text-family);\n }\n\n /*\n * The colour content (area, tracks, preview, swatches) is the value itself, not the theme's\n * paint: it is never mirrored in right-to-left text, and forced colours leave it untouched.\n * Its gradients run between literal black and white, the ends of the colour model.\n */\n\n /*\n * The edge of the preview and the swatches is a translucent border over the colour, which it\n * shades rather than covers: an opaque grey ring left a pale fringe on the antialiased pixels.\n * The colour runs under the border and the checkerboard stops inside it, so no square of the\n * checkerboard shows through those pixels either.\n */\n .v-color-picker-area,\n .v-color-picker-track,\n .v-color-picker-preview,\n .v-color-picker-chip {\n direction: ltr;\n forced-color-adjust: none;\n }\n\n .v-color-picker-area {\n position: relative;\n block-size: var(--vectis-control-size-color-picker-area);\n /* Capped like VTextarea so a pill theme rounds the area as much as a md VInput, not into a stadium. */\n border-radius: min(var(--vectis-radius-interactive), calc(var(--vectis-control-height-md) / 2));\n background:\n linear-gradient(to top, black, transparent), linear-gradient(to right, white, transparent),\n hsl(var(--color-picker-hue) 100% 50%);\n cursor: crosshair;\n touch-action: none;\n }\n\n /*\n * Every thumb carries a light ring and a dark one, whatever the theme: it sits on colours of\n * any lightness, against which a single themed ring would vanish.\n */\n .v-color-picker-thumb {\n position: absolute;\n inset-inline-start: calc(var(--color-picker-s) * 100%);\n inset-block-start: calc((1 - var(--color-picker-v)) * 100%);\n inline-size: var(--color-picker-thumb);\n block-size: var(--color-picker-thumb);\n border: 2px solid white;\n border-radius: var(--vectis-radius-pill);\n background: var(--color-picker-opaque);\n box-shadow:\n 0 0 0 1px rgb(0 0 0 / 0.4),\n inset 0 0 0 1px rgb(0 0 0 / 0.4);\n translate: -50% -50%;\n pointer-events: none;\n }\n\n .v-color-picker-area:has(.v-hidden-input:focus-visible) .v-color-picker-thumb {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n .v-color-picker-controls {\n display: flex;\n align-items: center;\n gap: var(--vectis-space-2);\n }\n\n .v-color-picker-preview {\n flex: none;\n inline-size: var(--vectis-control-size-color-picker-preview);\n block-size: var(--vectis-control-size-color-picker-preview);\n border-radius: var(--vectis-radius-pill);\n border: 1px solid var(--vectis-color-border-on-fill);\n background:\n linear-gradient(var(--color-picker-color), var(--color-picker-color)) border-box,\n var(--color-picker-checker) padding-box;\n }\n\n .v-color-picker-tracks {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--vectis-space-3);\n min-inline-size: 0;\n }\n\n .v-color-picker-track {\n display: block;\n inline-size: 100%;\n block-size: var(--vectis-control-size-color-picker-track);\n margin: 0;\n border-radius: var(--vectis-radius-pill);\n appearance: none;\n cursor: pointer;\n }\n\n .v-color-picker-hue {\n --color-picker-track-thumb: hsl(var(--color-picker-hue) 100% 50%);\n background: linear-gradient(\n to right,\n hsl(0 100% 50%),\n hsl(60 100% 50%),\n hsl(120 100% 50%),\n hsl(180 100% 50%),\n hsl(240 100% 50%),\n hsl(300 100% 50%),\n hsl(360 100% 50%)\n );\n }\n\n .v-color-picker-alpha {\n --color-picker-track-thumb: var(--color-picker-color);\n background:\n linear-gradient(to right, transparent, var(--color-picker-opaque)),\n var(--color-picker-checker);\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.\n */\n .v-color-picker-track::-webkit-slider-thumb {\n appearance: none;\n inline-size: var(--color-picker-thumb);\n block-size: var(--color-picker-thumb);\n border: 2px solid white;\n border-radius: var(--vectis-radius-pill);\n background:\n linear-gradient(var(--color-picker-track-thumb), var(--color-picker-track-thumb)),\n var(--color-picker-checker);\n box-shadow:\n 0 0 0 1px rgb(0 0 0 / 0.4),\n inset 0 0 0 1px rgb(0 0 0 / 0.4);\n }\n\n .v-color-picker-track::-moz-range-thumb {\n box-sizing: border-box;\n inline-size: var(--color-picker-thumb);\n block-size: var(--color-picker-thumb);\n border: 2px solid white;\n border-radius: var(--vectis-radius-pill);\n background:\n linear-gradient(var(--color-picker-track-thumb), var(--color-picker-track-thumb)),\n var(--color-picker-checker);\n box-shadow:\n 0 0 0 1px rgb(0 0 0 / 0.4),\n inset 0 0 0 1px rgb(0 0 0 / 0.4);\n }\n\n .v-color-picker-track:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n .v-color-picker-fields {\n display: flex;\n gap: var(--vectis-space-2);\n }\n\n /* Qualified to outweigh the full width VSelect takes by default. */\n .v-color-picker-fields .v-color-picker-format {\n flex: none;\n inline-size: var(--vectis-control-size-color-picker-format);\n }\n\n .v-color-picker-value {\n flex: 1;\n min-inline-size: 0;\n }\n\n /*\n * A colour is written left to right in every language: in right-to-left text the bidi\n * algorithm would move the `#` of `#f59e0b` to the end. The field keeps its right alignment.\n */\n .v-color-picker-value .v-input-control {\n direction: ltr;\n font-variant-numeric: tabular-nums;\n }\n\n .v-color-picker-value .v-input-control:dir(rtl) {\n text-align: right;\n }\n\n .v-color-picker-swatches {\n display: flex;\n flex-wrap: wrap;\n gap: var(--vectis-space-2);\n min-inline-size: 0;\n margin: 0;\n padding: 0;\n border: 0;\n }\n\n .v-color-picker-swatch {\n position: relative;\n display: inline-flex;\n cursor: pointer;\n }\n\n .v-color-picker-chip {\n inline-size: var(--vectis-control-size-color-picker-swatch);\n block-size: var(--vectis-control-size-color-picker-swatch);\n border-radius: var(--vectis-radius-interactive);\n border: 1px solid var(--vectis-color-border-on-fill);\n background:\n linear-gradient(var(--color-picker-swatch), var(--color-picker-swatch)) border-box,\n var(--color-picker-checker) padding-box;\n }\n\n /* An outline rather than a shadow marks the chosen swatch: forced colours keep outlines. */\n .v-color-picker-swatch .v-hidden-input:checked + .v-color-picker-chip {\n outline: 2px solid var(--vectis-color-text);\n outline-offset: 1px;\n }\n\n .v-color-picker-swatch .v-hidden-input:focus-visible + .v-color-picker-chip {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /* Colour content is media, which may fade; the text controls take the disabled tokens. */\n .v-color-picker[data-disabled]\n :is(\n .v-color-picker-area,\n .v-color-picker-preview,\n .v-color-picker-track,\n .v-color-picker-chip\n ) {\n opacity: 0.5;\n }\n\n .v-color-picker[data-disabled]\n :is(.v-color-picker-area, .v-color-picker-track, .v-color-picker-swatch) {\n cursor: not-allowed;\n }\n\n /* A read-only picker is still read, so it keeps the ordinary cursor, as VTimePicker does. */\n .v-color-picker[data-readonly]\n :is(.v-color-picker-area, .v-color-picker-track, .v-color-picker-swatch) {\n cursor: default;\n }\n}\n</style>\n"],"mappings":""}
|
|
@@ -153,11 +153,7 @@ var le = [
|
|
|
153
153
|
}
|
|
154
154
|
function K(e, t, n) {
|
|
155
155
|
let r = n.target;
|
|
156
|
-
|
|
157
|
-
r.value = String(Math.round(N.value[e] * t));
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
I({
|
|
156
|
+
O.readonly ? r.value = String(Math.round(N.value[e] * t)) : I({
|
|
161
157
|
...N.value,
|
|
162
158
|
[e]: Number(r.value) / t
|
|
163
159
|
});
|