vectis-ui 0.12.0 → 0.13.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 +6 -5
- package/dist/components/VAccordion/VAccordion.js.map +1 -1
- package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAccordion/VAccordionItem.css +1 -1
- package/dist/components/VAccordion/VAccordionItem.d.ts +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/VAlert/VAlert.css +2 -0
- package/dist/components/VAlert/VAlert.d.ts +73 -0
- package/dist/components/VAlert/VAlert.js +9 -0
- package/dist/components/VAlert/VAlert.js.map +1 -0
- package/dist/components/VAlert/VAlert.vue_vue_type_script_setup_true_lang.js +96 -0
- package/dist/components/VAlert/VAlert.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VAvatar/VAvatar.d.ts +2 -2
- package/dist/components/VAvatar/VAvatarGroup.d.ts +1 -1
- package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js +8 -8
- package/dist/components/VBadge/VBadge.d.ts +3 -3
- package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js +9 -9
- package/dist/components/VButton/VButton.d.ts +4 -4
- package/dist/components/VButton/VButtonGroup.css +1 -1
- package/dist/components/VButton/VButtonGroup.d.ts +2 -2
- package/dist/components/VButton/VButtonGroup.js.map +1 -1
- package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendar.css +1 -1
- package/dist/components/VCalendar/VCalendar.d.ts +7 -1
- package/dist/components/VCalendar/VCalendar.js.map +1 -1
- package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js +20 -18
- 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.css +1 -1
- package/dist/components/VCalendar/VCalendarMonth.d.ts +2 -2
- package/dist/components/VCalendar/VCalendarMonth.js.map +1 -1
- package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendarTimeGrid.css +1 -1
- package/dist/components/VCalendar/VCalendarTimeGrid.d.ts +3 -3
- package/dist/components/VCalendar/VCalendarTimeGrid.js.map +1 -1
- package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendarYear.css +1 -1
- package/dist/components/VCalendar/VCalendarYear.js.map +1 -1
- package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/gesture.d.ts +1 -16
- package/dist/components/VCalendar/gesture.js +1 -1
- package/dist/components/VCalendar/gesture.js.map +1 -1
- package/dist/components/VCalendar/types.d.ts +2 -0
- package/dist/components/VCard/VCard.css +2 -0
- package/dist/components/VCard/VCard.d.ts +87 -0
- package/dist/components/VCard/VCard.js +9 -0
- package/dist/components/VCard/VCard.js.map +1 -0
- package/dist/components/VCard/VCard.vue_vue_type_script_setup_true_lang.js +103 -0
- package/dist/components/VCard/VCard.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VCarousel/VCarousel.d.ts +1 -1
- package/dist/components/VCarousel/VCarousel.js.map +1 -1
- package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js +18 -18
- package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCheckbox/VCheckMark.css +2 -0
- package/dist/components/VCheckbox/VCheckMark.js +9 -0
- package/dist/components/VCheckbox/VCheckMark.js.map +1 -0
- package/dist/components/VCheckbox/VCheckMark.vue_vue_type_script_setup_true_lang.js +33 -0
- package/dist/components/VCheckbox/VCheckMark.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VCheckbox/VCheckbox.css +1 -1
- package/dist/components/VCheckbox/VCheckbox.d.ts +8 -3
- package/dist/components/VCheckbox/VCheckbox.js.map +1 -1
- package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js +54 -69
- package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VChip/VChip.d.ts +6 -6
- package/dist/components/VChip/VChip.js.map +1 -1
- package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js +26 -26
- package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VColorInput/VColorInput.css +2 -0
- package/dist/components/VColorInput/VColorInput.d.ts +113 -0
- package/dist/components/VColorInput/VColorInput.js +9 -0
- package/dist/components/VColorInput/VColorInput.js.map +1 -0
- package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js +230 -0
- package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VColorPicker/VColorPicker.css +2 -0
- package/dist/components/VColorPicker/VColorPicker.d.ts +67 -0
- package/dist/components/VColorPicker/VColorPicker.js +9 -0
- package/dist/components/VColorPicker/VColorPicker.js.map +1 -0
- package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js +390 -0
- package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VColorPicker/color.d.ts +42 -0
- package/dist/components/VColorPicker/color.js +158 -0
- package/dist/components/VColorPicker/color.js.map +1 -0
- package/dist/components/VCombobox/VCombobox.css +1 -1
- package/dist/components/VCombobox/VCombobox.d.ts +46 -60
- package/dist/components/VCombobox/VCombobox.js.map +1 -1
- package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +271 -372
- package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCommandPalette/VCommandPalette.css +2 -0
- package/dist/components/VCommandPalette/VCommandPalette.d.ts +163 -0
- package/dist/components/VCommandPalette/VCommandPalette.js +9 -0
- package/dist/components/VCommandPalette/VCommandPalette.js.map +1 -0
- package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js +321 -0
- package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VCommandPalette/VCommandPaletteItem.css +2 -0
- package/dist/components/VCommandPalette/VCommandPaletteItem.d.ts +38 -0
- package/dist/components/VCommandPalette/VCommandPaletteItem.js +9 -0
- package/dist/components/VCommandPalette/VCommandPaletteItem.js.map +1 -0
- package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js +69 -0
- package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VContextMenu/VContextMenu.css +2 -0
- package/dist/components/VContextMenu/VContextMenu.d.ts +66 -0
- package/dist/components/VContextMenu/VContextMenu.js +9 -0
- package/dist/components/VContextMenu/VContextMenu.js.map +1 -0
- package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js +148 -0
- package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VDataTable/VDataTable.css +1 -1
- package/dist/components/VDataTable/VDataTable.d.ts +41 -16
- package/dist/components/VDataTable/VDataTable.js.map +1 -1
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +307 -195
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDateInput/VDateInput.d.ts +27 -7
- package/dist/components/VDateInput/VDateInput.js.map +1 -1
- package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +170 -162
- package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDatePicker/VDatePicker.d.ts +3 -3
- package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
- package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js +228 -227
- package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDatePicker/keyboard.d.ts +7 -7
- package/dist/components/VDatePicker/keyboard.js +8 -6
- package/dist/components/VDatePicker/keyboard.js.map +1 -1
- package/dist/components/VDatePicker/names.js +16 -20
- package/dist/components/VDatePicker/names.js.map +1 -1
- package/dist/components/VDialog/VDialog.d.ts +2 -4
- package/dist/components/VDialog/VDialog.js.map +1 -1
- package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js +46 -77
- package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDrawer/VDrawer.css +2 -0
- package/dist/components/VDrawer/VDrawer.d.ts +102 -0
- package/dist/components/VDrawer/VDrawer.js +9 -0
- package/dist/components/VDrawer/VDrawer.js.map +1 -0
- package/dist/components/VDrawer/VDrawer.vue_vue_type_script_setup_true_lang.js +128 -0
- package/dist/components/VDrawer/VDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VEmptyState/VEmptyState.css +2 -0
- package/dist/components/VEmptyState/VEmptyState.d.ts +43 -0
- package/dist/components/VEmptyState/VEmptyState.js +9 -0
- package/dist/components/VEmptyState/VEmptyState.js.map +1 -0
- package/dist/components/VEmptyState/VEmptyState.vue_vue_type_script_setup_true_lang.js +48 -0
- package/dist/components/VEmptyState/VEmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VField/VField.css +2 -0
- package/dist/components/VField/VField.d.ts +84 -0
- package/dist/components/VField/VField.js +9 -0
- package/dist/components/VField/VField.js.map +1 -0
- package/dist/components/VField/VField.vue_vue_type_script_setup_true_lang.js +99 -0
- package/dist/components/VField/VField.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VField/VFieldAnnouncer.d.ts +7 -0
- package/dist/components/VField/VFieldAnnouncer.js +7 -0
- package/dist/components/VField/VFieldAnnouncer.js.map +1 -0
- package/dist/components/VField/VFieldAnnouncer.vue_vue_type_script_setup_true_lang.js +19 -0
- package/dist/components/VField/VFieldAnnouncer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VField/VFieldMessage.d.ts +11 -0
- package/dist/components/VField/VFieldMessage.js +7 -0
- package/dist/components/VField/VFieldMessage.js.map +1 -0
- package/dist/components/VField/VFieldMessage.vue_vue_type_script_setup_true_lang.js +32 -0
- package/dist/components/VField/VFieldMessage.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VFieldset/VFieldset.css +2 -0
- package/dist/components/VFieldset/VFieldset.d.ts +52 -0
- package/dist/components/VFieldset/VFieldset.js +9 -0
- package/dist/components/VFieldset/VFieldset.js.map +1 -0
- package/dist/components/VFieldset/VFieldset.vue_vue_type_script_setup_true_lang.js +63 -0
- package/dist/components/VFieldset/VFieldset.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VFileInput/VFileInput.css +1 -1
- package/dist/components/VFileInput/VFileInput.d.ts +31 -7
- package/dist/components/VFileInput/VFileInput.js.map +1 -1
- package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +168 -161
- package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VFilePicker/VFilePicker.d.ts +4 -4
- package/dist/components/VHotkeys/VHotkeys.css +1 -1
- package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
- package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js +27 -30
- package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VHotkeys/platform.d.ts +2 -0
- package/dist/components/VHotkeys/platform.js +24 -5
- package/dist/components/VHotkeys/platform.js.map +1 -1
- package/dist/components/VHoverCard/VHoverCard.css +2 -0
- package/dist/components/VHoverCard/VHoverCard.d.ts +62 -0
- package/dist/components/VHoverCard/VHoverCard.js +9 -0
- package/dist/components/VHoverCard/VHoverCard.js.map +1 -0
- package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js +97 -0
- package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VIcon/icons/add.d.ts +5 -0
- package/dist/components/VIcon/icons/add.js +9 -0
- package/dist/components/VIcon/icons/add.js.map +1 -0
- package/dist/components/VIcon/icons/colorize.d.ts +5 -0
- package/dist/components/VIcon/icons/colorize.js +9 -0
- package/dist/components/VIcon/icons/colorize.js.map +1 -0
- package/dist/components/VIcon/icons/inbox.d.ts +5 -0
- package/dist/components/VIcon/icons/inbox.js +9 -0
- package/dist/components/VIcon/icons/inbox.js.map +1 -0
- package/dist/components/VIcon/icons/index.d.ts +42 -2
- package/dist/components/VIcon/icons/index.js +88 -72
- package/dist/components/VIcon/icons/index.js.map +1 -1
- package/dist/components/VIcon/icons/names.d.ts +2 -2
- package/dist/components/VIcon/icons/names.js +1 -1
- package/dist/components/VIcon/icons/names.js.map +1 -1
- package/dist/components/VIcon/icons/open_in_new.d.ts +5 -0
- package/dist/components/VIcon/icons/open_in_new.js +9 -0
- package/dist/components/VIcon/icons/open_in_new.js.map +1 -0
- package/dist/components/VIcon/icons/priority_high.d.ts +5 -0
- package/dist/components/VIcon/icons/priority_high.js +9 -0
- package/dist/components/VIcon/icons/priority_high.js.map +1 -0
- package/dist/components/VIcon/icons/remove.d.ts +5 -0
- package/dist/components/VIcon/icons/remove.js +9 -0
- package/dist/components/VIcon/icons/remove.js.map +1 -0
- package/dist/components/VIcon/icons/search_off.d.ts +5 -0
- package/dist/components/VIcon/icons/search_off.js +9 -0
- package/dist/components/VIcon/icons/search_off.js.map +1 -0
- package/dist/components/VIcon/icons/star.d.ts +5 -0
- package/dist/components/VIcon/icons/star.js +9 -0
- package/dist/components/VIcon/icons/star.js.map +1 -0
- package/dist/components/VIconButton/VIconButton.d.ts +4 -4
- package/dist/components/VInput/VInput.css +1 -1
- package/dist/components/VInput/VInput.d.ts +51 -2
- package/dist/components/VInput/VInput.js.map +1 -1
- package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +129 -106
- package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/VInputGroup.css +1 -1
- package/dist/components/VInput/VInputGroup.d.ts +20 -0
- package/dist/components/VInput/VInputGroup.js.map +1 -1
- package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js +50 -51
- package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInputOTP/VInputOTP.css +1 -1
- package/dist/components/VInputOTP/VInputOTP.d.ts +27 -4
- package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
- package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +131 -113
- package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VLink/VLink.css +2 -0
- package/dist/components/VLink/VLink.d.ts +53 -0
- package/dist/components/VLink/VLink.js +9 -0
- package/dist/components/VLink/VLink.js.map +1 -0
- package/dist/components/VLink/VLink.vue_vue_type_script_setup_true_lang.js +58 -0
- package/dist/components/VLink/VLink.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VListbox/VListboxChevron.css +2 -0
- package/dist/components/VListbox/VListboxChevron.d.ts +12 -0
- package/dist/components/VListbox/VListboxChevron.js +9 -0
- package/dist/components/VListbox/VListboxChevron.js.map +1 -0
- package/dist/components/VListbox/VListboxChevron.vue_vue_type_script_setup_true_lang.js +25 -0
- package/dist/components/VListbox/VListboxChevron.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VListbox/VListboxGroup.css +2 -0
- package/dist/components/VListbox/VListboxGroup.d.ts +27 -0
- package/dist/components/VListbox/VListboxGroup.js +9 -0
- package/dist/components/VListbox/VListboxGroup.js.map +1 -0
- package/dist/components/VListbox/VListboxGroup.vue_vue_type_script_setup_true_lang.js +21 -0
- package/dist/components/VListbox/VListboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VListbox/VListboxOption.css +2 -0
- package/dist/components/{VCombobox/VComboboxOption.d.ts → VListbox/VListboxOption.d.ts} +4 -4
- package/dist/components/VListbox/VListboxOption.js +9 -0
- package/dist/components/VListbox/VListboxOption.js.map +1 -0
- package/dist/components/{VCombobox/VComboboxOption.vue_vue_type_script_setup_true_lang.js → VListbox/VListboxOption.vue_vue_type_script_setup_true_lang.js} +6 -6
- package/dist/components/VListbox/VListboxOption.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VListbox/VListboxPanel.css +2 -0
- package/dist/components/VListbox/VListboxPanel.d.ts +59 -0
- package/dist/components/VListbox/VListboxPanel.js +9 -0
- package/dist/components/VListbox/VListboxPanel.js.map +1 -0
- package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js +123 -0
- package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VListbox/VListboxSeparator.css +2 -0
- package/dist/components/VListbox/VListboxSeparator.d.ts +3 -0
- package/dist/components/VListbox/VListboxSeparator.js +9 -0
- package/dist/components/VListbox/VListboxSeparator.js.map +1 -0
- package/dist/components/{VCombobox/VComboboxSeparator.vue_vue_type_script_setup_true_lang.js → VListbox/VListboxSeparator.vue_vue_type_script_setup_true_lang.js} +4 -4
- package/dist/components/VListbox/VListboxSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VListbox/VListboxValues.css +2 -0
- package/dist/components/VListbox/VListboxValues.d.ts +52 -0
- package/dist/components/VListbox/VListboxValues.js +9 -0
- package/dist/components/VListbox/VListboxValues.js.map +1 -0
- package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js +103 -0
- package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VMenu/VMenu.d.ts +1 -1
- package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js +9 -9
- package/dist/components/VMenu/VMenuItem.d.ts +1 -1
- package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js +16 -16
- package/dist/components/VMenu/VMenuPanel.d.ts +2 -2
- package/dist/components/VMenu/VMenuPanel.js.map +1 -1
- package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +7 -7
- package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMeter/VMeter.css +2 -0
- package/dist/components/VMeter/VMeter.d.ts +77 -0
- package/dist/components/VMeter/VMeter.js +9 -0
- package/dist/components/VMeter/VMeter.js.map +1 -0
- package/dist/components/VMeter/VMeter.vue_vue_type_script_setup_true_lang.js +113 -0
- package/dist/components/VMeter/VMeter.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VNumberInput/VNumberInput.css +2 -0
- package/dist/components/VNumberInput/VNumberInput.d.ts +114 -0
- package/dist/components/VNumberInput/VNumberInput.js +9 -0
- package/dist/components/VNumberInput/VNumberInput.js.map +1 -0
- package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js +251 -0
- package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VNumberInput/number.d.ts +30 -0
- package/dist/components/VNumberInput/number.js +38 -0
- package/dist/components/VNumberInput/number.js.map +1 -0
- package/dist/components/VPagination/VPagination.d.ts +1 -1
- package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js +76 -76
- package/dist/components/VProgressCircular/VProgressCircular.d.ts +2 -2
- package/dist/components/VProgressLinear/VProgressLinear.css +1 -1
- package/dist/components/VProgressLinear/VProgressLinear.d.ts +10 -7
- package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
- package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js +52 -34
- package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VRadio/VRadio.d.ts +8 -3
- package/dist/components/VRadio/VRadio.js.map +1 -1
- package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js +52 -46
- package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VRating/VRating.css +2 -0
- package/dist/components/VRating/VRating.d.ts +92 -0
- package/dist/components/VRating/VRating.js +9 -0
- package/dist/components/VRating/VRating.js.map +1 -0
- package/dist/components/VRating/VRating.vue_vue_type_script_setup_true_lang.js +158 -0
- package/dist/components/VRating/VRating.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VResizable/VResizable.css +2 -0
- package/dist/components/VResizable/VResizable.d.ts +50 -0
- package/dist/components/VResizable/VResizable.js +9 -0
- package/dist/components/VResizable/VResizable.js.map +1 -0
- package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js +225 -0
- package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VResizable/VResizablePanel.css +2 -0
- package/dist/components/VResizable/VResizablePanel.d.ts +60 -0
- package/dist/components/VResizable/VResizablePanel.js +9 -0
- package/dist/components/VResizable/VResizablePanel.js.map +1 -0
- package/dist/components/VResizable/VResizablePanel.vue_vue_type_script_setup_true_lang.js +83 -0
- package/dist/components/VResizable/VResizablePanel.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VResizable/context.d.ts +24 -0
- package/dist/components/VResizable/context.js +6 -0
- package/dist/components/VResizable/context.js.map +1 -0
- package/dist/components/VResizable/layout.d.ts +28 -0
- package/dist/components/VResizable/layout.js +41 -0
- package/dist/components/VResizable/layout.js.map +1 -0
- package/dist/components/VSelect/VSelect.css +2 -0
- package/dist/components/VSelect/VSelect.d.ts +193 -0
- package/dist/components/VSelect/VSelect.js +9 -0
- package/dist/components/VSelect/VSelect.js.map +1 -0
- package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js +424 -0
- package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js.map +1 -0
- 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.vue_vue_type_script_setup_true_lang.js +8 -8
- package/dist/components/VSideNavigation/VSideNavigationItem.d.ts +2 -2
- package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSkeletonLoader/VSkeletonLoader.d.ts +3 -3
- package/dist/components/VSlider/VSlider.css +1 -1
- package/dist/components/VSlider/VSlider.d.ts +34 -8
- package/dist/components/VSlider/VSlider.js.map +1 -1
- package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js +236 -215
- package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js +4 -4
- package/dist/components/VSnackbar/state.js.map +1 -1
- package/dist/components/VSplitButton/VSplitButton.css +2 -0
- package/dist/components/VSplitButton/VSplitButton.d.ts +114 -0
- package/dist/components/VSplitButton/VSplitButton.js +9 -0
- package/dist/components/VSplitButton/VSplitButton.js.map +1 -0
- package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js +148 -0
- package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VStepper/VStepper.css +2 -0
- package/dist/components/VStepper/VStepper.d.ts +73 -0
- package/dist/components/VStepper/VStepper.js +9 -0
- package/dist/components/VStepper/VStepper.js.map +1 -0
- package/dist/components/VStepper/VStepper.vue_vue_type_script_setup_true_lang.js +96 -0
- package/dist/components/VStepper/VStepper.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VSwitch/VSwitch.d.ts +8 -3
- package/dist/components/VSwitch/VSwitch.js.map +1 -1
- package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js +53 -47
- package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/VTab.css +1 -1
- package/dist/components/VTabs/VTab.d.ts +1 -1
- package/dist/components/VTabs/VTab.js.map +1 -1
- package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/VTabs.css +1 -1
- package/dist/components/VTabs/VTabs.d.ts +13 -5
- package/dist/components/VTabs/VTabs.js.map +1 -1
- package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js +20 -20
- package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTextarea/VTextarea.css +1 -1
- package/dist/components/VTextarea/VTextarea.d.ts +22 -2
- package/dist/components/VTextarea/VTextarea.js.map +1 -1
- package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js +108 -100
- package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeInput/VTimeInput.d.ts +27 -7
- package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
- package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +187 -171
- package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimePicker/VTimePicker.d.ts +2 -2
- package/dist/components/VTimePicker/VTimePicker.js.map +1 -1
- package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js +15 -15
- package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeline/VTimeline.css +2 -0
- package/dist/components/VTimeline/VTimeline.d.ts +59 -0
- package/dist/components/VTimeline/VTimeline.js +9 -0
- package/dist/components/VTimeline/VTimeline.js.map +1 -0
- package/dist/components/VTimeline/VTimeline.vue_vue_type_script_setup_true_lang.js +53 -0
- package/dist/components/VTimeline/VTimeline.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VTimeline/VTimelineItem.css +2 -0
- package/dist/components/VTimeline/VTimelineItem.d.ts +48 -0
- package/dist/components/VTimeline/VTimelineItem.js +9 -0
- package/dist/components/VTimeline/VTimelineItem.js.map +1 -0
- package/dist/components/VTimeline/VTimelineItem.vue_vue_type_script_setup_true_lang.js +70 -0
- package/dist/components/VTimeline/VTimelineItem.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VTimeline/context.d.ts +11 -0
- package/dist/components/VTimeline/context.js +6 -0
- package/dist/components/VTimeline/context.js.map +1 -0
- package/dist/components/VTimeline/datetime.d.ts +8 -0
- package/dist/components/VTimeline/datetime.js +36 -0
- package/dist/components/VTimeline/datetime.js.map +1 -0
- package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js +8 -8
- package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js +12 -12
- package/dist/components/VToggle/VToggle.d.ts +4 -4
- package/dist/components/VToggle/VToggleItem.d.ts +1 -1
- package/dist/components/VTooltip/VTooltip.d.ts +8 -2
- package/dist/components/VTooltip/VTooltip.js.map +1 -1
- package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js +45 -43
- package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTreeView/VTreeView.css +2 -0
- package/dist/components/VTreeView/VTreeView.d.ts +112 -0
- package/dist/components/VTreeView/VTreeView.js +9 -0
- package/dist/components/VTreeView/VTreeView.js.map +1 -0
- package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js +321 -0
- package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VTypography/VTypography.d.ts +1 -1
- package/dist/components/VVirtualList/VVirtualList.css +2 -0
- package/dist/components/VVirtualList/VVirtualList.d.ts +78 -0
- package/dist/components/VVirtualList/VVirtualList.js +9 -0
- package/dist/components/VVirtualList/VVirtualList.js.map +1 -0
- package/dist/components/VVirtualList/VVirtualList.vue_vue_type_script_setup_true_lang.js +114 -0
- package/dist/components/VVirtualList/VVirtualList.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/composables/useChoice.d.ts +28 -0
- package/dist/composables/useChoice.js +28 -0
- package/dist/composables/useChoice.js.map +1 -0
- package/dist/composables/useDebouncedSearch.d.ts +17 -0
- package/dist/composables/useDebouncedSearch.js +18 -0
- package/dist/composables/useDebouncedSearch.js.map +1 -0
- package/dist/composables/useEscapeDismiss.d.ts +8 -0
- package/dist/composables/useEscapeDismiss.js +16 -0
- package/dist/composables/useEscapeDismiss.js.map +1 -0
- package/dist/composables/useFieldIds.d.ts +2 -1
- package/dist/composables/useFieldIds.js +7 -6
- package/dist/composables/useFieldIds.js.map +1 -1
- package/dist/composables/useFieldPanel.d.ts +1 -0
- package/dist/composables/useFieldPanel.js +52 -42
- package/dist/composables/useFieldPanel.js.map +1 -1
- package/dist/composables/useFileField.d.ts +1 -3
- package/dist/composables/useFileField.js +48 -50
- package/dist/composables/useFileField.js.map +1 -1
- package/dist/composables/useHotkeyListener.d.ts +17 -0
- package/dist/composables/useHotkeyListener.js +26 -0
- package/dist/composables/useHotkeyListener.js.map +1 -0
- package/dist/composables/useIconClickHandlers.d.ts +2 -10
- package/dist/composables/useIconClickHandlers.js +1 -1
- package/dist/composables/useIconClickHandlers.js.map +1 -1
- package/dist/{components/VCombobox/infiniteScroll.d.ts → composables/useInfiniteScroll.d.ts} +6 -0
- package/dist/{components/VCombobox/infiniteScroll.js → composables/useInfiniteScroll.js} +7 -7
- package/dist/composables/useInfiniteScroll.js.map +1 -0
- package/dist/composables/useListbox.d.ts +132 -0
- package/dist/composables/useListbox.js +227 -0
- package/dist/composables/useListbox.js.map +1 -0
- package/dist/composables/useModalDialog.d.ts +28 -0
- package/dist/composables/useModalDialog.js +60 -0
- package/dist/composables/useModalDialog.js.map +1 -0
- package/dist/composables/useRootAttrs.d.ts +2 -3
- package/dist/composables/useRootAttrs.js.map +1 -1
- package/dist/composables/useVirtualList.d.ts +46 -0
- package/dist/composables/useVirtualList.js +145 -0
- package/dist/composables/useVirtualList.js.map +1 -0
- package/dist/i18n/en.js +50 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/fr.js +50 -0
- package/dist/i18n/fr.js.map +1 -1
- package/dist/i18n/types.d.ts +103 -2
- package/dist/icons.js +45 -37
- package/dist/index.d.ts +49 -3
- package/dist/index.js +80 -56
- package/dist/styles.css +1 -1
- package/dist/tokens/index.d.ts +43 -0
- package/dist/tokens/semantic.d.ts +42 -0
- package/dist/tokens/semantic.js +43 -1
- package/dist/tokens/semantic.js.map +1 -1
- package/dist/tokens/themes/dark.d.ts +1 -0
- package/dist/tokens/themes/dark.js +1 -0
- package/dist/tokens/themes/dark.js.map +1 -1
- package/dist/tokens.json +215 -1
- package/dist/types.d.ts +57 -4
- package/dist/utils/arrowNav.js.map +1 -1
- package/dist/utils/attrs.d.ts +9 -0
- package/dist/utils/attrs.js +14 -0
- package/dist/utils/attrs.js.map +1 -0
- package/dist/utils/date.d.ts +1 -0
- package/dist/utils/date.js +2 -2
- package/dist/utils/date.js.map +1 -1
- package/package.json +1 -1
- package/dist/components/VCombobox/VComboboxGroup.css +0 -2
- package/dist/components/VCombobox/VComboboxGroup.d.ts +0 -17
- package/dist/components/VCombobox/VComboboxGroup.js +0 -9
- package/dist/components/VCombobox/VComboboxGroup.js.map +0 -1
- package/dist/components/VCombobox/VComboboxGroup.vue_vue_type_script_setup_true_lang.js +0 -21
- package/dist/components/VCombobox/VComboboxGroup.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/components/VCombobox/VComboboxOption.css +0 -2
- package/dist/components/VCombobox/VComboboxOption.js +0 -9
- package/dist/components/VCombobox/VComboboxOption.js.map +0 -1
- package/dist/components/VCombobox/VComboboxOption.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/components/VCombobox/VComboboxSeparator.css +0 -2
- package/dist/components/VCombobox/VComboboxSeparator.js +0 -9
- package/dist/components/VCombobox/VComboboxSeparator.js.map +0 -1
- package/dist/components/VCombobox/VComboboxSeparator.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/components/VCombobox/infiniteScroll.js.map +0 -1
- /package/dist/components/{VCombobox/VComboboxSeparator.d.ts → VCheckbox/VCheckMark.d.ts} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VTimeInput.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../src/components/VTimeInput/VTimeInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * Compose typing and clock modes from VInput and VTimePicker; list mode delegates to VCombobox.\n * JavaScript supplies masking and manual panel focus because text inputs cannot use\n * popovertarget.\n */\n\nimport { computed, inject, provide, ref, watchEffect } from 'vue'\n\nimport VButton from '../VButton/VButton.vue'\nimport VCombobox from '../VCombobox/VCombobox.vue'\nimport type { ComboboxOption } from '../VCombobox/VCombobox.vue'\nimport { inputGroupKey } from '../VInput/context'\nimport { schedule as scheduleIcon } from '../VIcon/icons/schedule'\nimport type { IconSource } from '../VIcon/types'\nimport VInput from '../VInput/VInput.vue'\nimport VPopover from '../VPopover/VPopover.vue'\nimport VSeparator from '../VSeparator/VSeparator.vue'\nimport VTimePicker from '../VTimePicker/VTimePicker.vue'\nimport type { TimePickerFormat } from '../VTimePicker/VTimePicker.vue'\nimport {\n isTimeAllowed,\n limitsProblem,\n nearestAllowedTime,\n resolveLimits,\n} from '../VTimePicker/limits'\nimport type { TimePickerAllowed } from '../VTimePicker/limits'\nimport {\n formatTime,\n formatTimeDisplay,\n hourCycleFor,\n isValidTime,\n parseTime,\n} from '../../utils/time'\nimport {\n formatTimeMask,\n parseTimeMask,\n snapMinute,\n timeCaret,\n timeList,\n timeToMask,\n to12h,\n withMeridiem,\n} from '../../utils/clock'\nimport type { Meridiem } from '../../utils/time'\nimport type { TimeOption } from '../../utils/clock'\nimport { readWrittenTime, timeMatches } from './search'\nimport { isDev } from '../../utils/env'\nimport { hostWarnsKey } from '../../utils/hostWarns'\nimport { digitsOf } from '../../utils/text'\n\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\n\nimport { useFieldPanel } from '../../composables/useFieldPanel'\nimport { canClear } from '../../composables/useClearable'\nimport { iconStartListener } from '../../composables/useIconClickHandlers'\nimport { useMaskedField } from '../../composables/useMaskedField'\nimport { useMessages, useResolvedLocale } from '../../i18n/state'\n\n/**\n * Whether the field can be typed into, only filled from the clock, or picked from a list of\n * times.\n */\nexport type TimeInputMode = 'picker' | 'input' | 'list'\n\n/** What the `#footer` slot receives. */\nexport interface TimeInputFooterSlotProps {\n /** Writes the time on the clock into the value, then closes. */\n confirm: () => void\n /** Closes and drops the time on the clock, the value left as it was. */\n cancel: () => void\n /** The same as `cancel`, under the name VDateInput's footer uses. */\n close: () => void\n}\n\nconst MODES: TimeInputMode[] = ['picker', 'input', 'list']\n\n/** Where the panel opens relative to the field, whether it holds the clock or the list. */\nexport type TimeInputPlacement =\n 'bottom' | 'bottom-start' | 'bottom-end' | 'top' | 'top-start' | 'top-end'\n\n/** The height of the field: 32, 40 or 48 pixels. */\nexport type TimeInputSize = 'sm' | 'md' | 'lg'\n\ninterface TimeInputProps {\n /**\n * Whether times are shown on a 12- or a 24-hour clock. Left out, the reader's language\n * decides, which is almost always what one wants.\n */\n format?: TimePickerFormat\n /**\n * Which form the field takes: one that can be TYPED into, a `picker` one where the\n * clock is the only way in (so the picker is forced on there), or a searchable LIST of\n * times at a fixed interval, where a picker would make no sense.\n */\n mode?: TimeInputMode\n /**\n * Offers the picker beside a field one can type into: an icon at the end of the field, and a\n * panel it opens.\n */\n showPicker?: boolean\n /**\n * The interval between two times that can be chosen. It applies to the picker, to the\n * arrow keys and to the rows of the list.\n */\n minuteStep?: number\n /**\n * The earliest time that can be chosen, inclusive, as a canonical 24-hour `'HH:mm'`.\n * The picker and the list both LEAVE OUT what it rules out, and a time typed past it\n * makes the field invalid.\n */\n min?: string\n /** The latest time that can be chosen, inclusive, written like `min`. */\n max?: string\n /**\n * Which hours can be chosen: the list of them, or a rule answering for one. The hour\n * handed to a rule is always the 24-hour one, whichever clock is on display.\n */\n allowedHours?: TimePickerAllowed\n /** Which minutes can be chosen: the list of them, or a rule answering for one. */\n allowedMinutes?: TimePickerAllowed\n /**\n * A BCP 47 locale, which decides the clock and how a time is written out. It TAKES\n * PRECEDENCE over the design system's global locale and falls back to it, which is why\n * it has no literal default: `undefined` has to stay recognizable for the global locale\n * to have its chance.\n */\n locale?: string\n /** The label above the field. */\n label?: string\n /** A line of help under the field. */\n hint?: string\n /** What the field says while empty. */\n placeholder?: string\n /** The height of the field: 32, 40 or 48 pixels. */\n size?: TimeInputSize\n /** Takes 4px off the height. */\n compact?: boolean\n /** Makes the field unusable, greyed out through the colour tokens. */\n disabled?: boolean\n /**\n * Shows the time without letting it be changed: nothing can be typed, there is no clock and\n * no clear cross.\n */\n readonly?: boolean\n /** Marks the field as invalid, for a rule of your own. */\n invalid?: boolean\n /**\n * An icon inside the field, at the start. It is decorative by default and becomes a\n * real button as soon as a `@click:icon-start` listener is attached, in which case it\n * needs `iconStartLabel`.\n */\n iconStart?: IconSource\n /** What the start icon does, in words, once it is clickable. */\n iconStartLabel?: string\n /**\n * What the clock button does, in words. It names the button `pickerIcon` renders,\n * and falls back to the design system dictionary.\n */\n pickerIconLabel?: string\n /**\n * Shows a spinner at the end of the field, in place of the clock icon. It says that\n * something is being loaded and changes nothing else: the field can still be typed\n * into and the panel still opens.\n */\n loading?: boolean\n /**\n * What screen readers announce while the spinner turns. It falls back to the design\n * system dictionary.\n */\n loadingText?: string\n /** Offers a cross that empties the value, shown before the end icon. */\n clearable?: boolean\n /** What that cross does, in words. It falls back to the design system dictionary. */\n clearLabel?: string\n /**\n * The icon that opens the PICKER, at the end of the field. It has no effect on the list\n * form, whose chevron follows the combobox convention. The clear cross appears to its\n * left rather than in its place.\n */\n pickerIcon?: IconSource\n /** Where the panel opens relative to the field, whether it holds the clock or the list. */\n placement?: TimeInputPlacement\n}\n\nconst props = withDefaults(defineProps<TimeInputProps>(), {\n format: undefined,\n // Deliberately left undefined rather than defaulted to the typed mode: that is what\n // distinguishes \"the prop was not given\" from \"the prop was given this value\", and therefore\n // what allows warning only the consumer who explicitly asked for something that cannot work.\n mode: undefined,\n showPicker: false,\n minuteStep: 1,\n min: undefined,\n max: undefined,\n allowedHours: undefined,\n allowedMinutes: undefined,\n locale: undefined,\n label: undefined,\n hint: undefined,\n placeholder: undefined,\n size: 'md',\n compact: false,\n disabled: false,\n readonly: false,\n invalid: false,\n iconStart: undefined,\n iconStartLabel: undefined,\n pickerIconLabel: undefined,\n loading: false,\n loadingText: undefined,\n clearable: false,\n clearLabel: undefined,\n pickerIcon: () => scheduleIcon,\n placement: 'bottom-start',\n})\n\n/**\n * The time, always as a 24-hour \"HH:mm\" string whatever clock is displayed. A consumer\n * therefore never has to know which clock the reader's language uses.\n */\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 /** The start icon was clicked. Attaching this listener is what makes it a button. */\n 'click:icon-start': [event: MouseEvent]\n}>()\n\ndefineSlots<{\n /**\n * Content at the start of the field, rendered after `iconStart` rather than in its place,\n * as in VInput.\n */\n start?(): unknown\n /**\n * Controls of your own inside the field, after the AM/PM button when there is one and before\n * the ones the field owns: the clear cross and the icon that opens the panel.\n */\n 'value-end'?(): unknown\n /**\n * The strip at the foot of the clock, which REPLACES the Cancel and OK buttons rather than\n * joining them.\n */\n footer?(props: TimeInputFooterSlotProps): 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\n// Declared as this component's own event, `click:icon-start` is out of `$attrs`, so the\n// listener is relayed to the field by hand; and only when the consumer wrote one.\nconst iconStartClick = iconStartListener((event) => emit('click:icon-start', event))\n\n/** What reaches the field on screen; the masked VInput or the list's VCombobox. */\nconst fieldAttrs = computed(() => ({ ...forwardedAttrs.value, ...iconStartClick }))\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelRef = ref<InstanceType<typeof VPopover> | null>(null)\nconst inputRef = ref<InstanceType<typeof VInput> | null>(null)\nconst listRef = ref<InstanceType<typeof VCombobox> | null>(null)\nconst pickerRef = ref<InstanceType<typeof VTimePicker> | null>(null)\n\n/** The real input inside the field, which the mask and the caret work on. */\nconst fieldEl = computed<HTMLInputElement | null>(() => inputRef.value?.el ?? null)\n\nconst resolvedMode = computed<TimeInputMode>(() => {\n // The default is applied before the value is checked. A prop that was never given is not an\n // unknown value, and treating it as one would warn on a bare `<VTimeInput />`.\n const mode = props.mode ?? 'input'\n return MODES.includes(mode) ? mode : 'input'\n})\nconst typing = computed(() => resolvedMode.value === 'input')\nconst isList = computed(() => resolvedMode.value === 'list')\n/**\n * Whether this component builds a panel of its own, which is the clock's: the list form hands\n * that job to VCombobox.\n */\nconst hasPanel = computed(\n () => !props.readonly && (resolvedMode.value === 'picker' || (typing.value && props.showPicker)),\n)\n\nconst resolvedLocale = useResolvedLocale(() => props.locale)\n\nconst resolvedFormat = computed<TimePickerFormat>(\n () => props.format ?? hourCycleFor(resolvedLocale.value),\n)\n\n/** The restrictions, resolved once. */\nconst limits = computed(() => resolveLimits(props))\n\n/** Whether the value is one the restrictions allow. Nothing at all is not a breach. */\nconst valueAllowed = computed(() => {\n const parts = modelParts.value\n return !parts || isTimeAllowed(parts.hour, parts.minute, limits.value)\n})\n\n// @devwarn\nif (isDev) {\n // The clock receives `minuteStep` and the restrictions as they stand, so what it would say\n // about them is said here, under the name the consumer wrote (utils/hostWarns).\n provide(hostWarnsKey, true)\n watchEffect(() => {\n if (!Number.isInteger(props.minuteStep) || props.minuteStep < 1 || 60 % props.minuteStep !== 0)\n console.warn(`[VTimeInput] minuteStep ${props.minuteStep} — a divisor of 60 is expected.`)\n if (props.mode !== undefined && !MODES.includes(props.mode))\n console.warn(\n `[VTimeInput] unknown mode \"${props.mode}\": use \"input\" (the default), \"picker\" or \"list\".`,\n )\n if (isList.value && props.showPicker)\n console.warn(\n '[VTimeInput] showPicker is ignored in \"list\" mode: the list of times is the only panel.',\n )\n // The list form is a VCombobox, which draws the end of the field itself: its own chevron in\n // place of the clock. The two props that describe this component's end icon therefore reach\n // nothing at all, and their absence is invisible on screen.\n const inert = ([] as string[]).concat(\n props.pickerIcon !== scheduleIcon ? 'pickerIcon' : [],\n props.pickerIconLabel ? 'pickerIconLabel' : [],\n )\n const isAre = inert.length > 1 ? 'are' : 'is'\n if (isList.value && inert.length > 0)\n console.warn(\n `[VTimeInput] ${inert.join(', ')} ${isAre} ignored in \"list\" mode: the list draws its own chevron.`,\n )\n // A field one types into without the picker has no end icon either, as in VDateInput.\n // A read-only field is left out: it is a state that comes and goes, not a configuration\n // to correct.\n if (typing.value && !props.showPicker && inert.length > 0)\n console.warn(\n `[VTimeInput] ${inert.join(', ')} ${isAre} ignored without showPicker: a field one types into has no clock icon unless it offers the clock.`,\n )\n const problem = limitsProblem(limits.value)\n if (problem) console.warn(`[VTimeInput] ${problem}`)\n if (isList.value && props.minuteStep < 5)\n console.warn(\n `[VTimeInput] minuteStep ${props.minuteStep} in \"list\" mode would render ${Math.ceil(1440 / props.minuteStep)} rows: a step of 15 or 30 minutes is expected.`,\n )\n })\n}\n\n/** The time being built on the clock, in the same canonical form as the value itself. */\nconst pickerDraft = ref<string | null>(null)\n\nconst modelParts = computed(() => parseTime(model.value))\n\n/** The half of the day chosen while no time is set: VTimePicker's own `pendingMeridiem`. */\nconst pendingMeridiem = ref<Meridiem>('AM')\n\n/**\n * The half of the day the FIELD is showing. It is what the mask needs: typing \"9:30\" says\n * nothing about the morning or the afternoon, so the answer has to be readable while the\n * panel is shut.\n */\nconst currentMeridiem = computed<Meridiem>(() => {\n // With the picker open it is the DRAFT that is on screen, and this control has to read\n // the same thing: two controls showing opposite halves of the day until OK is pressed\n // is the one state that must not happen.\n const parts = open.value && hasPanel.value ? parseTime(pickerDraft.value) : modelParts.value\n return parts ? to12h(parts.hour).meridiem : pendingMeridiem.value\n})\n\n/**\n * The button beside the value flips the half of the day. There is no third state to reach,\n * so it names the half it is showing and swaps to the other one on a click.\n */\nfunction toggleMeridiem() {\n const next: Meridiem = currentMeridiem.value === 'PM' ? 'AM' : 'PM'\n pendingMeridiem.value = next\n // With no time set there is nothing to convert, so the choice is simply REMEMBERED\n // and applies to the first time typed or chosen.\n const moved = withMeridiem(model.value, next)\n if (moved) model.value = moved\n // With the picker open the draft has to follow as well, or OK would write back the\n // half of the day the reader has just changed.\n const pending = open.value && hasPanel.value ? withMeridiem(pickerDraft.value, next) : null\n if (pending) pickerDraft.value = pending\n}\n\n/** Whether the button is rendered at all. */\nconst hasMeridiem = computed(\n () => typing.value && !props.readonly && resolvedFormat.value === '12h',\n)\n\nconst hasValue = computed(() => !!modelParts.value)\nconst displayText = computed(() =>\n model.value ? formatTimeDisplay(model.value, resolvedLocale.value, resolvedFormat.value) : '',\n)\n\n// @a11y\n// Where the focus goes when the panel opens. The picker publishes where its own focus belongs,\n// so nothing here has to know that the target is a slider; the same arrangement VDateInput has\n// with VDatePicker.\nfunction focusInPanel() {\n pickerRef.value?.focus()\n}\n\n// A VInputGroup joins several controls into one object, so the shape of this field is the\n// row's decision rather than its own (VInput/context.ts). Nothing else is needed here: the\n// AM/PM button lives INSIDE the field, so a 12 hour field is one box like any other and\n// the row has nothing of its own to join.\nconst group = inject(inputGroupKey, null)\nconst {\n size: resolvedSize,\n compact: resolvedCompact,\n disabled: resolvedDisabled,\n} = useControlShape(props, group)\n\n// The whole \"field plus panel\" shell, shared with VDateInput. What is specific to this\n// component is only what happens around it: preparing the draft as the panel opens, and\n// clearing the announcement as it closes.\nconst {\n open,\n panelId,\n closeAndFocus,\n focusField,\n onFieldFocus,\n toggleFromIcon,\n onControlClick,\n onFocusout,\n onKeydown,\n onPanelMousedown,\n} = useFieldPanel({\n rootEl,\n panelRef,\n field: inputRef,\n // With no panel there is nothing to open. This is the composable's SINGLE cut-off point, and\n // every way in passes through it; clicking the field, focusing it, the down arrow, Enter, the\n // icon.\n disabled: () => resolvedDisabled.value || !hasPanel.value,\n focusInPanel,\n // Beside a field one types into, the panel opens on focus WITHOUT taking it, so typing\n // carries on.\n openOnFocus: () => typing.value,\n onOpen: () => {\n const parts = modelParts.value\n if (parts) pickerDraft.value = formatTime(parts.hour, parts.minute)\n // A clock opened BESIDE a field one types into starts empty: it opened because the\n // field took the focus, not because a time was asked for, and a draft set to the\n // current time would hand its half of the day to the digits about to be typed, so\n // \"0930\" typed in the afternoon would become 21:30.\n else if (typing.value) pickerDraft.value = null\n else {\n // Reading the clock is safe here: this runs from a handler, hence in a browser, never\n // during a render; and it is why the picker itself never reads it, so that it stays\n // identical on both sides of hydration. The instant is taken once: two reads could\n // straddle a minute.\n const now = new Date()\n const wanted = snapMinute(now.getMinutes(), props.minuteStep)\n // Pulled to the nearest time the restrictions allow, so that the panel never opens\n // on one the reader would be refused. Null is a set of restrictions that allows\n // nothing at all, which the warning above has already reported.\n pickerDraft.value =\n nearestAllowedTime(now.getHours(), wanted, limits.value) ??\n formatTime(now.getHours(), wanted)\n }\n pickerRef.value?.reset()\n },\n onClose: () => {\n pickerRef.value?.reset()\n },\n})\n\n/** OK: the ONE route by which the picker's draft becomes the value. */\nfunction confirm() {\n if (pickerDraft.value) model.value = pickerDraft.value\n closeAndFocus()\n}\n\n/** Cancel: the draft is simply dropped with the panel. */\nconst cancel = closeAndFocus\n\n// @a11y\n// Spread popup wiring over forwarded attrs: absent panels must contribute no keys, while\n// present comboboxes require their own role and controls reference.\nconst inputAttrs = computed(() =>\n hasPanel.value\n ? {\n ...fieldAttrs.value,\n role: 'combobox',\n 'aria-haspopup': 'dialog',\n 'aria-expanded': open.value,\n 'aria-controls': panelId,\n }\n : fieldAttrs.value,\n)\n\n/*\n * Emptying the value, called by the field as it emits its clear event. The focus is taken\n * through `focusField`, whose note says why that stops the panel reopening.\n */\nfunction clearValue() {\n model.value = null\n // The typed text is emptied EXPLICITLY: when the value was already empty, nothing\n // changes, and the guard that keeps the field and the value from chasing each other\n // would leave the text where it was.\n if (typing.value) writeField('')\n focusField()\n emit('clear')\n}\n\n// Escape and the focus leaving both close WITHOUT committing, which is the picker's Cancel\n// behaviour: closing never writes the value, and only OK does.\n\nconst m = useMessages()\n\n// @core\n/**\n * Saying no to a time that is not one the restrictions allow. It is written unconditionally\n * rather than only while typing: the browser BARS a read-only control from constraint\n * validation, so the read-only form settles it without a branch here.\n */\nwatchEffect(\n () => {\n const el = fieldEl.value\n if (!el) return\n const own = m.value.timeInput.unavailable\n if (!valueAllowed.value) el.setCustomValidity(own)\n // Only the component's own message is taken back: a consumer's verdict written over it,\n // a server saying the slot is taken, stays until the consumer clears it.\n else if (el.validationMessage === own) el.setCustomValidity('')\n },\n { flush: 'post' },\n)\n\n/*\n * That is why the caret can be computed outright here, where a date has to look for a separator\n * it cannot predict; and why nothing distinguishes a final commit from a live one, there being\n * no equivalent of expanding a two-digit year.\n */\nconst {\n draft,\n fieldModel,\n writeField,\n commitOrRevert,\n onFieldInput,\n onKeydown: onMaskKeydown,\n onPaste,\n} = useMaskedField({\n fieldEl,\n typing: () => typing.value,\n displayText: () => displayText.value,\n readValue: () => model.value,\n writeValue: (time) => {\n model.value = time\n // With the clock open beside the field, it follows what is typed: otherwise OK would\n // write the clock's time back over the one the reader has just typed.\n if (open.value && hasPanel.value) pickerDraft.value = time\n },\n maxDigits: () => 4,\n format: formatTimeMask,\n caret: (_text, digitsBefore, inserting) => timeCaret(digitsBefore, inserting),\n parse: (text) => parseTimeMask(text, resolvedFormat.value, currentMeridiem.value),\n toMask: (time) => timeToMask(time, resolvedFormat.value),\n})\n\n// @keyboard\n// What the mask's keys mean for a time: typing anything that is not a digit,\n// the separator included so that \"9:30\" can be typed exactly as it reads, completes the\n// hour with a leading zero and moves on to the minutes; the down arrow is the one explicit\n// way from the field into the picker.\nfunction onFieldKeydown(event: KeyboardEvent) {\n onMaskKeydown(event, {\n onSeparator: (el) => {\n const digits = digitsOf(el.value)\n if (digits.length === 1) writeField(formatTimeMask(`0${digits}`), 3)\n },\n onArrowDown: () => {\n if (!open.value) return false\n focusInPanel()\n return true\n },\n onEnter: closeAndFocus,\n })\n}\n\n/** Pasting. */\nfunction onFieldPaste(event: ClipboardEvent) {\n onPaste(event, (pasted) =>\n isValidTime(pasted)\n ? pasted\n : readWrittenTime(pasted, resolvedFormat.value, currentMeridiem.value),\n )\n}\n\n/*\n * The list form must not reach these two at all, and being panel-less is not enough to keep it\n * out. `useFieldPanel.onKeydown` cancels the default on Enter before asking whether a panel can\n * open, so an Enter travelling up from the VCombobox would stop a surrounding form from being\n * submitted with nothing to show for it.\n */\nfunction onRootKeydown(event: KeyboardEvent) {\n if (!isList.value) onKeydown(event)\n}\n\nfunction onRootFocusout(event: FocusEvent) {\n if (!isList.value) onFocusout(event)\n}\n\n/*\n * What is left is the three things a combobox cannot know about times: which rows to offer, how\n * a search matches one, and the fact that this component's value is a nullable string where a\n * combobox's is a plain one.\n */\n\n/**\n * The rows the step and the restrictions offer, which the value does not enter into: a\n * change of value must not format up to 1440 times again.\n */\nconst baseOptions = computed<TimeOption[]>(() => {\n if (!isList.value) return []\n return timeList(props.minuteStep, resolvedLocale.value, resolvedFormat.value).filter((row) => {\n // A list is read before it is chosen from, so a time it may not take has no reason to be in\n // it, which is the picker's own rule for its numerals.\n const parts = parseTime(row.value)\n return !parts || isTimeAllowed(parts.hour, parts.minute, limits.value)\n })\n})\n\n/**\n * The rows on offer. Without it the field would fall back to showing the raw canonical string;\n * \"09:07\" in a field that spells every other row \"9:07 AM\"; since a combobox labels a value\n * through the option carrying it.\n */\nconst options = computed<TimeOption[]>(() => {\n const rows = baseOptions.value\n const current = model.value\n if (!isValidTime(current) || rows.some((row) => row.value === current)) return rows\n const at = rows.findIndex((row) => row.value > current)\n const extra: TimeOption = {\n value: current,\n label: formatTimeDisplay(current, resolvedLocale.value, resolvedFormat.value),\n }\n return at < 0 ? [...rows, extra] : [...rows.slice(0, at), extra, ...rows.slice(at)]\n})\n\n/**\n * The search, which accepts the digit run as well as the words; \"930\" finds 9:30, the way the\n * typed form's mask reads what it is given.\n */\nconst matchTime = (option: ComboboxOption, query: string) =>\n timeMatches(option.label, String(option.value), query)\n\n/**\n * The bridge to VCombobox, whose value is a plain string where this component's is\n * nullable: an emptied combobox says so with an empty string, which is not a time.\n */\nconst listModel = computed<string | string[]>({\n get: () => model.value ?? '',\n set: (value) => {\n model.value = typeof value === 'string' && value ? value : null\n },\n})\n\n/*\n * Whether the cross is shown has to be answered explicitly here: outside the typed mode the\n * field is read-only and would hide it, while the value comes from the panel. The `readonly`\n * PROP is the one case where that default answer was right: frozen, the field offers no route\n * to a new value, so it offers no route to none either.\n */\nconst clearVisible = computed(() =>\n canClear(props, resolvedDisabled.value, hasValue.value || (typing.value && !!draft.value)),\n)\nconst endIcon = computed<IconSource | undefined>(() =>\n hasPanel.value ? props.pickerIcon : undefined,\n)\n// @a11y @devwarn\n/*\n * The label is passed always, even when no icon is rendered at all. The helper detecting a\n * click handler on an icon warns AT setup if one is attached without a label, and it has no way\n * of knowing whether an icon exists.\n */\nconst endIconLabel = computed(() => props.pickerIconLabel ?? m.value.timeInput.openPicker)\nconst resolvedClearLabel = computed(() => props.clearLabel ?? m.value.timeInput.clear)\n\n/*\n * The three of them go through whichever field is on screen: in the list form there is no\n * VInput of this component's own, the VCombobox owning the one the reader sees. Exposing\n * the same trio as the other fields is what keeps a consumer's `ref` working whatever the\n * mode, which is a runtime prop they may well be binding.\n */\nconst field = computed(() => (isList.value ? listRef.value : inputRef.value))\n\ndefineExpose({\n /** Moves the focus to the text field. */\n focus: (options?: FocusOptions) => field.value?.focus(options),\n /** Selects what the field is showing. */\n select: () => field.value?.select(),\n /** The real `<input>` behind the field, for what neither of the two above covers. */\n el: computed(() => field.value?.el ?? null),\n})\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n class=\"v-time-input\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n :data-open=\"open ? '' : undefined\"\n :data-mode=\"resolvedMode\"\n :data-disabled=\"resolvedDisabled ? '' : undefined\"\n :data-readonly=\"readonly ? '' : undefined\"\n @focusout=\"onRootFocusout\"\n @keydown=\"onRootKeydown\"\n >\n <!--\n All this component adds is the three things a combobox cannot know about times; the rows,\n the search, and a value that may be nothing.\n -->\n <VCombobox\n v-if=\"isList\"\n ref=\"listRef\"\n v-model=\"listModel\"\n v-bind=\"fieldAttrs\"\n :options=\"options\"\n :filter=\"matchTime\"\n :label=\"label\"\n :hint=\"hint\"\n :placeholder=\"placeholder\"\n :size=\"resolvedSize\"\n :compact=\"resolvedCompact\"\n :disabled=\"resolvedDisabled\"\n :readonly=\"readonly\"\n :invalid=\"invalid\"\n :icon-start=\"iconStart\"\n :icon-start-label=\"iconStartLabel\"\n :clearable=\"clearable\"\n :clear-label=\"clearLabel\"\n :loading=\"loading\"\n :loading-text=\"loadingText\"\n :placement=\"placement\"\n @clear=\"emit('clear')\"\n />\n\n <div v-else class=\"v-time-input-control\" @click=\"onControlClick\">\n <!--\n The field is declared a combobox rather than left as the plain text box it implicitly\n is, because a text box may not carry the attribute saying whether something is expanded;\n the same reasoning as in VDateInput.\n -->\n <VInput\n ref=\"inputRef\"\n v-model=\"fieldModel\"\n :inputmode=\"typing ? 'numeric' : undefined\"\n :autocomplete=\"typing ? 'off' : undefined\"\n v-bind=\"inputAttrs\"\n :readonly=\"readonly\"\n :no-typing=\"!typing\"\n :label=\"label\"\n :hint=\"hint\"\n :placeholder=\"placeholder ?? (typing ? m.timeInput.maskPlaceholder : undefined)\"\n :size=\"resolvedSize\"\n :compact=\"resolvedCompact\"\n :disabled=\"resolvedDisabled\"\n :invalid=\"invalid\"\n :clearable=\"clearable\"\n :clear-visible=\"clearVisible\"\n :clear-label=\"resolvedClearLabel\"\n :icon-start=\"iconStart\"\n :icon-start-label=\"iconStartLabel\"\n :loading=\"loading\"\n :loading-text=\"loadingText\"\n :icon-end=\"endIcon\"\n :icon-end-label=\"endIconLabel\"\n @click:icon-end=\"toggleFromIcon\"\n @clear=\"clearValue\"\n @focus=\"onFieldFocus\"\n @input=\"onFieldInput\"\n @change=\"commitOrRevert\"\n @blur=\"commitOrRevert\"\n @keydown=\"onFieldKeydown\"\n @paste=\"onFieldPaste\"\n >\n <!--\n It is dropped when nothing follows it, or it would trail off the end of an otherwise\n bare field.\n -->\n <template v-if=\"$slots.start\" #start><slot name=\"start\" /></template>\n <template v-if=\"hasMeridiem || $slots['value-end']\" #value-end>\n <button\n v-if=\"hasMeridiem\"\n type=\"button\"\n class=\"v-input-action v-field-action v-time-input-meridiem\"\n :disabled=\"resolvedDisabled\"\n :aria-label=\"\n m.timeInput.meridiemValue(\n currentMeridiem === 'PM' ? m.timePicker.pm : m.timePicker.am,\n )\n \"\n @click=\"toggleMeridiem\"\n >\n {{ currentMeridiem === 'PM' ? m.timePicker.pm : m.timePicker.am }}\n </button>\n <VSeparator\n v-if=\"hasMeridiem && (clearVisible || endIcon)\"\n orientation=\"vertical\"\n class=\"v-time-input-divider\"\n />\n <slot name=\"value-end\" />\n </template>\n </VInput>\n </div>\n\n <!-- The absence of a panel is therefore self-enforcing. -->\n <VPopover\n v-if=\"hasPanel\"\n :id=\"panelId\"\n ref=\"panelRef\"\n v-model:open=\"open\"\n mode=\"manual\"\n anchor=\"--time-input-anchor\"\n :placement=\"placement\"\n role=\"dialog\"\n class=\"v-time-input-panel\"\n :aria-label=\"label ?? m.timeInput.pickerLabel\"\n @mousedown=\"onPanelMousedown\"\n >\n <!--\n The picker works on the DRAFT and not on the value, which makes OK the only way in:\n Cancel, Escape and the focus leaving simply drop it. The two actions are handed to its\n own footer slot, since they belong to this panel rather than to a clock shown on its\n own.\n -->\n <VTimePicker\n ref=\"pickerRef\"\n v-model=\"pickerDraft\"\n :format=\"resolvedFormat\"\n :locale=\"resolvedLocale\"\n :minute-step=\"minuteStep\"\n :min=\"min\"\n :max=\"max\"\n :allowed-hours=\"allowedHours\"\n :allowed-minutes=\"allowedMinutes\"\n @confirm=\"confirm\"\n >\n <!--\n The slot REPLACES the two buttons rather than sitting beside them, so it is handed\n both actions: the clock writes a draft, and without `confirm` a footer of one's own\n could never commit it; the value would become unchangeable through the panel. The\n `remove` of VCombobox's `#chip` slot exists for the same reason.\n -->\n <template #footer>\n <slot name=\"footer\" :confirm=\"confirm\" :cancel=\"cancel\" :close=\"cancel\">\n <VButton variant=\"ghost\" tone=\"neutral\" @click=\"cancel\">{{ m.common.cancel }}</VButton>\n <VButton @click=\"confirm\">{{ m.common.confirm }}</VButton>\n </slot>\n </template>\n </VTimePicker>\n </VPopover>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-time-input {\n /* Confines the anchor name to this instance. It is declared on the root because that\n is the common ancestor of the field and the panel. */\n anchor-scope: --time-input-anchor;\n display: block;\n width: 100%;\n font-family: var(--vectis-text-family);\n }\n\n .v-time-input:not([data-disabled]):not([data-readonly]) .v-time-input-control {\n cursor: pointer;\n }\n\n /*\n * Anchor through the clock/input wrapper only; list mode belongs to VCombobox and must retain\n * its own anchor independently of stylesheet order.\n */\n .v-time-input-control .v-input-field {\n anchor-name: --time-input-anchor;\n }\n\n /* A field one types into shows the text cursor rather than the pointer a clickable\n control shows. Its figures are also given equal widths: with proportional ones the\n text shifts as digits are typed, and the caret appears to jitter. */\n .v-time-input[data-mode='input']:not([data-disabled]):not([data-readonly]) .v-time-input-control {\n cursor: text;\n }\n\n .v-time-input[data-mode='input'] .v-input-control {\n font-variant-numeric: tabular-nums;\n }\n\n /*\n * Give the AM/PM action room for text while preserving the neighbouring icon buttons' minimum\n * hit target.\n */\n .v-field-action.v-time-input-meridiem {\n inline-size: auto;\n min-inline-size: var(--control-action-size);\n padding-inline: calc((var(--control-action-size) - var(--vectis-icon-size)) / 2);\n font: inherit;\n }\n\n /*\n * The rule between the AM/PM button and the field's own controls, which keeps a word from\n * reading as a third icon in the row. A vertical VSeparator stretches to its flex line by\n * contract, which here is the full height of the field: it would meet the border at both ends\n * and read as a division of the box rather than a break in the row.\n */\n .v-separator[data-orientation='vertical'].v-time-input-divider {\n align-self: center;\n block-size: var(--control-action-size);\n }\n\n /*\n * No `display` here either: the column layout is `.v-panel`'s, and a panel declaring its own\n * is what the doubled-class `.v-overlay` guard exists to overrule. The selector compounds two\n * classes VPopover puts on the same element, because the padding is also declared by the\n * shared panel class: at equal specificity the winner would be whichever sheet the consumer's\n * bundler put last.\n */\n .v-popover-panel.v-time-input-panel {\n width: max-content;\n padding: 0;\n }\n\n /*\n * The list form is a VCombobox and takes everything from it; this is the one declaration that\n * does not belong to a combobox in general. A column of times read down the panel is a column\n * of FIGURES, and proportional ones slide the colon from row to row.\n */\n .v-time-input[data-mode='list'] .v-combobox-panel {\n font-variant-numeric: tabular-nums;\n }\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VTimeInput.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../src/components/VTimeInput/VTimeInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * Compose typing and clock modes from VInput and VTimePicker; list mode delegates to VCombobox.\n * JavaScript supplies masking and manual panel focus because text inputs cannot use\n * popovertarget.\n */\n\nimport { computed, inject, provide, ref, watchEffect } from 'vue'\n\nimport VButton from '../VButton/VButton.vue'\nimport VCombobox from '../VCombobox/VCombobox.vue'\nimport type { ListboxOption } from '../../types'\nimport { inputGroupKey } from '../VInput/context'\nimport { schedule as scheduleIcon } from '../VIcon/icons/schedule'\nimport type { IconSource } from '../VIcon/types'\nimport type { FieldLabelPosition } from '../VField/VField.vue'\nimport VInput from '../VInput/VInput.vue'\nimport VPopover from '../VPopover/VPopover.vue'\nimport VSeparator from '../VSeparator/VSeparator.vue'\nimport VTimePicker from '../VTimePicker/VTimePicker.vue'\nimport type { TimePickerFormat } from '../VTimePicker/VTimePicker.vue'\nimport {\n isTimeAllowed,\n limitsProblem,\n nearestAllowedTime,\n resolveLimits,\n} from '../VTimePicker/limits'\nimport type { TimePickerAllowed } from '../VTimePicker/limits'\nimport {\n formatTime,\n formatTimeDisplay,\n hourCycleFor,\n isValidTime,\n parseTime,\n} from '../../utils/time'\nimport {\n formatTimeMask,\n parseTimeMask,\n snapMinute,\n timeCaret,\n timeList,\n timeToMask,\n to12h,\n withMeridiem,\n} from '../../utils/clock'\nimport type { Meridiem } from '../../utils/time'\nimport type { TimeOption } from '../../utils/clock'\nimport { readWrittenTime, timeMatches } from './search'\nimport { isDev } from '../../utils/env'\nimport { hostWarnsKey } from '../../utils/hostWarns'\nimport { digitsOf } from '../../utils/text'\n\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\n\nimport { useFieldPanel } from '../../composables/useFieldPanel'\nimport { canClear } from '../../composables/useClearable'\nimport { iconStartListener } from '../../composables/useIconClickHandlers'\nimport { useMaskedField } from '../../composables/useMaskedField'\nimport { useMessages, useResolvedLocale } from '../../i18n/state'\n\n/**\n * Whether the field can be typed into, only filled from the clock, or picked from a list of\n * times.\n */\nexport type TimeInputMode = 'picker' | 'input' | 'list'\n\n/** What the `#footer` slot receives. */\nexport interface TimeInputFooterSlotProps {\n /** Writes the time on the clock into the value, then closes. */\n confirm: () => void\n /** Closes and drops the time on the clock, the value left as it was. */\n cancel: () => void\n /** The same as `cancel`, under the name VDateInput's footer uses. */\n close: () => void\n}\n\nconst MODES: TimeInputMode[] = ['picker', 'input', 'list']\n\n/** Where the panel opens relative to the field, whether it holds the clock or the list. */\nexport type TimeInputPlacement =\n 'bottom' | 'bottom-start' | 'bottom-end' | 'top' | 'top-start' | 'top-end'\n\n/** The height of the field: 32, 40 or 48 pixels. */\nexport type TimeInputSize = 'sm' | 'md' | 'lg'\n\ninterface TimeInputProps {\n /**\n * Whether times are shown on a 12- or a 24-hour clock. Left out, the reader's language\n * decides, which is almost always what one wants.\n */\n format?: TimePickerFormat\n /**\n * Which form the field takes: one that can be TYPED into, a `picker` one where the\n * clock is the only way in (so the picker is forced on there), or a searchable LIST of\n * times at a fixed interval, where a picker would make no sense.\n */\n mode?: TimeInputMode\n /**\n * Offers the picker beside a field one can type into: an icon at the end of the field, and a\n * panel it opens.\n */\n showPicker?: boolean\n /**\n * The interval between two times that can be chosen. It applies to the picker, to the\n * arrow keys and to the rows of the list.\n */\n minuteStep?: number\n /**\n * The earliest time that can be chosen, inclusive, as a canonical 24-hour `'HH:mm'`.\n * The picker and the list both LEAVE OUT what it rules out, and a time typed past it\n * makes the field invalid.\n */\n min?: string\n /** The latest time that can be chosen, inclusive, written like `min`. */\n max?: string\n /**\n * Which hours can be chosen: the list of them, or a rule answering for one. The hour\n * handed to a rule is always the 24-hour one, whichever clock is on display.\n */\n allowedHours?: TimePickerAllowed\n /** Which minutes can be chosen: the list of them, or a rule answering for one. */\n allowedMinutes?: TimePickerAllowed\n /**\n * A BCP 47 locale, which decides the clock and how a time is written out. It TAKES\n * PRECEDENCE over the design system's global locale and falls back to it, which is why\n * it has no literal default: `undefined` has to stay recognizable for the global locale\n * to have its chance.\n */\n locale?: string\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 so that the field\n * does not grow. It marks the field invalid, is read along with it, and is announced when it\n * appears or changes.\n */\n error?: string\n /** 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?: TimeInputSize\n /** Takes 4px off the height. */\n compact?: boolean\n /** Makes the field unusable, greyed out through the colour tokens. */\n disabled?: boolean\n /**\n * Shows the time without letting it be changed: nothing can be typed, there is no clock and\n * no clear cross.\n */\n readonly?: boolean\n /** Marks the field as invalid, for a rule of your own. */\n invalid?: boolean\n /**\n * An icon inside the field, at the start. It is decorative by default and becomes a\n * real button as soon as a `@click:icon-start` listener is attached, in which case it\n * needs `iconStartLabel`.\n */\n iconStart?: IconSource\n /** What the start icon does, in words, once it is clickable. */\n iconStartLabel?: string\n /**\n * What the clock button does, in words. It names the button `pickerIcon` renders,\n * and falls back to the design system dictionary.\n */\n pickerLabel?: string\n /**\n * Shows a spinner at the end of the field, in place of the clock icon. It says that\n * something is being loaded and changes nothing else: the field can still be typed\n * into and the panel still opens.\n */\n loading?: boolean\n /**\n * What screen readers announce while the spinner turns. It falls back to the design\n * system dictionary.\n */\n loadingText?: string\n /** Offers a cross that empties the value, shown before the end icon. */\n clearable?: boolean\n /** What that cross does, in words. It falls back to the design system dictionary. */\n clearLabel?: string\n /**\n * The icon that opens the PICKER, at the end of the field. It has no effect on the list\n * form, whose chevron follows the combobox convention. The clear cross appears to its\n * left rather than in its place.\n */\n pickerIcon?: IconSource\n /** Where the panel opens relative to the field, whether it holds the clock or the list. */\n placement?: TimeInputPlacement\n}\n\nconst props = withDefaults(defineProps<TimeInputProps>(), {\n format: undefined,\n // Deliberately left undefined rather than defaulted to the typed mode: that is what\n // distinguishes \"the prop was not given\" from \"the prop was given this value\", and therefore\n // what allows warning only the consumer who explicitly asked for something that cannot work.\n mode: undefined,\n showPicker: false,\n minuteStep: 1,\n min: undefined,\n max: undefined,\n allowedHours: undefined,\n allowedMinutes: undefined,\n locale: undefined,\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 iconStart: undefined,\n iconStartLabel: undefined,\n pickerLabel: undefined,\n loading: false,\n loadingText: undefined,\n clearable: false,\n clearLabel: undefined,\n pickerIcon: () => scheduleIcon,\n placement: 'bottom-start',\n})\n\n/**\n * The time, always as a 24-hour \"HH:mm\" string whatever clock is displayed. A consumer\n * therefore never has to know which clock the reader's language uses.\n */\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 /** The start icon was clicked. Attaching this listener is what makes it a button. */\n 'click:icon-start': [event: MouseEvent]\n}>()\n\ndefineSlots<{\n /**\n * Content at the start of the field, rendered after `iconStart` rather than in its place,\n * as in VInput.\n */\n start?(): unknown\n /**\n * Controls of your own inside the field, after the AM/PM button when there is one and before\n * the ones the field owns: the clear cross and the icon that opens the panel.\n */\n 'value-end'?(): unknown\n /**\n * The strip at the foot of the clock, which REPLACES the Cancel and OK buttons rather than\n * joining them.\n */\n footer?(props: TimeInputFooterSlotProps): 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\n// Declared as this component's own event, `click:icon-start` is out of `$attrs`, so the\n// listener is relayed to the field by hand; and only when the consumer wrote one.\nconst iconStartClick = iconStartListener((event) => emit('click:icon-start', event))\n\n/** What reaches the field on screen; the masked VInput or the list's VCombobox. */\nconst fieldAttrs = computed(() => ({ ...forwardedAttrs.value, ...iconStartClick }))\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelRef = ref<InstanceType<typeof VPopover> | null>(null)\nconst inputRef = ref<InstanceType<typeof VInput> | null>(null)\nconst listRef = ref<InstanceType<typeof VCombobox> | null>(null)\nconst pickerRef = ref<InstanceType<typeof VTimePicker> | null>(null)\n\n/** The real input inside the field, which the mask and the caret work on. */\nconst fieldEl = computed<HTMLInputElement | null>(() => inputRef.value?.el ?? null)\n\nconst resolvedMode = computed<TimeInputMode>(() => {\n // The default is applied before the value is checked. A prop that was never given is not an\n // unknown value, and treating it as one would warn on a bare `<VTimeInput />`.\n const mode = props.mode ?? 'input'\n return MODES.includes(mode) ? mode : 'input'\n})\nconst typing = computed(() => resolvedMode.value === 'input')\nconst isList = computed(() => resolvedMode.value === 'list')\n/**\n * Whether this component builds a panel of its own, which is the clock's: the list form hands\n * that job to VCombobox.\n */\nconst hasPanel = computed(\n () => !props.readonly && (resolvedMode.value === 'picker' || (typing.value && props.showPicker)),\n)\n\nconst resolvedLocale = useResolvedLocale(() => props.locale)\n\nconst resolvedFormat = computed<TimePickerFormat>(\n () => props.format ?? hourCycleFor(resolvedLocale.value),\n)\n\n/** The restrictions, resolved once. */\nconst limits = computed(() => resolveLimits(props))\n\n/** Whether the value is one the restrictions allow. Nothing at all is not a breach. */\nconst valueAllowed = computed(() => {\n const parts = modelParts.value\n return !parts || isTimeAllowed(parts.hour, parts.minute, limits.value)\n})\n\n// @devwarn\nif (isDev) {\n // The clock receives `minuteStep` and the restrictions as they stand, so what it would say\n // about them is said here, under the name the consumer wrote (utils/hostWarns).\n provide(hostWarnsKey, true)\n watchEffect(() => {\n if (!Number.isInteger(props.minuteStep) || props.minuteStep < 1 || 60 % props.minuteStep !== 0)\n console.warn(`[VTimeInput] minuteStep ${props.minuteStep} — a divisor of 60 is expected.`)\n if (props.mode !== undefined && !MODES.includes(props.mode))\n console.warn(\n `[VTimeInput] unknown mode \"${props.mode}\": use \"input\" (the default), \"picker\" or \"list\".`,\n )\n if (isList.value && props.showPicker)\n console.warn(\n '[VTimeInput] showPicker is ignored in \"list\" mode: the list of times is the only panel.',\n )\n // The list form is a VCombobox, which draws the end of the field itself: its own chevron in\n // place of the clock. The two props that describe this component's end icon therefore reach\n // nothing at all, and their absence is invisible on screen.\n const inert = ([] as string[]).concat(\n props.pickerIcon !== scheduleIcon ? 'pickerIcon' : [],\n props.pickerLabel ? 'pickerLabel' : [],\n )\n const isAre = inert.length > 1 ? 'are' : 'is'\n if (isList.value && inert.length > 0)\n console.warn(\n `[VTimeInput] ${inert.join(', ')} ${isAre} ignored in \"list\" mode: the list draws its own chevron.`,\n )\n // A field one types into without the picker has no end icon either, as in VDateInput.\n // A read-only field is left out: it is a state that comes and goes, not a configuration\n // to correct.\n if (typing.value && !props.showPicker && inert.length > 0)\n console.warn(\n `[VTimeInput] ${inert.join(', ')} ${isAre} ignored without showPicker: a field one types into has no clock icon unless it offers the clock.`,\n )\n const problem = limitsProblem(limits.value)\n if (problem) console.warn(`[VTimeInput] ${problem}`)\n if (isList.value && props.minuteStep < 5)\n console.warn(\n `[VTimeInput] minuteStep ${props.minuteStep} in \"list\" mode would render ${Math.ceil(1440 / props.minuteStep)} rows: a step of 15 or 30 minutes is expected.`,\n )\n })\n}\n\n/** The time being built on the clock, in the same canonical form as the value itself. */\nconst pickerDraft = ref<string | null>(null)\n\nconst modelParts = computed(() => parseTime(model.value))\n\n/** The half of the day chosen while no time is set: VTimePicker's own `pendingMeridiem`. */\nconst pendingMeridiem = ref<Meridiem>('AM')\n\n/**\n * The half of the day the FIELD is showing. It is what the mask needs: typing \"9:30\" says\n * nothing about the morning or the afternoon, so the answer has to be readable while the\n * panel is shut.\n */\nconst currentMeridiem = computed<Meridiem>(() => {\n // With the picker open it is the DRAFT that is on screen, and this control has to read\n // the same thing: two controls showing opposite halves of the day until OK is pressed\n // is the one state that must not happen.\n const parts = open.value && hasPanel.value ? parseTime(pickerDraft.value) : modelParts.value\n return parts ? to12h(parts.hour).meridiem : pendingMeridiem.value\n})\n\n/**\n * The button beside the value flips the half of the day. There is no third state to reach,\n * so it names the half it is showing and swaps to the other one on a click.\n */\nfunction toggleMeridiem() {\n const next: Meridiem = currentMeridiem.value === 'PM' ? 'AM' : 'PM'\n pendingMeridiem.value = next\n // With no time set there is nothing to convert, so the choice is simply REMEMBERED\n // and applies to the first time typed or chosen.\n const moved = withMeridiem(model.value, next)\n if (moved) model.value = moved\n // With the picker open the draft has to follow as well, or OK would write back the\n // half of the day the reader has just changed.\n const pending = open.value && hasPanel.value ? withMeridiem(pickerDraft.value, next) : null\n if (pending) pickerDraft.value = pending\n}\n\n/** Whether the button is rendered at all. */\nconst hasMeridiem = computed(\n () => typing.value && !props.readonly && resolvedFormat.value === '12h',\n)\n\nconst hasValue = computed(() => !!modelParts.value)\nconst displayText = computed(() =>\n model.value ? formatTimeDisplay(model.value, resolvedLocale.value, resolvedFormat.value) : '',\n)\n\n// @a11y\n// Where the focus goes when the panel opens. The picker publishes where its own focus belongs,\n// so nothing here has to know that the target is a slider; the same arrangement VDateInput has\n// with VDatePicker.\nfunction focusInPanel() {\n pickerRef.value?.focus()\n}\n\n// A VInputGroup joins several controls into one object, so the shape of this field is the\n// row's decision rather than its own (VInput/context.ts). Nothing else is needed here: the\n// AM/PM button lives INSIDE the field, so a 12 hour field is one box like any other and\n// the row has nothing of its own to join.\nconst group = inject(inputGroupKey, null)\nconst {\n size: resolvedSize,\n compact: resolvedCompact,\n disabled: resolvedDisabled,\n} = useControlShape(props, group)\n\n// The whole \"field plus panel\" shell, shared with VDateInput. What is specific to this\n// component is only what happens around it: preparing the draft as the panel opens, and\n// clearing the announcement as it closes.\nconst {\n open,\n panelId,\n popupAttrs,\n closeAndFocus,\n focusField,\n onFieldFocus,\n toggleFromIcon,\n onControlClick,\n onFocusout,\n onKeydown,\n onPanelMousedown,\n} = useFieldPanel({\n rootEl,\n panelRef,\n field: inputRef,\n // With no panel there is nothing to open. This is the composable's SINGLE cut-off point, and\n // every way in passes through it; clicking the field, focusing it, the down arrow, Enter, the\n // icon.\n disabled: () => resolvedDisabled.value || !hasPanel.value,\n focusInPanel,\n // Beside a field one types into, the panel opens on focus WITHOUT taking it, so typing\n // carries on.\n openOnFocus: () => typing.value,\n onOpen: () => {\n const parts = modelParts.value\n if (parts) pickerDraft.value = formatTime(parts.hour, parts.minute)\n // A clock opened BESIDE a field one types into starts empty: it opened because the\n // field took the focus, not because a time was asked for, and a draft set to the\n // current time would hand its half of the day to the digits about to be typed, so\n // \"0930\" typed in the afternoon would become 21:30.\n else if (typing.value) pickerDraft.value = null\n else {\n // Reading the clock is safe here: this runs from a handler, hence in a browser, never\n // during a render; and it is why the picker itself never reads it, so that it stays\n // identical on both sides of hydration. The instant is taken once: two reads could\n // straddle a minute.\n const now = new Date()\n const wanted = snapMinute(now.getMinutes(), props.minuteStep)\n // Pulled to the nearest time the restrictions allow, so that the panel never opens\n // on one the reader would be refused. Null is a set of restrictions that allows\n // nothing at all, which the warning above has already reported.\n pickerDraft.value =\n nearestAllowedTime(now.getHours(), wanted, limits.value) ??\n formatTime(now.getHours(), wanted)\n }\n pickerRef.value?.reset()\n },\n onClose: () => {\n pickerRef.value?.reset()\n },\n})\n\n// Escape and the focus leaving both close without committing, as Cancel does: only OK writes\n// the value.\n\n/** OK: the ONE route by which the picker's draft becomes the value. */\nfunction confirm() {\n if (pickerDraft.value) model.value = pickerDraft.value\n closeAndFocus()\n}\n\n/** Cancel: the draft is simply dropped with the panel. */\nconst cancel = closeAndFocus\n\nconst inputAttrs = popupAttrs(\n () => fieldAttrs.value,\n () => hasPanel.value,\n)\n\n/*\n * Emptying the value, called by the field as it emits its clear event. The focus is taken\n * through `focusField`, whose note says why that stops the panel reopening.\n */\nfunction clearValue() {\n model.value = null\n // The typed text is emptied EXPLICITLY: when the value was already empty, nothing\n // changes, and the guard that keeps the field and the value from chasing each other\n // would leave the text where it was.\n if (typing.value) writeField('')\n focusField()\n emit('clear')\n}\n\nconst m = useMessages()\n\n// @core\n/**\n * Saying no to a time that is not one the restrictions allow. It is written unconditionally\n * rather than only while typing: the browser BARS a read-only control from constraint\n * validation, so the read-only form settles it without a branch here.\n */\nwatchEffect(\n () => {\n const el = fieldEl.value\n if (!el) return\n const own = m.value.timeInput.unavailable\n if (!valueAllowed.value) el.setCustomValidity(own)\n // Only the component's own message is taken back: a consumer's verdict written over it,\n // a server saying the slot is taken, stays until the consumer clears it.\n else if (el.validationMessage === own) el.setCustomValidity('')\n },\n { flush: 'post' },\n)\n\n/*\n * A time always has the HH:MM shape, so the caret is computed outright where a date has to look\n * for a separator it cannot predict, and a final commit is no different from a live one: there\n * is no two-digit year to expand.\n */\nconst {\n draft,\n fieldModel,\n writeField,\n commitOrRevert,\n onFieldInput,\n onKeydown: onMaskKeydown,\n onPaste,\n} = useMaskedField({\n fieldEl,\n typing: () => typing.value,\n displayText: () => displayText.value,\n readValue: () => model.value,\n writeValue: (time) => {\n model.value = time\n // With the clock open beside the field, it follows what is typed: otherwise OK would\n // write the clock's time back over the one the reader has just typed.\n if (open.value && hasPanel.value) pickerDraft.value = time\n },\n maxDigits: () => 4,\n format: formatTimeMask,\n caret: (_text, digitsBefore, inserting) => timeCaret(digitsBefore, inserting),\n parse: (text) => parseTimeMask(text, resolvedFormat.value, currentMeridiem.value),\n toMask: (time) => timeToMask(time, resolvedFormat.value),\n})\n\n// @keyboard\n// What the mask's keys mean for a time: typing anything that is not a digit,\n// the separator included so that \"9:30\" can be typed exactly as it reads, completes the\n// hour with a leading zero and moves on to the minutes; the down arrow is the one explicit\n// way from the field into the picker.\nfunction onFieldKeydown(event: KeyboardEvent) {\n onMaskKeydown(event, {\n onSeparator: (el) => {\n const digits = digitsOf(el.value)\n if (digits.length === 1) writeField(formatTimeMask(`0${digits}`), 3)\n },\n onArrowDown: () => {\n if (!open.value) return false\n focusInPanel()\n return true\n },\n onEnter: closeAndFocus,\n })\n}\n\n/** Pasting. */\nfunction onFieldPaste(event: ClipboardEvent) {\n onPaste(event, (pasted) =>\n isValidTime(pasted)\n ? pasted\n : readWrittenTime(pasted, resolvedFormat.value, currentMeridiem.value),\n )\n}\n\n/*\n * The list form must not reach these two at all, and being panel-less is not enough to keep it\n * out. `useFieldPanel.onKeydown` cancels the default on Enter before asking whether a panel can\n * open, so an Enter travelling up from the VCombobox would stop a surrounding form from being\n * submitted with nothing to show for it.\n */\nfunction onRootKeydown(event: KeyboardEvent) {\n if (!isList.value) onKeydown(event)\n}\n\nfunction onRootFocusout(event: FocusEvent) {\n if (!isList.value) onFocusout(event)\n}\n\n/*\n * The list form is a VCombobox, given what a combobox cannot know about times: which rows to\n * offer, how a search matches one, and a value that is a nullable string where a combobox's is a\n * plain one.\n */\n\n/**\n * The rows the step and the restrictions offer, which the value does not enter into: a\n * change of value must not format up to 1440 times again.\n */\nconst baseOptions = computed<TimeOption[]>(() => {\n if (!isList.value) return []\n return timeList(props.minuteStep, resolvedLocale.value, resolvedFormat.value).filter((row) => {\n // A list is read before it is chosen from, so a time it may not take has no reason to be in\n // it, which is the picker's own rule for its numerals.\n const parts = parseTime(row.value)\n return !parts || isTimeAllowed(parts.hour, parts.minute, limits.value)\n })\n})\n\n/**\n * The rows on offer. Without it the field would fall back to showing the raw canonical string;\n * \"09:07\" in a field that spells every other row \"9:07 AM\"; since a combobox labels a value\n * through the option carrying it.\n */\nconst options = computed<TimeOption[]>(() => {\n const rows = baseOptions.value\n const current = model.value\n if (!isValidTime(current) || rows.some((row) => row.value === current)) return rows\n const at = rows.findIndex((row) => row.value > current)\n const extra: TimeOption = {\n value: current,\n label: formatTimeDisplay(current, resolvedLocale.value, resolvedFormat.value),\n }\n return at < 0 ? [...rows, extra] : [...rows.slice(0, at), extra, ...rows.slice(at)]\n})\n\n/**\n * The search, which accepts the digit run as well as the words; \"930\" finds 9:30, the way the\n * typed form's mask reads what it is given.\n */\nconst matchTime = (option: ListboxOption, query: string) =>\n timeMatches(option.label, String(option.value), query)\n\n/**\n * The bridge to VCombobox, whose value is a plain string where this component's is\n * nullable: an emptied combobox says so with an empty string, which is not a time.\n */\nconst listModel = computed<string | string[]>({\n get: () => model.value ?? '',\n set: (value) => {\n model.value = typeof value === 'string' && value ? value : null\n },\n})\n\n/*\n * Whether the cross is shown has to be answered explicitly here: outside the typed mode the\n * field is read-only and would hide it, while the value comes from the panel. The `readonly`\n * PROP is the one case where that default answer was right: frozen, the field offers no route\n * to a new value, so it offers no route to none either.\n */\nconst clearVisible = computed(() =>\n canClear(props, resolvedDisabled.value, hasValue.value || (typing.value && !!draft.value)),\n)\nconst endIcon = computed<IconSource | undefined>(() =>\n hasPanel.value ? props.pickerIcon : undefined,\n)\n// @a11y @devwarn\n// Defined even when no icon is rendered: VInput warns at setup about a clickable icon without a\n// label, and cannot know whether the icon exists.\nconst endIconLabel = computed(() => props.pickerLabel ?? m.value.timeInput.openPicker)\nconst resolvedClearLabel = computed(() => props.clearLabel ?? m.value.timeInput.clear)\n\n/*\n * The three of them go through whichever field is on screen: in the list form there is no\n * VInput of this component's own, the VCombobox owning the one the reader sees. Exposing\n * the same trio as the other fields is what keeps a consumer's `ref` working whatever the\n * mode, which is a runtime prop they may well be binding.\n */\nconst field = computed(() => (isList.value ? listRef.value : inputRef.value))\n\ndefineExpose({\n /** Moves the focus to the text field. */\n focus: (options?: FocusOptions) => field.value?.focus(options),\n /** Selects what the field is showing. */\n select: () => field.value?.select(),\n /** The real `<input>` behind the field, for what neither of the two above covers. */\n el: computed(() => field.value?.el ?? null),\n})\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n class=\"v-time-input\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n :data-open=\"open ? '' : undefined\"\n :data-mode=\"resolvedMode\"\n :data-disabled=\"resolvedDisabled ? '' : undefined\"\n :data-readonly=\"readonly ? '' : undefined\"\n @focusout=\"onRootFocusout\"\n @keydown=\"onRootKeydown\"\n >\n <!--\n All this component adds is the three things a combobox cannot know about times; the rows,\n the search, and a value that may be nothing.\n -->\n <VCombobox\n v-if=\"isList\"\n ref=\"listRef\"\n v-model=\"listModel\"\n v-bind=\"fieldAttrs\"\n :options=\"options\"\n :filter=\"matchTime\"\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 :readonly=\"readonly\"\n :invalid=\"invalid\"\n :icon-start=\"iconStart\"\n :icon-start-label=\"iconStartLabel\"\n :clearable=\"clearable\"\n :clear-label=\"clearLabel\"\n :loading=\"loading\"\n :loading-text=\"loadingText\"\n :placement=\"placement\"\n @clear=\"emit('clear')\"\n />\n\n <div v-else class=\"v-time-input-control\" @click=\"onControlClick\">\n <!--\n The field is declared a combobox rather than left as the plain text box it implicitly\n is, because a text box may not carry the attribute saying whether something is expanded;\n the same reasoning as in VDateInput.\n -->\n <VInput\n ref=\"inputRef\"\n v-model=\"fieldModel\"\n :inputmode=\"typing ? 'numeric' : undefined\"\n :autocomplete=\"typing ? 'off' : undefined\"\n v-bind=\"inputAttrs\"\n :readonly=\"readonly\"\n :no-typing=\"!typing\"\n :label=\"label\"\n :hint=\"hint\"\n :error=\"error\"\n :required=\"required\"\n :hide-label=\"hideLabel\"\n :label-position=\"labelPosition\"\n :placeholder=\"placeholder ?? (typing ? m.timeInput.maskPlaceholder : undefined)\"\n :size=\"resolvedSize\"\n :compact=\"resolvedCompact\"\n :disabled=\"resolvedDisabled\"\n :invalid=\"invalid\"\n :clearable=\"clearable\"\n :clear-visible=\"clearVisible\"\n :clear-label=\"resolvedClearLabel\"\n :icon-start=\"iconStart\"\n :icon-start-label=\"iconStartLabel\"\n :loading=\"loading\"\n :loading-text=\"loadingText\"\n :icon-end=\"endIcon\"\n :icon-end-label=\"endIconLabel\"\n @click:icon-end=\"toggleFromIcon\"\n @clear=\"clearValue\"\n @focus=\"onFieldFocus\"\n @input=\"onFieldInput\"\n @change=\"commitOrRevert\"\n @blur=\"commitOrRevert\"\n @keydown=\"onFieldKeydown\"\n @paste=\"onFieldPaste\"\n >\n <!--\n It is dropped when nothing follows it, or it would trail off the end of an otherwise\n bare field.\n -->\n <template v-if=\"$slots.start\" #start><slot name=\"start\" /></template>\n <template v-if=\"hasMeridiem || $slots['value-end']\" #value-end>\n <button\n v-if=\"hasMeridiem\"\n type=\"button\"\n class=\"v-input-action v-field-action v-time-input-meridiem\"\n :disabled=\"resolvedDisabled\"\n :aria-label=\"\n m.timeInput.meridiemValue(\n currentMeridiem === 'PM' ? m.timePicker.pm : m.timePicker.am,\n )\n \"\n @click=\"toggleMeridiem\"\n >\n {{ currentMeridiem === 'PM' ? m.timePicker.pm : m.timePicker.am }}\n </button>\n <VSeparator\n v-if=\"hasMeridiem && (clearVisible || endIcon)\"\n orientation=\"vertical\"\n class=\"v-time-input-divider\"\n />\n <slot name=\"value-end\" />\n </template>\n </VInput>\n </div>\n\n <!-- The absence of a panel is therefore self-enforcing. -->\n <VPopover\n v-if=\"hasPanel\"\n :id=\"panelId\"\n ref=\"panelRef\"\n v-model:open=\"open\"\n mode=\"manual\"\n anchor=\"--time-input-anchor\"\n :placement=\"placement\"\n role=\"dialog\"\n class=\"v-time-input-panel\"\n :aria-label=\"label ?? m.timeInput.pickerLabel\"\n @mousedown=\"onPanelMousedown\"\n >\n <!--\n The picker works on the DRAFT and not on the value, which makes OK the only way in:\n Cancel, Escape and the focus leaving simply drop it. The two actions are handed to its\n own footer slot, since they belong to this panel rather than to a clock shown on its\n own.\n -->\n <VTimePicker\n ref=\"pickerRef\"\n v-model=\"pickerDraft\"\n :format=\"resolvedFormat\"\n :locale=\"resolvedLocale\"\n :minute-step=\"minuteStep\"\n :min=\"min\"\n :max=\"max\"\n :allowed-hours=\"allowedHours\"\n :allowed-minutes=\"allowedMinutes\"\n @confirm=\"confirm\"\n >\n <!--\n The slot REPLACES the two buttons rather than sitting beside them, so it is handed\n both actions: the clock writes a draft, and without `confirm` a footer of one's own\n could never commit it; the value would become unchangeable through the panel. The\n `remove` of VCombobox's `#chip` slot exists for the same reason.\n -->\n <template #footer>\n <slot name=\"footer\" :confirm=\"confirm\" :cancel=\"cancel\" :close=\"cancel\">\n <VButton variant=\"ghost\" tone=\"neutral\" @click=\"cancel\">{{ m.common.cancel }}</VButton>\n <VButton @click=\"confirm\">{{ m.common.confirm }}</VButton>\n </slot>\n </template>\n </VTimePicker>\n </VPopover>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-time-input {\n /* Confines the anchor name to this instance. It is declared on the root because that\n is the common ancestor of the field and the panel. */\n anchor-scope: --time-input-anchor;\n display: block;\n width: 100%;\n font-family: var(--vectis-text-family);\n }\n\n .v-time-input:not([data-disabled]):not([data-readonly]) .v-time-input-control {\n cursor: pointer;\n }\n\n /*\n * Anchor through the clock/input wrapper only; list mode belongs to VCombobox and must retain\n * its own anchor independently of stylesheet order.\n */\n .v-time-input-control .v-input-field {\n anchor-name: --time-input-anchor;\n }\n\n /* A field one types into shows the text cursor rather than the pointer a clickable\n control shows. Its figures are also given equal widths: with proportional ones the\n text shifts as digits are typed, and the caret appears to jitter. */\n .v-time-input[data-mode='input']:not([data-disabled]):not([data-readonly]) .v-time-input-control {\n cursor: text;\n }\n\n .v-time-input[data-mode='input'] .v-input-control {\n font-variant-numeric: tabular-nums;\n }\n\n /*\n * Give the AM/PM action room for text while preserving the neighbouring icon buttons' minimum\n * hit target.\n */\n .v-field-action.v-time-input-meridiem {\n inline-size: auto;\n min-inline-size: var(--control-action-size);\n padding-inline: calc((var(--control-action-size) - var(--vectis-icon-size)) / 2);\n font: inherit;\n }\n\n /*\n * The rule between the AM/PM button and the field's own controls, which keeps a word from\n * reading as a third icon in the row. A vertical VSeparator stretches to its flex line by\n * contract, which here is the full height of the field: it would meet the border at both ends\n * and read as a division of the box rather than a break in the row.\n */\n .v-separator[data-orientation='vertical'].v-time-input-divider {\n align-self: center;\n block-size: var(--control-action-size);\n }\n\n /*\n * No `display` here either: the column layout is `.v-panel`'s, and a panel declaring its own\n * is what the doubled-class `.v-overlay` guard exists to overrule. The selector compounds two\n * classes VPopover puts on the same element, because the padding is also declared by the\n * shared panel class: at equal specificity the winner would be whichever sheet the consumer's\n * bundler put last.\n */\n .v-popover-panel.v-time-input-panel {\n width: max-content;\n padding: 0;\n }\n\n /*\n * The list form is a VCombobox and takes everything from it; this is the one declaration that\n * does not belong to a combobox in general. A column of times read down the panel is a column\n * of FIGURES, and proportional ones slide the colon from row to row.\n */\n .v-time-input[data-mode='list'] .v-combobox-panel {\n font-variant-numeric: tabular-nums;\n }\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -69,11 +69,11 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_PublicProps, {
|
|
|
69
69
|
"onUpdate:modelValue"?: ((value: string | null) => any) | undefined;
|
|
70
70
|
}>, {
|
|
71
71
|
label: string;
|
|
72
|
+
format: TimePickerFormat;
|
|
72
73
|
disabled: boolean;
|
|
73
74
|
readonly: boolean;
|
|
74
|
-
max: string;
|
|
75
|
-
format: TimePickerFormat;
|
|
76
75
|
min: string;
|
|
76
|
+
max: string;
|
|
77
77
|
locale: string;
|
|
78
78
|
minuteStep: number;
|
|
79
79
|
allowedHours: TimePickerAllowed;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VTimePicker.js","names":[],"sources":["../../../src/components/VTimePicker/VTimePicker.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @keyboard @core\n/**\n * CSS places clock numerals and the hand. JavaScript maps pointer angles and keyboard steps to\n * a time because HTML has no interactive clock-face primitive.\n */\n\nimport { computed, inject, ref, watchEffect } from 'vue'\n\nimport VButton from '../VButton/VButton.vue'\nimport VToggle from '../VToggle/VToggle.vue'\nimport type { ToggleModelValue } from '../VToggle/VToggle.vue'\nimport VToggleItem from '../VToggle/VToggleItem.vue'\nimport { formatTime, hourCycleFor, parseTime } from '../../utils/time'\nimport {\n DIAL_DEAD_ZONE,\n DIAL_INNER_THRESHOLD,\n angleToIndex,\n dialIndexToHour24,\n distanceFraction,\n hour24ToDial,\n hourWithMeridiem,\n snapMinute,\n to12h,\n to24h,\n} from '../../utils/clock'\nimport type { HourFormat, Meridiem } from '../../utils/time'\nimport {\n allowedMinutesFor,\n firstAllowed,\n isHourAllowed,\n isTimeAllowed,\n limitsProblem,\n minuteGrid,\n minuteInterval,\n nearestAllowedMinute,\n resolveLimits,\n} from './limits'\nimport type { TimePickerAllowed } from './limits'\nimport { pad2 } from '../../utils/text'\nimport { isDev } from '../../utils/env'\nimport { hostWarnsKey } from '../../utils/hostWarns'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useMessages, useResolvedLocale } from '../../i18n/state'\n\n/** Whether the face shows a 12- or a 24-hour clock. */\nexport type TimePickerFormat = HourFormat\n\n/** Which of the two halves of a time is being adjusted. */\ntype TimePickerStep = 'hour' | 'minute'\n\ninterface TimePickerProps {\n /**\n * Whether the face shows a 12- or a 24-hour clock. Left out, the reader's language\n * decides, which is almost always what one wants.\n */\n format?: TimePickerFormat\n /**\n * A BCP 47 locale, which decides the clock. It TAKES PRECEDENCE over the design\n * system's global locale and falls back to it, which is why it has no literal default:\n * `undefined` has to stay recognizable for the global locale to have its chance.\n */\n locale?: string\n /**\n * The interval the minutes snap to, both when dragging and with the arrow keys. The face\n * prints only the minutes it can reach, so a step of a quarter of an hour marks four.\n */\n minuteStep?: number\n /** The earliest time that can be chosen, inclusive, as a canonical 24-hour `'HH:mm'`. */\n min?: string\n /** The latest time that can be chosen, inclusive, written like `min`. */\n max?: string\n /** Which hours can be chosen: the list of them, or a rule answering for one. */\n allowedHours?: TimePickerAllowed\n /**\n * Which minutes can be chosen: the list of them, or a rule answering for one. The\n * minutes it leaves out are not printed.\n */\n allowedMinutes?: TimePickerAllowed\n /**\n * Makes the whole clock unusable: the hand cannot be moved, the half-day cannot be\n * changed, and everything greys out through the colour tokens.\n */\n disabled?: boolean\n /** Shows the time without letting it be changed. */\n readonly?: boolean\n /**\n * The accessible name of the whole clock, its two numerals and its face together. It falls\n * back to the dictionary, and a consumer `aria-label` wins over it.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<TimePickerProps>(), {\n format: undefined,\n locale: undefined,\n minuteStep: 1,\n min: undefined,\n max: undefined,\n allowedHours: undefined,\n allowedMinutes: undefined,\n disabled: false,\n readonly: false,\n label: undefined,\n})\n\n/** The time, always as a 24-hour \"HH:mm\" string whatever clock is displayed. */\nconst model = defineModel<string | null>({ default: null })\n\nconst emit = defineEmits<{\n /**\n * The reader has FINISHED, carrying the time as it stands: the minutes were settled from the\n * keyboard.\n */\n confirm: [value: string | null]\n}>()\n\ndefineSlots<{\n /** A strip at the foot of the clock, the place for actions such as Cancel and OK. */\n footer?(): unknown\n}>()\n\nconst m = useMessages()\n// @a11y\n// A roleless box cannot carry an accessible name (axe: aria-prohibited-attr),\n// so the root is a named group: the VCarousel viewport arrangement.\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.timePicker.label)\nconst resolvedLocale = useResolvedLocale(() => props.locale)\nconst resolvedFormat = computed<TimePickerFormat>(\n () => props.format ?? hourCycleFor(resolvedLocale.value),\n)\n\n/** The restrictions, resolved once for the whole render. */\nconst limits = computed(() => resolveLimits(props))\n\n/** The hours the restrictions still leave something in, worked out once per set of restrictions. */\nconst availableHours = computed(() => {\n const hours = new Set<number>()\n for (let candidate = 0; candidate < 24; candidate += 1)\n if (isHourAllowed(candidate, limits.value)) hours.add(candidate)\n return hours\n})\n\n/** Whether an hour is one the restrictions still leave something in. */\nconst isAvailableHour = (candidate: number) => availableHours.value.has(candidate)\n\n// @devwarn\nif (isDev && !inject(hostWarnsKey, false)) {\n watchEffect(() => {\n if (!Number.isInteger(props.minuteStep) || props.minuteStep < 1 || 60 % props.minuteStep !== 0)\n console.warn(`[VTimePicker] minuteStep ${props.minuteStep} — a divisor of 60 is expected.`)\n const problem = limitsProblem(limits.value)\n if (problem) console.warn(`[VTimePicker] ${problem}`)\n })\n}\n\n/** Which of the two is being adjusted. It starts on the hour and moves on by itself. */\nconst step = ref<TimePickerStep>('hour')\n\n// With no value the clock shows the first hour of the half of the day chosen so far, which\n// is midnight until PM is picked. It is also the hour a time started from the MINUTES is\n// written with, so that choice is not lost when the reader begins there.\nconst parts = computed(\n () =>\n parseTime(model.value) ?? {\n hour: resolvedFormat.value === '12h' ? to24h(12, pendingMeridiem.value) : 0,\n minute: 0,\n },\n)\nconst hour = computed(() => parts.value.hour)\nconst minute = computed(() => parts.value.minute)\n\n/*\n * The two writers are the SINGLE cut-off point for the restrictions: the pointer, the six\n * keys and the AM/PM control all write through them and through nothing else, so not one\n * handler carries a guard of its own.\n */\n\n/**\n * Writing the hour. An hour with nothing left in it has no minute to fall back on, which\n * refuses it; one that is only PARTLY available pulls the minutes to what it does allow, since\n * `min: '09:30'` must not be left holding 09:00.\n */\nfunction setHour(value: number | null) {\n // Changing which face is shown stays allowed under `readonly`: reading the minutes is not\n // changing them.\n if (props.disabled || props.readonly) return\n if (value === null) return\n const minutes = isTimeAllowed(value, minute.value, limits.value)\n ? minute.value\n : nearestAllowedMinute(value, minute.value, limits.value)\n if (minutes === null) return\n model.value = formatTime(value, minutes)\n}\n\n/** Writing the minutes. Null is \"there was nowhere to go\", which every key can produce. */\nfunction setMinute(value: number | null) {\n if (props.disabled || props.readonly) return\n if (value === null || !isTimeAllowed(hour.value, value, limits.value)) return\n model.value = formatTime(hour.value, value)\n}\n\n// @a11y\n/*\n * Which step the clock is on is otherwise carried by the face's own name alone, and a change of\n * name is not reliably announced. A politely announced region says it a second time.\n */\nconst liveMessage = ref('')\n\nfunction setStep(next: TimePickerStep) {\n step.value = next\n liveMessage.value =\n next === 'minute' ? m.value.timePicker.choosingMinutes : m.value.timePicker.choosingHour\n}\n\n/** One numeral on the face. */\ninterface DialCell {\n key: string\n /** What is printed. */\n label: string\n /** Where it sits, as a fraction of a full turn from twelve o'clock. */\n turn: number\n /** Which of the two circles it belongs to, a 24-hour face having an inner one. */\n ring: 'outer' | 'inner'\n /** Whether it is the value currently being pointed at. */\n selected: boolean\n}\n\n/** The hour a position on the face stands for. */\nfunction hourAt(index: number, ring: 'outer' | 'inner'): number {\n return resolvedFormat.value === '24h'\n ? dialIndexToHour24(index, ring)\n : to24h(index === 0 ? 12 : index, currentMeridiem.value)\n}\n\n/** The minutes the face prints. */\nconst minuteMarks = computed(() => {\n const interval = minuteInterval(props.minuteStep)\n const spacing = 60 / interval <= 12 ? interval : 5\n const marks: number[] = []\n for (let minutes = 0; minutes < 60; minutes += spacing)\n if (minutes % interval === 0 && isTimeAllowed(hour.value, minutes, limits.value))\n marks.push(minutes)\n return marks\n})\n\n/*\n * A numeral that is only there to be refused says nothing the missing numeral does not say\n * better, and it says it in grey text a screen reader has no way to reach: the markers are\n * `aria-hidden`, so what the restrictions leave is spoken by the face's own value and by\n * nothing else.\n */\nconst cells = computed<DialCell[]>(() => {\n if (step.value === 'minute') {\n return minuteMarks.value.map((minutes) => ({\n key: `m-${minutes}`,\n label: pad2(minutes),\n turn: minutes / 60,\n ring: 'outer' as const,\n selected: minute.value === minutes,\n }))\n }\n // Each position's hour is worked out once, since on a 12-hour face that takes the half of the\n // day in force. The selection there still compares numerals and not hours: an empty clock\n // holding a PM choice sits on midnight, and its hand points at the 12 all the same.\n const twelve = resolvedFormat.value === '12h'\n const shown12 = to12h(hour.value).hour\n const rings: DialCell['ring'][] = twelve ? ['outer'] : ['outer', 'inner']\n const out: DialCell[] = []\n for (const ring of rings)\n for (let i = 0; i < 12; i += 1) {\n const target = hourAt(i, ring)\n if (!isAvailableHour(target)) continue\n const numeral = i === 0 ? 12 : i\n out.push({\n key: `${ring === 'outer' ? 'o' : 'i'}-${i}`,\n label: ring === 'inner' ? pad2(target) : String(numeral),\n turn: i / 12,\n ring,\n selected: twelve ? shown12 === numeral : hour.value === target,\n })\n }\n return out\n})\n\nconst handTurn = computed(() => {\n if (step.value === 'minute') return minute.value / 60\n const index =\n resolvedFormat.value === '24h' ? hour24ToDial(hour.value).index : to12h(hour.value).hour % 12\n return index / 12\n})\n\nconst handRing = computed(() =>\n step.value === 'hour' && resolvedFormat.value === '24h' ? hour24ToDial(hour.value).ring : 'outer',\n)\n\n/**\n * Whether the hand points at a minute that has no marker of its own. The tip is then\n * drawn small: at full size it would cover the two neighbouring markers and read as\n * pointing at neither.\n */\nconst handMinor = computed(\n () => step.value === 'minute' && !minuteMarks.value.includes(minute.value),\n)\n\n/** The hour as the two large numerals show it, on whichever clock is displayed. */\nconst displayHourText = computed(() =>\n pad2(resolvedFormat.value === '12h' ? to12h(hour.value).hour : hour.value),\n)\n\n/** Which half of the day is chosen while no time is set at all. */\nconst pendingMeridiem = ref<Meridiem>('AM')\n\n/**\n * The half of the day in force: the value's own once there is one, the remembered choice until\n * then.\n */\nconst currentMeridiem = computed<Meridiem>(() =>\n model.value ? to12h(hour.value).meridiem : pendingMeridiem.value,\n)\n\nconst meridiemModel = computed<ToggleModelValue>({\n get: () => currentMeridiem.value,\n set: (value) => {\n const meridiem: Meridiem = value === 'PM' ? 'PM' : 'AM'\n pendingMeridiem.value = meridiem\n // With no time set there is nothing to convert, so the choice is simply REMEMBERED\n // and applies to the first time chosen.\n if (!model.value) return\n // What is being chosen here is the HALF OF THE DAY, so the hour gives way to it: kept\n // when the new half allows it, and otherwise the first hour of that half that does.\n // Refusing the write instead would leave the control snapping straight back, since it\n // reads the half of the day off the value.\n const wanted = hourWithMeridiem(hour.value, meridiem)\n setHour(\n isAvailableHour(wanted)\n ? wanted\n : firstAllowed(12, (i) => to24h(i, meridiem), isAvailableHour),\n )\n },\n})\n\n/**\n * Whether each half of the day still holds an hour one could choose. An empty one takes its\n * button with it: a control that can only ever be refused is worse than no control.\n */\nconst meridiemAvailable = computed<Record<Meridiem, boolean>>(() => ({\n AM: firstAllowed(12, (i) => to24h(i, 'AM'), isAvailableHour) !== null,\n PM: firstAllowed(12, (i) => to24h(i, 'PM'), isAvailableHour) !== null,\n}))\n\n// @a11y\n// The entire spoken value of the face. The numerals are hidden from screen readers, so these\n// four attributes are the only thing assistive technology has: what the value is, what its\n// bounds are, and how to say it.\nconst ariaValueNow = computed(() => {\n if (step.value === 'minute') return minute.value\n return resolvedFormat.value === '12h' ? to12h(hour.value).hour : hour.value\n})\nconst ariaValueMin = computed(() =>\n step.value === 'minute' ? 0 : resolvedFormat.value === '12h' ? 1 : 0,\n)\nconst ariaValueMax = computed(() =>\n step.value === 'minute' ? 59 : resolvedFormat.value === '12h' ? 12 : 23,\n)\n// On a 12-hour face the half of the day is spoken with the hour: the AM/PM control is not\n// on the slider's way, and \"9 o'clock\" alone is two different times.\nconst ariaValueText = computed(() => {\n const m_ = m.value.timePicker\n if (step.value === 'minute') return m_.minutesValue(minute.value)\n const spoken = m_.hourValue(ariaValueNow.value)\n if (resolvedFormat.value === '24h') return spoken\n return `${spoken} ${currentMeridiem.value === 'PM' ? m_.pm : m_.am}`\n})\n\n/**\n * A step has been settled: after the hour come the minutes, and after the minutes the whole\n * thing is confirmed; but by KEYBOARD only, for the reason given on the emit.\n */\nfunction settleStep(via: 'pointer' | 'keyboard') {\n if (step.value === 'hour') setStep('minute')\n else if (via === 'keyboard') emit('confirm', model.value)\n}\n\nconst faceEl = ref<HTMLElement | null>(null)\nconst dragging = ref(false)\n/** Whether anything in the gesture under way landed on a numeral. */\nlet landed = false\n\n/**\n * Where a point on the face lands, and whether there was anything there at all. The write\n * itself still goes through `setHour`/ `setMinute` and their own guards, which is why\n * `readonly` is refused THERE and not here: a frozen clock is still one whose numerals are\n * real, and a tap on one moves the step on as it always did.\n */\nfunction applyPoint(event: PointerEvent): boolean {\n const face = faceEl.value\n // A disabled clock aims at nothing, so no step is moved on either: its header buttons\n // are disabled for the same reason.\n if (!face || props.disabled) return false\n // Measuring the face is safe here: this runs from a handler, hence in a browser, never\n // during a render.\n const rect = face.getBoundingClientRect()\n const dx = event.clientX - (rect.left + rect.width / 2)\n const dy = event.clientY - (rect.top + rect.height / 2)\n if (Math.hypot(dx, dy) <= DIAL_DEAD_ZONE * (rect.width / 2)) return false\n if (step.value === 'minute') {\n // The step's own snapping stands: a point between two markers has always been pulled\n // to the one it is nearest. What it is pulled to still has to BE on the face.\n const minutes = snapMinute(angleToIndex(dx, dy, 60), props.minuteStep)\n if (!isTimeAllowed(hour.value, minutes, limits.value)) return false\n setMinute(minutes)\n return true\n }\n const index = angleToIndex(dx, dy, 12)\n const ring =\n resolvedFormat.value === '24h' &&\n distanceFraction(dx, dy, rect.width / 2) < DIAL_INNER_THRESHOLD\n ? 'inner'\n : 'outer'\n const target = hourAt(index, ring)\n if (!isAvailableHour(target)) return false\n setHour(target)\n return true\n}\n\nfunction onPointerdown(event: PointerEvent) {\n // @fallback\n // Capture keeps dragging active outside the clock face; synthetic events may not reference a\n // capturable pointer.\n try {\n faceEl.value?.setPointerCapture(event.pointerId)\n } catch {\n /* Synthetic pointers cannot be captured. */\n }\n dragging.value = true\n landed = applyPoint(event)\n}\n\nfunction onPointermove(event: PointerEvent) {\n if (dragging.value && applyPoint(event)) landed = true\n}\n\nfunction onPointerup() {\n if (!dragging.value) return\n dragging.value = false\n // A gesture that never landed on a numeral has chosen nothing, so there is nothing to\n // move on FROM: the reader is left on the face they aimed at, looking at what they missed.\n if (landed) settleStep('pointer')\n}\n\nfunction onPointercancel() {\n dragging.value = false\n}\n\n// @keyboard @a11y\n// The keyboard a slider is expected to have.\n/*\n * Every key below walks until it finds something it MAY land on, rather than stopping at the\n * first thing it may not: with scattered hours a key that stopped would die at the first hole\n * and never reach what lies past it. Against a bound there is nothing past the hole, so the\n * same walk comes back empty and the key holds still, which a bound means.\n */\nfunction moveHour(delta: number) {\n if (resolvedFormat.value === '24h') {\n setHour(firstAllowed(23, (i) => (hour.value + delta * i + 24) % 24, isAvailableHour))\n return\n }\n // On a 12-hour face the hours cycle from 1 to 12 WITHIN the current half of the day:\n // passing midday is done on the AM/PM control, not by walking the hand past twelve.\n const hour12 = to12h(hour.value).hour\n setHour(\n firstAllowed(\n 11,\n (i) => to24h(((hour12 - 1 + delta * i + 12) % 12) + 1, currentMeridiem.value),\n isAvailableHour,\n ),\n )\n}\n\n/** The first or the last hour the face allows, on whichever clock it is showing. */\nfunction edgeHour(edge: 'first' | 'last'): number | null {\n if (resolvedFormat.value === '24h')\n return firstAllowed(24, (i) => (edge === 'first' ? i - 1 : 24 - i), isAvailableHour)\n return firstAllowed(\n 12,\n (i) => to24h(edge === 'first' ? i : 13 - i, currentMeridiem.value),\n isAvailableHour,\n )\n}\n\n/**\n * The minute a key lands on: the first step of the grid at least `distance` minutes away, that\n * way round, and then the next one the hour allows.\n */\nfunction minuteToward(direction: 1 | -1, distance: number): number | null {\n const grid = minuteGrid(props.minuteStep)\n const target = (((minute.value + direction * distance) % 60) + 60) % 60\n const n = grid.length\n let start = direction > 0 ? 0 : n - 1\n for (let k = 0; k < n; k += 1) {\n const i = direction > 0 ? k : n - 1 - k\n if (direction > 0 ? grid[i]! >= target : grid[i]! <= target) {\n start = i\n break\n }\n }\n return firstAllowed(\n n,\n (i) => grid[(((start + direction * (i - 1)) % n) + n) % n]!,\n (candidate) => isTimeAllowed(hour.value, candidate, limits.value),\n )\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n // The face of a disabled clock is out of the tab order, but `focus()` can still put the\n // focus there: nothing it is sent may move the step on or confirm.\n if (props.disabled) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n settleStep('keyboard')\n return\n }\n // The arrows are not flipped in a right-to-left page: a clock face is never mirrored;\n // clockwise means the same thing everywhere; so forward stays forward.\n const delta =\n event.key === 'ArrowUp' || event.key === 'ArrowRight'\n ? 1\n : event.key === 'ArrowDown' || event.key === 'ArrowLeft'\n ? -1\n : 0\n if (step.value === 'hour') {\n if (delta) moveHour(delta)\n else if (event.key === 'Home') setHour(edgeHour('first'))\n else if (event.key === 'End') setHour(edgeHour('last'))\n else return\n event.preventDefault()\n return\n }\n if (delta) setMinute(minuteToward(delta, 1))\n else if (event.key === 'PageUp') setMinute(minuteToward(1, 5))\n else if (event.key === 'PageDown') setMinute(minuteToward(-1, 5))\n // The list is built in these two branches alone: the arrows walk to a neighbour and never\n // need the whole of it.\n else if (event.key === 'Home') setMinute(allowedMinutesFor(hour.value, limits.value)[0] ?? null)\n else if (event.key === 'End')\n setMinute(allowedMinutesFor(hour.value, limits.value).at(-1) ?? null)\n else return\n event.preventDefault()\n}\n\ndefineExpose({\n /** Moves the focus onto the clock face, which the arrow keys drive. */\n focus: (options?: FocusOptions) => faceEl.value?.focus(options),\n /**\n * Puts the clock back on the hour and clears the announcement, WITHOUT announcing anything\n * itself.\n */\n reset: () => {\n step.value = 'hour'\n liveMessage.value = ''\n },\n})\n</script>\n\n<template>\n <div\n class=\"v-time-picker\"\n role=\"group\"\n :aria-label=\"ariaLabel\"\n :data-step=\"step\"\n :data-disabled=\"disabled ? '' : undefined\"\n :data-readonly=\"readonly ? '' : undefined\"\n >\n <!--\n The two large numerals switch between adjusting the hour and the minutes. The one being\n adjusted takes the accent tone, which on a quiet button shows as the colour of the numeral\n rather than as a filled background.\n -->\n <div class=\"v-time-picker-header\">\n <div class=\"v-time-picker-time\">\n <VButton\n class=\"v-time-picker-cell\"\n variant=\"ghost\"\n size=\"lg\"\n :tone=\"step === 'hour' ? 'accent' : 'neutral'\"\n :aria-pressed=\"step === 'hour' ? 'true' : 'false'\"\n :aria-label=\"`${displayHourText}, ${m.timePicker.selectHour}`\"\n :disabled=\"disabled\"\n @click=\"setStep('hour')\"\n >\n {{ displayHourText }}\n </VButton>\n <span class=\"v-time-picker-sep\" aria-hidden=\"true\">:</span>\n <VButton\n class=\"v-time-picker-cell\"\n variant=\"ghost\"\n size=\"lg\"\n :tone=\"step === 'minute' ? 'accent' : 'neutral'\"\n :aria-pressed=\"step === 'minute' ? 'true' : 'false'\"\n :aria-label=\"`${pad2(minute)}, ${m.timePicker.selectMinutes}`\"\n :disabled=\"disabled\"\n @click=\"setStep('minute')\"\n >\n {{ pad2(minute) }}\n </VButton>\n </div>\n\n <VToggle\n v-if=\"resolvedFormat === '12h'\"\n v-model=\"meridiemModel\"\n class=\"v-time-picker-meridiem\"\n mandatory\n variant=\"outline\"\n selected-variant=\"soft\"\n orientation=\"vertical\"\n size=\"sm\"\n :label=\"m.timePicker.meridiem\"\n :disabled=\"disabled || readonly\"\n >\n <VToggleItem value=\"AM\" :label=\"m.timePicker.am\" :disabled=\"!meridiemAvailable.AM\" />\n <VToggleItem value=\"PM\" :label=\"m.timePicker.pm\" :disabled=\"!meridiemAvailable.PM\" />\n </VToggle>\n </div>\n\n <div\n ref=\"faceEl\"\n role=\"slider\"\n :tabindex=\"disabled ? -1 : 0\"\n class=\"v-time-picker-face\"\n :aria-label=\"step === 'hour' ? m.timePicker.hour : m.timePicker.minutes\"\n :aria-valuemin=\"ariaValueMin\"\n :aria-valuemax=\"ariaValueMax\"\n :aria-valuenow=\"ariaValueNow\"\n :aria-valuetext=\"ariaValueText\"\n :data-dragging=\"dragging ? '' : undefined\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :aria-readonly=\"readonly ? 'true' : undefined\"\n @pointerdown=\"onPointerdown\"\n @pointermove=\"onPointermove\"\n @pointerup=\"onPointerup\"\n @pointercancel=\"onPointercancel\"\n @keydown=\"onKeydown\"\n >\n <div\n class=\"v-time-picker-hand\"\n aria-hidden=\"true\"\n :data-ring=\"handRing\"\n :data-minor=\"handMinor ? '' : undefined\"\n :style=\"{ '--dial-turn': String(handTurn) }\"\n />\n <span class=\"v-time-picker-center\" aria-hidden=\"true\" />\n <span\n v-for=\"cell in cells\"\n :key=\"cell.key\"\n class=\"v-time-picker-number\"\n aria-hidden=\"true\"\n :data-ring=\"cell.ring\"\n :data-selected=\"cell.selected ? '' : undefined\"\n :style=\"{ '--dial-turn': String(cell.turn) }\"\n >{{ cell.label }}</span\n >\n </div>\n\n <div class=\"v-visually-hidden\" aria-live=\"polite\">{{ liveMessage }}</div>\n\n <div v-if=\"$slots.footer\" class=\"v-time-picker-footer\"><slot name=\"footer\" /></div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /* The same root as VDatePicker's: an inline column that sizes to its content and pads\n itself, so the clock reads the same on a page as in VTimeInput's panel, which adds no\n padding of its own. The gap is the clock's, its parts being larger. */\n .v-time-picker {\n display: inline-flex;\n flex-direction: column;\n gap: var(--vectis-space-4);\n padding: var(--vectis-space-3);\n font-family: var(--vectis-text-family);\n color: var(--vectis-color-text);\n }\n\n /*\n * The numerals and the choice of half-day on one row, the second beside the first. The gap is\n * logical, so the control follows the reading direction; the numerals inside it stay in a\n * group of their own, which keeps the direction they force off it.\n */\n .v-time-picker-header {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--vectis-space-3);\n }\n\n /* A time written in figures always reads hours then minutes, in every language. Forcing\n the direction here is what stops bidirectional reordering from swapping the two in a\n right-to-left page. */\n .v-time-picker-time {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--vectis-space-2);\n direction: ltr;\n }\n\n /*\n * What is overridden here is the \"large numeral\" look alone; the width and the type; the\n * height, the states, the focus ring and the transitions all come from the button itself. The\n * type is the `heading-1` recipe taken whole, and the colon the `heading-2` one: read in\n * halves, a consumer repointing `--vectis-text-heading-1-weight` would move every heading but\n * these two numerals.\n */\n .v-time-picker-cell[data-size] {\n width: var(--control-height);\n font-size: var(--vectis-text-heading-1-size);\n font-weight: var(--vectis-text-heading-1-weight);\n line-height: var(--vectis-text-heading-1-leading);\n letter-spacing: var(--vectis-text-heading-1-tracking);\n /* Figures of equal width, so that going from \"11\" to \"00\" does not shift the cell. */\n font-variant-numeric: tabular-nums;\n }\n\n .v-time-picker-sep {\n font-size: var(--vectis-text-heading-2-size);\n font-weight: var(--vectis-text-heading-2-weight);\n line-height: var(--vectis-text-heading-2-leading);\n color: var(--vectis-color-text);\n user-select: none;\n }\n\n .v-time-picker-meridiem {\n flex: none;\n }\n\n .v-time-picker-face {\n position: relative;\n /*\n * The row above is wider than the face as soon as the half-day control is rendered, and the\n * column is only as wide as its widest child: without this the clock would hang at the\n * start edge of that row instead of under the middle of it. The face itself stays physical;\n * a clock is never mirrored.\n */\n align-self: center;\n inline-size: var(--vectis-control-size-time-picker-dial);\n block-size: var(--vectis-control-size-time-picker-dial);\n border-radius: var(--vectis-radius-pill);\n background: var(--vectis-color-surface-muted);\n /* Dragging across the face IS how one chooses, so it must do nothing else: no\n scrolling under a finger, and no selecting the numerals as text. */\n touch-action: none;\n user-select: none;\n cursor: pointer;\n /*\n * This is coupled to the threshold that decides which of the two circles a point belongs\n * to, in `utils/clock`. Changing one without the other makes the face answer with the wrong\n * ring, and no test can catch it: the unit tests measure nothing.\n */\n --dial-radius: calc(\n var(--vectis-control-size-time-picker-dial) / 2 -\n var(--vectis-control-size-time-picker-number) / 2\n );\n }\n\n .v-time-picker-face: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 properties used are PHYSICAL on purpose, where the rest of the design system prefers\n * logical ones: a clock face is never mirrored, clockwise meaning the same thing in every\n * language.\n */\n .v-time-picker-number {\n position: absolute;\n left: calc(50% + var(--dial-radius) * sin(var(--dial-turn) * 1turn));\n top: calc(50% - var(--dial-radius) * cos(var(--dial-turn) * 1turn));\n translate: -50% -50%;\n inline-size: var(--vectis-control-size-time-picker-number);\n block-size: var(--vectis-control-size-time-picker-number);\n display: grid;\n place-items: center;\n border-radius: var(--vectis-radius-pill);\n /* The outer circle is set at the large body size: these numerals are read at arm's\n length on a phone, not scanned like a label. */\n font-size: var(--vectis-text-body-lg-size);\n color: var(--vectis-color-text);\n /*\n * Above the hand, so that the numeral being pointed at reads on its dot rather than being\n * covered by it.\n */\n z-index: 1;\n pointer-events: none;\n }\n\n .v-time-picker-number[data-ring='inner'],\n .v-time-picker-hand[data-ring='inner'] {\n --dial-radius: calc(\n var(--vectis-control-size-time-picker-dial) / 2 -\n var(--vectis-control-size-time-picker-number) * 1.5\n );\n }\n\n .v-time-picker-number[data-ring='inner'] {\n font-size: var(--vectis-text-body-md-size);\n color: var(--vectis-color-text-muted);\n }\n\n .v-time-picker-number[data-selected] {\n color: var(--vectis-color-text-on-accent);\n }\n\n /*\n * The rotation is deliberately not animated. Going from 55 minutes to 0 takes the angle from\n * nearly a full turn back to none, and an interpolation would sweep the hand all the way\n * round anticlockwise.\n */\n .v-time-picker-hand {\n position: absolute;\n left: calc(50% - var(--vectis-control-size-time-picker-hand) / 2);\n bottom: 50%;\n inline-size: var(--vectis-control-size-time-picker-hand);\n block-size: var(--dial-radius);\n background: var(--vectis-color-accent);\n transform-origin: 50% 100%;\n rotate: calc(var(--dial-turn) * 1turn);\n }\n\n /*\n * The dot at the tip of the hand. It is exactly the size of a numeral's cell, so it covers\n * the one being pointed at; whose text turns to the colour that reads on it.\n */\n .v-time-picker-hand::before {\n content: '';\n position: absolute;\n top: 0;\n left: 50%;\n translate: -50% -50%;\n inline-size: var(--vectis-control-size-time-picker-number);\n block-size: var(--vectis-control-size-time-picker-number);\n border-radius: var(--vectis-radius-pill);\n background: var(--vectis-color-accent);\n /* The ONE thing animated on the hand: the dot changing size between its two forms. It\n interpolates between two bounded values and can therefore never take a wrong path,\n unlike the rotation above. */\n transition:\n inline-size var(--vectis-duration-fast) var(--vectis-ease-default),\n block-size var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n /*\n * Only the DOT's own size changes. Redefining the numeral size variable here would also move\n * the radius derived from it, and with it the hand's length and the position of both circles.\n */\n .v-time-picker-hand[data-minor]::before {\n inline-size: var(--vectis-control-size-time-picker-hand-minor);\n block-size: var(--vectis-control-size-time-picker-hand-minor);\n }\n\n .v-time-picker-center {\n position: absolute;\n left: 50%;\n top: 50%;\n translate: -50% -50%;\n inline-size: var(--vectis-control-size-time-picker-center);\n block-size: var(--vectis-control-size-time-picker-center);\n border-radius: var(--vectis-radius-pill);\n background: var(--vectis-color-accent);\n }\n\n /* Neither state is a pointer target. A read-only clock is still read, so it keeps the\n ordinary cursor; a disabled one refuses. */\n .v-time-picker[data-readonly] .v-time-picker-face {\n cursor: default;\n }\n\n .v-time-picker[data-disabled] .v-time-picker-face {\n cursor: not-allowed;\n }\n\n /*\n * The whole block sits after the rules it overrides, every one of them at the same\n * specificity: this is one sheet, so its own order is what settles them.\n */\n .v-time-picker[data-disabled] .v-time-picker-hand,\n .v-time-picker[data-disabled] .v-time-picker-hand::before,\n .v-time-picker[data-disabled] .v-time-picker-center {\n background: var(--vectis-color-border-strong);\n }\n\n .v-time-picker[data-disabled] .v-time-picker-sep,\n .v-time-picker[data-disabled] .v-time-picker-number,\n .v-time-picker[data-disabled] .v-time-picker-number[data-selected] {\n color: var(--vectis-color-text-subtle);\n }\n\n .v-time-picker-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--vectis-space-2);\n padding-block-start: var(--vectis-space-2);\n border-block-start: 1px solid var(--vectis-color-border);\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-time-picker-hand::before {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"VTimePicker.js","names":[],"sources":["../../../src/components/VTimePicker/VTimePicker.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @a11y @core\n/**\n * CSS places clock numerals and the hand. JavaScript maps pointer angles and keyboard steps to\n * a time because HTML has no interactive clock-face primitive.\n */\n\nimport { computed, inject, ref, watchEffect } from 'vue'\n\nimport VButton from '../VButton/VButton.vue'\nimport VToggle from '../VToggle/VToggle.vue'\nimport type { ToggleModelValue } from '../VToggle/VToggle.vue'\nimport VToggleItem from '../VToggle/VToggleItem.vue'\nimport { formatTime, hourCycleFor, parseTime } from '../../utils/time'\nimport {\n DIAL_DEAD_ZONE,\n DIAL_INNER_THRESHOLD,\n angleToIndex,\n dialIndexToHour24,\n distanceFraction,\n hour24ToDial,\n hourWithMeridiem,\n snapMinute,\n to12h,\n to24h,\n} from '../../utils/clock'\nimport type { HourFormat, Meridiem } from '../../utils/time'\nimport {\n allowedMinutesFor,\n firstAllowed,\n isHourAllowed,\n isTimeAllowed,\n limitsProblem,\n minuteGrid,\n minuteInterval,\n nearestAllowedMinute,\n resolveLimits,\n} from './limits'\nimport type { TimePickerAllowed } from './limits'\nimport { pad2 } from '../../utils/text'\nimport { isDev } from '../../utils/env'\nimport { hostWarnsKey } from '../../utils/hostWarns'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useMessages, useResolvedLocale } from '../../i18n/state'\n\n/** Whether the face shows a 12- or a 24-hour clock. */\nexport type TimePickerFormat = HourFormat\n\n/** Which of the two halves of a time is being adjusted. */\ntype TimePickerStep = 'hour' | 'minute'\n\ninterface TimePickerProps {\n /**\n * Whether the face shows a 12- or a 24-hour clock. Left out, the reader's language\n * decides, which is almost always what one wants.\n */\n format?: TimePickerFormat\n /**\n * A BCP 47 locale, which decides the clock. It TAKES PRECEDENCE over the design\n * system's global locale and falls back to it, which is why it has no literal default:\n * `undefined` has to stay recognizable for the global locale to have its chance.\n */\n locale?: string\n /**\n * The interval the minutes snap to, both when dragging and with the arrow keys. The face\n * prints only the minutes it can reach, so a step of a quarter of an hour marks four.\n */\n minuteStep?: number\n /** The earliest time that can be chosen, inclusive, as a canonical 24-hour `'HH:mm'`. */\n min?: string\n /** The latest time that can be chosen, inclusive, written like `min`. */\n max?: string\n /** Which hours can be chosen: the list of them, or a rule answering for one. */\n allowedHours?: TimePickerAllowed\n /**\n * Which minutes can be chosen: the list of them, or a rule answering for one. The\n * minutes it leaves out are not printed.\n */\n allowedMinutes?: TimePickerAllowed\n /**\n * Makes the whole clock unusable: the hand cannot be moved, the half-day cannot be\n * changed, and everything greys out through the colour tokens.\n */\n disabled?: boolean\n /** Shows the time without letting it be changed. */\n readonly?: boolean\n /**\n * The accessible name of the whole clock, its two numerals and its face together. It falls\n * back to the dictionary, and a consumer `aria-label` wins over it.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<TimePickerProps>(), {\n format: undefined,\n locale: undefined,\n minuteStep: 1,\n min: undefined,\n max: undefined,\n allowedHours: undefined,\n allowedMinutes: undefined,\n disabled: false,\n readonly: false,\n label: undefined,\n})\n\n/** The time, always as a 24-hour \"HH:mm\" string whatever clock is displayed. */\nconst model = defineModel<string | null>({ default: null })\n\nconst emit = defineEmits<{\n /**\n * The reader has FINISHED, carrying the time as it stands: the minutes were settled from the\n * keyboard.\n */\n confirm: [value: string | null]\n}>()\n\ndefineSlots<{\n /** A strip at the foot of the clock, the place for actions such as Cancel and OK. */\n footer?(): unknown\n}>()\n\nconst m = useMessages()\n// @a11y\n// A roleless box cannot carry an accessible name (axe: aria-prohibited-attr),\n// so the root is a named group: the VCarousel viewport arrangement.\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.timePicker.label)\nconst resolvedLocale = useResolvedLocale(() => props.locale)\nconst resolvedFormat = computed<TimePickerFormat>(\n () => props.format ?? hourCycleFor(resolvedLocale.value),\n)\n\n/** The restrictions, resolved once for the whole render. */\nconst limits = computed(() => resolveLimits(props))\n\n/** The hours the restrictions still leave something in, worked out once per set of restrictions. */\nconst availableHours = computed(() => {\n const hours = new Set<number>()\n for (let candidate = 0; candidate < 24; candidate += 1)\n if (isHourAllowed(candidate, limits.value)) hours.add(candidate)\n return hours\n})\n\n/** Whether an hour is one the restrictions still leave something in. */\nconst isAvailableHour = (candidate: number) => availableHours.value.has(candidate)\n\n// @devwarn\nif (isDev && !inject(hostWarnsKey, false)) {\n watchEffect(() => {\n if (!Number.isInteger(props.minuteStep) || props.minuteStep < 1 || 60 % props.minuteStep !== 0)\n console.warn(`[VTimePicker] minuteStep ${props.minuteStep} — a divisor of 60 is expected.`)\n const problem = limitsProblem(limits.value)\n if (problem) console.warn(`[VTimePicker] ${problem}`)\n })\n}\n\n/** Which of the two is being adjusted. It starts on the hour and moves on by itself. */\nconst step = ref<TimePickerStep>('hour')\n\n// With no value the clock shows the first hour of the half of the day chosen so far, which\n// is midnight until PM is picked. It is also the hour a time started from the MINUTES is\n// written with, so that choice is not lost when the reader begins there.\nconst parts = computed(\n () =>\n parseTime(model.value) ?? {\n hour: resolvedFormat.value === '12h' ? to24h(12, pendingMeridiem.value) : 0,\n minute: 0,\n },\n)\nconst hour = computed(() => parts.value.hour)\nconst minute = computed(() => parts.value.minute)\n\n/*\n * The two writers are the SINGLE cut-off point for the restrictions: the pointer, the six\n * keys and the AM/PM control all write through them and through nothing else, so not one\n * handler carries a guard of its own.\n */\n\n/**\n * Writing the hour. An hour with nothing left in it has no minute to fall back on, which\n * refuses it; one that is only PARTLY available pulls the minutes to what it does allow, since\n * `min: '09:30'` must not be left holding 09:00.\n */\nfunction setHour(value: number | null) {\n // Changing which face is shown stays allowed under `readonly`: reading the minutes is not\n // changing them.\n if (props.disabled || props.readonly) return\n if (value === null) return\n const minutes = isTimeAllowed(value, minute.value, limits.value)\n ? minute.value\n : nearestAllowedMinute(value, minute.value, limits.value)\n if (minutes === null) return\n model.value = formatTime(value, minutes)\n}\n\n/** Writing the minutes. Null is \"there was nowhere to go\", which every key can produce. */\nfunction setMinute(value: number | null) {\n if (props.disabled || props.readonly) return\n if (value === null || !isTimeAllowed(hour.value, value, limits.value)) return\n model.value = formatTime(hour.value, value)\n}\n\n// @a11y\n/*\n * Which step the clock is on is otherwise carried by the face's own name alone, and a change of\n * name is not reliably announced. A politely announced region says it a second time.\n */\nconst liveMessage = ref('')\n\nfunction setStep(next: TimePickerStep) {\n step.value = next\n liveMessage.value =\n next === 'minute' ? m.value.timePicker.choosingMinutes : m.value.timePicker.choosingHour\n}\n\n/** One numeral on the face. */\ninterface DialCell {\n key: string\n /** What is printed. */\n label: string\n /** Where it sits, as a fraction of a full turn from twelve o'clock. */\n turn: number\n /** Which of the two circles it belongs to, a 24-hour face having an inner one. */\n ring: 'outer' | 'inner'\n /** Whether it is the value currently being pointed at. */\n selected: boolean\n}\n\n/** The hour a position on the face stands for. */\nfunction hourAt(index: number, ring: 'outer' | 'inner'): number {\n return resolvedFormat.value === '24h'\n ? dialIndexToHour24(index, ring)\n : to24h(index === 0 ? 12 : index, currentMeridiem.value)\n}\n\n/** The minutes the face prints. */\nconst minuteMarks = computed(() => {\n const interval = minuteInterval(props.minuteStep)\n const spacing = 60 / interval <= 12 ? interval : 5\n const marks: number[] = []\n for (let minutes = 0; minutes < 60; minutes += spacing)\n if (minutes % interval === 0 && isTimeAllowed(hour.value, minutes, limits.value))\n marks.push(minutes)\n return marks\n})\n\n/*\n * A numeral that is only there to be refused says nothing the missing numeral does not say\n * better, and it says it in grey text a screen reader has no way to reach: the markers are\n * `aria-hidden`, so what the restrictions leave is spoken by the face's own value and by\n * nothing else.\n */\nconst cells = computed<DialCell[]>(() => {\n if (step.value === 'minute') {\n return minuteMarks.value.map((minutes) => ({\n key: `m-${minutes}`,\n label: pad2(minutes),\n turn: minutes / 60,\n ring: 'outer' as const,\n selected: minute.value === minutes,\n }))\n }\n // Each position's hour is worked out once, since on a 12-hour face that takes the half of the\n // day in force. The selection there still compares numerals and not hours: an empty clock\n // holding a PM choice sits on midnight, and its hand points at the 12 all the same.\n const twelve = resolvedFormat.value === '12h'\n const shown12 = to12h(hour.value).hour\n const rings: DialCell['ring'][] = twelve ? ['outer'] : ['outer', 'inner']\n const out: DialCell[] = []\n for (const ring of rings)\n for (let i = 0; i < 12; i += 1) {\n const target = hourAt(i, ring)\n if (!isAvailableHour(target)) continue\n const numeral = i === 0 ? 12 : i\n out.push({\n key: `${ring === 'outer' ? 'o' : 'i'}-${i}`,\n label: ring === 'inner' ? pad2(target) : String(numeral),\n turn: i / 12,\n ring,\n selected: twelve ? shown12 === numeral : hour.value === target,\n })\n }\n return out\n})\n\nconst handTurn = computed(() => {\n if (step.value === 'minute') return minute.value / 60\n const index =\n resolvedFormat.value === '24h' ? hour24ToDial(hour.value).index : to12h(hour.value).hour % 12\n return index / 12\n})\n\nconst handRing = computed(() =>\n step.value === 'hour' && resolvedFormat.value === '24h' ? hour24ToDial(hour.value).ring : 'outer',\n)\n\n/**\n * Whether the hand points at a minute that has no marker of its own. The tip is then\n * drawn small: at full size it would cover the two neighbouring markers and read as\n * pointing at neither.\n */\nconst handMinor = computed(\n () => step.value === 'minute' && !minuteMarks.value.includes(minute.value),\n)\n\n/** The hour as the two large numerals show it, on whichever clock is displayed. */\nconst displayHourText = computed(() =>\n pad2(resolvedFormat.value === '12h' ? to12h(hour.value).hour : hour.value),\n)\n\n/** Which half of the day is chosen while no time is set at all. */\nconst pendingMeridiem = ref<Meridiem>('AM')\n\n/**\n * The half of the day in force: the value's own once there is one, the remembered choice until\n * then.\n */\nconst currentMeridiem = computed<Meridiem>(() =>\n model.value ? to12h(hour.value).meridiem : pendingMeridiem.value,\n)\n\nconst meridiemModel = computed<ToggleModelValue>({\n get: () => currentMeridiem.value,\n set: (value) => {\n const meridiem: Meridiem = value === 'PM' ? 'PM' : 'AM'\n pendingMeridiem.value = meridiem\n // With no time set there is nothing to convert, so the choice is simply REMEMBERED\n // and applies to the first time chosen.\n if (!model.value) return\n // What is being chosen here is the HALF OF THE DAY, so the hour gives way to it: kept\n // when the new half allows it, and otherwise the first hour of that half that does.\n // Refusing the write instead would leave the control snapping straight back, since it\n // reads the half of the day off the value.\n const wanted = hourWithMeridiem(hour.value, meridiem)\n setHour(\n isAvailableHour(wanted)\n ? wanted\n : firstAllowed(12, (i) => to24h(i, meridiem), isAvailableHour),\n )\n },\n})\n\n/**\n * Whether each half of the day still holds an hour one could choose. An empty one takes its\n * button with it: a control that can only ever be refused is worse than no control.\n */\nconst meridiemAvailable = computed<Record<Meridiem, boolean>>(() => ({\n AM: firstAllowed(12, (i) => to24h(i, 'AM'), isAvailableHour) !== null,\n PM: firstAllowed(12, (i) => to24h(i, 'PM'), isAvailableHour) !== null,\n}))\n\n// @a11y\n// The entire spoken value of the face. The numerals are hidden from screen readers, so these\n// four attributes are the only thing assistive technology has: what the value is, what its\n// bounds are, and how to say it.\nconst ariaValueNow = computed(() => {\n if (step.value === 'minute') return minute.value\n return resolvedFormat.value === '12h' ? to12h(hour.value).hour : hour.value\n})\nconst ariaValueMin = computed(() =>\n step.value === 'minute' ? 0 : resolvedFormat.value === '12h' ? 1 : 0,\n)\nconst ariaValueMax = computed(() =>\n step.value === 'minute' ? 59 : resolvedFormat.value === '12h' ? 12 : 23,\n)\n// On a 12-hour face the half of the day is spoken with the hour: the AM/PM control is not\n// on the slider's way, and \"9 o'clock\" alone is two different times.\nconst ariaValueText = computed(() => {\n const m_ = m.value.timePicker\n if (step.value === 'minute') return m_.minutesValue(minute.value)\n const spoken = m_.hourValue(ariaValueNow.value)\n if (resolvedFormat.value === '24h') return spoken\n return `${spoken} ${currentMeridiem.value === 'PM' ? m_.pm : m_.am}`\n})\n\n/**\n * A step has been settled: after the hour come the minutes, and after the minutes the whole\n * thing is confirmed; but by KEYBOARD only, for the reason given on the emit.\n */\nfunction settleStep(via: 'pointer' | 'keyboard') {\n if (step.value === 'hour') setStep('minute')\n else if (via === 'keyboard') emit('confirm', model.value)\n}\n\nconst faceEl = ref<HTMLElement | null>(null)\nconst dragging = ref(false)\n/** Whether anything in the gesture under way landed on a numeral. */\nlet landed = false\n\n/**\n * Where a point on the face lands, and whether there was anything there at all. The write\n * itself still goes through `setHour`/ `setMinute` and their own guards, which is why\n * `readonly` is refused THERE and not here: a frozen clock is still one whose numerals are\n * real, and a tap on one moves the step on as it always did.\n */\nfunction applyPoint(event: PointerEvent): boolean {\n const face = faceEl.value\n // A disabled clock aims at nothing, so no step is moved on either: its header buttons\n // are disabled for the same reason.\n if (!face || props.disabled) return false\n // Measuring the face is safe here: this runs from a handler, hence in a browser, never\n // during a render.\n const rect = face.getBoundingClientRect()\n const dx = event.clientX - (rect.left + rect.width / 2)\n const dy = event.clientY - (rect.top + rect.height / 2)\n if (Math.hypot(dx, dy) <= DIAL_DEAD_ZONE * (rect.width / 2)) return false\n if (step.value === 'minute') {\n // The step's own snapping stands: a point between two markers has always been pulled\n // to the one it is nearest. What it is pulled to still has to BE on the face.\n const minutes = snapMinute(angleToIndex(dx, dy, 60), props.minuteStep)\n if (!isTimeAllowed(hour.value, minutes, limits.value)) return false\n setMinute(minutes)\n return true\n }\n const index = angleToIndex(dx, dy, 12)\n const ring =\n resolvedFormat.value === '24h' &&\n distanceFraction(dx, dy, rect.width / 2) < DIAL_INNER_THRESHOLD\n ? 'inner'\n : 'outer'\n const target = hourAt(index, ring)\n if (!isAvailableHour(target)) return false\n setHour(target)\n return true\n}\n\nfunction onPointerdown(event: PointerEvent) {\n // @fallback\n // Capture keeps dragging active outside the clock face; synthetic events may not reference a\n // capturable pointer.\n try {\n faceEl.value?.setPointerCapture(event.pointerId)\n } catch {\n /* Synthetic pointers cannot be captured. */\n }\n dragging.value = true\n landed = applyPoint(event)\n}\n\nfunction onPointermove(event: PointerEvent) {\n if (dragging.value && applyPoint(event)) landed = true\n}\n\nfunction onPointerup() {\n if (!dragging.value) return\n dragging.value = false\n // A gesture that never landed on a numeral has chosen nothing, so there is nothing to\n // move on FROM: the reader is left on the face they aimed at, looking at what they missed.\n if (landed) settleStep('pointer')\n}\n\nfunction onPointercancel() {\n dragging.value = false\n}\n\n// @keyboard @a11y\n// The keyboard a slider is expected to have.\n/*\n * Every key below walks until it finds something it MAY land on, rather than stopping at the\n * first thing it may not: with scattered hours a key that stopped would die at the first hole\n * and never reach what lies past it. Against a bound there is nothing past the hole, so the\n * same walk comes back empty and the key holds still, which a bound means.\n */\nfunction moveHour(delta: number) {\n if (resolvedFormat.value === '24h') {\n setHour(firstAllowed(23, (i) => (hour.value + delta * i + 24) % 24, isAvailableHour))\n return\n }\n // On a 12-hour face the hours cycle from 1 to 12 WITHIN the current half of the day:\n // passing midday is done on the AM/PM control, not by walking the hand past twelve.\n const hour12 = to12h(hour.value).hour\n setHour(\n firstAllowed(\n 11,\n (i) => to24h(((hour12 - 1 + delta * i + 12) % 12) + 1, currentMeridiem.value),\n isAvailableHour,\n ),\n )\n}\n\n/** The first or the last hour the face allows, on whichever clock it is showing. */\nfunction edgeHour(edge: 'first' | 'last'): number | null {\n if (resolvedFormat.value === '24h')\n return firstAllowed(24, (i) => (edge === 'first' ? i - 1 : 24 - i), isAvailableHour)\n return firstAllowed(\n 12,\n (i) => to24h(edge === 'first' ? i : 13 - i, currentMeridiem.value),\n isAvailableHour,\n )\n}\n\n/**\n * The minute a key lands on: the first step of the grid at least `distance` minutes away, that\n * way round, and then the next one the hour allows.\n */\nfunction minuteToward(direction: 1 | -1, distance: number): number | null {\n const grid = minuteGrid(props.minuteStep)\n const target = (((minute.value + direction * distance) % 60) + 60) % 60\n const n = grid.length\n let start = direction > 0 ? 0 : n - 1\n for (let k = 0; k < n; k += 1) {\n const i = direction > 0 ? k : n - 1 - k\n if (direction > 0 ? grid[i]! >= target : grid[i]! <= target) {\n start = i\n break\n }\n }\n return firstAllowed(\n n,\n (i) => grid[(((start + direction * (i - 1)) % n) + n) % n]!,\n (candidate) => isTimeAllowed(hour.value, candidate, limits.value),\n )\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n // The face of a disabled clock is out of the tab order, but `focus()` can still put the\n // focus there: nothing it is sent may move the step on or confirm.\n if (props.disabled) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n settleStep('keyboard')\n return\n }\n // The arrows are not flipped in a right-to-left page: a clock face is never mirrored;\n // clockwise means the same thing everywhere; so forward stays forward.\n const delta =\n event.key === 'ArrowUp' || event.key === 'ArrowRight'\n ? 1\n : event.key === 'ArrowDown' || event.key === 'ArrowLeft'\n ? -1\n : 0\n if (step.value === 'hour') {\n if (delta) moveHour(delta)\n else if (event.key === 'Home') setHour(edgeHour('first'))\n else if (event.key === 'End') setHour(edgeHour('last'))\n else return\n event.preventDefault()\n return\n }\n if (delta) setMinute(minuteToward(delta, 1))\n else if (event.key === 'PageUp') setMinute(minuteToward(1, 5))\n else if (event.key === 'PageDown') setMinute(minuteToward(-1, 5))\n // The list is built in these two branches alone: the arrows walk to a neighbour and never\n // need the whole of it.\n else if (event.key === 'Home') setMinute(allowedMinutesFor(hour.value, limits.value)[0] ?? null)\n else if (event.key === 'End')\n setMinute(allowedMinutesFor(hour.value, limits.value).at(-1) ?? null)\n else return\n event.preventDefault()\n}\n\ndefineExpose({\n /** Moves the focus onto the clock face, which the arrow keys drive. */\n focus: (options?: FocusOptions) => faceEl.value?.focus(options),\n /**\n * Puts the clock back on the hour and clears the announcement, WITHOUT announcing anything\n * itself.\n */\n reset: () => {\n step.value = 'hour'\n liveMessage.value = ''\n },\n})\n</script>\n\n<template>\n <div\n class=\"v-time-picker\"\n role=\"group\"\n :aria-label=\"ariaLabel\"\n :data-step=\"step\"\n :data-disabled=\"disabled ? '' : undefined\"\n :data-readonly=\"readonly ? '' : undefined\"\n >\n <!--\n The two large numerals switch between adjusting the hour and the minutes. The one being\n adjusted takes the accent tone, which on a quiet button shows as the colour of the numeral\n rather than as a filled background.\n -->\n <div class=\"v-time-picker-header\">\n <div class=\"v-time-picker-time\">\n <VButton\n class=\"v-time-picker-cell\"\n variant=\"ghost\"\n size=\"lg\"\n :tone=\"step === 'hour' ? 'accent' : 'neutral'\"\n :aria-pressed=\"step === 'hour' ? 'true' : 'false'\"\n :aria-label=\"`${displayHourText}, ${m.timePicker.selectHour}`\"\n :disabled=\"disabled\"\n @click=\"setStep('hour')\"\n >\n {{ displayHourText }}\n </VButton>\n <span class=\"v-time-picker-sep\" aria-hidden=\"true\">:</span>\n <VButton\n class=\"v-time-picker-cell\"\n variant=\"ghost\"\n size=\"lg\"\n :tone=\"step === 'minute' ? 'accent' : 'neutral'\"\n :aria-pressed=\"step === 'minute' ? 'true' : 'false'\"\n :aria-label=\"`${pad2(minute)}, ${m.timePicker.selectMinutes}`\"\n :disabled=\"disabled\"\n @click=\"setStep('minute')\"\n >\n {{ pad2(minute) }}\n </VButton>\n </div>\n\n <VToggle\n v-if=\"resolvedFormat === '12h'\"\n v-model=\"meridiemModel\"\n class=\"v-time-picker-meridiem\"\n mandatory\n variant=\"outline\"\n selected-variant=\"soft\"\n orientation=\"vertical\"\n size=\"sm\"\n :label=\"m.timePicker.meridiem\"\n :disabled=\"disabled || readonly\"\n >\n <VToggleItem value=\"AM\" :label=\"m.timePicker.am\" :disabled=\"!meridiemAvailable.AM\" />\n <VToggleItem value=\"PM\" :label=\"m.timePicker.pm\" :disabled=\"!meridiemAvailable.PM\" />\n </VToggle>\n </div>\n\n <div\n ref=\"faceEl\"\n role=\"slider\"\n :tabindex=\"disabled ? -1 : 0\"\n class=\"v-time-picker-face\"\n :aria-label=\"step === 'hour' ? m.timePicker.hour : m.timePicker.minutes\"\n :aria-valuemin=\"ariaValueMin\"\n :aria-valuemax=\"ariaValueMax\"\n :aria-valuenow=\"ariaValueNow\"\n :aria-valuetext=\"ariaValueText\"\n :data-dragging=\"dragging ? '' : undefined\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :aria-readonly=\"readonly ? 'true' : undefined\"\n @pointerdown=\"onPointerdown\"\n @pointermove=\"onPointermove\"\n @pointerup=\"onPointerup\"\n @pointercancel=\"onPointercancel\"\n @keydown=\"onKeydown\"\n >\n <div\n class=\"v-time-picker-hand\"\n aria-hidden=\"true\"\n :data-ring=\"handRing\"\n :data-minor=\"handMinor ? '' : undefined\"\n :style=\"{ '--dial-turn': String(handTurn) }\"\n />\n <span class=\"v-time-picker-center\" aria-hidden=\"true\" />\n <span\n v-for=\"cell in cells\"\n :key=\"cell.key\"\n class=\"v-time-picker-number\"\n aria-hidden=\"true\"\n :data-ring=\"cell.ring\"\n :data-selected=\"cell.selected ? '' : undefined\"\n :style=\"{ '--dial-turn': String(cell.turn) }\"\n >{{ cell.label }}</span\n >\n </div>\n\n <div class=\"v-visually-hidden\" aria-live=\"polite\">{{ liveMessage }}</div>\n\n <div v-if=\"$slots.footer\" class=\"v-time-picker-footer\"><slot name=\"footer\" /></div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /* The same root as VDatePicker's: an inline column that sizes to its content and pads\n itself, so the clock reads the same on a page as in VTimeInput's panel, which adds no\n padding of its own. The gap is the clock's, its parts being larger. */\n .v-time-picker {\n display: inline-flex;\n flex-direction: column;\n gap: var(--vectis-space-4);\n padding: var(--vectis-space-3);\n font-family: var(--vectis-text-family);\n color: var(--vectis-color-text);\n }\n\n /*\n * The numerals and the choice of half-day on one row, the second beside the first. The gap is\n * logical, so the control follows the reading direction; the numerals inside it stay in a\n * group of their own, which keeps the direction they force off it.\n */\n .v-time-picker-header {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--vectis-space-3);\n }\n\n /* A time written in figures always reads hours then minutes, in every language. Forcing\n the direction here is what stops bidirectional reordering from swapping the two in a\n right-to-left page. */\n .v-time-picker-time {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--vectis-space-2);\n direction: ltr;\n }\n\n /*\n * What is overridden here is the \"large numeral\" look alone; the width and the type; the\n * height, the states, the focus ring and the transitions all come from the button itself. The\n * type is the `heading-1` recipe taken whole, and the colon the `heading-2` one: read in\n * halves, a consumer repointing `--vectis-text-heading-1-weight` would move every heading but\n * these two numerals.\n */\n .v-time-picker-cell[data-size] {\n width: var(--control-height);\n font-size: var(--vectis-text-heading-1-size);\n font-weight: var(--vectis-text-heading-1-weight);\n line-height: var(--vectis-text-heading-1-leading);\n letter-spacing: var(--vectis-text-heading-1-tracking);\n /* Figures of equal width, so that going from \"11\" to \"00\" does not shift the cell. */\n font-variant-numeric: tabular-nums;\n }\n\n .v-time-picker-sep {\n font-size: var(--vectis-text-heading-2-size);\n font-weight: var(--vectis-text-heading-2-weight);\n line-height: var(--vectis-text-heading-2-leading);\n color: var(--vectis-color-text);\n user-select: none;\n }\n\n .v-time-picker-meridiem {\n flex: none;\n }\n\n .v-time-picker-face {\n position: relative;\n /*\n * The row above is wider than the face as soon as the half-day control is rendered, and the\n * column is only as wide as its widest child: without this the clock would hang at the\n * start edge of that row instead of under the middle of it. The face itself stays physical;\n * a clock is never mirrored.\n */\n align-self: center;\n inline-size: var(--vectis-control-size-time-picker-dial);\n block-size: var(--vectis-control-size-time-picker-dial);\n border-radius: var(--vectis-radius-pill);\n background: var(--vectis-color-surface-muted);\n /* Dragging across the face IS how one chooses, so it must do nothing else: no\n scrolling under a finger, and no selecting the numerals as text. */\n touch-action: none;\n user-select: none;\n cursor: pointer;\n /*\n * This is coupled to the threshold that decides which of the two circles a point belongs\n * to, in `utils/clock`. Changing one without the other makes the face answer with the wrong\n * ring, and no test can catch it: the unit tests measure nothing.\n */\n --dial-radius: calc(\n var(--vectis-control-size-time-picker-dial) / 2 -\n var(--vectis-control-size-time-picker-number) / 2\n );\n }\n\n .v-time-picker-face: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 properties used are PHYSICAL on purpose, where the rest of the design system prefers\n * logical ones: a clock face is never mirrored, clockwise meaning the same thing in every\n * language.\n */\n .v-time-picker-number {\n position: absolute;\n left: calc(50% + var(--dial-radius) * sin(var(--dial-turn) * 1turn));\n top: calc(50% - var(--dial-radius) * cos(var(--dial-turn) * 1turn));\n translate: -50% -50%;\n inline-size: var(--vectis-control-size-time-picker-number);\n block-size: var(--vectis-control-size-time-picker-number);\n display: grid;\n place-items: center;\n border-radius: var(--vectis-radius-pill);\n /* The outer circle is set at the large body size: these numerals are read at arm's\n length on a phone, not scanned like a label. */\n font-size: var(--vectis-text-body-lg-size);\n color: var(--vectis-color-text);\n /*\n * Above the hand, so that the numeral being pointed at reads on its dot rather than being\n * covered by it.\n */\n z-index: 1;\n pointer-events: none;\n }\n\n .v-time-picker-number[data-ring='inner'],\n .v-time-picker-hand[data-ring='inner'] {\n --dial-radius: calc(\n var(--vectis-control-size-time-picker-dial) / 2 -\n var(--vectis-control-size-time-picker-number) * 1.5\n );\n }\n\n .v-time-picker-number[data-ring='inner'] {\n font-size: var(--vectis-text-body-md-size);\n color: var(--vectis-color-text-muted);\n }\n\n .v-time-picker-number[data-selected] {\n color: var(--vectis-color-text-on-accent);\n }\n\n /*\n * The rotation is deliberately not animated. Going from 55 minutes to 0 takes the angle from\n * nearly a full turn back to none, and an interpolation would sweep the hand all the way\n * round anticlockwise.\n */\n .v-time-picker-hand {\n position: absolute;\n left: calc(50% - var(--vectis-control-size-time-picker-hand) / 2);\n bottom: 50%;\n inline-size: var(--vectis-control-size-time-picker-hand);\n block-size: var(--dial-radius);\n background: var(--vectis-color-accent);\n transform-origin: 50% 100%;\n rotate: calc(var(--dial-turn) * 1turn);\n }\n\n /*\n * The dot at the tip of the hand. It is exactly the size of a numeral's cell, so it covers\n * the one being pointed at; whose text turns to the colour that reads on it.\n */\n .v-time-picker-hand::before {\n content: '';\n position: absolute;\n top: 0;\n left: 50%;\n translate: -50% -50%;\n inline-size: var(--vectis-control-size-time-picker-number);\n block-size: var(--vectis-control-size-time-picker-number);\n border-radius: var(--vectis-radius-pill);\n background: var(--vectis-color-accent);\n /* The ONE thing animated on the hand: the dot changing size between its two forms. It\n interpolates between two bounded values and can therefore never take a wrong path,\n unlike the rotation above. */\n transition:\n inline-size var(--vectis-duration-fast) var(--vectis-ease-default),\n block-size var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n /*\n * Only the DOT's own size changes. Redefining the numeral size variable here would also move\n * the radius derived from it, and with it the hand's length and the position of both circles.\n */\n .v-time-picker-hand[data-minor]::before {\n inline-size: var(--vectis-control-size-time-picker-hand-minor);\n block-size: var(--vectis-control-size-time-picker-hand-minor);\n }\n\n .v-time-picker-center {\n position: absolute;\n left: 50%;\n top: 50%;\n translate: -50% -50%;\n inline-size: var(--vectis-control-size-time-picker-center);\n block-size: var(--vectis-control-size-time-picker-center);\n border-radius: var(--vectis-radius-pill);\n background: var(--vectis-color-accent);\n }\n\n /* Neither state is a pointer target. A read-only clock is still read, so it keeps the\n ordinary cursor; a disabled one refuses. */\n .v-time-picker[data-readonly] .v-time-picker-face {\n cursor: default;\n }\n\n .v-time-picker[data-disabled] .v-time-picker-face {\n cursor: not-allowed;\n }\n\n /*\n * The whole block sits after the rules it overrides, every one of them at the same\n * specificity: this is one sheet, so its own order is what settles them.\n */\n .v-time-picker[data-disabled] .v-time-picker-hand,\n .v-time-picker[data-disabled] .v-time-picker-hand::before,\n .v-time-picker[data-disabled] .v-time-picker-center {\n background: var(--vectis-color-border-strong);\n }\n\n .v-time-picker[data-disabled] .v-time-picker-sep,\n .v-time-picker[data-disabled] .v-time-picker-number,\n .v-time-picker[data-disabled] .v-time-picker-number[data-selected] {\n color: var(--vectis-color-text-subtle);\n }\n\n .v-time-picker-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--vectis-space-2);\n padding-block-start: var(--vectis-space-2);\n border-block-start: 1px solid var(--vectis-color-border);\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-time-picker-hand::before {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":""}
|
|
@@ -2,9 +2,9 @@ import { isDev as e } from "../../utils/env.js";
|
|
|
2
2
|
import { useMessages as t, useResolvedLocale as n } from "../../i18n/state.js";
|
|
3
3
|
import r from "../VButton/VButton.js";
|
|
4
4
|
import { useAriaLabel as i } from "../../composables/useAriaLabel.js";
|
|
5
|
-
import a from "
|
|
6
|
-
import o from "../VToggle/
|
|
7
|
-
import
|
|
5
|
+
import { pad2 as a } from "../../utils/text.js";
|
|
6
|
+
import o from "../VToggle/VToggle.js";
|
|
7
|
+
import s from "../VToggle/VToggleItem.js";
|
|
8
8
|
import { hostWarnsKey as ee } from "../../utils/hostWarns.js";
|
|
9
9
|
import { formatTime as c, hourCycleFor as te, parseTime as ne } from "../../utils/time.js";
|
|
10
10
|
import { angleToIndex as l, dialIndexToHour24 as re, distanceFraction as ie, hour24ToDial as ae, hourWithMeridiem as oe, snapMinute as se, to12h as u, to24h as d } from "../../utils/clock.js";
|
|
@@ -99,26 +99,26 @@ var Ce = [
|
|
|
99
99
|
}), Fe = m(() => {
|
|
100
100
|
if (P.value === "minute") return U.value.map((e) => ({
|
|
101
101
|
key: `m-${e}`,
|
|
102
|
-
label:
|
|
102
|
+
label: a(e),
|
|
103
103
|
turn: e / 60,
|
|
104
104
|
ring: "outer",
|
|
105
105
|
selected: L.value === e
|
|
106
106
|
}));
|
|
107
107
|
let e = j.value === "12h", t = u(I.value).hour, n = e ? ["outer"] : ["outer", "inner"], r = [];
|
|
108
108
|
for (let i of n) for (let n = 0; n < 12; n += 1) {
|
|
109
|
-
let
|
|
110
|
-
if (!N(
|
|
111
|
-
let
|
|
109
|
+
let o = H(n, i);
|
|
110
|
+
if (!N(o)) continue;
|
|
111
|
+
let s = n === 0 ? 12 : n;
|
|
112
112
|
r.push({
|
|
113
113
|
key: `${i === "outer" ? "o" : "i"}-${n}`,
|
|
114
|
-
label: i === "inner" ?
|
|
114
|
+
label: i === "inner" ? a(o) : String(s),
|
|
115
115
|
turn: n / 12,
|
|
116
116
|
ring: i,
|
|
117
|
-
selected: e ? t ===
|
|
117
|
+
selected: e ? t === s : I.value === o
|
|
118
118
|
});
|
|
119
119
|
}
|
|
120
120
|
return r;
|
|
121
|
-
}), Ie = m(() => P.value === "minute" ? L.value / 60 : (j.value === "24h" ? ae(I.value).index : u(I.value).hour % 12) / 12), Le = m(() => P.value === "hour" && j.value === "24h" ? ae(I.value).ring : "outer"), Re = m(() => P.value === "minute" && !U.value.includes(L.value)), W = m(() =>
|
|
121
|
+
}), Ie = m(() => P.value === "minute" ? L.value / 60 : (j.value === "24h" ? ae(I.value).index : u(I.value).hour % 12) / 12), Le = m(() => P.value === "hour" && j.value === "24h" ? ae(I.value).ring : "outer"), Re = m(() => P.value === "minute" && !U.value.includes(L.value)), W = m(() => a(j.value === "12h" ? u(I.value).hour : I.value)), G = C("AM"), K = m(() => k.value ? u(I.value).meridiem : G.value), q = m({
|
|
122
122
|
get: () => K.value,
|
|
123
123
|
set: (e) => {
|
|
124
124
|
let t = e === "PM" ? "PM" : "AM";
|
|
@@ -253,11 +253,11 @@ var Ce = [
|
|
|
253
253
|
size: "lg",
|
|
254
254
|
tone: P.value === "minute" ? "accent" : "neutral",
|
|
255
255
|
"aria-pressed": P.value === "minute" ? "true" : "false",
|
|
256
|
-
"aria-label": `${T(
|
|
256
|
+
"aria-label": `${T(a)(L.value)}, ${T(A).timePicker.selectMinutes}`,
|
|
257
257
|
disabled: y.disabled,
|
|
258
258
|
onClick: t[1] ||= (e) => V("minute")
|
|
259
259
|
}, {
|
|
260
|
-
default: E(() => [_(w(T(
|
|
260
|
+
default: E(() => [_(w(T(a)(L.value)), 1)]),
|
|
261
261
|
_: 1
|
|
262
262
|
}, 8, [
|
|
263
263
|
"tone",
|
|
@@ -265,7 +265,7 @@ var Ce = [
|
|
|
265
265
|
"aria-label",
|
|
266
266
|
"disabled"
|
|
267
267
|
])
|
|
268
|
-
]), j.value === "12h" ? (S(), ge(
|
|
268
|
+
]), j.value === "12h" ? (S(), ge(o, {
|
|
269
269
|
key: 0,
|
|
270
270
|
modelValue: q.value,
|
|
271
271
|
"onUpdate:modelValue": t[2] ||= (e) => q.value = e,
|
|
@@ -278,11 +278,11 @@ var Ce = [
|
|
|
278
278
|
label: T(A).timePicker.meridiem,
|
|
279
279
|
disabled: y.disabled || y.readonly
|
|
280
280
|
}, {
|
|
281
|
-
default: E(() => [v(
|
|
281
|
+
default: E(() => [v(s, {
|
|
282
282
|
value: "AM",
|
|
283
283
|
label: T(A).timePicker.am,
|
|
284
284
|
disabled: !J.value.AM
|
|
285
|
-
}, null, 8, ["label", "disabled"]), v(
|
|
285
|
+
}, null, 8, ["label", "disabled"]), v(s, {
|
|
286
286
|
value: "PM",
|
|
287
287
|
label: T(A).timePicker.pm,
|
|
288
288
|
disabled: !J.value.PM
|