vectis-ui 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/components/VAccordion/VAccordionItem.d.ts +4 -6
- package/dist/components/VAccordion/VAccordionItem.js.map +1 -1
- package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js +21 -19
- package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAccordion/context.d.ts +6 -1
- package/dist/components/VAccordion/context.js.map +1 -1
- package/dist/components/VAvatar/VAvatar.css +1 -1
- package/dist/components/VAvatar/VAvatar.d.ts +5 -3
- package/dist/components/VAvatar/VAvatar.js.map +1 -1
- package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js +53 -39
- package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAvatar/VAvatarGroup.d.ts +12 -3
- package/dist/components/VAvatar/VAvatarGroup.js.map +1 -1
- package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js +27 -20
- package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAvatar/context.d.ts +2 -2
- package/dist/components/VAvatar/context.js.map +1 -1
- package/dist/components/VBadge/VBadge.css +1 -1
- package/dist/components/VBadge/VBadge.d.ts +8 -4
- package/dist/components/VBadge/VBadge.js.map +1 -1
- package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js +6 -3
- package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VBreadcrumb/VBreadcrumb.d.ts +5 -4
- package/dist/components/VBreadcrumb/VBreadcrumb.js.map +1 -1
- package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js +8 -3
- package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VButton/VButton.css +1 -1
- package/dist/components/VButton/VButton.d.ts +4 -1
- package/dist/components/VButton/VButton.js.map +1 -1
- package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js +8 -7
- package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VButton/VButtonGroup.js.map +1 -1
- package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendar.css +1 -1
- package/dist/components/VCalendar/VCalendar.d.ts +15 -6
- package/dist/components/VCalendar/VCalendar.js.map +1 -1
- package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js +144 -126
- package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendarEvent.css +1 -1
- package/dist/components/VCalendar/VCalendarEvent.d.ts +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 +1 -1
- package/dist/components/VCalendar/VCalendarMonth.js.map +1 -1
- package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js +99 -90
- 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 +256 -246
- 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.d.ts +8 -1
- package/dist/components/VCalendar/VCalendarYear.js.map +1 -1
- package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js +39 -32
- package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/color.d.ts +1 -1
- package/dist/components/VCalendar/color.js.map +1 -1
- package/dist/components/VCalendar/edgeStep.d.ts +3 -3
- package/dist/components/VCalendar/edgeStep.js +5 -0
- package/dist/components/VCalendar/edgeStep.js.map +1 -1
- package/dist/components/VCalendar/gesture.d.ts +10 -9
- package/dist/components/VCalendar/gesture.js +60 -45
- package/dist/components/VCalendar/gesture.js.map +1 -1
- package/dist/components/VCalendar/keyboard.d.ts +22 -5
- package/dist/components/VCalendar/keyboard.js +17 -16
- package/dist/components/VCalendar/keyboard.js.map +1 -1
- package/dist/components/VCalendar/layout.d.ts +47 -37
- package/dist/components/VCalendar/layout.js +47 -37
- package/dist/components/VCalendar/layout.js.map +1 -1
- package/dist/components/VCalendar/timeGrid.d.ts +4 -4
- package/dist/components/VCalendar/timeGrid.js +6 -6
- package/dist/components/VCalendar/timeGrid.js.map +1 -1
- package/dist/components/VCalendar/types.d.ts +5 -5
- package/dist/components/VCarousel/VCarousel.css +1 -1
- package/dist/components/VCarousel/VCarousel.d.ts +59 -38
- package/dist/components/VCarousel/VCarousel.js.map +1 -1
- package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js +161 -132
- package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCarousel/VCarouselItem.d.ts +1 -1
- package/dist/components/VCarousel/VCarouselItem.js.map +1 -1
- package/dist/components/VCarousel/VCarouselItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCarousel/context.d.ts +1 -1
- package/dist/components/VCarousel/context.js.map +1 -1
- package/dist/components/VCheckbox/VCheckbox.css +1 -1
- package/dist/components/VCheckbox/VCheckbox.d.ts +2 -2
- package/dist/components/VCheckbox/VCheckbox.js.map +1 -1
- package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js +10 -8
- package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VChip/VChip.css +1 -1
- package/dist/components/VChip/VChip.d.ts +7 -2
- package/dist/components/VChip/VChip.js.map +1 -1
- package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js +69 -55
- package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCombobox/VCombobox.css +1 -1
- package/dist/components/VCombobox/VCombobox.d.ts +50 -36
- package/dist/components/VCombobox/VCombobox.js.map +1 -1
- package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +167 -161
- package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCombobox/VComboboxGroup.css +1 -1
- package/dist/components/VCombobox/VComboboxGroup.js.map +1 -1
- package/dist/components/VCombobox/VComboboxGroup.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/VCombobox/VComboboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCombobox/infiniteScroll.d.ts +12 -3
- package/dist/components/VCombobox/infiniteScroll.js +18 -12
- package/dist/components/VCombobox/infiniteScroll.js.map +1 -1
- package/dist/components/VDataTable/VDataTable.css +1 -1
- package/dist/components/VDataTable/VDataTable.d.ts +55 -24
- package/dist/components/VDataTable/VDataTable.js.map +1 -1
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +192 -155
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDateInput/VDateInput.css +1 -1
- package/dist/components/VDateInput/VDateInput.d.ts +15 -12
- package/dist/components/VDateInput/VDateInput.js.map +1 -1
- package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +180 -160
- package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDateInput/mask.d.ts +95 -0
- package/dist/components/VDateInput/mask.js +118 -0
- package/dist/components/VDateInput/mask.js.map +1 -0
- package/dist/components/VDatePicker/VDatePicker.css +1 -1
- package/dist/components/VDatePicker/VDatePicker.d.ts +6 -5
- package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
- package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js +204 -173
- package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDatePicker/keyboard.d.ts +3 -3
- package/dist/components/VDatePicker/keyboard.js.map +1 -1
- package/dist/components/VDatePicker/names.d.ts +34 -0
- package/dist/components/VDatePicker/names.js +42 -0
- package/dist/components/VDatePicker/names.js.map +1 -0
- package/dist/components/VDialog/VDialog.css +1 -1
- package/dist/components/VDialog/VDialog.d.ts +2 -2
- package/dist/components/VDialog/VDialog.js.map +1 -1
- package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js +63 -41
- package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VFileInput/VFileInput.d.ts +38 -25
- package/dist/components/VFileInput/VFileInput.js.map +1 -1
- package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +74 -71
- package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VFileInput/truncate.d.ts +4 -4
- package/dist/components/VFileInput/truncate.js.map +1 -1
- package/dist/components/VFilePicker/VFilePicker.css +1 -1
- package/dist/components/VFilePicker/VFilePicker.d.ts +54 -37
- package/dist/components/VFilePicker/VFilePicker.js.map +1 -1
- package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js +152 -135
- package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VFilePicker/fileKind.d.ts +2 -2
- package/dist/components/VFilePicker/fileKind.js.map +1 -1
- package/dist/components/VHotkeys/VHotkeys.css +1 -1
- package/dist/components/VHotkeys/VHotkeys.d.ts +10 -8
- package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
- package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js +22 -20
- package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VHotkeys/platform.d.ts +9 -7
- package/dist/components/VHotkeys/platform.js +41 -30
- package/dist/components/VHotkeys/platform.js.map +1 -1
- package/dist/components/VIcon/VIcon.css +1 -1
- package/dist/components/VIcon/VIcon.d.ts +6 -6
- package/dist/components/VIcon/VIcon.js.map +1 -1
- package/dist/components/VIcon/VIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VIcon/iconProps.d.ts +4 -4
- package/dist/components/VIcon/iconProps.js.map +1 -1
- package/dist/components/VIcon/icons/index.d.ts +1 -1
- package/dist/components/VIcon/icons/index.js.map +1 -1
- package/dist/components/VIcon/icons/names.d.ts +1 -1
- package/dist/components/VIcon/icons/names.js.map +1 -1
- package/dist/components/VIcon/resolver.d.ts +3 -3
- package/dist/components/VIcon/resolver.js +23 -20
- package/dist/components/VIcon/resolver.js.map +1 -1
- package/dist/components/VIcon/types.d.ts +4 -4
- package/dist/components/VIconButton/VIconButton.d.ts +4 -4
- package/dist/components/VIconButton/VIconButton.js.map +1 -1
- package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/VInput.d.ts +16 -8
- package/dist/components/VInput/VInput.js.map +1 -1
- package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +22 -16
- 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.js.map +1 -1
- package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js +43 -28
- 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 +3 -3
- package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
- package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +152 -85
- package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenu.d.ts +4 -4
- package/dist/components/VMenu/VMenu.js.map +1 -1
- package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenuGroup.css +1 -1
- package/dist/components/VMenu/VMenuGroup.js.map +1 -1
- package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js +18 -11
- package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenuItem.css +1 -1
- package/dist/components/VMenu/VMenuItem.d.ts +6 -6
- package/dist/components/VMenu/VMenuItem.js.map +1 -1
- package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js +83 -69
- package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenuPanel.css +1 -1
- 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 +56 -55
- package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/context.d.ts +2 -2
- package/dist/components/VMenu/context.js.map +1 -1
- package/dist/components/VPagination/VPagination.css +1 -1
- package/dist/components/VPagination/VPagination.d.ts +22 -9
- package/dist/components/VPagination/VPagination.js.map +1 -1
- package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js +67 -57
- package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VPopover/VPopover.css +1 -1
- package/dist/components/VPopover/VPopover.d.ts +1 -1
- package/dist/components/VPopover/VPopover.js.map +1 -1
- package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VProgressCircular/VProgressCircular.css +1 -1
- package/dist/components/VProgressCircular/VProgressCircular.d.ts +12 -7
- package/dist/components/VProgressCircular/VProgressCircular.js.map +1 -1
- package/dist/components/VProgressCircular/VProgressCircular.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VProgressLinear/VProgressLinear.css +1 -1
- package/dist/components/VProgressLinear/VProgressLinear.d.ts +15 -10
- package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
- package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VRadio/VRadio.css +1 -1
- package/dist/components/VRadio/VRadio.js.map +1 -1
- package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSeparator/VSeparator.d.ts +1 -1
- package/dist/components/VSeparator/VSeparator.js.map +1 -1
- package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigation.d.ts +2 -2
- package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationGroup.css +1 -1
- package/dist/components/VSideNavigation/VSideNavigationGroup.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationItem.css +1 -1
- package/dist/components/VSideNavigation/VSideNavigationItem.d.ts +12 -13
- package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js +87 -68
- package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSkeletonLoader/VSkeletonLoader.css +1 -1
- package/dist/components/VSkeletonLoader/VSkeletonLoader.d.ts +10 -7
- package/dist/components/VSkeletonLoader/VSkeletonLoader.js.map +1 -1
- package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js +5 -5
- package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSlider/VSlider.css +1 -1
- package/dist/components/VSlider/VSlider.d.ts +21 -7
- package/dist/components/VSlider/VSlider.js.map +1 -1
- package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js +159 -140
- package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSnackbar/VSnackbar.css +1 -1
- package/dist/components/VSnackbar/VSnackbar.d.ts +4 -4
- package/dist/components/VSnackbar/VSnackbar.js.map +1 -1
- package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js +85 -59
- package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSnackbar/state.d.ts +7 -7
- package/dist/components/VSnackbar/state.js +13 -11
- package/dist/components/VSnackbar/state.js.map +1 -1
- package/dist/components/VSpinner/VSpinner.css +1 -1
- package/dist/components/VSpinner/VSpinner.js.map +1 -1
- package/dist/components/VSpinner/VSpinner.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSwitch/VSwitch.css +1 -1
- package/dist/components/VSwitch/VSwitch.d.ts +1 -1
- package/dist/components/VSwitch/VSwitch.js.map +1 -1
- package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js +4 -4
- 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 +4 -0
- package/dist/components/VTabs/VTab.js.map +1 -1
- package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js +9 -8
- 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 +17 -7
- package/dist/components/VTabs/VTabs.js.map +1 -1
- package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js +84 -77
- package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTextarea/VTextarea.d.ts +8 -8
- package/dist/components/VTextarea/VTextarea.js.map +1 -1
- package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js +71 -69
- package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeInput/VTimeInput.css +1 -1
- package/dist/components/VTimeInput/VTimeInput.d.ts +21 -12
- package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
- package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +230 -206
- package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeInput/search.d.ts +17 -2
- package/dist/components/VTimeInput/search.js +19 -9
- package/dist/components/VTimeInput/search.js.map +1 -1
- package/dist/components/VTimePicker/VTimePicker.css +1 -1
- package/dist/components/VTimePicker/VTimePicker.d.ts +8 -7
- package/dist/components/VTimePicker/VTimePicker.js.map +1 -1
- package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js +128 -106
- package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimePicker/limits.d.ts +11 -4
- package/dist/components/VTimePicker/limits.js +3 -2
- package/dist/components/VTimePicker/limits.js.map +1 -1
- package/dist/components/VToast/VToast.css +1 -1
- package/dist/components/VToast/VToast.js.map +1 -1
- package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js +33 -37
- package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToast/VToaster.js.map +1 -1
- package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js +91 -61
- package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToast/state.d.ts +8 -6
- package/dist/components/VToast/state.js +15 -13
- package/dist/components/VToast/state.js.map +1 -1
- package/dist/components/VToggle/VToggle.css +1 -1
- package/dist/components/VToggle/VToggle.d.ts +9 -4
- package/dist/components/VToggle/VToggle.js.map +1 -1
- package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToggle/VToggleItem.d.ts +4 -0
- package/dist/components/VToggle/VToggleItem.js.map +1 -1
- package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js +5 -5
- package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTooltip/VTooltip.css +1 -1
- package/dist/components/VTooltip/VTooltip.d.ts +17 -10
- package/dist/components/VTooltip/VTooltip.js.map +1 -1
- package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js +46 -26
- package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTypography/VTypography.css +1 -1
- package/dist/components/VTypography/VTypography.d.ts +6 -4
- package/dist/components/VTypography/VTypography.js.map +1 -1
- package/dist/components/VTypography/VTypography.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/composables/useAriaLabel.js +1 -1
- package/dist/composables/useAriaLabel.js.map +1 -1
- package/dist/composables/useClearable.d.ts +2 -2
- package/dist/composables/useClearable.js.map +1 -1
- package/dist/composables/useDetailsOpen.d.ts +3 -3
- package/dist/composables/useDetailsOpen.js +14 -12
- package/dist/composables/useDetailsOpen.js.map +1 -1
- package/dist/composables/useFieldIds.d.ts +2 -1
- package/dist/composables/useFieldIds.js +6 -5
- package/dist/composables/useFieldIds.js.map +1 -1
- package/dist/composables/useFieldPanel.d.ts +2 -2
- package/dist/composables/useFieldPanel.js +47 -37
- package/dist/composables/useFieldPanel.js.map +1 -1
- package/dist/composables/useFileDrop.js +3 -3
- package/dist/composables/useFileDrop.js.map +1 -1
- package/dist/composables/useFileField.d.ts +1 -1
- package/dist/composables/useFileField.js +62 -41
- package/dist/composables/useFileField.js.map +1 -1
- package/dist/composables/useIconClickHandlers.d.ts +3 -3
- package/dist/composables/useIconClickHandlers.js +14 -13
- package/dist/composables/useIconClickHandlers.js.map +1 -1
- package/dist/composables/useInertLink.js +12 -10
- package/dist/composables/useInertLink.js.map +1 -1
- package/dist/composables/useLiveAnnouncer.d.ts +5 -0
- package/dist/composables/useLiveAnnouncer.js +23 -0
- package/dist/composables/useLiveAnnouncer.js.map +1 -0
- package/dist/composables/useMaskedField.d.ts +5 -5
- package/dist/composables/useMaskedField.js.map +1 -1
- package/dist/composables/usePopover.d.ts +3 -3
- package/dist/composables/usePopover.js +6 -4
- package/dist/composables/usePopover.js.map +1 -1
- package/dist/composables/useProgressValue.js +11 -2
- package/dist/composables/useProgressValue.js.map +1 -1
- package/dist/composables/useRootAttrs.d.ts +1 -1
- package/dist/composables/useRootAttrs.js.map +1 -1
- package/dist/composables/useSlotNodes.d.ts +18 -0
- package/dist/composables/useSlotNodes.js +13 -0
- package/dist/composables/useSlotNodes.js.map +1 -0
- package/dist/composables/useTextLimit.d.ts +2 -1
- package/dist/composables/useTextLimit.js +2 -2
- package/dist/composables/useTextLimit.js.map +1 -1
- package/dist/composables/useTimer.d.ts +5 -0
- package/dist/composables/useTimer.js +22 -11
- package/dist/composables/useTimer.js.map +1 -1
- package/dist/i18n/en.js +1 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/fr.js +14 -14
- package/dist/i18n/fr.js.map +1 -1
- package/dist/i18n/state.d.ts +7 -7
- package/dist/i18n/state.js +37 -28
- package/dist/i18n/state.js.map +1 -1
- package/dist/i18n/types.d.ts +12 -12
- package/dist/icons.d.ts +2 -2
- package/dist/index.d.ts +12 -11
- package/dist/styles.css +1 -1
- package/dist/tokens/css.d.ts +1 -1
- package/dist/tokens/css.js.map +1 -1
- package/dist/tokens/index.d.ts +6 -0
- package/dist/tokens/semantic.d.ts +6 -0
- package/dist/tokens/semantic.js +66 -60
- package/dist/tokens/semantic.js.map +1 -1
- package/dist/tokens/themes/dark.js +3 -3
- package/dist/tokens/themes/dark.js.map +1 -1
- package/dist/tokens.json +130 -66
- package/dist/types.d.ts +1 -1
- package/dist/utils/arrowNav.d.ts +4 -1
- package/dist/utils/arrowNav.js +7 -7
- package/dist/utils/arrowNav.js.map +1 -1
- package/dist/utils/chip.d.ts +1 -1
- package/dist/utils/chip.js.map +1 -1
- package/dist/utils/clock.d.ts +113 -0
- package/dist/utils/clock.js +84 -0
- package/dist/utils/clock.js.map +1 -0
- package/dist/utils/css.d.ts +6 -4
- package/dist/utils/css.js +2 -2
- package/dist/utils/css.js.map +1 -1
- package/dist/utils/date.d.ts +7 -102
- package/dist/utils/date.js +33 -160
- package/dist/utils/date.js.map +1 -1
- package/dist/utils/direction.d.ts +1 -1
- package/dist/utils/direction.js.map +1 -1
- package/dist/utils/env.d.ts +12 -3
- package/dist/utils/env.js +6 -0
- package/dist/utils/env.js.map +1 -0
- package/dist/utils/file.d.ts +3 -3
- package/dist/utils/file.js +1 -1
- package/dist/utils/file.js.map +1 -1
- package/dist/utils/focus.d.ts +1 -1
- package/dist/utils/focus.js.map +1 -1
- package/dist/utils/hostWarns.d.ts +1 -1
- package/dist/utils/hostWarns.js.map +1 -1
- package/dist/utils/ids.d.ts +1 -1
- package/dist/utils/ids.js.map +1 -1
- package/dist/utils/matcher.d.ts +2 -2
- package/dist/utils/matcher.js.map +1 -1
- package/dist/utils/memo.d.ts +1 -1
- package/dist/utils/memo.js.map +1 -1
- package/dist/utils/text.d.ts +3 -3
- package/dist/utils/text.js.map +1 -1
- package/dist/utils/time.d.ts +2 -101
- package/dist/utils/time.js +22 -98
- package/dist/utils/time.js.map +1 -1
- package/dist/utils/vnode.d.ts +5 -3
- package/dist/utils/vnode.js.map +1 -1
- package/package.json +2 -2
|
@@ -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 * An inline clock for choosing a time, the hour-and-minute counterpart of VDatePicker.\n * VTimeInput does no more than dress it in a field and a popover; it is usable on its own.\n *\n * Three parts: two large numerals saying what has been chosen and switching between hours\n * and minutes, the AM/PM toggle beside them on a 12-hour clock, and the face below, where a\n * time is POINTED AT rather than stepped through.\n *\n * Nothing native covers choosing a value by angle, so the JS does two things — turning a\n * point on the face into a time (the face measured once, then pure trigonometry in\n * `utils/time`) and implementing a slider's keyboard.\n *\n * Everything one SEES is CSS: the numerals are placed around the circle and the hand turned,\n * both from one unitless turn fraction set inline.\n *\n * Exactly ONE focusable element on the face, announced as a slider. The numerals are\n * `aria-hidden` markers reached by angle rather than one cell at a time, which is why that\n * single element has to carry the whole spoken value.\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 {\n DIAL_INNER_THRESHOLD,\n angleToIndex,\n dialIndexToHour24,\n distanceFraction,\n formatTime,\n hour24ToDial,\n hourCycleFor,\n hourWithMeridiem,\n parseTime,\n snapMinute,\n to12h,\n to24h,\n} from '../../utils/time'\nimport type { HourFormat, Meridiem } from '../../utils/time'\nimport {\n allowedMinutesFor,\n firstAllowed,\n isHourAllowed,\n isTimeAllowed,\n limitsProblem,\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\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 /**\n * The earliest time that can be chosen, inclusive, as a canonical 24-hour `'HH:mm'`.\n * What it rules out is LEFT OFF the face, the way `minuteStep` leaves off the minutes\n * it cannot reach: the clock prints what can be chosen and nothing else.\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, so the\n * same rule holds on both faces. The hours it leaves out are not printed.\n */\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 /**\n * Shows the time without letting it be changed. The face keeps its focus and the two\n * numerals still switch between the hour and the minutes, so the value can be read in\n * full — which is what separates it from `disabled`.\n */\n readonly?: boolean\n /**\n * The accessible name of the whole clock, its two numerals and its face together. It\n * falls back to the dictionary, and a consumer `aria-label` wins over it. The face\n * keeps its own name, which says whether the hand is on the hour or the minutes.\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/**\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 *\n * With no value at all the clock shows midnight. It is deliberately NOT the current time:\n * reading the clock while rendering would make a page drawn on a server disagree with the\n * same page in the browser. A component that wants to open on the current time sets it\n * from a handler — which is exactly what VTimeInput does.\n */\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\n * the keyboard. Releasing the pointer does not count — on a clock face, letting go of the\n * hand is how one stops adjusting it, not how one confirms. VTimeInput listens to it to\n * commit its draft and close its panel.\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 — 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/**\n * The hours the restrictions still leave something in, worked out once per set of\n * restrictions. Every hour on the face, both AM/PM buttons and every key ask the question,\n * and each answer walks a whole hour of minutes: asked afresh, a drag re-walked all 24 of\n * them at pointer rate.\n */\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\n// Silent inside a host that forwards these props and reports on them itself (utils/hostWarns).\nif (isDev && !inject(hostWarnsKey, false)) {\n watchEffect(() => {\n if (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\nconst parts = computed(() => parseTime(model.value) ?? { hour: 0, minute: 0 })\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 * is what refuses it; one that is only PARTLY available pulls the minutes to what it does\n * allow, since `min: '09:30'` must not be left holding 09:00.\n *\n * The minute in force is kept whenever it is allowed, which is also what leaves an\n * unrestricted clock exactly as it was: a value off the step, 09:07 on a quarter-hour\n * face, belongs to the consumer and is not ours to round.\n */\nfunction setHour(value: number | null) {\n // The two writers below are the ONLY way the value ever changes — pointer, keys and the\n // half-day control all come through them — so the two states are refused here and\n // nowhere else. Changing which face is shown stays allowed under `readonly`: reading\n // the minutes is not 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\n * change of name is not reliably announced. A politely announced region says it a second\n * time. The wording has no prop: the dictionary is where it is changed.\n *\n * It is written HERE, by the function the reader's own gestures go through, rather than by\n * a watcher on the step: putting the clock back on the hour between two openings — what\n * `reset` does — must stay silent, and a watcher could not tell the two apart.\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/**\n * The hour a position on the face stands for. On a 12-hour clock that is what the half of\n * the day in force turns it into, and it is the 24-hour hour either way: the one the\n * restrictions answer about, and the one written.\n */\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/**\n * The minutes the face prints. A numeral one can point at and not land on is a lie, which\n * the hand tells the moment it settles beside it rather than on it, so a marker is only\n * ever a minute `minuteStep` actually reaches AND the restrictions allow.\n *\n * How MANY of them is the other half of the question: sixty numerals would be unreadable,\n * so a step fine enough to offer more than twelve values keeps the five-minute grid a\n * clock is read on — minus, again, whatever it cannot reach, a step of two printing ten\n * past and not five past.\n */\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 * The face prints what can be chosen and nothing else — the minute step's own rule, held\n * to for all four restrictions rather than for one of them. A numeral that is only there\n * to be refused says nothing the missing numeral does not say better, and it says it in\n * grey text a screen reader has no way to reach: the markers are `aria-hidden`, so what\n * the restrictions leave is spoken by the face's own value and by 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\n // the day in force. The selection there still compares numerals and not hours: an empty\n // clock 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/**\n * Which half of the day is chosen while NO time is set at all. The AM/PM control always\n * needs a value — it refuses to have none — and \"nothing\" is not one.\n *\n * It starts at AM rather than at whatever the current time happens to be, which keeps the\n * component's first render identical on a server and in a browser, and its tests free of\n * a clock. VTimeInput keeps one for its own AM/PM button, on the same terms.\n */\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\n * until then. Every hour written on a 12-hour face goes through it — without which a\n * reader who picks PM on an empty clock and then an hour would get the morning back, the\n * value having no half-day of its own to read.\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 — the entire spoken value of the face. The numerals are hidden from screen\n// readers, so these four attributes are the ONLY thing assistive technology has: what the\n// value is, what its 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)\nconst ariaValueText = computed(() =>\n step.value === 'minute'\n ? m.value.timePicker.minutesValue(minute.value)\n : m.value.timePicker.hourValue(ariaValueNow.value),\n)\n\n/**\n * A step has been settled: after the hour come the minutes, and after the minutes the\n * whole 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\n// Pointing at the face, by click or by drag.\nconst faceEl = ref<HTMLElement | null>(null)\nconst dragging = ref(false)\n/**\n * Whether anything in the gesture under way landed on a numeral. It is the whole gesture\n * and not its last point: a drag that chose an hour and wandered off the ring before\n * being released has still chosen one, and the step follows the value rather than the\n * pixel let go of.\n */\nlet landed = false\n\n/**\n * Where a point on the face lands, and whether there was anything there at all. A\n * position the restrictions took a numeral off holds nothing to aim at, so the gesture\n * does nothing whatever: no value, and — through the answer returned here — no step moved\n * on either. The pointer catches nothing it was not aimed at.\n *\n * The question asked is the FACE's, `isAvailableHour`/`isTimeAllowed` being the pair\n * `cells` prints from, so \"there is a numeral here\" and \"this can be written\" cannot come\n * apart. The write itself still goes through `setHour`/`setMinute` and their own guards,\n * which is why `readonly` is refused THERE and not here: a frozen clock is still one\n * whose numerals are 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 if (!face) 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 (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 // Capturing the pointer is what keeps the drag alive when it wanders off the face.\n //\n // @fallback — it is wrapped because a pointer event fired by a TEST refers to no real\n // pointer, and the call then throws. Failing to capture merely means the drag stops at\n // the edge, which no test is checking.\n try {\n faceEl.value?.setPointerCapture(event.pointerId)\n } catch {\n /* a synthetic pointer: nothing to capture */\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 — 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\n * the first thing it may not: with scattered hours a key that stopped would die at the\n * first hole and never reach what lies past it. Against a bound there is nothing past the\n * hole, so the same walk comes back empty and the key holds still, which is what a bound\n * means. Restrict nothing and the first candidate answers: the walk is then the plain step\n * it has always been.\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 one it asked for when the hour allows it, and otherwise\n * the next one the same way round that it does.\n */\nfunction minuteFrom(start: number, direction: number): number | null {\n const interval = minuteInterval(props.minuteStep)\n const allowed = (candidate: number) => isTimeAllowed(hour.value, candidate, limits.value)\n if (allowed(start)) return start\n return firstAllowed(\n Math.ceil(60 / interval),\n (i) => snapMinute(start + direction * i * interval, interval),\n allowed,\n )\n}\n\nfunction onKeydown(event: KeyboardEvent) {\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)\n setMinute(\n minuteFrom(snapMinute(minute.value + delta * props.minuteStep, props.minuteStep), delta),\n )\n else if (event.key === 'PageUp')\n setMinute(minuteFrom(snapMinute(minute.value + 5, props.minuteStep), 1))\n else if (event.key === 'PageDown')\n setMinute(minuteFrom(snapMinute(minute.value - 5, props.minuteStep), -1))\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 is what 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\n * anything itself. A component holding this one in a panel calls it as the panel opens\n * and closes, so that every visit starts on the hour.\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 <!-- The two large numerals switch between adjusting the hour and the minutes. The one\n being adjusted takes the accent tone, which on a quiet button shows as the colour\n of the numeral rather than as a filled background.\n\n The choice of half-day sits beside them, on the same row: it is read as part of\n the time itself rather than as a property of the face. On a 24-hour clock there\n is no such choice, and the row then holds the numerals alone. -->\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=\"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=\"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 /* The numerals and the choice of half-day on one row, the second beside the first. The\n gap is logical, so the control follows the reading direction; the numerals inside it\n stay in a group of their own, which is what keeps the direction they force off it.\n\n The centring is what places the row whether or not that control is rendered — a\n 24-hour clock has none, and the numerals then come out centred on their own. */\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 /* The two large numerals are ordinary quiet buttons. What is overridden here is the\n \"large numeral\" look alone — the width and the type; the height, the states, the\n focus ring and the transitions all come from the button itself.\n\n The selector is qualified by an attribute that button always renders, which is what\n makes it win whatever order the two sheets end up in. */\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-2-weight);\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 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 /* The row above is wider than the face as soon as the half-day control is rendered,\n and the column is only as wide as its widest child: without this the clock would\n hang at the start edge of that row instead of under the middle of it. The face\n itself stays physical — a clock is never mirrored. */\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 /* How far from the centre the numerals sit — the outer circle. The numerals and the\n hand both read it, and the inner circle redefines it for itself.\n\n TRAP — this is coupled to the threshold that decides which of the two circles a\n point belongs to, in `utils/time`. Changing one without the other makes the face\n answer with the wrong ring, and no test can catch it: the unit tests measure\n nothing. */\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 numerals are placed around the circle by CSS trigonometry, from the fraction of a\n * turn each one was given — no coordinates are computed in code.\n *\n * The properties used are PHYSICAL on purpose, where the rest of the design system\n * prefers logical ones: a clock face is never mirrored, clockwise meaning the same thing\n * in every 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 /* Above the hand, so that the numeral being pointed at reads ON its dot rather than\n being covered by it. */\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 hand: a stroke anchored at the centre and turned by the same fraction of a turn.\n *\n * TRAP — the rotation is deliberately NOT animated. Going from 55 minutes to 0 takes the\n * angle from nearly a full turn back to none, and an interpolation would sweep the hand\n * all the way round anticlockwise. That is an accepted departure from the Material\n * design it follows otherwise.\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 /* The dot at the tip of the hand. It is exactly the size of a numeral's cell, so it\n covers the one being pointed at — whose text turns to the colour that reads on it. */\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 * On a minute with no marker of its own, the dot covers no numeral and at full size\n * would spill onto both neighbours, reading as pointing at neither. Shrunk, it becomes\n * the precise marker itself — which is also why the pale centre Material draws inside it\n * is dropped here: at this size it would turn the dot into a ring.\n *\n * TRAP — only the DOT's own size changes. Redefining the numeral size variable here\n * would also move the radius derived from it, and with it the hand's length and the\n * 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 * A disabled clock greys out through the colour tokens and never through opacity: the\n * hand and its centre dot are the two accent-painted parts, and the numeral under the\n * dot is painted for an accent background it no longer has.\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 /* The foot of the clock, separated from the face the way VDatePicker's is from its grid. */\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// @a11y @keyboard @core\n/**\n * An inline clock for choosing a time, the hour-and-minute counterpart of VDatePicker.\n * VTimeInput does no more than dress it in a field and a popover; it is usable on its own.\n *\n * Three parts: two large numerals saying what has been chosen and switching between hours\n * and minutes, the AM/PM toggle beside them on a 12-hour clock, and the face below, where a\n * time is POINTED AT rather than stepped through.\n *\n * Nothing native covers choosing a value by angle, so the JS does two things — turning a\n * point on the face into a time (the face measured once, then pure trigonometry in\n * `utils/clock`) and implementing a slider's keyboard.\n *\n * Everything one SEES is CSS: the numerals are placed around the circle and the hand turned,\n * both from one unitless turn fraction set inline.\n *\n * Exactly ONE focusable element on the face, announced as a slider. The numerals are\n * `aria-hidden` markers reached by angle rather than one cell at a time, which is why that\n * single element has to carry the whole spoken value.\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 /**\n * The earliest time that can be chosen, inclusive, as a canonical 24-hour `'HH:mm'`.\n * What it rules out is LEFT OFF the face, the way `minuteStep` leaves off the minutes\n * it cannot reach: the clock prints what can be chosen and nothing else.\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, so the\n * same rule holds on both faces. The hours it leaves out are not printed.\n */\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 /**\n * Shows the time without letting it be changed. The face keeps its focus and the two\n * numerals still switch between the hour and the minutes, so the value can be read in\n * full. That is what separates it from `disabled`.\n */\n readonly?: boolean\n /**\n * The accessible name of the whole clock, its two numerals and its face together. It\n * falls back to the dictionary, and a consumer `aria-label` wins over it. The face\n * keeps its own name, which says whether the hand is on the hour or the minutes.\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/**\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 *\n * With no value at all the clock shows midnight, or noon once PM has been chosen. It is\n * deliberately NOT the current time: reading the clock while rendering would make a page\n * drawn on a server disagree with the same page in the browser. A component that wants to\n * open on the current time sets it from a handler, which is what VTimeInput does.\n */\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\n * the keyboard. Releasing the pointer does not count: on a clock face, letting go of the\n * hand is how one stops adjusting it, not how one confirms. VTimeInput listens to it to\n * commit its draft and close its panel.\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/**\n * The hours the restrictions still leave something in, worked out once per set of\n * restrictions. Every hour on the face, both AM/PM buttons and every key ask the question,\n * and each answer walks a whole hour of minutes: asked afresh, a drag re-walked all 24 of\n * them at pointer rate.\n */\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\n// Silent inside a host that forwards these props and reports on them itself (utils/hostWarns).\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 * is what refuses it; one that is only PARTLY available pulls the minutes to what it does\n * allow, since `min: '09:30'` must not be left holding 09:00.\n *\n * The minute in force is kept whenever it is allowed, which is also what leaves an\n * unrestricted clock exactly as it was: a value off the step, 09:07 on a quarter-hour\n * face, belongs to the consumer and is not ours to round.\n */\nfunction setHour(value: number | null) {\n // The two writers below are the ONLY way the value ever changes — pointer, keys and the\n // half-day control all come through them — so the two states are refused here and\n // nowhere else. Changing which face is shown stays allowed under `readonly`: reading\n // the minutes is not 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\n * change of name is not reliably announced. A politely announced region says it a second\n * time. The wording has no prop: the dictionary is where it is changed.\n *\n * It is written HERE, by the function the reader's own gestures go through, rather than by\n * a watcher on the step: putting the clock back on the hour between two openings — what\n * `reset` does — must stay silent, and a watcher could not tell the two apart.\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/**\n * The hour a position on the face stands for. On a 12-hour clock that is what the half of\n * the day in force turns it into, and it is the 24-hour hour either way: the one the\n * restrictions answer about, and the one written.\n */\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/**\n * The minutes the face prints. A numeral one can point at and not land on is a lie, which\n * the hand tells the moment it settles beside it rather than on it, so a marker is only\n * ever a minute `minuteStep` actually reaches AND the restrictions allow.\n *\n * How MANY of them is the other half of the question: sixty numerals would be unreadable,\n * so a step fine enough to offer more than twelve values keeps the five-minute grid a\n * clock is read on — minus, again, whatever it cannot reach, a step of two printing ten\n * past and not five past.\n */\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 * The face prints what can be chosen and nothing else — the minute step's own rule, held\n * to for all four restrictions rather than for one of them. A numeral that is only there\n * to be refused says nothing the missing numeral does not say better, and it says it in\n * grey text a screen reader has no way to reach: the markers are `aria-hidden`, so what\n * the restrictions leave is spoken by the face's own value and by 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\n // the day in force. The selection there still compares numerals and not hours: an empty\n // clock 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/**\n * Which half of the day is chosen while NO time is set at all. The AM/PM control always\n * needs a value — it refuses to have none — and \"nothing\" is not one.\n *\n * It starts at AM rather than at whatever the current time happens to be, which keeps the\n * component's first render identical on a server and in a browser, and its tests free of\n * a clock. VTimeInput keeps one for its own AM/PM button, on the same terms.\n */\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\n * until then. Every hour written on a 12-hour face goes through it — without which a\n * reader who picks PM on an empty clock and then an hour would get the morning back, the\n * value having no half-day of its own to read.\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\n// readers, so these four attributes are the ONLY thing assistive technology has: what the\n// value is, what its 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\n * whole 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\n// Pointing at the face, by click or by drag.\nconst faceEl = ref<HTMLElement | null>(null)\nconst dragging = ref(false)\n/**\n * Whether anything in the gesture under way landed on a numeral. It is the whole gesture\n * and not its last point: a drag that chose an hour and wandered off the ring before\n * being released has still chosen one, and the step follows the value rather than the\n * pixel let go of.\n */\nlet landed = false\n\n/**\n * Where a point on the face lands, and whether there was anything there at all. A\n * position the restrictions took a numeral off holds nothing to aim at, so the gesture\n * does nothing whatever: no value, and — through the answer returned here — no step moved\n * on either. The pointer catches nothing it was not aimed at.\n *\n * The question asked is the FACE's, `isAvailableHour`/`isTimeAllowed` being the pair\n * `cells` prints from, so \"there is a numeral here\" and \"this can be written\" cannot come\n * apart. The write itself still goes through `setHour`/`setMinute` and their own guards,\n * which is why `readonly` is refused THERE and not here: a frozen clock is still one\n * whose numerals are 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 // `<=` so that the exact centre is refused even on a face measured at zero.\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 // Capturing the pointer is what keeps the drag alive when it wanders off the face. It\n // is wrapped because a pointer event fired by a TEST refers to no real pointer, and the\n // call then throws. Failing to capture merely means the drag stops at the edge, which\n // no test is checking.\n try {\n faceEl.value?.setPointerCapture(event.pointerId)\n } catch {\n /* a synthetic pointer: nothing to capture */\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\n * the first thing it may not: with scattered hours a key that stopped would die at the\n * first hole and never reach what lies past it. Against a bound there is nothing past the\n * hole, so the same walk comes back empty and the key holds still, which is what a bound\n * means. Restrict nothing and the first candidate answers: the walk is then the plain step\n * it has always been.\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,\n * that way round, and then the next one the hour allows.\n *\n * It walks the GRID rather than adding a step and rounding: from a minute off the grid,\n * 09:07 on a quarter-hour face, rounding sent the down arrow to :45 and let PageUp round\n * straight back to where it started. Past the end of the hour it wraps, onto the grid's own\n * first step, which is also what keeps a step that does not divide sixty on its grid.\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 // The first step at or past the target that way round, the grid's own end when none is.\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 is what 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\n * anything itself. A component holding this one in a panel calls it as the panel opens\n * and closes, so that every visit starts on the hour.\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 <!-- The two large numerals switch between adjusting the hour and the minutes. The one\n being adjusted takes the accent tone, which on a quiet button shows as the colour\n of the numeral rather than as a filled background.\n\n The choice of half-day sits beside them, on the same row: it is read as part of\n the time itself rather than as a property of the face. On a 24-hour clock there\n is no such choice, and the row then holds the numerals alone. -->\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 /* The numerals and the choice of half-day on one row, the second beside the first. The\n gap is logical, so the control follows the reading direction; the numerals inside it\n stay in a group of their own, which is what keeps the direction they force off it.\n\n The centring is what places the row whether or not that control is rendered — a\n 24-hour clock has none, and the numerals then come out centred on their own. */\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 /* The two large numerals are ordinary quiet buttons. What is overridden here is the\n \"large numeral\" look alone — the width and the type; the height, the states, the\n focus ring and the transitions all come from the button itself.\n\n The type is the `heading-1` recipe taken WHOLE, and the colon the `heading-2` one:\n read in halves, a consumer repointing `--vectis-text-heading-1-weight` would move every\n heading but these two numerals.\n\n The selector is qualified by an attribute that button always renders, which is what\n makes it win whatever order the two sheets end up in. */\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 /* The row above is wider than the face as soon as the half-day control is rendered,\n and the column is only as wide as its widest child: without this the clock would\n hang at the start edge of that row instead of under the middle of it. The face\n itself stays physical — a clock is never mirrored. */\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 /* How far from the centre the numerals sit — the outer circle. The numerals and the\n hand both read it, and the inner circle redefines it for itself.\n\n TRAP — this is coupled to the threshold that decides which of the two circles a\n point belongs to, in `utils/clock`. Changing one without the other makes the face\n answer with the wrong ring, and no test can catch it: the unit tests measure\n nothing. */\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 numerals are placed around the circle by CSS trigonometry, from the fraction of a\n * turn each one was given — no coordinates are computed in code.\n *\n * The properties used are PHYSICAL on purpose, where the rest of the design system\n * prefers logical ones: a clock face is never mirrored, clockwise meaning the same thing\n * in every 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 /* Above the hand, so that the numeral being pointed at reads ON its dot rather than\n being covered by it. */\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 hand: a stroke anchored at the centre and turned by the same fraction of a turn.\n *\n * TRAP — the rotation is deliberately NOT animated. Going from 55 minutes to 0 takes the\n * angle from nearly a full turn back to none, and an interpolation would sweep the hand\n * all the way round anticlockwise. That is an accepted departure from the Material\n * design it follows otherwise.\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 /* The dot at the tip of the hand. It is exactly the size of a numeral's cell, so it\n covers the one being pointed at — whose text turns to the colour that reads on it. */\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 * On a minute with no marker of its own, the dot covers no numeral and at full size\n * would spill onto both neighbours, reading as pointing at neither. Shrunk, it becomes\n * the precise marker itself — which is also why the pale centre Material draws inside it\n * is dropped here: at this size it would turn the dot into a ring.\n *\n * TRAP — only the DOT's own size changes. Redefining the numeral size variable here\n * would also move the radius derived from it, and with it the hand's length and the\n * 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 * A disabled clock greys out through the colour tokens and never through opacity: the\n * hand and its centre dot are the two accent-painted parts, and the numeral under the\n * dot is painted for an accent background it no longer has.\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 /* The foot of the clock, separated from the face the way VDatePicker's is from its grid. */\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,20 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import n from "
|
|
3
|
-
import
|
|
4
|
-
import i from "
|
|
5
|
-
import a from "../VToggle/
|
|
6
|
-
import
|
|
7
|
-
import "../../utils/
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
1
|
+
import { isDev as e } from "../../utils/env.js";
|
|
2
|
+
import { useMessages as t, useResolvedLocale as n } from "../../i18n/state.js";
|
|
3
|
+
import r from "../VButton/VButton.js";
|
|
4
|
+
import { useAriaLabel as i } from "../../composables/useAriaLabel.js";
|
|
5
|
+
import a from "../VToggle/VToggle.js";
|
|
6
|
+
import o from "../VToggle/VToggleItem.js";
|
|
7
|
+
import { pad2 as s } from "../../utils/text.js";
|
|
8
|
+
import { hostWarnsKey as ee } from "../../utils/hostWarns.js";
|
|
9
|
+
import { formatTime as c, hourCycleFor as te, parseTime as ne } from "../../utils/time.js";
|
|
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";
|
|
11
|
+
import { allowedMinutesFor as ce, firstAllowed as f, isHourAllowed as le, isTimeAllowed as p, limitsProblem as ue, minuteGrid as de, minuteInterval as fe, nearestAllowedMinute as pe, resolveLimits as me } from "./limits.js";
|
|
12
|
+
import { Fragment as he, computed as m, createBlock as ge, createCommentVNode as _e, createElementBlock as h, createElementVNode as g, createTextVNode as _, createVNode as v, defineComponent as y, inject as ve, mergeModels as b, normalizeStyle as x, openBlock as S, ref as C, renderList as ye, renderSlot as be, toDisplayString as w, unref as T, useModel as xe, watchEffect as Se, withCtx as E } from "vue";
|
|
11
13
|
//#region src/components/VTimePicker/VTimePicker.vue?vue&type=script&setup=true&lang.ts
|
|
12
|
-
var
|
|
14
|
+
var Ce = [
|
|
13
15
|
"aria-label",
|
|
14
16
|
"data-step",
|
|
15
17
|
"data-disabled",
|
|
16
18
|
"data-readonly"
|
|
17
|
-
],
|
|
19
|
+
], we = { class: "v-time-picker-header" }, Te = { class: "v-time-picker-time" }, Ee = [
|
|
18
20
|
"tabindex",
|
|
19
21
|
"aria-label",
|
|
20
22
|
"aria-valuemin",
|
|
@@ -24,10 +26,10 @@ var _e = [
|
|
|
24
26
|
"data-dragging",
|
|
25
27
|
"aria-disabled",
|
|
26
28
|
"aria-readonly"
|
|
27
|
-
],
|
|
29
|
+
], De = ["data-ring", "data-minor"], Oe = ["data-ring", "data-selected"], ke = {
|
|
28
30
|
class: "v-visually-hidden",
|
|
29
31
|
"aria-live": "polite"
|
|
30
|
-
},
|
|
32
|
+
}, Ae = {
|
|
31
33
|
key: 0,
|
|
32
34
|
class: "v-time-picker-footer"
|
|
33
35
|
}, D = /*@__PURE__*/ y({
|
|
@@ -61,131 +63,151 @@ var _e = [
|
|
|
61
63
|
}),
|
|
62
64
|
emits: /*@__PURE__*/ b(["confirm"], ["update:modelValue"]),
|
|
63
65
|
setup(y, { expose: b, emit: D }) {
|
|
64
|
-
let O = y, k =
|
|
66
|
+
let O = y, k = xe(y, "modelValue"), je = D, A = t(), Me = i(() => O.label ?? A.value.timePicker.label), Ne = n(() => O.locale), j = m(() => O.format ?? te(Ne.value)), M = m(() => me(O)), Pe = m(() => {
|
|
65
67
|
let e = /* @__PURE__ */ new Set();
|
|
66
|
-
for (let t = 0; t < 24; t += 1)
|
|
68
|
+
for (let t = 0; t < 24; t += 1) le(t, M.value) && e.add(t);
|
|
67
69
|
return e;
|
|
68
|
-
}), N = (e) =>
|
|
69
|
-
|
|
70
|
+
}), N = (e) => Pe.value.has(e);
|
|
71
|
+
e && !ve(ee, !1) && Se(() => {
|
|
72
|
+
(!Number.isInteger(O.minuteStep) || O.minuteStep < 1 || 60 % O.minuteStep != 0) && console.warn(`[VTimePicker] minuteStep ${O.minuteStep} — a divisor of 60 is expected.`);
|
|
73
|
+
let e = ue(M.value);
|
|
74
|
+
e && console.warn(`[VTimePicker] ${e}`);
|
|
75
|
+
});
|
|
76
|
+
let P = C("hour"), F = m(() => ne(k.value) ?? {
|
|
77
|
+
hour: j.value === "12h" ? d(12, G.value) : 0,
|
|
70
78
|
minute: 0
|
|
71
79
|
}), I = m(() => F.value.hour), L = m(() => F.value.minute);
|
|
72
80
|
function R(e) {
|
|
73
81
|
if (O.disabled || O.readonly || e === null) return;
|
|
74
|
-
let t =
|
|
75
|
-
t !== null && (k.value =
|
|
82
|
+
let t = p(e, L.value, M.value) ? L.value : pe(e, L.value, M.value);
|
|
83
|
+
t !== null && (k.value = c(e, t));
|
|
76
84
|
}
|
|
77
85
|
function z(e) {
|
|
78
|
-
O.disabled || O.readonly || e === null || !
|
|
86
|
+
O.disabled || O.readonly || e === null || !p(I.value, e, M.value) || (k.value = c(I.value, e));
|
|
79
87
|
}
|
|
80
88
|
let B = C("");
|
|
81
89
|
function V(e) {
|
|
82
90
|
P.value = e, B.value = e === "minute" ? A.value.timePicker.choosingMinutes : A.value.timePicker.choosingHour;
|
|
83
91
|
}
|
|
84
92
|
function H(e, t) {
|
|
85
|
-
return j.value === "24h" ?
|
|
93
|
+
return j.value === "24h" ? re(e, t) : d(e === 0 ? 12 : e, K.value);
|
|
86
94
|
}
|
|
87
95
|
let U = m(() => {
|
|
88
|
-
let e =
|
|
89
|
-
for (let r = 0; r < 60; r += t) r % e === 0 &&
|
|
96
|
+
let e = fe(O.minuteStep), t = 60 / e <= 12 ? e : 5, n = [];
|
|
97
|
+
for (let r = 0; r < 60; r += t) r % e === 0 && p(I.value, r, M.value) && n.push(r);
|
|
90
98
|
return n;
|
|
91
|
-
}),
|
|
99
|
+
}), Fe = m(() => {
|
|
92
100
|
if (P.value === "minute") return U.value.map((e) => ({
|
|
93
101
|
key: `m-${e}`,
|
|
94
|
-
label:
|
|
102
|
+
label: s(e),
|
|
95
103
|
turn: e / 60,
|
|
96
104
|
ring: "outer",
|
|
97
105
|
selected: L.value === e
|
|
98
106
|
}));
|
|
99
|
-
let e = j.value === "12h", t =
|
|
107
|
+
let e = j.value === "12h", t = u(I.value).hour, n = e ? ["outer"] : ["outer", "inner"], r = [];
|
|
100
108
|
for (let i of n) for (let n = 0; n < 12; n += 1) {
|
|
101
109
|
let a = H(n, i);
|
|
102
110
|
if (!N(a)) continue;
|
|
103
|
-
let
|
|
111
|
+
let o = n === 0 ? 12 : n;
|
|
104
112
|
r.push({
|
|
105
113
|
key: `${i === "outer" ? "o" : "i"}-${n}`,
|
|
106
|
-
label: i === "inner" ?
|
|
114
|
+
label: i === "inner" ? s(a) : String(o),
|
|
107
115
|
turn: n / 12,
|
|
108
116
|
ring: i,
|
|
109
|
-
selected: e ? t ===
|
|
117
|
+
selected: e ? t === o : I.value === a
|
|
110
118
|
});
|
|
111
119
|
}
|
|
112
120
|
return r;
|
|
113
|
-
}),
|
|
114
|
-
get: () =>
|
|
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(() => s(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
|
+
get: () => K.value,
|
|
115
123
|
set: (e) => {
|
|
116
124
|
let t = e === "PM" ? "PM" : "AM";
|
|
117
|
-
if (
|
|
118
|
-
let n =
|
|
119
|
-
R(N(n) ? n :
|
|
125
|
+
if (G.value = t, !k.value) return;
|
|
126
|
+
let n = oe(I.value, t);
|
|
127
|
+
R(N(n) ? n : f(12, (e) => d(e, t), N));
|
|
120
128
|
}
|
|
121
|
-
}),
|
|
122
|
-
AM:
|
|
123
|
-
PM:
|
|
124
|
-
})),
|
|
125
|
-
|
|
126
|
-
P.value === "
|
|
129
|
+
}), J = m(() => ({
|
|
130
|
+
AM: f(12, (e) => d(e, "AM"), N) !== null,
|
|
131
|
+
PM: f(12, (e) => d(e, "PM"), N) !== null
|
|
132
|
+
})), Y = m(() => P.value === "minute" ? L.value : j.value === "12h" ? u(I.value).hour : I.value), ze = m(() => P.value === "minute" ? 0 : +(j.value === "12h")), Be = m(() => P.value === "minute" ? 59 : j.value === "12h" ? 12 : 23), Ve = m(() => {
|
|
133
|
+
let e = A.value.timePicker;
|
|
134
|
+
if (P.value === "minute") return e.minutesValue(L.value);
|
|
135
|
+
let t = e.hourValue(Y.value);
|
|
136
|
+
return j.value === "24h" ? t : `${t} ${K.value === "PM" ? e.pm : e.am}`;
|
|
137
|
+
});
|
|
138
|
+
function He(e) {
|
|
139
|
+
P.value === "hour" ? V("minute") : e === "keyboard" && je("confirm", k.value);
|
|
127
140
|
}
|
|
128
141
|
let X = C(null), Z = C(!1), Q = !1;
|
|
129
|
-
function
|
|
142
|
+
function Ue(e) {
|
|
130
143
|
let t = X.value;
|
|
131
|
-
if (!t) return !1;
|
|
144
|
+
if (!t || O.disabled) return !1;
|
|
132
145
|
let n = t.getBoundingClientRect(), r = e.clientX - (n.left + n.width / 2), i = e.clientY - (n.top + n.height / 2);
|
|
146
|
+
if (Math.hypot(r, i) <= .2 * (n.width / 2)) return !1;
|
|
133
147
|
if (P.value === "minute") {
|
|
134
|
-
let e =
|
|
135
|
-
return
|
|
148
|
+
let e = se(l(r, i, 60), O.minuteStep);
|
|
149
|
+
return p(I.value, e, M.value) ? (z(e), !0) : !1;
|
|
136
150
|
}
|
|
137
|
-
let a = H(
|
|
151
|
+
let a = H(l(r, i, 12), j.value === "24h" && ie(r, i, n.width / 2) < .625 ? "inner" : "outer");
|
|
138
152
|
return N(a) ? (R(a), !0) : !1;
|
|
139
153
|
}
|
|
140
|
-
function
|
|
154
|
+
function We(e) {
|
|
141
155
|
try {
|
|
142
156
|
X.value?.setPointerCapture(e.pointerId);
|
|
143
157
|
} catch {}
|
|
144
|
-
Z.value = !0, Q =
|
|
158
|
+
Z.value = !0, Q = Ue(e);
|
|
145
159
|
}
|
|
146
|
-
function
|
|
147
|
-
Z.value &&
|
|
160
|
+
function Ge(e) {
|
|
161
|
+
Z.value && Ue(e) && (Q = !0);
|
|
148
162
|
}
|
|
149
|
-
function
|
|
150
|
-
Z.value && (Z.value = !1, Q &&
|
|
163
|
+
function Ke() {
|
|
164
|
+
Z.value && (Z.value = !1, Q && He("pointer"));
|
|
151
165
|
}
|
|
152
|
-
function
|
|
166
|
+
function qe() {
|
|
153
167
|
Z.value = !1;
|
|
154
168
|
}
|
|
155
|
-
function
|
|
169
|
+
function Je(e) {
|
|
156
170
|
if (j.value === "24h") {
|
|
157
|
-
R(
|
|
171
|
+
R(f(23, (t) => (I.value + e * t + 24) % 24, N));
|
|
158
172
|
return;
|
|
159
173
|
}
|
|
160
|
-
let t =
|
|
161
|
-
R(
|
|
174
|
+
let t = u(I.value).hour;
|
|
175
|
+
R(f(11, (n) => d((t - 1 + e * n + 12) % 12 + 1, K.value), N));
|
|
162
176
|
}
|
|
163
|
-
function
|
|
164
|
-
return j.value === "24h" ?
|
|
177
|
+
function Ye(e) {
|
|
178
|
+
return j.value === "24h" ? f(24, (t) => e === "first" ? t - 1 : 24 - t, N) : f(12, (t) => d(e === "first" ? t : 13 - t, K.value), N);
|
|
165
179
|
}
|
|
166
180
|
function $(e, t) {
|
|
167
|
-
let n =
|
|
168
|
-
|
|
181
|
+
let n = de(O.minuteStep), r = ((L.value + e * t) % 60 + 60) % 60, i = n.length, a = e > 0 ? 0 : i - 1;
|
|
182
|
+
for (let t = 0; t < i; t += 1) {
|
|
183
|
+
let o = e > 0 ? t : i - 1 - t;
|
|
184
|
+
if (e > 0 ? n[o] >= r : n[o] <= r) {
|
|
185
|
+
a = o;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return f(i, (t) => n[((a + e * (t - 1)) % i + i) % i], (e) => p(I.value, e, M.value));
|
|
169
190
|
}
|
|
170
|
-
function
|
|
191
|
+
function Xe(e) {
|
|
192
|
+
if (O.disabled) return;
|
|
171
193
|
if (e.key === "Enter" || e.key === " ") {
|
|
172
|
-
e.preventDefault(),
|
|
194
|
+
e.preventDefault(), He("keyboard");
|
|
173
195
|
return;
|
|
174
196
|
}
|
|
175
197
|
let t = e.key === "ArrowUp" || e.key === "ArrowRight" ? 1 : e.key === "ArrowDown" || e.key === "ArrowLeft" ? -1 : 0;
|
|
176
198
|
if (P.value === "hour") {
|
|
177
|
-
if (t)
|
|
178
|
-
else if (e.key === "Home") R(
|
|
179
|
-
else if (e.key === "End") R(
|
|
199
|
+
if (t) Je(t);
|
|
200
|
+
else if (e.key === "Home") R(Ye("first"));
|
|
201
|
+
else if (e.key === "End") R(Ye("last"));
|
|
180
202
|
else return;
|
|
181
203
|
e.preventDefault();
|
|
182
204
|
return;
|
|
183
205
|
}
|
|
184
|
-
if (t) z($(
|
|
185
|
-
else if (e.key === "PageUp") z($(
|
|
186
|
-
else if (e.key === "PageDown") z($(
|
|
187
|
-
else if (e.key === "Home") z(
|
|
188
|
-
else if (e.key === "End") z(
|
|
206
|
+
if (t) z($(t, 1));
|
|
207
|
+
else if (e.key === "PageUp") z($(1, 5));
|
|
208
|
+
else if (e.key === "PageDown") z($(-1, 5));
|
|
209
|
+
else if (e.key === "Home") z(ce(I.value, M.value)[0] ?? null);
|
|
210
|
+
else if (e.key === "End") z(ce(I.value, M.value).at(-1) ?? null);
|
|
189
211
|
else return;
|
|
190
212
|
e.preventDefault();
|
|
191
213
|
}
|
|
@@ -197,23 +219,23 @@ var _e = [
|
|
|
197
219
|
}), (e, t) => (S(), h("div", {
|
|
198
220
|
class: "v-time-picker",
|
|
199
221
|
role: "group",
|
|
200
|
-
"aria-label": T(
|
|
222
|
+
"aria-label": T(Me),
|
|
201
223
|
"data-step": P.value,
|
|
202
224
|
"data-disabled": y.disabled ? "" : void 0,
|
|
203
225
|
"data-readonly": y.readonly ? "" : void 0
|
|
204
226
|
}, [
|
|
205
|
-
g("div",
|
|
206
|
-
v(
|
|
227
|
+
g("div", we, [g("div", Te, [
|
|
228
|
+
v(r, {
|
|
207
229
|
class: "v-time-picker-cell",
|
|
208
230
|
variant: "ghost",
|
|
209
231
|
size: "lg",
|
|
210
232
|
tone: P.value === "hour" ? "accent" : "neutral",
|
|
211
233
|
"aria-pressed": P.value === "hour" ? "true" : "false",
|
|
212
|
-
"aria-label": T(A).timePicker.selectHour
|
|
234
|
+
"aria-label": `${W.value}, ${T(A).timePicker.selectHour}`,
|
|
213
235
|
disabled: y.disabled,
|
|
214
236
|
onClick: t[0] ||= (e) => V("hour")
|
|
215
237
|
}, {
|
|
216
|
-
default: E(() => [_(w(
|
|
238
|
+
default: E(() => [_(w(W.value), 1)]),
|
|
217
239
|
_: 1
|
|
218
240
|
}, 8, [
|
|
219
241
|
"tone",
|
|
@@ -225,17 +247,17 @@ var _e = [
|
|
|
225
247
|
class: "v-time-picker-sep",
|
|
226
248
|
"aria-hidden": "true"
|
|
227
249
|
}, ":", -1),
|
|
228
|
-
v(
|
|
250
|
+
v(r, {
|
|
229
251
|
class: "v-time-picker-cell",
|
|
230
252
|
variant: "ghost",
|
|
231
253
|
size: "lg",
|
|
232
254
|
tone: P.value === "minute" ? "accent" : "neutral",
|
|
233
255
|
"aria-pressed": P.value === "minute" ? "true" : "false",
|
|
234
|
-
"aria-label": T(A).timePicker.selectMinutes
|
|
256
|
+
"aria-label": `${T(s)(L.value)}, ${T(A).timePicker.selectMinutes}`,
|
|
235
257
|
disabled: y.disabled,
|
|
236
258
|
onClick: t[1] ||= (e) => V("minute")
|
|
237
259
|
}, {
|
|
238
|
-
default: E(() => [_(w(T(
|
|
260
|
+
default: E(() => [_(w(T(s)(L.value)), 1)]),
|
|
239
261
|
_: 1
|
|
240
262
|
}, 8, [
|
|
241
263
|
"tone",
|
|
@@ -243,10 +265,10 @@ var _e = [
|
|
|
243
265
|
"aria-label",
|
|
244
266
|
"disabled"
|
|
245
267
|
])
|
|
246
|
-
]), j.value === "12h" ? (S(),
|
|
268
|
+
]), j.value === "12h" ? (S(), ge(a, {
|
|
247
269
|
key: 0,
|
|
248
|
-
modelValue:
|
|
249
|
-
"onUpdate:modelValue": t[2] ||= (e) =>
|
|
270
|
+
modelValue: q.value,
|
|
271
|
+
"onUpdate:modelValue": t[2] ||= (e) => q.value = e,
|
|
250
272
|
class: "v-time-picker-meridiem",
|
|
251
273
|
mandatory: "",
|
|
252
274
|
variant: "outline",
|
|
@@ -256,21 +278,21 @@ var _e = [
|
|
|
256
278
|
label: T(A).timePicker.meridiem,
|
|
257
279
|
disabled: y.disabled || y.readonly
|
|
258
280
|
}, {
|
|
259
|
-
default: E(() => [v(
|
|
281
|
+
default: E(() => [v(o, {
|
|
260
282
|
value: "AM",
|
|
261
283
|
label: T(A).timePicker.am,
|
|
262
|
-
disabled: !
|
|
263
|
-
}, null, 8, ["label", "disabled"]), v(
|
|
284
|
+
disabled: !J.value.AM
|
|
285
|
+
}, null, 8, ["label", "disabled"]), v(o, {
|
|
264
286
|
value: "PM",
|
|
265
287
|
label: T(A).timePicker.pm,
|
|
266
|
-
disabled: !
|
|
288
|
+
disabled: !J.value.PM
|
|
267
289
|
}, null, 8, ["label", "disabled"])]),
|
|
268
290
|
_: 1
|
|
269
291
|
}, 8, [
|
|
270
292
|
"modelValue",
|
|
271
293
|
"label",
|
|
272
294
|
"disabled"
|
|
273
|
-
])) :
|
|
295
|
+
])) : _e("", !0)]),
|
|
274
296
|
g("div", {
|
|
275
297
|
ref_key: "faceEl",
|
|
276
298
|
ref: X,
|
|
@@ -278,42 +300,42 @@ var _e = [
|
|
|
278
300
|
tabindex: y.disabled ? -1 : 0,
|
|
279
301
|
class: "v-time-picker-face",
|
|
280
302
|
"aria-label": P.value === "hour" ? T(A).timePicker.hour : T(A).timePicker.minutes,
|
|
281
|
-
"aria-valuemin":
|
|
282
|
-
"aria-valuemax":
|
|
283
|
-
"aria-valuenow":
|
|
284
|
-
"aria-valuetext":
|
|
303
|
+
"aria-valuemin": ze.value,
|
|
304
|
+
"aria-valuemax": Be.value,
|
|
305
|
+
"aria-valuenow": Y.value,
|
|
306
|
+
"aria-valuetext": Ve.value,
|
|
285
307
|
"data-dragging": Z.value ? "" : void 0,
|
|
286
308
|
"aria-disabled": y.disabled ? "true" : void 0,
|
|
287
309
|
"aria-readonly": y.readonly ? "true" : void 0,
|
|
288
|
-
onPointerdown:
|
|
289
|
-
onPointermove:
|
|
290
|
-
onPointerup:
|
|
291
|
-
onPointercancel:
|
|
292
|
-
onKeydown:
|
|
310
|
+
onPointerdown: We,
|
|
311
|
+
onPointermove: Ge,
|
|
312
|
+
onPointerup: Ke,
|
|
313
|
+
onPointercancel: qe,
|
|
314
|
+
onKeydown: Xe
|
|
293
315
|
}, [
|
|
294
316
|
g("div", {
|
|
295
317
|
class: "v-time-picker-hand",
|
|
296
318
|
"aria-hidden": "true",
|
|
297
|
-
"data-ring":
|
|
298
|
-
"data-minor":
|
|
299
|
-
style: x({ "--dial-turn": String(
|
|
300
|
-
}, null, 12,
|
|
319
|
+
"data-ring": Le.value,
|
|
320
|
+
"data-minor": Re.value ? "" : void 0,
|
|
321
|
+
style: x({ "--dial-turn": String(Ie.value) })
|
|
322
|
+
}, null, 12, De),
|
|
301
323
|
t[4] ||= g("span", {
|
|
302
324
|
class: "v-time-picker-center",
|
|
303
325
|
"aria-hidden": "true"
|
|
304
326
|
}, null, -1),
|
|
305
|
-
(S(!0), h(
|
|
327
|
+
(S(!0), h(he, null, ye(Fe.value, (e) => (S(), h("span", {
|
|
306
328
|
key: e.key,
|
|
307
329
|
class: "v-time-picker-number",
|
|
308
330
|
"aria-hidden": "true",
|
|
309
331
|
"data-ring": e.ring,
|
|
310
332
|
"data-selected": e.selected ? "" : void 0,
|
|
311
333
|
style: x({ "--dial-turn": String(e.turn) })
|
|
312
|
-
}, w(e.label), 13,
|
|
313
|
-
], 40,
|
|
314
|
-
g("div",
|
|
315
|
-
e.$slots.footer ? (S(), h("div",
|
|
316
|
-
], 8,
|
|
334
|
+
}, w(e.label), 13, Oe))), 128))
|
|
335
|
+
], 40, Ee),
|
|
336
|
+
g("div", ke, w(B.value), 1),
|
|
337
|
+
e.$slots.footer ? (S(), h("div", Ae, [be(e.$slots, "footer")])) : _e("", !0)
|
|
338
|
+
], 8, Ce));
|
|
317
339
|
}
|
|
318
340
|
});
|
|
319
341
|
//#endregion
|