vectis-ui 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/VAccordion/VAccordion.css +1 -1
- package/dist/components/VAccordion/VAccordion.d.ts +0 -6
- package/dist/components/VAccordion/VAccordion.js.map +1 -1
- package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js +3 -8
- package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAccordion/VAccordionItem.js.map +1 -1
- package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAccordion/context.js.map +1 -1
- package/dist/components/VAlert/VAlert.js.map +1 -1
- package/dist/components/VAlert/VAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAvatar/VAvatar.d.ts +0 -3
- package/dist/components/VAvatar/VAvatar.js.map +1 -1
- package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js +21 -27
- package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAvatar/VAvatarGroup.d.ts +0 -6
- package/dist/components/VAvatar/VAvatarGroup.js.map +1 -1
- package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js +5 -19
- package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAvatar/context.d.ts +0 -1
- package/dist/components/VAvatar/context.js.map +1 -1
- package/dist/components/VBadge/VBadge.js.map +1 -1
- package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VBreadcrumb/VBreadcrumb.js.map +1 -1
- package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VButton/VButton.d.ts +0 -6
- package/dist/components/VButton/VButton.js.map +1 -1
- package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js +11 -17
- package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VButton/VButtonGroup.d.ts +0 -6
- package/dist/components/VButton/VButtonGroup.js.map +1 -1
- package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js +0 -7
- package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VButton/context.d.ts +0 -2
- package/dist/components/VButton/context.js.map +1 -1
- package/dist/components/VCalendar/VCalendar.js.map +1 -1
- package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendarEvent.js.map +1 -1
- package/dist/components/VCalendar/VCalendarEvent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendarMonth.js.map +1 -1
- package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js +5 -17
- package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendarTimeGrid.js.map +1 -1
- package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js +19 -31
- package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendarYear.js.map +1 -1
- package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/color.js.map +1 -1
- package/dist/components/VCalendar/edgeStep.js +1 -5
- package/dist/components/VCalendar/edgeStep.js.map +1 -1
- package/dist/components/VCalendar/gesture.js +4 -12
- package/dist/components/VCalendar/gesture.js.map +1 -1
- package/dist/components/VCalendar/keyboard.js.map +1 -1
- package/dist/components/VCalendar/layout.js +8 -12
- package/dist/components/VCalendar/layout.js.map +1 -1
- package/dist/components/VCalendar/timeGrid.js +5 -5
- package/dist/components/VCalendar/timeGrid.js.map +1 -1
- package/dist/components/VCard/VCard.js.map +1 -1
- package/dist/components/VCard/VCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCarousel/VCarousel.js.map +1 -1
- package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCarousel/VCarouselItem.js.map +1 -1
- package/dist/components/VCarousel/VCarouselItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCarousel/context.js.map +1 -1
- package/dist/components/VCheckbox/VCheckMark.js.map +1 -1
- package/dist/components/VCheckbox/VCheckMark.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCheckbox/VCheckbox.js.map +1 -1
- package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VChip/VChip.d.ts +0 -3
- package/dist/components/VChip/VChip.js.map +1 -1
- package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VColorInput/VColorInput.d.ts +0 -3
- package/dist/components/VColorInput/VColorInput.js.map +1 -1
- package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js +3 -9
- package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VColorPicker/VColorPicker.js.map +1 -1
- package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js +1 -5
- package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VColorPicker/color.js.map +1 -1
- package/dist/components/VCombobox/VCombobox.d.ts +0 -3
- package/dist/components/VCombobox/VCombobox.js.map +1 -1
- package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +100 -119
- package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCommandPalette/VCommandPalette.js.map +1 -1
- package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js +0 -1
- package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCommandPalette/VCommandPaletteItem.js.map +1 -1
- package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js +1 -5
- package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VContextMenu/VContextMenu.d.ts +0 -3
- package/dist/components/VContextMenu/VContextMenu.js.map +1 -1
- package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js +3 -11
- package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDataTable/VDataTable.css +1 -1
- package/dist/components/VDataTable/VDataTable.d.ts +0 -2
- package/dist/components/VDataTable/VDataTable.js.map +1 -1
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +69 -88
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDateInput/VDateInput.d.ts +0 -3
- package/dist/components/VDateInput/VDateInput.js.map +1 -1
- package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +5 -10
- package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDateInput/mask.js.map +1 -1
- package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
- package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDatePicker/keyboard.js.map +1 -1
- package/dist/components/VDatePicker/names.js.map +1 -1
- package/dist/components/VDialog/VDialog.js.map +1 -1
- package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDialog/VDialogAlert.js.map +1 -1
- package/dist/components/VDialog/VDialogAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDrawer/VDrawer.js.map +1 -1
- package/dist/components/VDrawer/VDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VEmptyState/VEmptyState.js.map +1 -1
- package/dist/components/VEmptyState/VEmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VField/VField.js.map +1 -1
- package/dist/components/VField/VField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VField/VFieldAnnouncer.js.map +1 -1
- package/dist/components/VField/VFieldAnnouncer.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VField/VFieldMessage.js.map +1 -1
- package/dist/components/VField/VFieldMessage.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VFieldset/VFieldset.js.map +1 -1
- package/dist/components/VFieldset/VFieldset.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VFileInput/VFileInput.d.ts +0 -5
- package/dist/components/VFileInput/VFileInput.js.map +1 -1
- package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +54 -65
- package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VFileInput/truncate.js.map +1 -1
- package/dist/components/VFilePicker/VFilePicker.js.map +1 -1
- package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VFilePicker/fileKind.js.map +1 -1
- package/dist/components/VHotkeys/VHotkeys.d.ts +0 -3
- package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
- package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VHotkeys/platform.js +1 -1
- package/dist/components/VHotkeys/platform.js.map +1 -1
- package/dist/components/VHoverCard/VHoverCard.js.map +1 -1
- package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js +2 -12
- package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VIcon/VIcon.js.map +1 -1
- package/dist/components/VIcon/VIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VIcon/iconProps.js.map +1 -1
- package/dist/components/VIcon/icons/add.js.map +1 -1
- package/dist/components/VIcon/icons/arrow_downward.js.map +1 -1
- package/dist/components/VIcon/icons/arrow_downward_alt.js.map +1 -1
- package/dist/components/VIcon/icons/arrow_drop_down.js.map +1 -1
- package/dist/components/VIcon/icons/arrow_drop_up.js.map +1 -1
- package/dist/components/VIcon/icons/arrow_left_alt.js.map +1 -1
- package/dist/components/VIcon/icons/arrow_right_alt.js.map +1 -1
- package/dist/components/VIcon/icons/arrow_upward.js.map +1 -1
- package/dist/components/VIcon/icons/arrow_upward_alt.js.map +1 -1
- package/dist/components/VIcon/icons/attach_file.js.map +1 -1
- package/dist/components/VIcon/icons/audio_file.js.map +1 -1
- package/dist/components/VIcon/icons/calendar_today.js.map +1 -1
- package/dist/components/VIcon/icons/check.js.map +1 -1
- package/dist/components/VIcon/icons/check_circle.js.map +1 -1
- package/dist/components/VIcon/icons/chevron_left.js.map +1 -1
- package/dist/components/VIcon/icons/chevron_right.js.map +1 -1
- package/dist/components/VIcon/icons/close.js.map +1 -1
- package/dist/components/VIcon/icons/cloud_upload.js.map +1 -1
- package/dist/components/VIcon/icons/code.js.map +1 -1
- package/dist/components/VIcon/icons/colorize.js.map +1 -1
- package/dist/components/VIcon/icons/description.js.map +1 -1
- package/dist/components/VIcon/icons/error.js.map +1 -1
- package/dist/components/VIcon/icons/expand_less.js.map +1 -1
- package/dist/components/VIcon/icons/expand_more.js.map +1 -1
- package/dist/components/VIcon/icons/first_page.js.map +1 -1
- package/dist/components/VIcon/icons/folder_zip.js.map +1 -1
- package/dist/components/VIcon/icons/image.js.map +1 -1
- package/dist/components/VIcon/icons/inbox.js.map +1 -1
- package/dist/components/VIcon/icons/index.js.map +1 -1
- package/dist/components/VIcon/icons/info.js.map +1 -1
- package/dist/components/VIcon/icons/last_page.js.map +1 -1
- package/dist/components/VIcon/icons/more_horiz.js.map +1 -1
- package/dist/components/VIcon/icons/names.js.map +1 -1
- package/dist/components/VIcon/icons/notifications.js.map +1 -1
- package/dist/components/VIcon/icons/open_in_new.js.map +1 -1
- package/dist/components/VIcon/icons/picture_as_pdf.js.map +1 -1
- package/dist/components/VIcon/icons/priority_high.js.map +1 -1
- package/dist/components/VIcon/icons/remove.js.map +1 -1
- package/dist/components/VIcon/icons/schedule.js.map +1 -1
- package/dist/components/VIcon/icons/search.js.map +1 -1
- package/dist/components/VIcon/icons/search_off.js.map +1 -1
- package/dist/components/VIcon/icons/star.js.map +1 -1
- package/dist/components/VIcon/icons/swap_vert.js.map +1 -1
- package/dist/components/VIcon/icons/table_chart.js.map +1 -1
- package/dist/components/VIcon/icons/video_file.js.map +1 -1
- package/dist/components/VIcon/icons/viewBox.js.map +1 -1
- package/dist/components/VIcon/icons/warning.js.map +1 -1
- package/dist/components/VIcon/resolver.js.map +1 -1
- package/dist/components/VIconButton/VIconButton.d.ts +0 -3
- package/dist/components/VIconButton/VIconButton.js.map +1 -1
- package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/VInput.css +1 -1
- package/dist/components/VInput/VInput.d.ts +0 -3
- package/dist/components/VInput/VInput.js.map +1 -1
- package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +2 -8
- package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/VInputGroup.d.ts +0 -3
- package/dist/components/VInput/VInputGroup.js.map +1 -1
- package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js +0 -7
- package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/context.d.ts +0 -2
- package/dist/components/VInput/context.js.map +1 -1
- package/dist/components/VInputOTP/VInputOTP.d.ts +0 -3
- package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
- package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +2 -14
- package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VLink/VLink.js.map +1 -1
- package/dist/components/VLink/VLink.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VListbox/VListboxChevron.js.map +1 -1
- package/dist/components/VListbox/VListboxChevron.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VListbox/VListboxGroup.js.map +1 -1
- package/dist/components/VListbox/VListboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VListbox/VListboxOption.js.map +1 -1
- package/dist/components/VListbox/VListboxOption.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VListbox/VListboxPanel.d.ts +0 -3
- package/dist/components/VListbox/VListboxPanel.js.map +1 -1
- package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VListbox/VListboxSeparator.js.map +1 -1
- package/dist/components/VListbox/VListboxSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VListbox/VListboxValues.js.map +1 -1
- package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js +4 -14
- package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenu.d.ts +0 -3
- package/dist/components/VMenu/VMenu.js.map +1 -1
- package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenuGroup.js.map +1 -1
- package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenuItem.js.map +1 -1
- package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js +6 -12
- package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenuPanel.d.ts +0 -3
- package/dist/components/VMenu/VMenuPanel.js.map +1 -1
- package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +9 -16
- package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenuSeparator.js.map +1 -1
- package/dist/components/VMenu/VMenuSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/context.js.map +1 -1
- package/dist/components/VMeter/VMeter.js.map +1 -1
- package/dist/components/VMeter/VMeter.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VNumberInput/VNumberInput.d.ts +0 -3
- package/dist/components/VNumberInput/VNumberInput.js.map +1 -1
- package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js +85 -90
- package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VNumberInput/number.js.map +1 -1
- package/dist/components/VPagination/VPagination.d.ts +0 -3
- package/dist/components/VPagination/VPagination.js.map +1 -1
- package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VPopover/VPopover.js.map +1 -1
- package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VProgressCircular/VProgressCircular.js.map +1 -1
- package/dist/components/VProgressCircular/VProgressCircular.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
- package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VRadio/VRadio.js.map +1 -1
- package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VRating/VRating.js.map +1 -1
- package/dist/components/VRating/VRating.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VResizable/VResizable.js.map +1 -1
- package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js +8 -6
- package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VResizable/VResizablePanel.js.map +1 -1
- package/dist/components/VResizable/VResizablePanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VResizable/context.js.map +1 -1
- package/dist/components/VResizable/layout.js.map +1 -1
- package/dist/components/VSelect/VSelect.d.ts +0 -3
- package/dist/components/VSelect/VSelect.js.map +1 -1
- package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js +80 -90
- package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSeparator/VSeparator.js.map +1 -1
- package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigation.d.ts +0 -6
- package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +2 -11
- package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationGroup.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js +1 -5
- package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationSeparator.js.map +1 -1
- package/dist/components/VSideNavigation/context.js.map +1 -1
- package/dist/components/VSkeletonLoader/VSkeletonLoader.d.ts +0 -3
- package/dist/components/VSkeletonLoader/VSkeletonLoader.js.map +1 -1
- package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSlider/VSlider.js.map +1 -1
- package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js +107 -112
- package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSnackbar/VSnackbar.js.map +1 -1
- package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSnackbar/state.js.map +1 -1
- package/dist/components/VSpinner/VSpinner.js.map +1 -1
- package/dist/components/VSpinner/VSpinner.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSplitButton/VSplitButton.d.ts +0 -3
- package/dist/components/VSplitButton/VSplitButton.js.map +1 -1
- package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VStepper/VStepper.js.map +1 -1
- package/dist/components/VStepper/VStepper.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSwitch/VSwitch.js.map +1 -1
- package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/VTab.js.map +1 -1
- package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js +0 -2
- package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/VTabPanel.js.map +1 -1
- package/dist/components/VTabs/VTabPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/VTabs.css +1 -1
- package/dist/components/VTabs/VTabs.d.ts +0 -3
- package/dist/components/VTabs/VTabs.js.map +1 -1
- package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js +2 -15
- package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/context.d.ts +0 -2
- package/dist/components/VTabs/context.js.map +1 -1
- package/dist/components/VTextarea/VTextarea.d.ts +0 -6
- package/dist/components/VTextarea/VTextarea.js.map +1 -1
- package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeInput/VTimeInput.d.ts +0 -3
- package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
- package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +197 -204
- package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeInput/search.js.map +1 -1
- package/dist/components/VTimePicker/VTimePicker.js.map +1 -1
- package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js +9 -9
- package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimePicker/limits.js.map +1 -1
- package/dist/components/VTimeline/VTimeline.js.map +1 -1
- package/dist/components/VTimeline/VTimeline.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeline/VTimelineItem.js.map +1 -1
- package/dist/components/VTimeline/VTimelineItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeline/context.js.map +1 -1
- package/dist/components/VTimeline/datetime.js.map +1 -1
- package/dist/components/VToast/VToast.js.map +1 -1
- package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToast/VToaster.js.map +1 -1
- package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToast/state.js.map +1 -1
- package/dist/components/VToggle/VToggle.d.ts +0 -3
- package/dist/components/VToggle/VToggle.js.map +1 -1
- package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js +1 -13
- package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToggle/VToggleItem.js.map +1 -1
- package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToggle/context.js.map +1 -1
- package/dist/components/VTooltip/VTooltip.js.map +1 -1
- package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTreeView/VTreeView.js.map +1 -1
- package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js +1 -5
- package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTypography/VTypography.js.map +1 -1
- package/dist/components/VTypography/VTypography.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VVirtualList/VVirtualList.js.map +1 -1
- package/dist/components/VVirtualList/VVirtualList.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/composables/useAriaLabel.js.map +1 -1
- package/dist/composables/useChoice.js.map +1 -1
- package/dist/composables/useClearable.js.map +1 -1
- package/dist/composables/useControlShape.d.ts +1 -5
- package/dist/composables/useControlShape.js +0 -1
- package/dist/composables/useControlShape.js.map +1 -1
- package/dist/composables/useDebouncedSearch.js.map +1 -1
- package/dist/composables/useDetailsOpen.js.map +1 -1
- package/dist/composables/useEscapeDismiss.js.map +1 -1
- package/dist/composables/useFieldIds.js.map +1 -1
- package/dist/composables/useFieldPanel.js +3 -7
- package/dist/composables/useFieldPanel.js.map +1 -1
- package/dist/composables/useFileDrop.js.map +1 -1
- package/dist/composables/useFileField.js +2 -2
- package/dist/composables/useFileField.js.map +1 -1
- package/dist/composables/useFocusoutDismiss.js.map +1 -1
- package/dist/composables/useHotkeyListener.js.map +1 -1
- package/dist/composables/useIconClickHandlers.js.map +1 -1
- package/dist/composables/useInertLink.js.map +1 -1
- package/dist/composables/useInfiniteScroll.js +2 -2
- package/dist/composables/useInfiniteScroll.js.map +1 -1
- package/dist/composables/useListbox.d.ts +0 -1
- package/dist/composables/useListbox.js +4 -10
- package/dist/composables/useListbox.js.map +1 -1
- package/dist/composables/useLiveAnnouncer.js.map +1 -1
- package/dist/composables/useMaskedField.js +28 -48
- package/dist/composables/useMaskedField.js.map +1 -1
- package/dist/composables/useModalDialog.js +1 -1
- package/dist/composables/useModalDialog.js.map +1 -1
- package/dist/composables/usePopover.js +1 -1
- package/dist/composables/usePopover.js.map +1 -1
- package/dist/composables/useProgressValue.js.map +1 -1
- package/dist/composables/useRootAttrs.js.map +1 -1
- package/dist/composables/useSlotNodes.js.map +1 -1
- package/dist/composables/useTextLimit.js.map +1 -1
- package/dist/composables/useTimer.js.map +1 -1
- package/dist/composables/useTypeahead.js.map +1 -1
- package/dist/composables/useVirtualList.js.map +1 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/fr.js.map +1 -1
- package/dist/i18n/state.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/tokens/css.js.map +1 -1
- package/dist/tokens/index.d.ts +3 -0
- package/dist/tokens/index.js.map +1 -1
- package/dist/tokens/primitives.js.map +1 -1
- package/dist/tokens/semantic.d.ts +3 -0
- package/dist/tokens/semantic.js +6 -5
- package/dist/tokens/semantic.js.map +1 -1
- package/dist/tokens/themes/dark.js.map +1 -1
- package/dist/tokens/types.js.map +1 -1
- package/dist/tokens.json +12 -5
- package/dist/types.d.ts +0 -2
- package/dist/utils/array.js.map +1 -1
- package/dist/utils/arrowNav.js.map +1 -1
- package/dist/utils/attrs.js.map +1 -1
- package/dist/utils/chip.d.ts +4 -6
- package/dist/utils/chip.js +8 -5
- package/dist/utils/chip.js.map +1 -1
- package/dist/utils/clock.js +3 -1
- package/dist/utils/clock.js.map +1 -1
- package/dist/utils/css.js +1 -1
- package/dist/utils/css.js.map +1 -1
- package/dist/utils/date.js.map +1 -1
- package/dist/utils/direction.js.map +1 -1
- package/dist/utils/env.js.map +1 -1
- package/dist/utils/file.js +13 -31
- package/dist/utils/file.js.map +1 -1
- package/dist/utils/focus.js.map +1 -1
- package/dist/utils/hostWarns.js.map +1 -1
- package/dist/utils/ids.js.map +1 -1
- package/dist/utils/matcher.js.map +1 -1
- package/dist/utils/memo.js.map +1 -1
- package/dist/utils/number.js.map +1 -1
- package/dist/utils/text.js.map +1 -1
- package/dist/utils/time.js.map +1 -1
- package/dist/utils/vnode.js.map +1 -1
- package/package.json +19 -19
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gesture.js","names":[],"sources":["../../../src/components/VCalendar/gesture.ts"],"sourcesContent":["// @keyboard @a11y @core\n/**\n * Share pointer and keyboard editing gestures across month and time-grid views; each view\n * supplies its own cell geometry.\n */\nimport { computed, nextTick, ref, watch } from 'vue'\nimport type { Ref } from 'vue'\n\nimport { isRtl as isElementRtl } from '../../utils/direction'\n\nimport { useMessages } from '../../i18n/state'\n\nimport { EDGE_BAND, useEdgeStep } from './edgeStep'\nimport { calendarIntent, type CalendarIntent } from './keyboard'\nimport { DRAG_THRESHOLD, inlineEdgeAt, pointWithin, type GridGeometry } from './layout'\nimport type { CalendarEvent, CalendarEventId, CalendarEventTimes } from './types'\n\n/**\n * The prefix marking the copy an event leaves behind at its old place while it is being\n * dragged.\n */\nconst GHOST_PREFIX = '__vectis-calendar-ghost__'\n\n/**\n * The id the ghost of a dragged event carries. It has to DIFFER from the event's own, or the\n * two would collide in every map the layout keys by id and only one would ever be drawn.\n */\nconst ghostIdOf = (id: CalendarEventId) => `${GHOST_PREFIX}${id}`\n/** The dragged event's own id, read back off its ghost's. */\nconst originalIdOf = (id: CalendarEventId) => String(id).slice(GHOST_PREFIX.length)\nconst isGhostId = (id: CalendarEventId) => String(id).startsWith(GHOST_PREFIX)\n\n/** The id a card belongs to, read back off the DOM. */\nfunction cardIdOf(card: HTMLElement, has: (id: CalendarEventId) => boolean) {\n const raw = card.dataset.eventId ?? ''\n return has(raw) ? raw : Number(raw)\n}\n\n/**\n * `inlineStart` is the edge the first column starts at, which is the RIGHT one in a\n * right-to-left page.\n */\nexport function gridGeometryOf(rect: DOMRect, columns: number, rtl: boolean): GridGeometry {\n return {\n top: rect.top,\n height: rect.height,\n inlineStart: rtl ? rect.right : rect.left,\n inlineSize: rect.width,\n columns,\n }\n}\n\n/** The state every gesture carries, whatever each grid adds to it. */\nexport interface GestureBase {\n id: CalendarEventId\n /**\n * Where the event was when the gesture began. Every pointer frame is computed from this and\n * never from the frame before it, so a long drag cannot accumulate rounding drift.\n */\n origin: CalendarEventTimes\n /** What the card shows right now. The model is not touched until the gesture ends. */\n preview: CalendarEventTimes\n /** The pointer holding the gesture, or null for a keyboard grab. */\n pointerId: number | null\n originX: number\n originY: number\n /**\n * Where the pointer last was. Two things move the calendar UNDER a still pointer: paging at\n * an edge, and auto-scrolling. Each has to work out afresh what the pointer is now over.\n */\n lastX: number\n lastY: number\n /** Whether it moved past the threshold, which separates a drag from a click. */\n moved: boolean\n /**\n * Whether the pointer is off the calendar altogether: over the toolbar, or over the page\n * beside it. A KEYBOARD grab can never set it, and structurally rather than by a guard:\n * `onPointermove` is the only writer, and it returns on the id mismatch first: a grab's\n * `pointerId` is null, which no real pointer's id ever equals.\n */\n outside: boolean\n /**\n * The reading direction, read once when the gesture begins. It cannot change under a live\n * drag, and asking the document on every frame costs a style resolution each time.\n */\n rtl: boolean\n}\n\n/** What a grid hands over to start a gesture; the rest is filled in here. */\nexport type GestureInit<G extends GestureBase> = Omit<\n G,\n 'preview' | 'lastX' | 'lastY' | 'moved' | 'outside' | 'rtl'\n>\n\n/**\n * A gesture held by the KEYBOARD rather than by a pointer: grabbed with Space, and waiting for\n * the arrows to move it.\n */\nconst isGrabbed = (state: GestureBase | null) => state !== null && state.pointerId === null\n\n/** The two intents that move a grabbed event, which only a grid can turn into times. */\nexport type GrabStep = Extract<CalendarIntent, { kind: 'grabMove' | 'grabResize' }>\n\nexport interface CalendarGestureOptions<E extends CalendarEvent, G extends GestureBase> {\n /** Every event as it is currently drawn, the ghost's original among them. */\n events: () => readonly E[]\n /** The event drawn under an id, for the handlers. */\n eventOf: (id: CalendarEventId) => E | undefined\n rootEl: Ref<HTMLElement | null>\n /** The element the pointer is captured on once a press travels. */\n captureEl: Ref<HTMLElement | null>\n /** The box the cells occupy, which points and edges are read against. */\n measureEl: Ref<HTMLElement | null>\n /** How many columns that box is divided into. */\n columns: () => number\n /** Whether events may be moved at all. */\n editable: () => boolean\n /** The node every card points at to say how it can be moved. */\n hintId: () => string\n /** The step a grabbed event moves by, handed to the keyboard table. */\n slotMinutes: () => number\n edgeStepDelay: () => number\n /** Whatever the grid's cells are made of, so paging under a still pointer re-places the card. */\n layout: () => unknown\n /** Works out where the gesture's event belongs from the pointer's last position. */\n applyPoint: (state: G, rect: DOMRect) => void\n /** Called on every pointer move past the threshold, after the edge watch, with both rects. */\n onEdges?: (state: G, rootRect: DOMRect | undefined) => void\n /** Everything else a gesture leaves running, dropped on every path out of one. */\n onRelease?: () => void\n /**\n * A pointer gesture let go inside the calendar. Returns true when the grid has answered it\n * itself, such as a slot drawn out, and nothing is left to drop.\n */\n onLetGo?: (state: G) => boolean\n /** A gesture that moved, committed: the grid reports the new times. */\n onDrop: (state: G) => void\n /** What a keyboard grab of this event starts, or null when it cannot be grabbed. */\n grab: (item: E) => GestureInit<G> | null\n /** Applies one arrow step to a grabbed event. Returns false when the step means nothing here. */\n onGrabStep: (state: G, step: GrabStep, item: E) => boolean\n step: (direction: -1 | 1) => void\n announce: (message: string) => void\n}\n\nexport function useCalendarGesture<E extends CalendarEvent, G extends GestureBase>(\n options: CalendarGestureOptions<E, G>,\n) {\n const m = useMessages()\n const gesture = ref<G | null>(null) as Ref<G | null>\n\n /** True while a card is being held by the keyboard rather than dragged by a pointer. */\n const grabbing = computed(() => isGrabbed(gesture.value))\n\n const isRtl = () => isElementRtl(options.measureEl.value)\n\n /**\n * The echo of where the gestured event began, so the reader can see what they are moving it\n * FROM. A press that has not travelled yet produces none, or a click would flash a second\n * card over the first.\n */\n const ghostEvent = computed<E | null>(() => {\n const state = gesture.value\n if (!state?.moved) return null\n const original = options.events().find((event) => event.id === state.id)\n return original ? ({ ...original, ...state.origin, id: ghostIdOf(state.id) } as E) : null\n })\n\n /** A list with the gestured event moved to wherever it is now, plus its echo. */\n function withPreview(events: readonly E[]): E[] {\n const state = gesture.value\n if (!state) return [...events]\n const moving = events.map((event) =>\n event.id === state.id ? ({ ...event, ...state.preview } as E) : event,\n )\n return ghostEvent.value ? [...moving, ghostEvent.value] : moving\n }\n\n /**\n * Set click suppression on pointerup, not pointercancel; clear it before every press guard\n * and keyboard activation. Captured releases may never send their click to an event card.\n */\n let dragged = false\n\n /** Closes the books on the previous gesture. */\n function endLastGesture() {\n dragged = false\n if (grabbing.value) revertGrab()\n }\n\n /** Puts a keyboard grab back where it was taken from, writing nothing, and says so. */\n function revertGrab() {\n const state = gesture.value\n if (!state) return\n gesture.value = null\n options.announce(m.value.calendar.reverted)\n return state\n }\n\n /**\n * A keyboard grab ends when the focus moves on to something else, the toolbar or another\n * control.\n */\n function onFocusout(event: FocusEvent) {\n const state = gesture.value\n const next = event.relatedTarget as HTMLElement | null\n if (!grabbing.value || !state || !next) return\n const card = next.closest<HTMLElement>('.v-calendar-event')\n if (card && card.dataset.eventId === String(state.id)) return\n revertGrab()\n }\n\n /** Whether the gesture that just ended actually moved; asking lowers the flag. */\n function consumeDrag() {\n const was = dragged\n dragged = false\n return was\n }\n\n const edge = useEdgeStep(options.step, options.edgeStepDelay)\n\n /** Which edge is counting down, as the attribute the stylesheet reads. */\n const edgeCue = computed(() =>\n edge.pending.value === -1 ? 'start' : edge.pending.value === 1 ? 'end' : undefined,\n )\n\n function release() {\n edge.cancel()\n options.onRelease?.()\n }\n\n /** Starts a gesture. */\n function start(init: GestureInit<G>, rtl = isRtl()) {\n gesture.value = {\n ...init,\n preview: { ...init.origin },\n lastX: init.originX,\n lastY: init.originY,\n moved: false,\n outside: false,\n rtl,\n } as G\n }\n\n /**\n * Called when the press crosses the drag threshold, never at the press. The browser sends the\n * `click` that follows a captured `pointerup` to the CAPTURE element, so a press captured at\n * once reaches the scroller instead of the card or the cell under it: clicking an event would\n * open nothing and clicking an empty slot would report nothing, on every calendar whose\n * events can be moved or drawn.\n */\n function capture(pointerId: number) {\n // @fallback\n /*\n * Synthetic pointer events may not have a capturable pointer; allow the gesture to continue\n * within the element if capture fails.\n */\n try {\n options.captureEl.value?.setPointerCapture(pointerId)\n } catch {\n /* Synthetic pointers cannot be captured. */\n }\n }\n\n function reapply(state: G) {\n const rect = options.measureEl.value?.getBoundingClientRect()\n if (rect) options.applyPoint(state, rect)\n }\n\n function onPointermove(event: PointerEvent) {\n const state = gesture.value\n if (!state || state.pointerId !== event.pointerId) return\n\n state.lastX = event.clientX\n state.lastY = event.clientY\n\n // Below the threshold nothing has happened yet. That is what keeps a click a click: the\n // hand's tremor during a press would otherwise register as a one-pixel drag.\n if (\n !state.moved &&\n Math.abs(event.clientX - state.originX) < DRAG_THRESHOLD &&\n Math.abs(event.clientY - state.originY) < DRAG_THRESHOLD\n ) {\n return\n }\n\n if (!state.moved) capture(event.pointerId)\n state.moved = true\n const rootRect = options.rootEl.value?.getBoundingClientRect()\n const rect = options.measureEl.value?.getBoundingClientRect()\n\n /*\n * Neither the placement nor the edge watch below is gated on it, and neither may become so.\n * The card must keep following the pointer while it is out, which makes coming back in\n * seamless, and paging must keep running, pushing past the edge being how one crosses into\n * the next period.\n */\n state.outside = rootRect ? !pointWithin({ x: state.lastX, y: state.lastY }, rootRect) : false\n\n if (!rect) return\n options.applyPoint(state, rect)\n edge.watchEdge(\n inlineEdgeAt(\n state.lastX,\n gridGeometryOf(rect, options.columns(), state.rtl),\n EDGE_BAND,\n state.rtl,\n ),\n )\n options.onEdges?.(state, rootRect)\n }\n\n function onPointerup(event: PointerEvent) {\n const state = gesture.value\n if (!state || state.pointerId !== event.pointerId) return\n gesture.value = null\n dragged = state.moved\n release()\n\n /*\n * Nothing is announced: the pointer says nothing on a SUCCESSFUL drop either; `grabbed`,\n * `dropped` and `reverted` belong to the keyboard grab, which has no pointer to show where\n * the event went.\n */\n if (state.outside) return\n if (options.onLetGo?.(state)) return\n // A press that never travelled is an ordinary click, which the button's own click event is\n // about to report. Writing anything here would move an event nobody dragged.\n if (state.moved) options.onDrop(state)\n }\n\n /*\n * The gesture was taken away; a system gesture, a context menu, the page starting to scroll.\n * Nothing is written and nothing is announced: the card goes back to where the model still\n * says it is.\n */\n function onPointercancel(event: PointerEvent) {\n // Another pointer's cancel, a second finger starting to pan, is not this gesture's. A\n // keyboard grab carries no pointer id, so no cancel can end it either.\n if (gesture.value?.pointerId !== event.pointerId) return\n abandon()\n }\n\n function abandon() {\n gesture.value = null\n release()\n }\n\n /*\n * Its `pointerup` then lands outside and never reaches the bindings, so without this the\n * gesture would outlive the press: a mouse keeps the same pointer id, and bringing it back\n * over the calendar with the button up would pick the event up and carry it. A captured\n * pointer never leaves, so a real drag is untouched.\n */\n function onPointerleave(event: PointerEvent) {\n const state = gesture.value\n if (!state || state.pointerId !== event.pointerId || state.moved) return\n abandon()\n }\n\n /*\n * Paging swaps the cells out from under a live drag, and the dragged event's day is then off\n * screen; the card would vanish until the next pointer move, which never comes if the hand is\n * holding still against the edge. Re-applying the last position once the new cells are\n * rendered is what keeps the card under the pointer across the boundary.\n */\n watch(\n options.layout,\n () => {\n const state = gesture.value\n if (state?.moved) reapply(state)\n },\n { flush: 'post' },\n )\n\n /**\n * The card is found by WALKING the cards rather than by building an attribute selector. An id\n * belongs to the consumer, so it may hold a quote or a backslash a selector would need\n * escaped, and `CSS.escape` cannot do that escaping here: jsdom defines no `CSS` object at\n * all, so the call threw on every keyboard grab, from inside a `nextTick` where nothing could\n * catch it; an unhandled rejection, which fails the run while every assertion still passes.\n */\n function refocusCard(id: CalendarEventId) {\n void nextTick(() => {\n // Searched from the view's root: the time grid's all-day bars sit in its sticky header,\n // outside the box the cells occupy.\n const cards = options.rootEl.value?.querySelectorAll<HTMLElement>('.v-calendar-event')\n Array.from(cards ?? [])\n .find((card) => card.dataset.eventId === String(id))\n ?.focus()\n })\n }\n\n /** Puts the focus on a cell by its id, once the render that made it tabbable has run. */\n function focusCell(id: string, deferred = false, focusOptions?: FocusOptions) {\n const move = () => document.getElementById(id)?.focus(focusOptions)\n if (deferred) void nextTick(move)\n else move()\n }\n\n /** The event a card stands for, read back off the attribute the card publishes. */\n const idOfCard = (card: HTMLElement) => cardIdOf(card, (id) => options.eventOf(id) !== undefined)\n\n /**\n * A card answers a different table from the grid it sits in; that is what a grab mode IS.\n * Without it the grid would offer a gesture the pointer alone could reach (WCAG 2.1.1).\n */\n function onCardKeydown(event: KeyboardEvent, card: HTMLElement) {\n // Whatever click follows is this key's, never the end of an earlier drag.\n dragged = false\n const item = options.eventOf(idOfCard(card))\n if (!item) return\n\n const held = grabbing.value && gesture.value?.id === item.id\n const state = held ? gesture.value! : null\n const intent = calendarIntent(\n event,\n held ? 'grabbed' : 'event',\n options.slotMinutes(),\n state?.rtl ?? isRtl(),\n )\n if (!intent) return\n\n if (!state) {\n // An event that cannot be moved keeps a button's ordinary behaviour: Space presses it,\n // which the card's click handler reports.\n const init = intent.kind === 'grab' ? options.grab(item) : null\n if (!init) return\n /*\n * Cancelling the keydown is what stops that press, so the keystroke that takes hold of\n * the event does not open it too: without it every attempt to move something would fire\n * the consumer's editor over it.\n */\n event.preventDefault()\n start(init)\n options.announce(m.value.calendar.grabbed)\n return\n }\n\n event.preventDefault()\n\n if (intent.kind === 'grabMove' || intent.kind === 'grabResize') {\n if (!options.onGrabStep(state, intent, item)) return\n state.moved = true\n refocusCard(state.id)\n return\n }\n\n if (intent.kind === 'activate') {\n gesture.value = null\n if (state.moved) options.onDrop(state)\n options.announce(m.value.calendar.dropped)\n refocusCard(state.id)\n return\n }\n\n if (intent.kind === 'cancel') {\n revertGrab()\n refocusCard(state.id)\n }\n }\n\n /**\n * The five things every card is told about the gesture, bound with `v-bind` so the two grids\n * cannot describe the same state in two different ways.\n */\n function cardState(id: CalendarEventId) {\n const state = gesture.value\n const mine = state !== null && state.id === id\n return {\n /*\n * Only once the press has travelled. A dragged card takes no pointer events, so marked at\n * the press it lets the release land on the cell beneath it: the click then goes to that\n * cell, and clicking an event reports an empty slot instead of opening it.\n */\n dragging: mine && state.pointerId !== null && state.moved,\n rejected: mine && state.outside,\n grabbed: mine && state.pointerId === null,\n hintId: options.editable() && !isGhostId(id) ? options.hintId() : undefined,\n ghostOf: isGhostId(id) ? originalIdOf(id) : undefined,\n }\n }\n\n return {\n gesture,\n grabbing,\n ghostEvent,\n withPreview,\n edgeCue,\n isRtl,\n start,\n reapply,\n release,\n endLastGesture,\n consumeDrag,\n onPointermove,\n onPointerup,\n onPointercancel,\n onPointerleave,\n onFocusout,\n onCardKeydown,\n refocusCard,\n focusCell,\n idOfCard,\n cardState,\n }\n}\n"],"mappings":";;;;;;;AAqBA,IAAM,IAAe,6BAMf,KAAa,MAAwB,GAAG,IAAe,KAEvD,KAAgB,MAAwB,OAAO,CAAE,CAAC,CAAC,MAAM,EAAmB,GAC5E,KAAa,MAAwB,OAAO,CAAE,CAAC,CAAC,WAAW,CAAY;AAG7E,SAAS,EAAS,GAAmB,GAAuC;CAC1E,IAAM,IAAM,EAAK,QAAQ,WAAW;CACpC,OAAO,EAAI,CAAG,IAAI,IAAM,OAAO,CAAG;AACpC;AAMA,SAAgB,EAAe,GAAe,GAAiB,GAA4B;CACzF,OAAO;EACL,KAAK,EAAK;EACV,QAAQ,EAAK;EACb,aAAa,IAAM,EAAK,QAAQ,EAAK;EACrC,YAAY,EAAK;EACjB;CACF;AACF;AAgDA,IAAM,KAAa,MAA8B,MAAU,QAAQ,EAAM,cAAc;AA+CvF,SAAgB,EACd,GACA;CACA,IAAM,IAAI,EAAY,GAChB,IAAU,EAAc,IAAI,GAG5B,IAAW,QAAe,EAAU,EAAQ,KAAK,CAAC,GAElD,UAAc,EAAa,EAAQ,UAAU,KAAK,GAOlD,IAAa,QAAyB;EAC1C,IAAM,IAAQ,EAAQ;EACtB,IAAI,CAAC,GAAO,OAAO,OAAO;EAC1B,IAAM,IAAW,EAAQ,OAAO,CAAC,CAAC,MAAM,MAAU,EAAM,OAAO,EAAM,EAAE;EACvE,OAAO,IAAY;GAAE,GAAG;GAAU,GAAG,EAAM;GAAQ,IAAI,EAAU,EAAM,EAAE;EAAE,IAAU;CACvF,CAAC;CAGD,SAAS,EAAY,GAA2B;EAC9C,IAAM,IAAQ,EAAQ;EACtB,IAAI,CAAC,GAAO,OAAO,CAAC,GAAG,CAAM;EAC7B,IAAM,IAAS,EAAO,KAAK,MACzB,EAAM,OAAO,EAAM,KAAM;GAAE,GAAG;GAAO,GAAG,EAAM;EAAQ,IAAU,CAClE;EACA,OAAO,EAAW,QAAQ,CAAC,GAAG,GAAQ,EAAW,KAAK,IAAI;CAC5D;CAMA,IAAI,IAAU;CAGd,SAAS,IAAiB;EAExB,AADA,IAAU,IACN,EAAS,SAAO,EAAW;CACjC;CAGA,SAAS,IAAa;EACpB,IAAM,IAAQ,EAAQ;EACjB,OAGL,OAFA,EAAQ,QAAQ,MAChB,EAAQ,SAAS,EAAE,MAAM,SAAS,QAAQ,GACnC;CACT;CAMA,SAAS,EAAW,GAAmB;EACrC,IAAM,IAAQ,EAAQ,OAChB,IAAO,EAAM;EACnB,IAAI,CAAC,EAAS,SAAS,CAAC,KAAS,CAAC,GAAM;EACxC,IAAM,IAAO,EAAK,QAAqB,mBAAmB;EACtD,KAAQ,EAAK,QAAQ,YAAY,OAAO,EAAM,EAAE,KACpD,EAAW;CACb;CAGA,SAAS,IAAc;EACrB,IAAM,IAAM;EAEZ,OADA,IAAU,IACH;CACT;CAEA,IAAM,IAAO,EAAY,EAAQ,MAAM,EAAQ,aAAa,GAGtD,IAAU,QACd,EAAK,QAAQ,UAAU,KAAK,UAAU,EAAK,QAAQ,UAAU,IAAI,QAAQ,KAAA,CAC3E;CAEA,SAAS,IAAU;EAEjB,AADA,EAAK,OAAO,GACZ,EAAQ,YAAY;CACtB;CAGA,SAAS,EAAM,GAAsB,IAAM,EAAM,GAAG;EAClD,EAAQ,QAAQ;GACd,GAAG;GACH,SAAS,EAAE,GAAG,EAAK,OAAO;GAC1B,OAAO,EAAK;GACZ,OAAO,EAAK;GACZ,OAAO;GACP,SAAS;GACT;EACF;CACF;CASA,SAAS,EAAQ,GAAmB;EAMlC,IAAI;GACF,EAAQ,UAAU,OAAO,kBAAkB,CAAS;EACtD,QAAQ,CAER;CACF;CAEA,SAAS,EAAQ,GAAU;EACzB,IAAM,IAAO,EAAQ,UAAU,OAAO,sBAAsB;EAC5D,AAAI,KAAM,EAAQ,WAAW,GAAO,CAAI;CAC1C;CAEA,SAAS,EAAc,GAAqB;EAC1C,IAAM,IAAQ,EAAQ;EAQtB,IAPI,CAAC,KAAS,EAAM,cAAc,EAAM,cAExC,EAAM,QAAQ,EAAM,SACpB,EAAM,QAAQ,EAAM,SAKlB,CAAC,EAAM,SACP,KAAK,IAAI,EAAM,UAAU,EAAM,OAAO,IAAA,KACtC,KAAK,IAAI,EAAM,UAAU,EAAM,OAAO,IAAA,IAEtC;EAIF,AADK,EAAM,SAAO,EAAQ,EAAM,SAAS,GACzC,EAAM,QAAQ;EACd,IAAM,IAAW,EAAQ,OAAO,OAAO,sBAAsB,GACvD,IAAO,EAAQ,UAAU,OAAO,sBAAsB;EAQ5D,EAAM,UAAU,IAAW,CAAC,EAAY;GAAE,GAAG,EAAM;GAAO,GAAG,EAAM;EAAM,GAAG,CAAQ,IAAI,IAEnF,MACL,EAAQ,WAAW,GAAO,CAAI,GAC9B,EAAK,UACH,EACE,EAAM,OACN,EAAe,GAAM,EAAQ,QAAQ,GAAG,EAAM,GAAG,GAAA,IAEjD,EAAM,GACR,CACF,GACA,EAAQ,UAAU,GAAO,CAAQ;CACnC;CAEA,SAAS,EAAY,GAAqB;EACxC,IAAM,IAAQ,EAAQ;EAClB,CAAC,KAAS,EAAM,cAAc,EAAM,cACxC,EAAQ,QAAQ,MAChB,IAAU,EAAM,OAChB,EAAQ,GAOJ,GAAM,YACN,EAAQ,UAAU,CAAK,KAGvB,EAAM,SAAO,EAAQ,OAAO,CAAK;CACvC;CAOA,SAAS,EAAgB,GAAqB;EAGxC,EAAQ,OAAO,cAAc,EAAM,aACvC,EAAQ;CACV;CAEA,SAAS,IAAU;EAEjB,AADA,EAAQ,QAAQ,MAChB,EAAQ;CACV;CAQA,SAAS,EAAe,GAAqB;EAC3C,IAAM,IAAQ,EAAQ;EAClB,CAAC,KAAS,EAAM,cAAc,EAAM,aAAa,EAAM,SAC3D,EAAQ;CACV;CAQA,EACE,EAAQ,cACF;EACJ,IAAM,IAAQ,EAAQ;EACtB,AAAI,GAAO,SAAO,EAAQ,CAAK;CACjC,GACA,EAAE,OAAO,OAAO,CAClB;CASA,SAAS,EAAY,GAAqB;EACxC,QAAoB;GAGlB,IAAM,IAAQ,EAAQ,OAAO,OAAO,iBAA8B,mBAAmB;GACrF,MAAM,KAAK,KAAS,CAAC,CAAC,CAAC,CACpB,MAAM,MAAS,EAAK,QAAQ,YAAY,OAAO,CAAE,CAAC,CAAC,EAClD,MAAM;EACZ,CAAC;CACH;CAGA,SAAS,EAAU,GAAY,IAAW,IAAO,GAA6B;EAC5E,IAAM,UAAa,SAAS,eAAe,CAAE,CAAC,EAAE,MAAM,CAAY;EAClE,AAAI,IAAU,EAAc,CAAI,IAC3B,EAAK;CACZ;CAGA,IAAM,KAAY,MAAsB,EAAS,IAAO,MAAO,EAAQ,QAAQ,CAAE,MAAM,KAAA,CAAS;CAMhG,SAAS,EAAc,GAAsB,GAAmB;EAE9D,IAAU;EACV,IAAM,IAAO,EAAQ,QAAQ,EAAS,CAAI,CAAC;EAC3C,IAAI,CAAC,GAAM;EAEX,IAAM,IAAO,EAAS,SAAS,EAAQ,OAAO,OAAO,EAAK,IACpD,IAAQ,IAAO,EAAQ,QAAS,MAChC,IAAS,EACb,GACA,IAAO,YAAY,SACnB,EAAQ,YAAY,GACpB,GAAO,OAAO,EAAM,CACtB;EACK,OAEL;OAAI,CAAC,GAAO;IAGV,IAAM,IAAO,EAAO,SAAS,SAAS,EAAQ,KAAK,CAAI,IAAI;IAC3D,IAAI,CAAC,GAAM;IAQX,AAFA,EAAM,eAAe,GACrB,EAAM,CAAI,GACV,EAAQ,SAAS,EAAE,MAAM,SAAS,OAAO;IACzC;GACF;GAIA,IAFA,EAAM,eAAe,GAEjB,EAAO,SAAS,cAAc,EAAO,SAAS,cAAc;IAC9D,IAAI,CAAC,EAAQ,WAAW,GAAO,GAAQ,CAAI,GAAG;IAE9C,AADA,EAAM,QAAQ,IACd,EAAY,EAAM,EAAE;IACpB;GACF;GAEA,IAAI,EAAO,SAAS,YAAY;IAI9B,AAHA,EAAQ,QAAQ,MACZ,EAAM,SAAO,EAAQ,OAAO,CAAK,GACrC,EAAQ,SAAS,EAAE,MAAM,SAAS,OAAO,GACzC,EAAY,EAAM,EAAE;IACpB;GACF;GAEA,AAAI,EAAO,SAAS,aAClB,EAAW,GACX,EAAY,EAAM,EAAE;EArBtB;CAuBF;CAMA,SAAS,EAAU,GAAqB;EACtC,IAAM,IAAQ,EAAQ,OAChB,IAAO,MAAU,QAAQ,EAAM,OAAO;EAC5C,OAAO;GAML,UAAU,KAAQ,EAAM,cAAc,QAAQ,EAAM;GACpD,UAAU,KAAQ,EAAM;GACxB,SAAS,KAAQ,EAAM,cAAc;GACrC,QAAQ,EAAQ,SAAS,KAAK,CAAC,EAAU,CAAE,IAAI,EAAQ,OAAO,IAAI,KAAA;GAClE,SAAS,EAAU,CAAE,IAAI,EAAa,CAAE,IAAI,KAAA;EAC9C;CACF;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA,OAAA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"gesture.js","names":["isRtl","isElementRtl","GHOST_PREFIX","ghostIdOf","id","originalIdOf","isGhostId","cardIdOf","raw","card","has","gridGeometryOf","rect","rtl","isGrabbed","state","useCalendarGesture","m","useMessages","gesture","ref","grabbing","computed","isRtl$1","isRtl","options","ghostEvent","original","event","withPreview","events","moving","dragged","endLastGesture","revertGrab","onFocusout","next","consumeDrag","was","edge","useEdgeStep","edgeCue","release","start","init","capture","pointerId","reapply","onPointermove","rootRect","pointWithin","inlineEdgeAt","onPointerup","onPointercancel","abandon","onPointerleave","refocusCard","cards","focusCell","move","focusOptions","deferred","nextTick","idOfCard","onCardKeydown","item","held","intent","calendarIntent","cardState","mine"],"sources":["../../../src/components/VCalendar/gesture.ts"],"sourcesContent":["// @keyboard @a11y @core\n/**\n * Share pointer and keyboard editing gestures across month and time-grid views; each view\n * supplies its own cell geometry.\n */\nimport { computed, nextTick, ref, watch } from 'vue'\nimport type { Ref } from 'vue'\n\nimport { isRtl as isElementRtl } from '../../utils/direction'\n\nimport { useMessages } from '../../i18n/state'\n\nimport { EDGE_BAND, useEdgeStep } from './edgeStep'\nimport { calendarIntent, type CalendarIntent } from './keyboard'\nimport { DRAG_THRESHOLD, inlineEdgeAt, pointWithin, type GridGeometry } from './layout'\nimport type { CalendarEvent, CalendarEventId, CalendarEventTimes } from './types'\n\n/**\n * The prefix marking the copy an event leaves behind at its old place while it is being\n * dragged.\n */\nconst GHOST_PREFIX = '__vectis-calendar-ghost__'\n\n/**\n * The id the ghost of a dragged event carries. It has to DIFFER from the event's own, or the\n * two would collide in every map the layout keys by id and only one would ever be drawn.\n */\nconst ghostIdOf = (id: CalendarEventId) => `${GHOST_PREFIX}${id}`\n/** The dragged event's own id, read back off its ghost's. */\nconst originalIdOf = (id: CalendarEventId) => String(id).slice(GHOST_PREFIX.length)\nconst isGhostId = (id: CalendarEventId) => String(id).startsWith(GHOST_PREFIX)\n\n/** The id a card belongs to, read back off the DOM. */\nfunction cardIdOf(card: HTMLElement, has: (id: CalendarEventId) => boolean) {\n const raw = card.dataset.eventId ?? ''\n return has(raw) ? raw : Number(raw)\n}\n\n/**\n * `inlineStart` is the edge the first column starts at, which is the RIGHT one in a\n * right-to-left page.\n */\nexport function gridGeometryOf(rect: DOMRect, columns: number, rtl: boolean): GridGeometry {\n return {\n top: rect.top,\n height: rect.height,\n inlineStart: rtl ? rect.right : rect.left,\n inlineSize: rect.width,\n columns,\n }\n}\n\n/** The state every gesture carries, whatever each grid adds to it. */\nexport interface GestureBase {\n id: CalendarEventId\n /**\n * Where the event was when the gesture began. Every pointer frame is computed from this and\n * never from the frame before it, so a long drag cannot accumulate rounding drift.\n */\n origin: CalendarEventTimes\n /** What the card shows right now. The model is not touched until the gesture ends. */\n preview: CalendarEventTimes\n /** The pointer holding the gesture, or null for a keyboard grab. */\n pointerId: number | null\n originX: number\n originY: number\n /**\n * Where the pointer last was. Two things move the calendar UNDER a still pointer: paging at\n * an edge, and auto-scrolling. Each has to work out afresh what the pointer is now over.\n */\n lastX: number\n lastY: number\n /** Whether it moved past the threshold, which separates a drag from a click. */\n moved: boolean\n /**\n * Whether the pointer is off the calendar altogether: over the toolbar, or over the page\n * beside it. A KEYBOARD grab can never set it, and structurally rather than by a guard:\n * `onPointermove` is the only writer, and it returns on the id mismatch first: a grab's\n * `pointerId` is null, which no real pointer's id ever equals.\n */\n outside: boolean\n /**\n * The reading direction, read once when the gesture begins. It cannot change under a live\n * drag, and asking the document on every frame costs a style resolution each time.\n */\n rtl: boolean\n}\n\n/** What a grid hands over to start a gesture; the rest is filled in here. */\nexport type GestureInit<G extends GestureBase> = Omit<\n G,\n 'preview' | 'lastX' | 'lastY' | 'moved' | 'outside' | 'rtl'\n>\n\n/**\n * A gesture held by the KEYBOARD rather than by a pointer: grabbed with Space, and waiting for\n * the arrows to move it.\n */\nconst isGrabbed = (state: GestureBase | null) => state !== null && state.pointerId === null\n\n/** The two intents that move a grabbed event, which only a grid can turn into times. */\nexport type GrabStep = Extract<CalendarIntent, { kind: 'grabMove' | 'grabResize' }>\n\nexport interface CalendarGestureOptions<E extends CalendarEvent, G extends GestureBase> {\n /** Every event as it is currently drawn, the ghost's original among them. */\n events: () => readonly E[]\n /** The event drawn under an id, for the handlers. */\n eventOf: (id: CalendarEventId) => E | undefined\n rootEl: Ref<HTMLElement | null>\n /** The element the pointer is captured on once a press travels. */\n captureEl: Ref<HTMLElement | null>\n /** The box the cells occupy, which points and edges are read against. */\n measureEl: Ref<HTMLElement | null>\n /** How many columns that box is divided into. */\n columns: () => number\n /** Whether events may be moved at all. */\n editable: () => boolean\n /** The node every card points at to say how it can be moved. */\n hintId: () => string\n /** The step a grabbed event moves by, handed to the keyboard table. */\n slotMinutes: () => number\n edgeStepDelay: () => number\n /** Whatever the grid's cells are made of, so paging under a still pointer re-places the card. */\n layout: () => unknown\n /** Works out where the gesture's event belongs from the pointer's last position. */\n applyPoint: (state: G, rect: DOMRect) => void\n /** Called on every pointer move past the threshold, after the edge watch, with both rects. */\n onEdges?: (state: G, rootRect: DOMRect | undefined) => void\n /** Everything else a gesture leaves running, dropped on every path out of one. */\n onRelease?: () => void\n /**\n * A pointer gesture let go inside the calendar. Returns true when the grid has answered it\n * itself, such as a slot drawn out, and nothing is left to drop.\n */\n onLetGo?: (state: G) => boolean\n /** A gesture that moved, committed: the grid reports the new times. */\n onDrop: (state: G) => void\n /** What a keyboard grab of this event starts, or null when it cannot be grabbed. */\n grab: (item: E) => GestureInit<G> | null\n /** Applies one arrow step to a grabbed event. Returns false when the step means nothing here. */\n onGrabStep: (state: G, step: GrabStep, item: E) => boolean\n step: (direction: -1 | 1) => void\n announce: (message: string) => void\n}\n\nexport function useCalendarGesture<E extends CalendarEvent, G extends GestureBase>(\n options: CalendarGestureOptions<E, G>,\n) {\n const m = useMessages()\n const gesture = ref<G | null>(null) as Ref<G | null>\n\n /** True while a card is being held by the keyboard rather than dragged by a pointer. */\n const grabbing = computed(() => isGrabbed(gesture.value))\n\n const isRtl = () => isElementRtl(options.measureEl.value)\n\n /**\n * The echo of where the gestured event began, so the reader can see what they are moving it\n * FROM. A press that has not travelled yet produces none, or a click would flash a second\n * card over the first.\n */\n const ghostEvent = computed<E | null>(() => {\n const state = gesture.value\n if (!state?.moved) return null\n const original = options.events().find((event) => event.id === state.id)\n return original ? ({ ...original, ...state.origin, id: ghostIdOf(state.id) } as E) : null\n })\n\n /** A list with the gestured event moved to wherever it is now, plus its echo. */\n function withPreview(events: readonly E[]): E[] {\n const state = gesture.value\n if (!state) return [...events]\n const moving = events.map((event) =>\n event.id === state.id ? ({ ...event, ...state.preview } as E) : event,\n )\n return ghostEvent.value ? [...moving, ghostEvent.value] : moving\n }\n\n /**\n * Set click suppression on pointerup, not pointercancel; clear it before every press guard\n * and keyboard activation. Captured releases may never send their click to an event card.\n */\n let dragged = false\n\n /** Closes the books on the previous gesture. */\n function endLastGesture() {\n dragged = false\n if (grabbing.value) revertGrab()\n }\n\n /** Puts a keyboard grab back where it was taken from, writing nothing, and says so. */\n function revertGrab() {\n const state = gesture.value\n if (!state) return\n gesture.value = null\n options.announce(m.value.calendar.reverted)\n return state\n }\n\n /**\n * A keyboard grab ends when the focus moves on to something else, the toolbar or another\n * control.\n */\n function onFocusout(event: FocusEvent) {\n const state = gesture.value\n const next = event.relatedTarget as HTMLElement | null\n if (!grabbing.value || !state || !next) return\n const card = next.closest<HTMLElement>('.v-calendar-event')\n if (card && card.dataset.eventId === String(state.id)) return\n revertGrab()\n }\n\n /** Whether the gesture that just ended actually moved; asking lowers the flag. */\n function consumeDrag() {\n const was = dragged\n dragged = false\n return was\n }\n\n const edge = useEdgeStep(options.step, options.edgeStepDelay)\n\n /** Which edge is counting down, as the attribute the stylesheet reads. */\n const edgeCue = computed(() =>\n edge.pending.value === -1 ? 'start' : edge.pending.value === 1 ? 'end' : undefined,\n )\n\n function release() {\n edge.cancel()\n options.onRelease?.()\n }\n\n /** Starts a gesture. */\n function start(init: GestureInit<G>, rtl = isRtl()) {\n gesture.value = {\n ...init,\n preview: { ...init.origin },\n lastX: init.originX,\n lastY: init.originY,\n moved: false,\n outside: false,\n rtl,\n } as G\n }\n\n /**\n * Called when the press crosses the drag threshold, never at the press. The browser sends the\n * `click` that follows a captured `pointerup` to the CAPTURE element, so a press captured at\n * once reaches the scroller instead of the card or the cell under it: clicking an event would\n * open nothing and clicking an empty slot would report nothing, on every calendar whose\n * events can be moved or drawn.\n */\n function capture(pointerId: number) {\n // @fallback\n /*\n * Synthetic pointer events may not have a capturable pointer; allow the gesture to continue\n * within the element if capture fails.\n */\n try {\n options.captureEl.value?.setPointerCapture(pointerId)\n } catch {\n /* Synthetic pointers cannot be captured. */\n }\n }\n\n function reapply(state: G) {\n const rect = options.measureEl.value?.getBoundingClientRect()\n if (rect) options.applyPoint(state, rect)\n }\n\n function onPointermove(event: PointerEvent) {\n const state = gesture.value\n if (!state || state.pointerId !== event.pointerId) return\n\n state.lastX = event.clientX\n state.lastY = event.clientY\n\n // Below the threshold nothing has happened yet. That is what keeps a click a click: the\n // hand's tremor during a press would otherwise register as a one-pixel drag.\n if (\n !state.moved &&\n Math.abs(event.clientX - state.originX) < DRAG_THRESHOLD &&\n Math.abs(event.clientY - state.originY) < DRAG_THRESHOLD\n ) {\n return\n }\n\n if (!state.moved) capture(event.pointerId)\n state.moved = true\n const rootRect = options.rootEl.value?.getBoundingClientRect()\n const rect = options.measureEl.value?.getBoundingClientRect()\n\n /*\n * Neither the placement nor the edge watch below is gated on it, and neither may become so.\n * The card must keep following the pointer while it is out, which makes coming back in\n * seamless, and paging must keep running, pushing past the edge being how one crosses into\n * the next period.\n */\n state.outside = rootRect ? !pointWithin({ x: state.lastX, y: state.lastY }, rootRect) : false\n\n if (!rect) return\n options.applyPoint(state, rect)\n edge.watchEdge(\n inlineEdgeAt(\n state.lastX,\n gridGeometryOf(rect, options.columns(), state.rtl),\n EDGE_BAND,\n state.rtl,\n ),\n )\n options.onEdges?.(state, rootRect)\n }\n\n function onPointerup(event: PointerEvent) {\n const state = gesture.value\n if (!state || state.pointerId !== event.pointerId) return\n gesture.value = null\n dragged = state.moved\n release()\n\n /*\n * Nothing is announced: the pointer says nothing on a SUCCESSFUL drop either; `grabbed`,\n * `dropped` and `reverted` belong to the keyboard grab, which has no pointer to show where\n * the event went.\n */\n if (state.outside) return\n if (options.onLetGo?.(state)) return\n // A press that never travelled is an ordinary click, which the button's own click event is\n // about to report. Writing anything here would move an event nobody dragged.\n if (state.moved) options.onDrop(state)\n }\n\n /*\n * The gesture was taken away; a system gesture, a context menu, the page starting to scroll.\n * Nothing is written and nothing is announced: the card goes back to where the model still\n * says it is.\n */\n function onPointercancel(event: PointerEvent) {\n // Another pointer's cancel, a second finger starting to pan, is not this gesture's. A\n // keyboard grab carries no pointer id, so no cancel can end it either.\n if (gesture.value?.pointerId !== event.pointerId) return\n abandon()\n }\n\n function abandon() {\n gesture.value = null\n release()\n }\n\n /*\n * Its `pointerup` then lands outside and never reaches the bindings, so without this the\n * gesture would outlive the press: a mouse keeps the same pointer id, and bringing it back\n * over the calendar with the button up would pick the event up and carry it. A captured\n * pointer never leaves, so a real drag is untouched.\n */\n function onPointerleave(event: PointerEvent) {\n const state = gesture.value\n if (!state || state.pointerId !== event.pointerId || state.moved) return\n abandon()\n }\n\n /*\n * Paging swaps the cells out from under a live drag, and the dragged event's day is then off\n * screen; the card would vanish until the next pointer move, which never comes if the hand is\n * holding still against the edge. Re-applying the last position once the new cells are\n * rendered is what keeps the card under the pointer across the boundary.\n */\n watch(\n options.layout,\n () => {\n const state = gesture.value\n if (state?.moved) reapply(state)\n },\n { flush: 'post' },\n )\n\n /**\n * The card is found by WALKING the cards rather than by building an attribute selector. An id\n * belongs to the consumer, so it may hold a quote or a backslash a selector would need\n * escaped, and `CSS.escape` cannot do that escaping here: jsdom defines no `CSS` object at\n * all, so the call threw on every keyboard grab, from inside a `nextTick` where nothing could\n * catch it; an unhandled rejection, which fails the run while every assertion still passes.\n */\n function refocusCard(id: CalendarEventId) {\n void nextTick(() => {\n // Searched from the view's root: the time grid's all-day bars sit in its sticky header,\n // outside the box the cells occupy.\n const cards = options.rootEl.value?.querySelectorAll<HTMLElement>('.v-calendar-event')\n Array.from(cards ?? [])\n .find((card) => card.dataset.eventId === String(id))\n ?.focus()\n })\n }\n\n /** Puts the focus on a cell by its id, once the render that made it tabbable has run. */\n function focusCell(id: string, deferred = false, focusOptions?: FocusOptions) {\n const move = () => document.getElementById(id)?.focus(focusOptions)\n if (deferred) void nextTick(move)\n else move()\n }\n\n /** The event a card stands for, read back off the attribute the card publishes. */\n const idOfCard = (card: HTMLElement) => cardIdOf(card, (id) => options.eventOf(id) !== undefined)\n\n /**\n * A card answers a different table from the grid it sits in; that is what a grab mode IS.\n * Without it the grid would offer a gesture the pointer alone could reach (WCAG 2.1.1).\n */\n function onCardKeydown(event: KeyboardEvent, card: HTMLElement) {\n // Whatever click follows is this key's, never the end of an earlier drag.\n dragged = false\n const item = options.eventOf(idOfCard(card))\n if (!item) return\n\n const held = grabbing.value && gesture.value?.id === item.id\n const state = held ? gesture.value! : null\n const intent = calendarIntent(\n event,\n held ? 'grabbed' : 'event',\n options.slotMinutes(),\n state?.rtl ?? isRtl(),\n )\n if (!intent) return\n\n if (!state) {\n // An event that cannot be moved keeps a button's ordinary behaviour: Space presses it,\n // which the card's click handler reports.\n const init = intent.kind === 'grab' ? options.grab(item) : null\n if (!init) return\n /*\n * Cancelling the keydown is what stops that press, so the keystroke that takes hold of\n * the event does not open it too: without it every attempt to move something would fire\n * the consumer's editor over it.\n */\n event.preventDefault()\n start(init)\n options.announce(m.value.calendar.grabbed)\n return\n }\n\n event.preventDefault()\n\n if (intent.kind === 'grabMove' || intent.kind === 'grabResize') {\n if (!options.onGrabStep(state, intent, item)) return\n state.moved = true\n refocusCard(state.id)\n return\n }\n\n if (intent.kind === 'activate') {\n gesture.value = null\n if (state.moved) options.onDrop(state)\n options.announce(m.value.calendar.dropped)\n refocusCard(state.id)\n return\n }\n\n if (intent.kind === 'cancel') {\n revertGrab()\n refocusCard(state.id)\n }\n }\n\n /**\n * The five things every card is told about the gesture, bound with `v-bind` so the two grids\n * cannot describe the same state in two different ways.\n */\n function cardState(id: CalendarEventId) {\n const state = gesture.value\n const mine = state !== null && state.id === id\n return {\n /*\n * Only once the press has travelled. A dragged card takes no pointer events, so marked at\n * the press it lets the release land on the cell beneath it: the click then goes to that\n * cell, and clicking an event reports an empty slot instead of opening it.\n */\n dragging: mine && state.pointerId !== null && state.moved,\n rejected: mine && state.outside,\n grabbed: mine && state.pointerId === null,\n hintId: options.editable() && !isGhostId(id) ? options.hintId() : undefined,\n ghostOf: isGhostId(id) ? originalIdOf(id) : undefined,\n }\n }\n\n return {\n gesture,\n grabbing,\n ghostEvent,\n withPreview,\n edgeCue,\n isRtl,\n start,\n reapply,\n release,\n endLastGesture,\n consumeDrag,\n onPointermove,\n onPointerup,\n onPointercancel,\n onPointerleave,\n onFocusout,\n onCardKeydown,\n refocusCard,\n focusCell,\n idOfCard,\n cardState,\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyboard.js","names":[],"sources":["../../../src/components/VCalendar/keyboard.ts"],"sourcesContent":["// @keyboard @a11y\n/**\n * Decode calendar keys without side effects; focus, model updates and announcements remain with\n * the view.\n */\n\nimport { MINUTES_PER_HOUR } from './layout'\n\n/** What one key means, given where the focus is. */\nexport type CalendarIntent =\n /** Move the focused cell by whole days and whole hours. */\n | { kind: 'moveFocus'; days: number; minutes: number }\n /** Jump to the first or last day of the row. */\n | { kind: 'rowEdge'; edge: 'start' | 'end' }\n /** Show the previous or next period, as the toolbar's buttons do. */\n | { kind: 'period'; delta: -1 | 1 }\n /** Take up what is focused: report an empty cell, or commit a move under way. */\n | { kind: 'activate' }\n /** Open the focused day, which only a month square offers. */\n | { kind: 'openDay' }\n /** Take hold of a card, so the arrows move it. */\n | { kind: 'grab' }\n /** Abandon a move under way and put the event back. */\n | { kind: 'cancel' }\n /** Move the grabbed event. */\n | { kind: 'grabMove'; days: number; minutes: number }\n /** Change how long the grabbed event lasts. */\n | { kind: 'grabResize'; minutes: number }\n\n/**\n * Where the focus is when the key arrives, which decides the answer: - `cell`: an empty part of\n * the grid.\n */\nexport type CalendarFocus = 'cell' | 'event' | 'grabbed'\n\n/** The part of a keyboard event the table reads; a `KeyboardEvent` is one. */\nexport interface KeyChord {\n key: string\n shiftKey?: boolean\n altKey?: boolean\n ctrlKey?: boolean\n metaKey?: boolean\n}\n\n/** What a key means. */\nexport function calendarIntent(\n chord: KeyChord,\n focus: CalendarFocus,\n slotMinutes: number,\n rtl: boolean,\n): CalendarIntent | undefined {\n if (chord.altKey || chord.ctrlKey || chord.metaKey) return undefined\n const { key } = chord\n const shiftKey = chord.shiftKey === true\n const step = slotMinutes > 0 ? slotMinutes : 1\n // The inline arrows follow the reading direction; the block ones never do, because down\n // is later in the day in every script.\n const inline = rtl ? -1 : 1\n\n if (focus === 'grabbed') {\n switch (key) {\n case 'ArrowLeft':\n return { kind: 'grabMove', days: -inline, minutes: 0 }\n case 'ArrowRight':\n return { kind: 'grabMove', days: inline, minutes: 0 }\n case 'ArrowUp':\n return shiftKey\n ? { kind: 'grabResize', minutes: -step }\n : { kind: 'grabMove', days: 0, minutes: -step }\n case 'ArrowDown':\n return shiftKey\n ? { kind: 'grabResize', minutes: step }\n : { kind: 'grabMove', days: 0, minutes: step }\n case 'Enter':\n case ' ':\n return { kind: 'activate' }\n case 'Escape':\n return { kind: 'cancel' }\n default:\n return undefined\n }\n }\n\n if (focus === 'event') return key === ' ' ? { kind: 'grab' } : undefined\n\n switch (key) {\n case 'ArrowLeft':\n return { kind: 'moveFocus', days: -inline, minutes: 0 }\n case 'ArrowRight':\n return { kind: 'moveFocus', days: inline, minutes: 0 }\n case 'ArrowUp':\n return { kind: 'moveFocus', days: 0, minutes: -MINUTES_PER_HOUR }\n case 'ArrowDown':\n return { kind: 'moveFocus', days: 0, minutes: MINUTES_PER_HOUR }\n case 'Home':\n return { kind: 'rowEdge', edge: 'start' }\n case 'End':\n return { kind: 'rowEdge', edge: 'end' }\n case 'PageUp':\n return { kind: 'period', delta: -1 }\n case 'PageDown':\n return { kind: 'period', delta: 1 }\n case 'Enter':\n return shiftKey ? { kind: 'openDay' } : { kind: 'activate' }\n case ' ':\n return { kind: 'activate' }\n default:\n return undefined\n }\n}\n"],"mappings":";AA6CA,
|
|
1
|
+
{"version":3,"file":"keyboard.js","names":["calendarIntent","chord","shiftKey","step","slotMinutes","inline","rtl","focus","key"],"sources":["../../../src/components/VCalendar/keyboard.ts"],"sourcesContent":["// @keyboard @a11y\n/**\n * Decode calendar keys without side effects; focus, model updates and announcements remain with\n * the view.\n */\n\nimport { MINUTES_PER_HOUR } from './layout'\n\n/** What one key means, given where the focus is. */\nexport type CalendarIntent =\n /** Move the focused cell by whole days and whole hours. */\n | { kind: 'moveFocus'; days: number; minutes: number }\n /** Jump to the first or last day of the row. */\n | { kind: 'rowEdge'; edge: 'start' | 'end' }\n /** Show the previous or next period, as the toolbar's buttons do. */\n | { kind: 'period'; delta: -1 | 1 }\n /** Take up what is focused: report an empty cell, or commit a move under way. */\n | { kind: 'activate' }\n /** Open the focused day, which only a month square offers. */\n | { kind: 'openDay' }\n /** Take hold of a card, so the arrows move it. */\n | { kind: 'grab' }\n /** Abandon a move under way and put the event back. */\n | { kind: 'cancel' }\n /** Move the grabbed event. */\n | { kind: 'grabMove'; days: number; minutes: number }\n /** Change how long the grabbed event lasts. */\n | { kind: 'grabResize'; minutes: number }\n\n/**\n * Where the focus is when the key arrives, which decides the answer: - `cell`: an empty part of\n * the grid.\n */\nexport type CalendarFocus = 'cell' | 'event' | 'grabbed'\n\n/** The part of a keyboard event the table reads; a `KeyboardEvent` is one. */\nexport interface KeyChord {\n key: string\n shiftKey?: boolean\n altKey?: boolean\n ctrlKey?: boolean\n metaKey?: boolean\n}\n\n/** What a key means. */\nexport function calendarIntent(\n chord: KeyChord,\n focus: CalendarFocus,\n slotMinutes: number,\n rtl: boolean,\n): CalendarIntent | undefined {\n if (chord.altKey || chord.ctrlKey || chord.metaKey) return undefined\n const { key } = chord\n const shiftKey = chord.shiftKey === true\n const step = slotMinutes > 0 ? slotMinutes : 1\n // The inline arrows follow the reading direction; the block ones never do, because down\n // is later in the day in every script.\n const inline = rtl ? -1 : 1\n\n if (focus === 'grabbed') {\n switch (key) {\n case 'ArrowLeft':\n return { kind: 'grabMove', days: -inline, minutes: 0 }\n case 'ArrowRight':\n return { kind: 'grabMove', days: inline, minutes: 0 }\n case 'ArrowUp':\n return shiftKey\n ? { kind: 'grabResize', minutes: -step }\n : { kind: 'grabMove', days: 0, minutes: -step }\n case 'ArrowDown':\n return shiftKey\n ? { kind: 'grabResize', minutes: step }\n : { kind: 'grabMove', days: 0, minutes: step }\n case 'Enter':\n case ' ':\n return { kind: 'activate' }\n case 'Escape':\n return { kind: 'cancel' }\n default:\n return undefined\n }\n }\n\n if (focus === 'event') return key === ' ' ? { kind: 'grab' } : undefined\n\n switch (key) {\n case 'ArrowLeft':\n return { kind: 'moveFocus', days: -inline, minutes: 0 }\n case 'ArrowRight':\n return { kind: 'moveFocus', days: inline, minutes: 0 }\n case 'ArrowUp':\n return { kind: 'moveFocus', days: 0, minutes: -MINUTES_PER_HOUR }\n case 'ArrowDown':\n return { kind: 'moveFocus', days: 0, minutes: MINUTES_PER_HOUR }\n case 'Home':\n return { kind: 'rowEdge', edge: 'start' }\n case 'End':\n return { kind: 'rowEdge', edge: 'end' }\n case 'PageUp':\n return { kind: 'period', delta: -1 }\n case 'PageDown':\n return { kind: 'period', delta: 1 }\n case 'Enter':\n return shiftKey ? { kind: 'openDay' } : { kind: 'activate' }\n case ' ':\n return { kind: 'activate' }\n default:\n return undefined\n }\n}\n"],"mappings":";AA6CA,SAAgBA,EACd,GACA,GACA,GACA,GAC4B;CAC5B,IAAIC,EAAM,UAAUA,EAAM,WAAWA,EAAM,SAAS;CACpD,IAAM,EAAE,WAAQA,GACVC,IAAWD,EAAM,aAAa,IAC9BE,IAAOC,IAAc,IAAIA,IAAc,GAGvCC,IAASC,IAAM,KAAK;CAE1B,IAAIC,MAAU,WACZ,QAAQC,GAAR;EACE,KAAK,aACH,OAAO;GAAE,MAAM;GAAY,MAAM,CAACH;GAAQ,SAAS;EAAE;EACvD,KAAK,cACH,OAAO;GAAE,MAAM;GAAY,MAAMA;GAAQ,SAAS;EAAE;EACtD,KAAK,WACH,OAAOH,IACH;GAAE,MAAM;GAAc,SAAS,CAACC;EAAK,IACrC;GAAE,MAAM;GAAY,MAAM;GAAG,SAAS,CAACA;EAAK;EAClD,KAAK,aACH,OAAOD,IACH;GAAE,MAAM;GAAc,SAASC;EAAK,IACpC;GAAE,MAAM;GAAY,MAAM;GAAG,SAASA;EAAK;EACjD,KAAK;EACL,KAAK,KACH,OAAO,EAAE,MAAM,WAAW;EAC5B,KAAK,UACH,OAAO,EAAE,MAAM,SAAS;EAC1B,SACE;CACJ;CAGF,IAAII,MAAU,SAAS,OAAOC,MAAQ,MAAM,EAAE,MAAM,OAAO,IAAI,KAAA;CAE/D,QAAQA,GAAR;EACE,KAAK,aACH,OAAO;GAAE,MAAM;GAAa,MAAM,CAACH;GAAQ,SAAS;EAAE;EACxD,KAAK,cACH,OAAO;GAAE,MAAM;GAAa,MAAMA;GAAQ,SAAS;EAAE;EACvD,KAAK,WACH,OAAO;GAAE,MAAM;GAAa,MAAM;GAAG,SAAS;EAAkB;EAClE,KAAK,aACH,OAAO;GAAE,MAAM;GAAa,MAAM;GAAG,SAAA;EAA0B;EACjE,KAAK,QACH,OAAO;GAAE,MAAM;GAAW,MAAM;EAAQ;EAC1C,KAAK,OACH,OAAO;GAAE,MAAM;GAAW,MAAM;EAAM;EACxC,KAAK,UACH,OAAO;GAAE,MAAM;GAAU,OAAO;EAAG;EACrC,KAAK,YACH,OAAO;GAAE,MAAM;GAAU,OAAO;EAAE;EACpC,KAAK,SACH,OAAOH,IAAW,EAAE,MAAM,UAAU,IAAI,EAAE,MAAM,WAAW;EAC7D,KAAK,KACH,OAAO,EAAE,MAAM,WAAW;EAC5B,SACE;CACJ;AACF"}
|
|
@@ -2,20 +2,20 @@ import { clamp as e } from "../../utils/number.js";
|
|
|
2
2
|
import { pad2 as t } from "../../utils/text.js";
|
|
3
3
|
import { addDays as n, addMonths as r, buildMonthGrid as i, compareISO as a, daysInMonth as o, formatISO as s, isoOf as c, parseISO as l } from "../../utils/date.js";
|
|
4
4
|
import { minutesOf as u } from "../../utils/time.js";
|
|
5
|
-
var d = 1440, f = 7, p =
|
|
5
|
+
var d = 1440, f = 7, p = 864e5;
|
|
6
6
|
function m(e, t) {
|
|
7
7
|
let n = String(e), r = String(t);
|
|
8
8
|
return n < r ? -1 : +(n > r);
|
|
9
9
|
}
|
|
10
10
|
function h(t, n) {
|
|
11
|
-
let r = e(Math.round(t * 60), 0,
|
|
11
|
+
let r = e(Math.round(t * 60), 0, 1380);
|
|
12
12
|
return {
|
|
13
13
|
start: r,
|
|
14
14
|
end: e(Math.round(n * 60), r + 60, d)
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
17
|
function g(n) {
|
|
18
|
-
let r = e(Math.round(n), 0,
|
|
18
|
+
let r = e(Math.round(n), 0, 1439);
|
|
19
19
|
return `${t(Math.floor(r / 60))}:${t(r % 60)}`;
|
|
20
20
|
}
|
|
21
21
|
function _(e, t) {
|
|
@@ -64,12 +64,12 @@ function C(e, t, r, i) {
|
|
|
64
64
|
return a;
|
|
65
65
|
}
|
|
66
66
|
if (t === "week") {
|
|
67
|
-
let t =
|
|
67
|
+
let t = (l(e).getDay() - r[0] + f) % f, i = n(e, -t), a = [];
|
|
68
68
|
for (let e = 0; e < f; e++) {
|
|
69
|
-
let
|
|
70
|
-
y(
|
|
69
|
+
let t = n(i, e);
|
|
70
|
+
y(t, r) && a.push(t);
|
|
71
71
|
}
|
|
72
|
-
return
|
|
72
|
+
return a;
|
|
73
73
|
}
|
|
74
74
|
let a = S(t, i), s = [b(e, r)];
|
|
75
75
|
for (let e = 1; e < a; e++) s.push(x(s[e - 1], 1, r));
|
|
@@ -132,11 +132,7 @@ function k(t, n, r, i) {
|
|
|
132
132
|
continue;
|
|
133
133
|
}
|
|
134
134
|
let n = _(e.endTime, 0);
|
|
135
|
-
|
|
136
|
-
c(e.id, o.get(e.start), t, d, "whole");
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
c(e.id, o.get(e.start), t, d, "head"), c(e.id, o.get(e.end), 0, n, "tail");
|
|
135
|
+
n === 0 ? c(e.id, o.get(e.start), t, d, "whole") : (c(e.id, o.get(e.start), t, d, "head"), c(e.id, o.get(e.end), 0, n, "tail"));
|
|
140
136
|
}
|
|
141
137
|
return s;
|
|
142
138
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.js","names":[],"sources":["../../../src/components/VCalendar/layout.ts"],"sourcesContent":["// @core\n/**\n * Pure calendar layout shared by its views; component ownership keeps agenda-specific contracts\n * out of general utilities.\n */\nimport {\n addDays,\n addMonths,\n buildMonthGrid,\n compareISO,\n daysInMonth,\n formatISO,\n isoOf,\n parseISO,\n} from '../../utils/date'\nimport { clamp } from '../../utils/number'\nimport { pad2 } from '../../utils/text'\nimport { minutesOf } from '../../utils/time'\n\nimport type { MonthCell } from '../../utils/date'\nimport type { CalendarEvent, CalendarEventId, CalendarEventTimes, CalendarView } from './types'\n\n/** An hour, in the minutes every time here is counted in: one row of a time grid. */\nexport const MINUTES_PER_HOUR = 60\n\n/** How many minutes a day holds. The upper bound of every time computed here. */\nexport const MINUTES_PER_DAY = 24 * MINUTES_PER_HOUR\n\nconst DAYS_PER_WEEK = 7\n\n/** A day in milliseconds, for counting the days between two dates. */\nconst MS_PER_DAY = 24 * 60 * 60 * 1000\n\n/** How far the pointer must travel before a press becomes a drag rather than a click. */\nexport const DRAG_THRESHOLD = 3\n\n/**\n * This must not be `localeCompare`. An id is an opaque key, never user-facing text, so there is\n * nothing here to collate; and `localeCompare` with no locale resolves against the RUNTIME's\n * default, which differs between Node and the browser.\n */\nfunction compareId(a: CalendarEventId, b: CalendarEventId): number {\n const left = String(a)\n const right = String(b)\n return left < right ? -1 : left > right ? 1 : 0\n}\n\n/** The slice of the day a calendar is showing, as minutes since midnight. */\nexport interface TimeWindow {\n start: number\n end: number\n}\n\n/** Turns the `dayStart`/`dayEnd` props, given in hours, into the window the rest works in. */\nexport function windowOf(startHour: number, endHour: number): TimeWindow {\n const start = clamp(\n Math.round(startHour * MINUTES_PER_HOUR),\n 0,\n MINUTES_PER_DAY - MINUTES_PER_HOUR,\n )\n const end = clamp(\n Math.round(endHour * MINUTES_PER_HOUR),\n start + MINUTES_PER_HOUR,\n MINUTES_PER_DAY,\n )\n return { start, end }\n}\n\n/**\n * The value is held one minute short of midnight rather than allowed to reach it. A day's last\n * moment is 1440 in this arithmetic, but `24:00` is not a time any of the design system's\n * helpers accept (`isValidTime` rejects it).\n */\nexport function timeOf(minutes: number): string {\n const held = clamp(Math.round(minutes), 0, MINUTES_PER_DAY - 1)\n return `${pad2(Math.floor(held / MINUTES_PER_HOUR))}:${pad2(held % MINUTES_PER_HOUR)}`\n}\n\n/** An `HH:mm` string into minutes since midnight; `fallback` when it is not one. */\nexport function minutesAt(time: string | undefined, fallback: number): number {\n return minutesOf(time) ?? fallback\n}\n\n/** The weekdays a calendar shows, in reading order. */\nexport function normalizeWeekdays(\n weekdays: readonly number[] | undefined,\n firstDayOfWeek: number,\n): number[] {\n const seen = new Set<number>()\n const kept: number[] = []\n for (const day of weekdays ?? []) {\n const value = Math.trunc(day)\n if (value >= 0 && value <= 6 && !seen.has(value)) {\n seen.add(value)\n kept.push(value)\n }\n }\n if (kept.length > 0) return kept\n // Monday when the day given is not a number, the fallback `firstDayOfWeekFor` uses too.\n const first = Number.isFinite(firstDayOfWeek) ? ((Math.trunc(firstDayOfWeek) % 7) + 7) % 7 : 1\n return Array.from({ length: DAYS_PER_WEEK }, (_, i) => (first + i) % 7)\n}\n\n/** Whether a date falls on one of the visible weekdays. */\nexport function isVisibleDay(iso: string, weekdays: readonly number[]): boolean {\n const date = parseISO(iso)\n return date !== null && weekdays.includes(date.getDay())\n}\n\n/** The first visible day at or after `iso` (or at or before it, going backwards). */\nexport function snapToVisibleDay(\n iso: string,\n weekdays: readonly number[],\n direction: -1 | 1 = 1,\n): string {\n if (weekdays.length === 0) return iso\n let current = iso\n for (let i = 0; i < DAYS_PER_WEEK; i++) {\n if (isVisibleDay(current, weekdays)) return current\n current = addDays(current, direction)\n }\n return iso\n}\n\n/** Moves `count` VISIBLE days from `iso`, skipping over the hidden ones. */\nexport function advanceVisibleDays(\n iso: string,\n count: number,\n weekdays: readonly number[],\n): string {\n if (count === 0 || weekdays.length === 0) return iso\n const direction = count > 0 ? 1 : -1\n let current = iso\n let remaining = Math.abs(count)\n // Bounded so a malformed weekday list can never spin: at worst one visible day costs\n // seven calendar days, so this is the same walk with its worst case written down.\n let guard = Math.abs(count) * DAYS_PER_WEEK + DAYS_PER_WEEK\n while (remaining > 0 && guard-- > 0) {\n current = addDays(current, direction)\n if (isVisibleDay(current, weekdays)) remaining--\n }\n return current\n}\n\n/** How many day columns a view shows, before the visible-weekday filter applies. */\nexport function columnCount(view: CalendarView, customDays: number): number {\n if (view === 'day') return 1\n if (view === '4days') return 4\n if (view === 'custom') return clamp(Math.trunc(customDays), 1, 31)\n return DAYS_PER_WEEK\n}\n\n/** The days a view shows, in reading order. */\nexport function visibleDays(\n anchor: string,\n view: CalendarView,\n weekdays: readonly number[],\n customDays: number,\n): string[] {\n if (weekdays.length === 0 || parseISO(anchor) === null) return []\n if (view === 'year') return []\n\n if (view === 'month') {\n const date = parseISO(anchor)!\n const year = date.getFullYear()\n const month0 = date.getMonth()\n const days: string[] = []\n for (let day = 1; day <= daysInMonth(year, month0); day++) {\n const iso = isoOf(year, month0, day)\n if (isVisibleDay(iso, weekdays)) days.push(iso)\n }\n return days\n }\n\n if (view === 'week') {\n const date = parseISO(anchor)!\n const offset = (date.getDay() - weekdays[0]! + DAYS_PER_WEEK) % DAYS_PER_WEEK\n const weekStart = addDays(anchor, -offset)\n const days: string[] = []\n for (let i = 0; i < DAYS_PER_WEEK; i++) {\n const iso = addDays(weekStart, i)\n if (isVisibleDay(iso, weekdays)) days.push(iso)\n }\n return days\n }\n\n const total = columnCount(view, customDays)\n const days: string[] = [snapToVisibleDay(anchor, weekdays)]\n for (let i = 1; i < total; i++) days.push(advanceVisibleDays(days[i - 1]!, 1, weekdays))\n return days\n}\n\n/**\n * The day-shaped views step over VISIBLE days, not calendar ones. Stepping by one calendar day\n * from a Friday in a Monday-to-Friday calendar would land on a Saturday the grid does not draw;\n * the view would then show the same week it already showed, and the button would look broken\n * with nothing in the console to say why.\n */\nexport function stepAnchor(\n anchor: string,\n view: CalendarView,\n direction: -1 | 1,\n weekdays: readonly number[],\n customDays: number,\n): string {\n if (parseISO(anchor) === null) return anchor\n if (view === 'month') return addMonths(anchor, direction)\n if (view === 'year') return addMonths(anchor, direction * 12)\n if (view === 'week') return snapToVisibleDay(addDays(anchor, direction * DAYS_PER_WEEK), weekdays)\n return advanceVisibleDays(anchor, direction * columnCount(view, customDays), weekdays)\n}\n\n/** The first and last day a view covers, which names it in the toolbar. */\nexport function visibleRange(\n anchor: string,\n view: CalendarView,\n weekdays: readonly number[],\n customDays: number,\n): { start: string; end: string } {\n if (view === 'year') {\n // No clock read here: the server and the browser must name the same range.\n const date = parseISO(anchor)\n if (!date) return { start: anchor, end: anchor }\n return { start: isoOf(date.getFullYear(), 0, 1), end: isoOf(date.getFullYear(), 11, 31) }\n }\n const days = visibleDays(anchor, view, weekdays, customDays)\n if (days.length === 0) return { start: anchor, end: anchor }\n return { start: days[0]!, end: days[days.length - 1]! }\n}\n\n/** How long an event lasts, in minutes, counting the days between its dates. */\nexport function durationOf(times: CalendarEventTimes): number {\n return (\n daySpan(times) * MINUTES_PER_DAY +\n minutesAt(times.endTime, MINUTES_PER_DAY) -\n minutesAt(times.startTime, 0)\n )\n}\n\n/** Whether an event runs from one day into the next without lasting a whole day. */\nexport function spansMidnight(times: CalendarEventTimes): boolean {\n return compareISO(times.start, times.end) < 0 && durationOf(times) < MINUTES_PER_DAY\n}\n\n/** Whether an event belongs in the band above the grid rather than in a day's column. */\nexport function isAllDayEvent(event: CalendarEvent): boolean {\n if (event.allDay === true) return true\n const order = compareISO(event.start, event.end)\n if (order === 0) return false\n return order > 0 || !spansMidnight(event)\n}\n\n/**\n * Which piece of its event a box is: the whole of it, or one side of midnight for an event\n * that runs from one day into the next.\n */\nexport type SegmentPart = 'whole' | 'head' | 'tail'\n\n/** One event's box in one day column, in minutes since midnight. */\nexport interface EventSegment {\n id: CalendarEventId\n /** Which of the visible columns it belongs to. */\n dayIndex: number\n start: number\n end: number\n /**\n * Whether the card carries on past its top or bottom edge: the window cut it off, or\n * midnight did. Both are drawn the same way.\n */\n clippedStart: boolean\n clippedEnd: boolean\n /**\n * `head` is the evening of an event running past midnight and `tail` its morning. Only a\n * `whole` or a `tail` holds the event's real end, which its resize strip drags.\n */\n part: SegmentPart\n}\n\n/**\n * Cuts the timed events into the boxes the day columns draw. Were the minimum applied during\n * packing instead, two events fifteen minutes apart would be laid out as though they did not\n * overlap and then drawn as though they did.\n */\nexport function timedSegments(\n events: readonly CalendarEvent[],\n days: readonly string[],\n timeWindow: TimeWindow,\n minDuration: number,\n): EventSegment[] {\n const index = new Map(days.map((iso, i) => [iso, i]))\n const segments: EventSegment[] = []\n\n const push = (\n id: CalendarEventId,\n dayIndex: number | undefined,\n rawStart: number,\n rawEnd: number,\n part: SegmentPart,\n ) => {\n if (dayIndex === undefined) return\n if (rawEnd <= timeWindow.start || rawStart >= timeWindow.end) return\n const start = clamp(rawStart, timeWindow.start, timeWindow.end - 1)\n const end = clamp(Math.max(rawEnd, start + minDuration), start + 1, timeWindow.end)\n segments.push({\n id,\n dayIndex,\n start,\n end,\n clippedStart: part === 'tail' || rawStart < timeWindow.start,\n clippedEnd: part === 'head' || rawEnd > timeWindow.end,\n part,\n })\n }\n\n for (const event of events) {\n if (isAllDayEvent(event)) continue\n const rawStart = minutesAt(event.startTime, timeWindow.start)\n\n if (compareISO(event.start, event.end) === 0) {\n const rawEnd = Math.max(minutesAt(event.endTime, rawStart + minDuration), rawStart + 1)\n push(event.id, index.get(event.start), rawStart, rawEnd, 'whole')\n continue\n }\n\n /*\n * An event ending at midnight exactly has nothing on its second day, so it is drawn whole;\n * a tail of no length would otherwise be stretched to a slot and show as a stub at 00:00.\n */\n const rawEnd = minutesAt(event.endTime, 0)\n if (rawEnd === 0) {\n push(event.id, index.get(event.start), rawStart, MINUTES_PER_DAY, 'whole')\n continue\n }\n push(event.id, index.get(event.start), rawStart, MINUTES_PER_DAY, 'head')\n push(event.id, index.get(event.end), 0, rawEnd, 'tail')\n }\n\n return segments\n}\n\n/** A segment once it knows how much of its column's width it gets. */\nexport interface PlacedSegment extends EventSegment {\n /** Which column of its cluster it sits in, counted from zero. */\n column: number\n /** How many columns it takes up: more than one when the room beside it is free. */\n span: number\n /** How many columns its cluster has. It is what both of the above are read against. */\n columns: number\n}\n\n/**\n * Shares one day's width out between the events happening at the same time. Overlap is strictly\n * `a.start < b.end && b.start < a.end`, so events that merely touch (one ending exactly as the\n * next begins) share a column instead of splitting the width.\n */\nexport function packDayColumn(segments: readonly EventSegment[]): PlacedSegment[] {\n const sorted = [...segments].sort(\n (a, b) => a.start - b.start || b.end - b.start - (a.end - a.start) || compareId(a.id, b.id),\n )\n\n const placed: PlacedSegment[] = []\n let cluster: PlacedSegment[] = []\n let columnEnds: number[] = []\n let clusterEnd = -Infinity\n\n const flush = () => {\n const columns = columnEnds.length\n /*\n * The cluster filed by column first, so asking \"is the room beside me free?\" reads that one\n * column's occupants rather than the whole cluster: a crowded morning of k events across c\n * columns otherwise costs k squared per column crossed.\n */\n const byColumn: PlacedSegment[][] = Array.from({ length: columns }, () => [])\n for (const item of cluster) byColumn[item.column]!.push(item)\n for (const item of cluster) {\n let span = 1\n while (\n item.column + span < columns &&\n !byColumn[item.column + span]!.some(\n (other) => other.start < item.end && item.start < other.end,\n )\n ) {\n span++\n }\n item.span = span\n item.columns = columns\n }\n placed.push(...cluster)\n cluster = []\n columnEnds = []\n clusterEnd = -Infinity\n }\n\n for (const segment of sorted) {\n if (segment.start >= clusterEnd && cluster.length > 0) flush()\n\n let column = columnEnds.findIndex((end) => end <= segment.start)\n if (column === -1) {\n column = columnEnds.length\n columnEnds.push(segment.end)\n } else {\n columnEnds[column] = segment.end\n }\n\n cluster.push({ ...segment, column, span: 1, columns: 1 })\n clusterEnd = Math.max(clusterEnd, segment.end)\n }\n if (cluster.length > 0) flush()\n\n return placed\n}\n\n/** One square of the month view: a day, and whether it belongs to the month on show. */\nexport type { MonthCell }\n\n/** The month view, cut into weeks. */\nexport function monthWeeks(anchor: string, weekdays: readonly number[]): MonthCell[][] {\n const date = parseISO(anchor)\n if (!date || weekdays.length === 0) return []\n\n const cells = buildMonthGrid(date.getFullYear(), date.getMonth(), weekdays[0]!)\n const weeks: MonthCell[][] = []\n for (let i = 0; i < cells.length; i += DAYS_PER_WEEK) {\n const week = cells\n .slice(i, i + DAYS_PER_WEEK)\n .filter((cell) => isVisibleDay(cell.iso, weekdays))\n if (week.length > 0) weeks.push(week)\n }\n return weeks\n}\n\n/** The first day of each month of the anchor's year, which the year view lays out. */\nexport function monthsOfYear(anchor: string): string[] {\n const date = parseISO(anchor)\n if (!date) return []\n const year = date.getFullYear()\n return Array.from({ length: 12 }, (_, month0) => isoOf(year, month0, 1))\n}\n\n/** The last day an event puts anything on. */\nexport function lastDayOf(event: CalendarEvent): string {\n const endsAtMidnight =\n event.allDay !== true &&\n compareISO(event.start, event.end) < 0 &&\n minutesOf(event.endTime) === 0\n return endsAtMidnight ? addDays(event.end, -1) : event.end\n}\n\n/** Whether an event covers a given day at all, all-day or timed. */\nexport function coversDay(event: CalendarEvent, iso: string): boolean {\n return compareISO(iso, event.start) >= 0 && compareISO(iso, lastDayOf(event)) <= 0\n}\n\n/**\n * Every visible day's events, in the order a summary lists them: the all-day ones first, since\n * they frame the day rather than sit inside it, then the rest by when they start, the last tie\n * broken by id so the list cannot reshuffle for no reason.\n */\nexport function eventsByDay<T extends CalendarEvent>(\n events: readonly T[],\n days: readonly string[],\n): Map<string, T[]> {\n const buckets = new Map<string, Ranked<T>[]>()\n for (const iso of days) buckets.set(iso, [])\n if (days.length === 0) return new Map()\n\n // The grid's own bounds. `days` is in order, so its ends bound every walk below; which keeps\n // a long-running event from being walked outside the month on show.\n const first = days[0]!\n const last = days[days.length - 1]!\n\n for (const event of events) {\n /*\n * The walk below steps from one date to the next through `addDays`, which hands back a\n * string that is not a date unchanged. A datetime slipped into `start` would never be\n * stepped past, and the page would hang, on the server as well as in the browser.\n */\n if (!parseISO(event.start) || !parseISO(event.end)) continue\n const end = lastDayOf(event)\n const from = compareISO(event.start, first) > 0 ? event.start : first\n const to = compareISO(end, last) < 0 ? end : last\n if (compareISO(from, to) > 0) continue\n\n /*\n * Decorate-sort-undecorate, for the classic reason: the sort key is derived once per event\n * rather than once per comparison, which is where the time in this function goes.\n * `minutesAt` re-parses an `HH:mm` string on every ask, and a 42-square month holding 2000\n * events sorts some 17 000 pairs.\n */\n const ranked: Ranked<T> = {\n event,\n allDay: isAllDayEvent(event) ? 0 : 1,\n minutes: minutesAt(event.startTime, 0),\n id: String(event.id),\n }\n\n // An overnight event has been running since midnight on its second day, so it is ranked\n // from there, ahead of that morning's appointments, rather than by the evening before.\n const morning =\n ranked.allDay === 1 && compareISO(event.start, event.end) < 0\n ? { ...ranked, minutes: 0 }\n : ranked\n\n for (let iso = from; compareISO(iso, to) <= 0; iso = addDays(iso, 1)) {\n // A day the weekday filter hides has no bucket, so it is simply skipped: that is what\n // makes a Monday-to-Friday month cost nothing for the weekends it does not show.\n buckets.get(iso)?.push(iso === event.start ? ranked : morning)\n }\n }\n\n const byDay = new Map<string, T[]>()\n for (const [iso, list] of buckets) {\n list.sort(compareRanked)\n byDay.set(\n iso,\n list.map((item) => item.event),\n )\n }\n return byDay\n}\n\n/** An event with its sort key already worked out; see `eventsByDay`. */\ninterface Ranked<T> {\n event: T\n allDay: number\n minutes: number\n id: string\n}\n\n/** The order `eventsByDay` lists a day in, reading keys that are already computed. */\nfunction compareRanked<T>(a: Ranked<T>, b: Ranked<T>): number {\n return a.allDay - b.allDay || a.minutes - b.minutes || compareId(a.id, b.id)\n}\n\n/** One bar in the all-day band: the columns it covers, and the row it sits on. */\nexport interface AllDaySpan {\n id: CalendarEventId\n /** The first visible column it covers. */\n startIndex: number\n /** How many visible columns it covers, at least one. */\n span: number\n /** Whether it reaches beyond the days on show, on either side. */\n continuesBefore: boolean\n continuesAfter: boolean\n /** Which row of the band it sits on. */\n lane: number\n}\n\n/** Stacks the all-day bars into as few rows as they fit in. */\nexport function packAllDay(\n events: readonly CalendarEvent[],\n days: readonly string[],\n): AllDaySpan[] {\n const spans: AllDaySpan[] = []\n\n for (const event of events) {\n if (!isAllDayEvent(event)) continue\n\n let first = -1\n let last = -1\n for (let i = 0; i < days.length; i++) {\n if (coversDay(event, days[i]!)) {\n if (first === -1) first = i\n last = i\n }\n }\n if (first === -1) continue\n\n spans.push({\n id: event.id,\n startIndex: first,\n span: last - first + 1,\n continuesBefore: compareISO(event.start, days[first]!) < 0,\n continuesAfter: compareISO(lastDayOf(event), days[last]!) > 0,\n lane: 0,\n })\n }\n\n spans.sort((a, b) => a.startIndex - b.startIndex || b.span - a.span || compareId(a.id, b.id))\n\n const laneEnds: number[] = []\n for (const span of spans) {\n let lane = laneEnds.findIndex((end) => end < span.startIndex)\n if (lane === -1) {\n lane = laneEnds.length\n laneEnds.push(0)\n }\n laneEnds[lane] = span.startIndex + span.span - 1\n span.lane = lane\n }\n\n return spans\n}\n\n/** Where a moment sits in the visible window, as a unitless fraction the stylesheet scales. */\nexport function fractionOf(minutes: number, timeWindow: TimeWindow): number {\n const span = timeWindow.end - timeWindow.start\n return span > 0 ? clamp((minutes - timeWindow.start) / span, 0, 1) : 0\n}\n\n/** The measurements of the grid a pointer is being read against, in viewport coordinates. */\nexport interface GridGeometry {\n top: number\n height: number\n /** The edge the first column starts at: the left one normally, the right one in RTL. */\n inlineStart: number\n inlineSize: number\n columns: number\n}\n\n/**\n * Turns a point on the screen into a point on the calendar: which day column it fell in, and\n * what moment that height stands for. That is what makes a drag that wanders off the component\n * behave as the user expects instead of stopping dead.\n */\nexport function pointToCell(\n point: { x: number; y: number },\n geometry: GridGeometry,\n timeWindow: TimeWindow,\n rtl: boolean,\n): { columnIndex: number; minutes: number } {\n const columnIndex = columnAt(point.x, geometry, rtl)\n\n const fraction = geometry.height > 0 ? (point.y - geometry.top) / geometry.height : 0\n const minutes = clamp(\n timeWindow.start + fraction * (timeWindow.end - timeWindow.start),\n timeWindow.start,\n timeWindow.end,\n )\n\n return { columnIndex, minutes }\n}\n\n/** Rounds a moment to the nearest step the calendar allows. */\nexport function snapToSlot(minutes: number, step: number): number {\n const size = step > 0 ? step : 1\n return clamp(Math.round(minutes / size) * size, 0, MINUTES_PER_DAY)\n}\n\n/** Rounds a moment DOWN to the step containing it: where a drawn slot begins. */\nexport function floorToSlot(minutes: number, step: number): number {\n const size = step > 0 ? step : 1\n return clamp(Math.floor(minutes / size) * size, 0, MINUTES_PER_DAY)\n}\n\n/**\n * How many whole days an event covers beyond its first: zero for one that starts and ends\n * on the same day, two for a trip from Monday to Wednesday.\n */\nexport function daySpan(times: CalendarEventTimes): number {\n const start = parseISO(times.start)\n const end = parseISO(times.end)\n if (!start || !end) return 0\n return Math.max(0, Math.round((end.getTime() - start.getTime()) / MS_PER_DAY))\n}\n\n/** Moves an event to another day, KEEPING its length in days as well as its times. */\nexport function moveEventToDay(origin: CalendarEventTimes, target: string): CalendarEventTimes {\n if (parseISO(target) === null) return origin\n return {\n start: target,\n end: addDays(target, daySpan(origin)),\n startTime: origin.startTime,\n endTime: origin.endTime,\n }\n}\n\n/**\n * Which column a horizontal position falls in, counted in reading order and held inside the\n * grid. The inline half both grids share: the time grid's block axis is a moment, the month's\n * a row.\n */\nfunction columnAt(x: number, geometry: GridGeometry, rtl: boolean): number {\n const columns = Math.max(1, geometry.columns)\n const columnSize = geometry.inlineSize / columns\n const offset = rtl ? geometry.inlineStart - x : x - geometry.inlineStart\n return clamp(columnSize > 0 ? Math.floor(offset / columnSize) : 0, 0, columns - 1)\n}\n\n/** The measurements of a month grid: the same struct, with rows where a time window was. */\nexport interface MonthGeometry extends GridGeometry {\n rows: number\n}\n\n/** Turns a point on the screen into a square of the month. */\nexport function pointToMonthCell(\n point: { x: number; y: number },\n geometry: MonthGeometry,\n rtl: boolean,\n): { columnIndex: number; rowIndex: number } {\n const rows = Math.max(1, geometry.rows)\n const columnIndex = columnAt(point.x, geometry, rtl)\n\n const rowSize = geometry.height / rows\n const rowIndex = clamp(\n rowSize > 0 ? Math.floor((point.y - geometry.top) / rowSize) : 0,\n 0,\n rows - 1,\n )\n\n return { columnIndex, rowIndex }\n}\n\n/**\n * Which inline edge a point is resting against, as a direction to page in: -1 for the start\n * edge, 1 for the end one, nothing in between.\n */\nexport function inlineEdgeAt(\n x: number,\n geometry: { inlineStart: number; inlineSize: number },\n band: number,\n rtl: boolean,\n): -1 | 1 | null {\n if (band <= 0 || geometry.inlineSize <= 0) return null\n const offset = rtl ? geometry.inlineStart - x : x - geometry.inlineStart\n // Past the far side of the box counts as being against that edge: a pointer dragged clean\n // off the calendar is asking to keep going, not to stop.\n if (offset < band) return -1\n if (offset > geometry.inlineSize - band) return 1\n return null\n}\n\n/**\n * How hard a point near the top or bottom of a box is asking it to scroll: -1 at the very top,\n * 1 at the very bottom, 0 anywhere in the middle.\n */\nexport function blockEdgeAt(\n y: number,\n geometry: { top: number; height: number },\n band: number,\n): number {\n if (band <= 0 || geometry.height <= 0) return 0\n const fromTop = y - geometry.top\n const fromBottom = geometry.top + geometry.height - y\n if (fromTop < band) return -clamp((band - fromTop) / band, 0, 1)\n if (fromBottom < band) return clamp((band - fromBottom) / band, 0, 1)\n return 0\n}\n\n/**\n * A box that measures nothing contains EVERYTHING. That is the same answer `inlineEdgeAt` and\n * `blockEdgeAt` give a box of no size, and for the same reason: with no measurement there is no\n * outside to be on.\n */\nexport function pointWithin(\n point: { x: number; y: number },\n box: { left: number; top: number; width: number; height: number },\n): boolean {\n if (box.width <= 0 || box.height <= 0) return true\n return (\n point.x >= box.left &&\n point.x <= box.left + box.width &&\n point.y >= box.top &&\n point.y <= box.top + box.height\n )\n}\n\n/** The times of an event, as the shape the move and resize events carry. */\nexport function timesOf(event: CalendarEvent): CalendarEventTimes {\n return {\n start: event.start,\n end: event.end,\n startTime: event.startTime,\n endTime: event.endTime,\n }\n}\n\n/**\n * Whether two placements are the same, field for field. A slot is a quarter of an hour, some\n * sixteen pixels at the default hour height, so most pointer moves land on the times already\n * shown, and the gesture state is deeply reactive, so an equal but new object would still\n * re-run the whole layout and re-render every cell of the grid for a picture that did not\n * change.\n */\nexport function sameTimes(a: CalendarEventTimes, b: CalendarEventTimes): boolean {\n return (\n a.start === b.start && a.end === b.end && a.startTime === b.startTime && a.endTime === b.endTime\n )\n}\n\n/** Today, as the calendar reads it. Kept here so the component has one place to call. */\nexport function todayISO(): string {\n return formatISO(new Date())\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"layout.js","names":["MINUTES_PER_DAY","DAYS_PER_WEEK","MS_PER_DAY","compareId","left","a","right","b","windowOf","start","clamp","startHour","endHour","timeOf","held","minutes","pad2","minutesAt","minutesOf","time","fallback","normalizeWeekdays","seen","kept","day","weekdays","value","first","firstDayOfWeek","i","isVisibleDay","date","parseISO","iso","snapToVisibleDay","current","addDays","direction","advanceVisibleDays","count","remaining","guard","columnCount","view","customDays","visibleDays","anchor","year","month0","days","daysInMonth","isoOf","offset","weekStart","total","stepAnchor","addMonths","visibleRange","durationOf","daySpan","times","spansMidnight","compareISO","isAllDayEvent","event","order","timedSegments","index","segments","push","dayIndex","rawEnd","timeWindow","rawStart","end","minDuration","part","events","packDayColumn","sorted","placed","cluster","columnEnds","clusterEnd","flush","columns","byColumn","item","span","other","segment","column","monthWeeks","cells","buildMonthGrid","weeks","week","cell","monthsOfYear","lastDayOf","coversDay","eventsByDay","buckets","last","from","to","ranked","morning","byDay","list","compareRanked","packAllDay","spans","laneEnds","lane","fractionOf","pointToCell","columnIndex","columnAt","point","geometry","rtl","fraction","snapToSlot","size","step","floorToSlot","moveEventToDay","target","origin","columnSize","x","pointToMonthCell","rows","rowSize","inlineEdgeAt","band","blockEdgeAt","fromTop","y","fromBottom","pointWithin","box","timesOf","sameTimes","todayISO","formatISO"],"sources":["../../../src/components/VCalendar/layout.ts"],"sourcesContent":["// @core\n/**\n * Pure calendar layout shared by its views; component ownership keeps agenda-specific contracts\n * out of general utilities.\n */\nimport {\n addDays,\n addMonths,\n buildMonthGrid,\n compareISO,\n daysInMonth,\n formatISO,\n isoOf,\n parseISO,\n} from '../../utils/date'\nimport { clamp } from '../../utils/number'\nimport { pad2 } from '../../utils/text'\nimport { minutesOf } from '../../utils/time'\n\nimport type { MonthCell } from '../../utils/date'\nimport type { CalendarEvent, CalendarEventId, CalendarEventTimes, CalendarView } from './types'\n\n/** An hour, in the minutes every time here is counted in: one row of a time grid. */\nexport const MINUTES_PER_HOUR = 60\n\n/** How many minutes a day holds. The upper bound of every time computed here. */\nexport const MINUTES_PER_DAY = 24 * MINUTES_PER_HOUR\n\nconst DAYS_PER_WEEK = 7\n\n/** A day in milliseconds, for counting the days between two dates. */\nconst MS_PER_DAY = 24 * 60 * 60 * 1000\n\n/** How far the pointer must travel before a press becomes a drag rather than a click. */\nexport const DRAG_THRESHOLD = 3\n\n/**\n * This must not be `localeCompare`. An id is an opaque key, never user-facing text, so there is\n * nothing here to collate; and `localeCompare` with no locale resolves against the RUNTIME's\n * default, which differs between Node and the browser.\n */\nfunction compareId(a: CalendarEventId, b: CalendarEventId): number {\n const left = String(a)\n const right = String(b)\n return left < right ? -1 : left > right ? 1 : 0\n}\n\n/** The slice of the day a calendar is showing, as minutes since midnight. */\nexport interface TimeWindow {\n start: number\n end: number\n}\n\n/** Turns the `dayStart`/`dayEnd` props, given in hours, into the window the rest works in. */\nexport function windowOf(startHour: number, endHour: number): TimeWindow {\n const start = clamp(\n Math.round(startHour * MINUTES_PER_HOUR),\n 0,\n MINUTES_PER_DAY - MINUTES_PER_HOUR,\n )\n const end = clamp(\n Math.round(endHour * MINUTES_PER_HOUR),\n start + MINUTES_PER_HOUR,\n MINUTES_PER_DAY,\n )\n return { start, end }\n}\n\n/**\n * The value is held one minute short of midnight rather than allowed to reach it. A day's last\n * moment is 1440 in this arithmetic, but `24:00` is not a time any of the design system's\n * helpers accept (`isValidTime` rejects it).\n */\nexport function timeOf(minutes: number): string {\n const held = clamp(Math.round(minutes), 0, MINUTES_PER_DAY - 1)\n return `${pad2(Math.floor(held / MINUTES_PER_HOUR))}:${pad2(held % MINUTES_PER_HOUR)}`\n}\n\n/** An `HH:mm` string into minutes since midnight; `fallback` when it is not one. */\nexport function minutesAt(time: string | undefined, fallback: number): number {\n return minutesOf(time) ?? fallback\n}\n\n/** The weekdays a calendar shows, in reading order. */\nexport function normalizeWeekdays(\n weekdays: readonly number[] | undefined,\n firstDayOfWeek: number,\n): number[] {\n const seen = new Set<number>()\n const kept: number[] = []\n for (const day of weekdays ?? []) {\n const value = Math.trunc(day)\n if (value >= 0 && value <= 6 && !seen.has(value)) {\n seen.add(value)\n kept.push(value)\n }\n }\n if (kept.length > 0) return kept\n // Monday when the day given is not a number, the fallback `firstDayOfWeekFor` uses too.\n const first = Number.isFinite(firstDayOfWeek) ? ((Math.trunc(firstDayOfWeek) % 7) + 7) % 7 : 1\n return Array.from({ length: DAYS_PER_WEEK }, (_, i) => (first + i) % 7)\n}\n\n/** Whether a date falls on one of the visible weekdays. */\nexport function isVisibleDay(iso: string, weekdays: readonly number[]): boolean {\n const date = parseISO(iso)\n return date !== null && weekdays.includes(date.getDay())\n}\n\n/** The first visible day at or after `iso` (or at or before it, going backwards). */\nexport function snapToVisibleDay(\n iso: string,\n weekdays: readonly number[],\n direction: -1 | 1 = 1,\n): string {\n if (weekdays.length === 0) return iso\n let current = iso\n for (let i = 0; i < DAYS_PER_WEEK; i++) {\n if (isVisibleDay(current, weekdays)) return current\n current = addDays(current, direction)\n }\n return iso\n}\n\n/** Moves `count` VISIBLE days from `iso`, skipping over the hidden ones. */\nexport function advanceVisibleDays(\n iso: string,\n count: number,\n weekdays: readonly number[],\n): string {\n if (count === 0 || weekdays.length === 0) return iso\n const direction = count > 0 ? 1 : -1\n let current = iso\n let remaining = Math.abs(count)\n // Bounded so a malformed weekday list can never spin: at worst one visible day costs\n // seven calendar days, so this is the same walk with its worst case written down.\n let guard = Math.abs(count) * DAYS_PER_WEEK + DAYS_PER_WEEK\n while (remaining > 0 && guard-- > 0) {\n current = addDays(current, direction)\n if (isVisibleDay(current, weekdays)) remaining--\n }\n return current\n}\n\n/** How many day columns a view shows, before the visible-weekday filter applies. */\nexport function columnCount(view: CalendarView, customDays: number): number {\n if (view === 'day') return 1\n if (view === '4days') return 4\n if (view === 'custom') return clamp(Math.trunc(customDays), 1, 31)\n return DAYS_PER_WEEK\n}\n\n/** The days a view shows, in reading order. */\nexport function visibleDays(\n anchor: string,\n view: CalendarView,\n weekdays: readonly number[],\n customDays: number,\n): string[] {\n if (weekdays.length === 0 || parseISO(anchor) === null) return []\n if (view === 'year') return []\n\n if (view === 'month') {\n const date = parseISO(anchor)!\n const year = date.getFullYear()\n const month0 = date.getMonth()\n const days: string[] = []\n for (let day = 1; day <= daysInMonth(year, month0); day++) {\n const iso = isoOf(year, month0, day)\n if (isVisibleDay(iso, weekdays)) days.push(iso)\n }\n return days\n }\n\n if (view === 'week') {\n const date = parseISO(anchor)!\n const offset = (date.getDay() - weekdays[0]! + DAYS_PER_WEEK) % DAYS_PER_WEEK\n const weekStart = addDays(anchor, -offset)\n const days: string[] = []\n for (let i = 0; i < DAYS_PER_WEEK; i++) {\n const iso = addDays(weekStart, i)\n if (isVisibleDay(iso, weekdays)) days.push(iso)\n }\n return days\n }\n\n const total = columnCount(view, customDays)\n const days: string[] = [snapToVisibleDay(anchor, weekdays)]\n for (let i = 1; i < total; i++) days.push(advanceVisibleDays(days[i - 1]!, 1, weekdays))\n return days\n}\n\n/**\n * The day-shaped views step over VISIBLE days, not calendar ones. Stepping by one calendar day\n * from a Friday in a Monday-to-Friday calendar would land on a Saturday the grid does not draw;\n * the view would then show the same week it already showed, and the button would look broken\n * with nothing in the console to say why.\n */\nexport function stepAnchor(\n anchor: string,\n view: CalendarView,\n direction: -1 | 1,\n weekdays: readonly number[],\n customDays: number,\n): string {\n if (parseISO(anchor) === null) return anchor\n if (view === 'month') return addMonths(anchor, direction)\n if (view === 'year') return addMonths(anchor, direction * 12)\n if (view === 'week') return snapToVisibleDay(addDays(anchor, direction * DAYS_PER_WEEK), weekdays)\n return advanceVisibleDays(anchor, direction * columnCount(view, customDays), weekdays)\n}\n\n/** The first and last day a view covers, which names it in the toolbar. */\nexport function visibleRange(\n anchor: string,\n view: CalendarView,\n weekdays: readonly number[],\n customDays: number,\n): { start: string; end: string } {\n if (view === 'year') {\n // No clock read here: the server and the browser must name the same range.\n const date = parseISO(anchor)\n if (!date) return { start: anchor, end: anchor }\n return { start: isoOf(date.getFullYear(), 0, 1), end: isoOf(date.getFullYear(), 11, 31) }\n }\n const days = visibleDays(anchor, view, weekdays, customDays)\n if (days.length === 0) return { start: anchor, end: anchor }\n return { start: days[0]!, end: days[days.length - 1]! }\n}\n\n/** How long an event lasts, in minutes, counting the days between its dates. */\nexport function durationOf(times: CalendarEventTimes): number {\n return (\n daySpan(times) * MINUTES_PER_DAY +\n minutesAt(times.endTime, MINUTES_PER_DAY) -\n minutesAt(times.startTime, 0)\n )\n}\n\n/** Whether an event runs from one day into the next without lasting a whole day. */\nexport function spansMidnight(times: CalendarEventTimes): boolean {\n return compareISO(times.start, times.end) < 0 && durationOf(times) < MINUTES_PER_DAY\n}\n\n/** Whether an event belongs in the band above the grid rather than in a day's column. */\nexport function isAllDayEvent(event: CalendarEvent): boolean {\n if (event.allDay === true) return true\n const order = compareISO(event.start, event.end)\n if (order === 0) return false\n return order > 0 || !spansMidnight(event)\n}\n\n/**\n * Which piece of its event a box is: the whole of it, or one side of midnight for an event\n * that runs from one day into the next.\n */\nexport type SegmentPart = 'whole' | 'head' | 'tail'\n\n/** One event's box in one day column, in minutes since midnight. */\nexport interface EventSegment {\n id: CalendarEventId\n /** Which of the visible columns it belongs to. */\n dayIndex: number\n start: number\n end: number\n /**\n * Whether the card carries on past its top or bottom edge: the window cut it off, or\n * midnight did. Both are drawn the same way.\n */\n clippedStart: boolean\n clippedEnd: boolean\n /**\n * `head` is the evening of an event running past midnight and `tail` its morning. Only a\n * `whole` or a `tail` holds the event's real end, which its resize strip drags.\n */\n part: SegmentPart\n}\n\n/**\n * Cuts the timed events into the boxes the day columns draw. Were the minimum applied during\n * packing instead, two events fifteen minutes apart would be laid out as though they did not\n * overlap and then drawn as though they did.\n */\nexport function timedSegments(\n events: readonly CalendarEvent[],\n days: readonly string[],\n timeWindow: TimeWindow,\n minDuration: number,\n): EventSegment[] {\n const index = new Map(days.map((iso, i) => [iso, i]))\n const segments: EventSegment[] = []\n\n const push = (\n id: CalendarEventId,\n dayIndex: number | undefined,\n rawStart: number,\n rawEnd: number,\n part: SegmentPart,\n ) => {\n if (dayIndex === undefined) return\n if (rawEnd <= timeWindow.start || rawStart >= timeWindow.end) return\n const start = clamp(rawStart, timeWindow.start, timeWindow.end - 1)\n const end = clamp(Math.max(rawEnd, start + minDuration), start + 1, timeWindow.end)\n segments.push({\n id,\n dayIndex,\n start,\n end,\n clippedStart: part === 'tail' || rawStart < timeWindow.start,\n clippedEnd: part === 'head' || rawEnd > timeWindow.end,\n part,\n })\n }\n\n for (const event of events) {\n if (isAllDayEvent(event)) continue\n const rawStart = minutesAt(event.startTime, timeWindow.start)\n\n if (compareISO(event.start, event.end) === 0) {\n const rawEnd = Math.max(minutesAt(event.endTime, rawStart + minDuration), rawStart + 1)\n push(event.id, index.get(event.start), rawStart, rawEnd, 'whole')\n continue\n }\n\n /*\n * An event ending at midnight exactly has nothing on its second day, so it is drawn whole;\n * a tail of no length would otherwise be stretched to a slot and show as a stub at 00:00.\n */\n const rawEnd = minutesAt(event.endTime, 0)\n if (rawEnd === 0) {\n push(event.id, index.get(event.start), rawStart, MINUTES_PER_DAY, 'whole')\n continue\n }\n push(event.id, index.get(event.start), rawStart, MINUTES_PER_DAY, 'head')\n push(event.id, index.get(event.end), 0, rawEnd, 'tail')\n }\n\n return segments\n}\n\n/** A segment once it knows how much of its column's width it gets. */\nexport interface PlacedSegment extends EventSegment {\n /** Which column of its cluster it sits in, counted from zero. */\n column: number\n /** How many columns it takes up: more than one when the room beside it is free. */\n span: number\n /** How many columns its cluster has. It is what both of the above are read against. */\n columns: number\n}\n\n/**\n * Shares one day's width out between the events happening at the same time. Overlap is strictly\n * `a.start < b.end && b.start < a.end`, so events that merely touch (one ending exactly as the\n * next begins) share a column instead of splitting the width.\n */\nexport function packDayColumn(segments: readonly EventSegment[]): PlacedSegment[] {\n const sorted = [...segments].sort(\n (a, b) => a.start - b.start || b.end - b.start - (a.end - a.start) || compareId(a.id, b.id),\n )\n\n const placed: PlacedSegment[] = []\n let cluster: PlacedSegment[] = []\n let columnEnds: number[] = []\n let clusterEnd = -Infinity\n\n const flush = () => {\n const columns = columnEnds.length\n /*\n * The cluster filed by column first, so asking \"is the room beside me free?\" reads that one\n * column's occupants rather than the whole cluster: a crowded morning of k events across c\n * columns otherwise costs k squared per column crossed.\n */\n const byColumn: PlacedSegment[][] = Array.from({ length: columns }, () => [])\n for (const item of cluster) byColumn[item.column]!.push(item)\n for (const item of cluster) {\n let span = 1\n while (\n item.column + span < columns &&\n !byColumn[item.column + span]!.some(\n (other) => other.start < item.end && item.start < other.end,\n )\n ) {\n span++\n }\n item.span = span\n item.columns = columns\n }\n placed.push(...cluster)\n cluster = []\n columnEnds = []\n clusterEnd = -Infinity\n }\n\n for (const segment of sorted) {\n if (segment.start >= clusterEnd && cluster.length > 0) flush()\n\n let column = columnEnds.findIndex((end) => end <= segment.start)\n if (column === -1) {\n column = columnEnds.length\n columnEnds.push(segment.end)\n } else {\n columnEnds[column] = segment.end\n }\n\n cluster.push({ ...segment, column, span: 1, columns: 1 })\n clusterEnd = Math.max(clusterEnd, segment.end)\n }\n if (cluster.length > 0) flush()\n\n return placed\n}\n\n/** One square of the month view: a day, and whether it belongs to the month on show. */\nexport type { MonthCell }\n\n/** The month view, cut into weeks. */\nexport function monthWeeks(anchor: string, weekdays: readonly number[]): MonthCell[][] {\n const date = parseISO(anchor)\n if (!date || weekdays.length === 0) return []\n\n const cells = buildMonthGrid(date.getFullYear(), date.getMonth(), weekdays[0]!)\n const weeks: MonthCell[][] = []\n for (let i = 0; i < cells.length; i += DAYS_PER_WEEK) {\n const week = cells\n .slice(i, i + DAYS_PER_WEEK)\n .filter((cell) => isVisibleDay(cell.iso, weekdays))\n if (week.length > 0) weeks.push(week)\n }\n return weeks\n}\n\n/** The first day of each month of the anchor's year, which the year view lays out. */\nexport function monthsOfYear(anchor: string): string[] {\n const date = parseISO(anchor)\n if (!date) return []\n const year = date.getFullYear()\n return Array.from({ length: 12 }, (_, month0) => isoOf(year, month0, 1))\n}\n\n/** The last day an event puts anything on. */\nexport function lastDayOf(event: CalendarEvent): string {\n const endsAtMidnight =\n event.allDay !== true &&\n compareISO(event.start, event.end) < 0 &&\n minutesOf(event.endTime) === 0\n return endsAtMidnight ? addDays(event.end, -1) : event.end\n}\n\n/** Whether an event covers a given day at all, all-day or timed. */\nexport function coversDay(event: CalendarEvent, iso: string): boolean {\n return compareISO(iso, event.start) >= 0 && compareISO(iso, lastDayOf(event)) <= 0\n}\n\n/**\n * Every visible day's events, in the order a summary lists them: the all-day ones first, since\n * they frame the day rather than sit inside it, then the rest by when they start, the last tie\n * broken by id so the list cannot reshuffle for no reason.\n */\nexport function eventsByDay<T extends CalendarEvent>(\n events: readonly T[],\n days: readonly string[],\n): Map<string, T[]> {\n const buckets = new Map<string, Ranked<T>[]>()\n for (const iso of days) buckets.set(iso, [])\n if (days.length === 0) return new Map()\n\n // The grid's own bounds. `days` is in order, so its ends bound every walk below; which keeps\n // a long-running event from being walked outside the month on show.\n const first = days[0]!\n const last = days[days.length - 1]!\n\n for (const event of events) {\n /*\n * The walk below steps from one date to the next through `addDays`, which hands back a\n * string that is not a date unchanged. A datetime slipped into `start` would never be\n * stepped past, and the page would hang, on the server as well as in the browser.\n */\n if (!parseISO(event.start) || !parseISO(event.end)) continue\n const end = lastDayOf(event)\n const from = compareISO(event.start, first) > 0 ? event.start : first\n const to = compareISO(end, last) < 0 ? end : last\n if (compareISO(from, to) > 0) continue\n\n /*\n * Decorate-sort-undecorate, for the classic reason: the sort key is derived once per event\n * rather than once per comparison, which is where the time in this function goes.\n * `minutesAt` re-parses an `HH:mm` string on every ask, and a 42-square month holding 2000\n * events sorts some 17 000 pairs.\n */\n const ranked: Ranked<T> = {\n event,\n allDay: isAllDayEvent(event) ? 0 : 1,\n minutes: minutesAt(event.startTime, 0),\n id: String(event.id),\n }\n\n // An overnight event has been running since midnight on its second day, so it is ranked\n // from there, ahead of that morning's appointments, rather than by the evening before.\n const morning =\n ranked.allDay === 1 && compareISO(event.start, event.end) < 0\n ? { ...ranked, minutes: 0 }\n : ranked\n\n for (let iso = from; compareISO(iso, to) <= 0; iso = addDays(iso, 1)) {\n // A day the weekday filter hides has no bucket, so it is simply skipped: that is what\n // makes a Monday-to-Friday month cost nothing for the weekends it does not show.\n buckets.get(iso)?.push(iso === event.start ? ranked : morning)\n }\n }\n\n const byDay = new Map<string, T[]>()\n for (const [iso, list] of buckets) {\n list.sort(compareRanked)\n byDay.set(\n iso,\n list.map((item) => item.event),\n )\n }\n return byDay\n}\n\n/** An event with its sort key already worked out; see `eventsByDay`. */\ninterface Ranked<T> {\n event: T\n allDay: number\n minutes: number\n id: string\n}\n\n/** The order `eventsByDay` lists a day in, reading keys that are already computed. */\nfunction compareRanked<T>(a: Ranked<T>, b: Ranked<T>): number {\n return a.allDay - b.allDay || a.minutes - b.minutes || compareId(a.id, b.id)\n}\n\n/** One bar in the all-day band: the columns it covers, and the row it sits on. */\nexport interface AllDaySpan {\n id: CalendarEventId\n /** The first visible column it covers. */\n startIndex: number\n /** How many visible columns it covers, at least one. */\n span: number\n /** Whether it reaches beyond the days on show, on either side. */\n continuesBefore: boolean\n continuesAfter: boolean\n /** Which row of the band it sits on. */\n lane: number\n}\n\n/** Stacks the all-day bars into as few rows as they fit in. */\nexport function packAllDay(\n events: readonly CalendarEvent[],\n days: readonly string[],\n): AllDaySpan[] {\n const spans: AllDaySpan[] = []\n\n for (const event of events) {\n if (!isAllDayEvent(event)) continue\n\n let first = -1\n let last = -1\n for (let i = 0; i < days.length; i++) {\n if (coversDay(event, days[i]!)) {\n if (first === -1) first = i\n last = i\n }\n }\n if (first === -1) continue\n\n spans.push({\n id: event.id,\n startIndex: first,\n span: last - first + 1,\n continuesBefore: compareISO(event.start, days[first]!) < 0,\n continuesAfter: compareISO(lastDayOf(event), days[last]!) > 0,\n lane: 0,\n })\n }\n\n spans.sort((a, b) => a.startIndex - b.startIndex || b.span - a.span || compareId(a.id, b.id))\n\n const laneEnds: number[] = []\n for (const span of spans) {\n let lane = laneEnds.findIndex((end) => end < span.startIndex)\n if (lane === -1) {\n lane = laneEnds.length\n laneEnds.push(0)\n }\n laneEnds[lane] = span.startIndex + span.span - 1\n span.lane = lane\n }\n\n return spans\n}\n\n/** Where a moment sits in the visible window, as a unitless fraction the stylesheet scales. */\nexport function fractionOf(minutes: number, timeWindow: TimeWindow): number {\n const span = timeWindow.end - timeWindow.start\n return span > 0 ? clamp((minutes - timeWindow.start) / span, 0, 1) : 0\n}\n\n/** The measurements of the grid a pointer is being read against, in viewport coordinates. */\nexport interface GridGeometry {\n top: number\n height: number\n /** The edge the first column starts at: the left one normally, the right one in RTL. */\n inlineStart: number\n inlineSize: number\n columns: number\n}\n\n/**\n * Turns a point on the screen into a point on the calendar: which day column it fell in, and\n * what moment that height stands for. That is what makes a drag that wanders off the component\n * behave as the user expects instead of stopping dead.\n */\nexport function pointToCell(\n point: { x: number; y: number },\n geometry: GridGeometry,\n timeWindow: TimeWindow,\n rtl: boolean,\n): { columnIndex: number; minutes: number } {\n const columnIndex = columnAt(point.x, geometry, rtl)\n\n const fraction = geometry.height > 0 ? (point.y - geometry.top) / geometry.height : 0\n const minutes = clamp(\n timeWindow.start + fraction * (timeWindow.end - timeWindow.start),\n timeWindow.start,\n timeWindow.end,\n )\n\n return { columnIndex, minutes }\n}\n\n/** Rounds a moment to the nearest step the calendar allows. */\nexport function snapToSlot(minutes: number, step: number): number {\n const size = step > 0 ? step : 1\n return clamp(Math.round(minutes / size) * size, 0, MINUTES_PER_DAY)\n}\n\n/** Rounds a moment DOWN to the step containing it: where a drawn slot begins. */\nexport function floorToSlot(minutes: number, step: number): number {\n const size = step > 0 ? step : 1\n return clamp(Math.floor(minutes / size) * size, 0, MINUTES_PER_DAY)\n}\n\n/**\n * How many whole days an event covers beyond its first: zero for one that starts and ends\n * on the same day, two for a trip from Monday to Wednesday.\n */\nexport function daySpan(times: CalendarEventTimes): number {\n const start = parseISO(times.start)\n const end = parseISO(times.end)\n if (!start || !end) return 0\n return Math.max(0, Math.round((end.getTime() - start.getTime()) / MS_PER_DAY))\n}\n\n/** Moves an event to another day, KEEPING its length in days as well as its times. */\nexport function moveEventToDay(origin: CalendarEventTimes, target: string): CalendarEventTimes {\n if (parseISO(target) === null) return origin\n return {\n start: target,\n end: addDays(target, daySpan(origin)),\n startTime: origin.startTime,\n endTime: origin.endTime,\n }\n}\n\n/**\n * Which column a horizontal position falls in, counted in reading order and held inside the\n * grid. The inline half both grids share: the time grid's block axis is a moment, the month's\n * a row.\n */\nfunction columnAt(x: number, geometry: GridGeometry, rtl: boolean): number {\n const columns = Math.max(1, geometry.columns)\n const columnSize = geometry.inlineSize / columns\n const offset = rtl ? geometry.inlineStart - x : x - geometry.inlineStart\n return clamp(columnSize > 0 ? Math.floor(offset / columnSize) : 0, 0, columns - 1)\n}\n\n/** The measurements of a month grid: the same struct, with rows where a time window was. */\nexport interface MonthGeometry extends GridGeometry {\n rows: number\n}\n\n/** Turns a point on the screen into a square of the month. */\nexport function pointToMonthCell(\n point: { x: number; y: number },\n geometry: MonthGeometry,\n rtl: boolean,\n): { columnIndex: number; rowIndex: number } {\n const rows = Math.max(1, geometry.rows)\n const columnIndex = columnAt(point.x, geometry, rtl)\n\n const rowSize = geometry.height / rows\n const rowIndex = clamp(\n rowSize > 0 ? Math.floor((point.y - geometry.top) / rowSize) : 0,\n 0,\n rows - 1,\n )\n\n return { columnIndex, rowIndex }\n}\n\n/**\n * Which inline edge a point is resting against, as a direction to page in: -1 for the start\n * edge, 1 for the end one, nothing in between.\n */\nexport function inlineEdgeAt(\n x: number,\n geometry: { inlineStart: number; inlineSize: number },\n band: number,\n rtl: boolean,\n): -1 | 1 | null {\n if (band <= 0 || geometry.inlineSize <= 0) return null\n const offset = rtl ? geometry.inlineStart - x : x - geometry.inlineStart\n // Past the far side of the box counts as being against that edge: a pointer dragged clean\n // off the calendar is asking to keep going, not to stop.\n if (offset < band) return -1\n if (offset > geometry.inlineSize - band) return 1\n return null\n}\n\n/**\n * How hard a point near the top or bottom of a box is asking it to scroll: -1 at the very top,\n * 1 at the very bottom, 0 anywhere in the middle.\n */\nexport function blockEdgeAt(\n y: number,\n geometry: { top: number; height: number },\n band: number,\n): number {\n if (band <= 0 || geometry.height <= 0) return 0\n const fromTop = y - geometry.top\n const fromBottom = geometry.top + geometry.height - y\n if (fromTop < band) return -clamp((band - fromTop) / band, 0, 1)\n if (fromBottom < band) return clamp((band - fromBottom) / band, 0, 1)\n return 0\n}\n\n/**\n * A box that measures nothing contains EVERYTHING. That is the same answer `inlineEdgeAt` and\n * `blockEdgeAt` give a box of no size, and for the same reason: with no measurement there is no\n * outside to be on.\n */\nexport function pointWithin(\n point: { x: number; y: number },\n box: { left: number; top: number; width: number; height: number },\n): boolean {\n if (box.width <= 0 || box.height <= 0) return true\n return (\n point.x >= box.left &&\n point.x <= box.left + box.width &&\n point.y >= box.top &&\n point.y <= box.top + box.height\n )\n}\n\n/** The times of an event, as the shape the move and resize events carry. */\nexport function timesOf(event: CalendarEvent): CalendarEventTimes {\n return {\n start: event.start,\n end: event.end,\n startTime: event.startTime,\n endTime: event.endTime,\n }\n}\n\n/**\n * Whether two placements are the same, field for field. A slot is a quarter of an hour, some\n * sixteen pixels at the default hour height, so most pointer moves land on the times already\n * shown, and the gesture state is deeply reactive, so an equal but new object would still\n * re-run the whole layout and re-render every cell of the grid for a picture that did not\n * change.\n */\nexport function sameTimes(a: CalendarEventTimes, b: CalendarEventTimes): boolean {\n return (\n a.start === b.start && a.end === b.end && a.startTime === b.startTime && a.endTime === b.endTime\n )\n}\n\n/** Today, as the calendar reads it. Kept here so the component has one place to call. */\nexport function todayISO(): string {\n return formatISO(new Date())\n}\n"],"mappings":"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"}
|
|
@@ -40,20 +40,20 @@ function d(r, a, c, u, d) {
|
|
|
40
40
|
}
|
|
41
41
|
function f(i, a, c, l, d, f) {
|
|
42
42
|
if (!f) return u(i, c, l, d);
|
|
43
|
-
let p = o(i.startTime, d.start), m =
|
|
43
|
+
let p = o(i.startTime, d.start), m = Math.min(r({
|
|
44
44
|
...i,
|
|
45
45
|
end: a
|
|
46
|
-
}), 1) * n + c, p + Math.max(l, 1), p + n - 1);
|
|
47
|
-
return
|
|
46
|
+
}), 1), h = e(m * n + c, p + Math.max(l, 1), p + n - 1);
|
|
47
|
+
return h <= 1440 ? {
|
|
48
48
|
start: i.start,
|
|
49
49
|
end: i.start,
|
|
50
50
|
startTime: s(p),
|
|
51
|
-
endTime: s(Math.max(Math.min(
|
|
51
|
+
endTime: s(Math.max(Math.min(h, d.end), p + Math.max(l, 1)))
|
|
52
52
|
} : {
|
|
53
53
|
start: i.start,
|
|
54
54
|
end: t(i.start, 1),
|
|
55
55
|
startTime: s(p),
|
|
56
|
-
endTime: s(
|
|
56
|
+
endTime: s(h - n)
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
function p(t, n, r, i, o) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeGrid.js","names":[],"sources":["../../../src/components/VCalendar/timeGrid.ts"],"sourcesContent":["// @core\n/**\n * Keep minute-based placement separate from common layout so month and year views do not import\n * unused time-grid arithmetic.\n */\nimport { addDays } from '../../utils/date'\nimport { clamp } from '../../utils/number'\n\nimport {\n MINUTES_PER_DAY,\n daySpan,\n durationOf,\n floorToSlot,\n minutesAt,\n timeOf,\n type TimeWindow,\n} from './layout'\nimport type { CalendarEventTimes } from './types'\n\n/**\n * Rounds a moment to the nearest step WITHOUT holding it inside the day, for a start that a\n * move may push past either midnight.\n */\nexport function roundToSlot(minutes: number, step: number): number {\n const size = step > 0 ? step : 1\n return Math.round(minutes / size) * size\n}\n\n/**\n * Where an event lands when it is dragged or nudged along its day: it keeps its length and\n * moves by `minuteDelta`.\n */\nexport function moveEvent(\n origin: CalendarEventTimes,\n minuteDelta: number,\n timeWindow: TimeWindow,\n): CalendarEventTimes {\n const startMinutes = minutesAt(origin.startTime, timeWindow.start)\n const endMinutes = Math.max(minutesAt(origin.endTime, startMinutes), startMinutes + 1)\n const duration = endMinutes - startMinutes\n\n const nextStart = clamp(\n startMinutes + minuteDelta,\n timeWindow.start,\n Math.max(timeWindow.start, timeWindow.end - duration),\n )\n\n return {\n start: origin.start,\n end: origin.start,\n startTime: timeOf(nextStart),\n endTime: timeOf(nextStart + duration),\n }\n}\n\n/**\n * Where the end of an event lands when its bottom edge is dragged. It can never cross the\n * start: the shortest an event may become is one step.\n */\nexport function resizeEvent(\n origin: CalendarEventTimes,\n endMinutes: number,\n minDuration: number,\n timeWindow: TimeWindow,\n): CalendarEventTimes {\n const startMinutes = minutesAt(origin.startTime, timeWindow.start)\n const floor = Math.max(minDuration, 1)\n const end = clamp(endMinutes, startMinutes + floor, timeWindow.end)\n\n return {\n start: origin.start,\n end: origin.start,\n startTime: timeOf(startMinutes),\n endTime: timeOf(end),\n }\n}\n\n/**\n * Where a timed event lands when it is dragged or nudged: it starts at `startMinutes` counted\n * from the midnight that begins `day`, and keeps its length.\n */\nexport function moveTimedEvent(\n times: CalendarEventTimes,\n day: string,\n startMinutes: number,\n timeWindow: TimeWindow,\n crossMidnight: boolean,\n): CalendarEventTimes {\n if (!crossMidnight) {\n const from = minutesAt(times.startTime, timeWindow.start)\n return { ...moveEvent(times, startMinutes - from, timeWindow), start: day, end: day }\n }\n\n const duration = clamp(durationOf(times), 1, MINUTES_PER_DAY - 1)\n const dayShift = Math.floor(startMinutes / MINUTES_PER_DAY)\n const wanted = startMinutes - dayShift * MINUTES_PER_DAY\n /*\n * Left where it was asked to go, it cannot vanish either, since moving it later lengthens its\n * morning and moving it earlier brings its evening into view.\n */\n const from = minutesAt(times.startTime, timeWindow.start)\n const start =\n from >= timeWindow.start && from < timeWindow.end\n ? clamp(wanted, timeWindow.start, timeWindow.end - 1)\n : wanted\n const startDay = addDays(day, dayShift)\n const end = start + duration\n return {\n start: startDay,\n end: end >= MINUTES_PER_DAY ? addDays(startDay, 1) : startDay,\n startTime: timeOf(start),\n endTime: timeOf(end % MINUTES_PER_DAY),\n }\n}\n\n/**\n * Where a timed event's end lands when it is dragged or nudged: `endMinutes` counted from the\n * midnight that begins `endDay`. With `crossMidnight` false this is `resizeEvent`, and `endDay`\n * is ignored.\n */\nexport function resizeTimedEvent(\n times: CalendarEventTimes,\n endDay: string,\n endMinutes: number,\n minDuration: number,\n timeWindow: TimeWindow,\n crossMidnight: boolean,\n): CalendarEventTimes {\n if (!crossMidnight) return resizeEvent(times, endMinutes, minDuration, timeWindow)\n\n const startMinutes = minutesAt(times.startTime, timeWindow.start)\n const dayOffset = Math.min(daySpan({ ...times, end: endDay }), 1)\n const end = clamp(\n dayOffset * MINUTES_PER_DAY + endMinutes,\n startMinutes + Math.max(minDuration, 1),\n startMinutes + MINUTES_PER_DAY - 1,\n )\n\n // Back inside the first day, the end obeys the window like any other; the `timeOf` clamp\n // then writes midnight as 23:59 of that day rather than 00:00 of the next.\n if (end <= MINUTES_PER_DAY) {\n return {\n start: times.start,\n end: times.start,\n startTime: timeOf(startMinutes),\n // The window caps the end, never below the shortest length: a start after the window\n // closes would otherwise be given an end before it.\n endTime: timeOf(\n Math.max(Math.min(end, timeWindow.end), startMinutes + Math.max(minDuration, 1)),\n ),\n }\n }\n return {\n start: times.start,\n end: addDays(times.start, 1),\n startTime: timeOf(startMinutes),\n endTime: timeOf(end - MINUTES_PER_DAY),\n }\n}\n\n/**\n * The stretch of a day drawn out by a press at `anchor` and a pointer now at `pointer`, both in\n * minutes since midnight.\n */\nexport function drawnSlot(\n day: string,\n anchor: number,\n pointer: number,\n step: number,\n timeWindow: TimeWindow,\n): CalendarEventTimes {\n const size = step > 0 ? step : 1\n const from = floorToSlot(anchor, size)\n const at = floorToSlot(pointer, size)\n const start = Math.max(timeWindow.start, Math.min(from, at))\n const end = clamp(Math.max(from, at) + size, start + 1, timeWindow.end)\n return { start: day, end: day, startTime: timeOf(start), endTime: timeOf(end) }\n}\n"],"mappings":";;;;AAuBA,
|
|
1
|
+
{"version":3,"file":"timeGrid.js","names":["roundToSlot","size","step","minutes","moveEvent","startMinutes","minutesAt","origin","timeWindow","duration","nextStart","clamp","minuteDelta","timeOf","resizeEvent","end","endMinutes","minDuration","moveTimedEvent","crossMidnight","times","day","durationOf","MINUTES_PER_DAY","dayShift","wanted","from","start","startDay","addDays","resizeTimedEvent","dayOffset","daySpan","endDay","drawnSlot","floorToSlot","anchor","at","pointer"],"sources":["../../../src/components/VCalendar/timeGrid.ts"],"sourcesContent":["// @core\n/**\n * Keep minute-based placement separate from common layout so month and year views do not import\n * unused time-grid arithmetic.\n */\nimport { addDays } from '../../utils/date'\nimport { clamp } from '../../utils/number'\n\nimport {\n MINUTES_PER_DAY,\n daySpan,\n durationOf,\n floorToSlot,\n minutesAt,\n timeOf,\n type TimeWindow,\n} from './layout'\nimport type { CalendarEventTimes } from './types'\n\n/**\n * Rounds a moment to the nearest step WITHOUT holding it inside the day, for a start that a\n * move may push past either midnight.\n */\nexport function roundToSlot(minutes: number, step: number): number {\n const size = step > 0 ? step : 1\n return Math.round(minutes / size) * size\n}\n\n/**\n * Where an event lands when it is dragged or nudged along its day: it keeps its length and\n * moves by `minuteDelta`.\n */\nexport function moveEvent(\n origin: CalendarEventTimes,\n minuteDelta: number,\n timeWindow: TimeWindow,\n): CalendarEventTimes {\n const startMinutes = minutesAt(origin.startTime, timeWindow.start)\n const endMinutes = Math.max(minutesAt(origin.endTime, startMinutes), startMinutes + 1)\n const duration = endMinutes - startMinutes\n\n const nextStart = clamp(\n startMinutes + minuteDelta,\n timeWindow.start,\n Math.max(timeWindow.start, timeWindow.end - duration),\n )\n\n return {\n start: origin.start,\n end: origin.start,\n startTime: timeOf(nextStart),\n endTime: timeOf(nextStart + duration),\n }\n}\n\n/**\n * Where the end of an event lands when its bottom edge is dragged. It can never cross the\n * start: the shortest an event may become is one step.\n */\nexport function resizeEvent(\n origin: CalendarEventTimes,\n endMinutes: number,\n minDuration: number,\n timeWindow: TimeWindow,\n): CalendarEventTimes {\n const startMinutes = minutesAt(origin.startTime, timeWindow.start)\n const floor = Math.max(minDuration, 1)\n const end = clamp(endMinutes, startMinutes + floor, timeWindow.end)\n\n return {\n start: origin.start,\n end: origin.start,\n startTime: timeOf(startMinutes),\n endTime: timeOf(end),\n }\n}\n\n/**\n * Where a timed event lands when it is dragged or nudged: it starts at `startMinutes` counted\n * from the midnight that begins `day`, and keeps its length.\n */\nexport function moveTimedEvent(\n times: CalendarEventTimes,\n day: string,\n startMinutes: number,\n timeWindow: TimeWindow,\n crossMidnight: boolean,\n): CalendarEventTimes {\n if (!crossMidnight) {\n const from = minutesAt(times.startTime, timeWindow.start)\n return { ...moveEvent(times, startMinutes - from, timeWindow), start: day, end: day }\n }\n\n const duration = clamp(durationOf(times), 1, MINUTES_PER_DAY - 1)\n const dayShift = Math.floor(startMinutes / MINUTES_PER_DAY)\n const wanted = startMinutes - dayShift * MINUTES_PER_DAY\n /*\n * Left where it was asked to go, it cannot vanish either, since moving it later lengthens its\n * morning and moving it earlier brings its evening into view.\n */\n const from = minutesAt(times.startTime, timeWindow.start)\n const start =\n from >= timeWindow.start && from < timeWindow.end\n ? clamp(wanted, timeWindow.start, timeWindow.end - 1)\n : wanted\n const startDay = addDays(day, dayShift)\n const end = start + duration\n return {\n start: startDay,\n end: end >= MINUTES_PER_DAY ? addDays(startDay, 1) : startDay,\n startTime: timeOf(start),\n endTime: timeOf(end % MINUTES_PER_DAY),\n }\n}\n\n/**\n * Where a timed event's end lands when it is dragged or nudged: `endMinutes` counted from the\n * midnight that begins `endDay`. With `crossMidnight` false this is `resizeEvent`, and `endDay`\n * is ignored.\n */\nexport function resizeTimedEvent(\n times: CalendarEventTimes,\n endDay: string,\n endMinutes: number,\n minDuration: number,\n timeWindow: TimeWindow,\n crossMidnight: boolean,\n): CalendarEventTimes {\n if (!crossMidnight) return resizeEvent(times, endMinutes, minDuration, timeWindow)\n\n const startMinutes = minutesAt(times.startTime, timeWindow.start)\n const dayOffset = Math.min(daySpan({ ...times, end: endDay }), 1)\n const end = clamp(\n dayOffset * MINUTES_PER_DAY + endMinutes,\n startMinutes + Math.max(minDuration, 1),\n startMinutes + MINUTES_PER_DAY - 1,\n )\n\n // Back inside the first day, the end obeys the window like any other; the `timeOf` clamp\n // then writes midnight as 23:59 of that day rather than 00:00 of the next.\n if (end <= MINUTES_PER_DAY) {\n return {\n start: times.start,\n end: times.start,\n startTime: timeOf(startMinutes),\n // The window caps the end, never below the shortest length: a start after the window\n // closes would otherwise be given an end before it.\n endTime: timeOf(\n Math.max(Math.min(end, timeWindow.end), startMinutes + Math.max(minDuration, 1)),\n ),\n }\n }\n return {\n start: times.start,\n end: addDays(times.start, 1),\n startTime: timeOf(startMinutes),\n endTime: timeOf(end - MINUTES_PER_DAY),\n }\n}\n\n/**\n * The stretch of a day drawn out by a press at `anchor` and a pointer now at `pointer`, both in\n * minutes since midnight.\n */\nexport function drawnSlot(\n day: string,\n anchor: number,\n pointer: number,\n step: number,\n timeWindow: TimeWindow,\n): CalendarEventTimes {\n const size = step > 0 ? step : 1\n const from = floorToSlot(anchor, size)\n const at = floorToSlot(pointer, size)\n const start = Math.max(timeWindow.start, Math.min(from, at))\n const end = clamp(Math.max(from, at) + size, start + 1, timeWindow.end)\n return { start: day, end: day, startTime: timeOf(start), endTime: timeOf(end) }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VCard.js","names":[],"sources":["../../../src/components/VCard/VCard.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * A clickable card is one real link, its title, stretched over the card by a pseudo-element:\n * the reader hears one link named by the title, and the controls inside the card stay separate\n * targets. Wrapping the whole card in an `<a>` would name the link with all its text and forbid\n * the buttons it holds.\n */\n\nimport { computed, watchEffect } from 'vue'\n\nimport VSkeletonLoader from '../VSkeletonLoader/VSkeletonLoader.vue'\nimport { useInertLink } from '../../composables/useInertLink'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { isDev } from '../../utils/env'\n\n/** How the card is set off from the page: nothing, a border, a shadow, or a muted fill. */\nexport type CardVariant = 'flat' | 'outline' | 'elevated' | 'filled'\n/** Whether the media sits above the content or beside it. */\nexport type CardOrientation = 'vertical' | 'horizontal'\n/** How much room the card leaves around and between its parts. */\nexport type CardSize = 'sm' | 'md' | 'lg'\n/** The heading level of the title, when it should be part of the page outline. */\nexport type CardHeadingLevel = 1 | 2 | 3 | 4 | 5 | 6\n\ninterface CardProps {\n /**\n * How the card is set off from the page: nothing at all (`flat`), a border (`outline`, the\n * default), a shadow over a raised surface (`elevated`), or a muted fill with no border\n * (`filled`).\n */\n variant?: CardVariant\n /**\n * Where the `#media` slot sits: above the content (`vertical`, the default), or at its start\n * (`horizontal`). A horizontal card puts the media back above the content when it is too\n * narrow for both side by side.\n */\n orientation?: CardOrientation\n /** The padding of the card and the gaps between its parts. */\n size?: CardSize\n /** The title of the card. With `href`, it is the text of the card's link. */\n title?: string\n /** A line under the title. */\n subtitle?: string\n /**\n * Renders the title as a heading of this level, so that the card takes its place in the\n * page outline. Left out, the title is a paragraph.\n */\n headingLevel?: CardHeadingLevel\n /**\n * Makes the whole card a link to this address. The link is the title, whose clickable area\n * covers the card; buttons and links inside the card remain separate targets. It requires\n * `title`.\n */\n href?: string\n /**\n * Makes a linked card unusable: the link loses its address and is marked disabled, and the\n * title and media are dimmed. Controls in the slots are not affected.\n */\n disabled?: boolean\n /**\n * Replaces the content with placeholders, and the media too when there is one. The footer\n * is hidden until the content arrives.\n */\n loading?: boolean\n /**\n * Announces the loading to screen readers with this text. Left out, a loading card is\n * silent, so that a grid of them does not read out the same word a dozen times.\n */\n loadingText?: string\n /** The element the card is rendered as: `article`, `section` or `li` instead of a `div`. */\n as?: string\n}\n\nconst props = withDefaults(defineProps<CardProps>(), {\n variant: 'outline',\n orientation: 'vertical',\n size: 'md',\n title: undefined,\n subtitle: undefined,\n headingLevel: undefined,\n href: undefined,\n disabled: false,\n loading: false,\n loadingText: undefined,\n as: 'div',\n})\n\nconst slots = defineSlots<{\n /** The body of the card. */\n default?(): unknown\n /** An image, a video or an illustration, drawn edge to edge. */\n media?(): unknown\n /** Replaces the title and subtitle with content of your own. A linked card loses its link. */\n header?(): unknown\n /** Actions at the foot of the card, pushed to the bottom when the card is stretched. */\n footer?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\n/*\n * A linked card follows the wrapper-root pattern: the link is the element that acts, so it\n * receives the forwarded attributes (`target`, `rel`, a router's click handler) and the root\n * keeps only `class` and `style`. A card that is not a link is a plain container and keeps all\n * of them.\n */\nconst { attrs, rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\nconst {\n isLink,\n isInertLink,\n linkHref,\n attrs: linkAttrs,\n} = useInertLink({\n href: () => props.href,\n inert: () => props.disabled,\n attrs: () => forwardedAttrs.value,\n})\n\nconst rootAttrs = computed(() =>\n isLink.value ? { class: rootClass.value, style: rootStyle.value } : attrs,\n)\n\nconst titleTag = computed(() => (props.headingLevel ? `h${props.headingLevel}` : 'p'))\n\n// @devwarn\n// The link is the title: without one, the card would hold an empty link.\nif (isDev) {\n watchEffect(() => {\n if (props.href !== undefined && (!props.title || slots.header))\n console.warn(\n '[VCard] `href` needs the `title` prop, which becomes the text of the link; the `#header` slot replaces it and drops the link.',\n )\n })\n}\n</script>\n\n<template>\n <component\n :is=\"as\"\n v-bind=\"rootAttrs\"\n class=\"v-card\"\n :data-variant=\"variant\"\n :data-orientation=\"orientation\"\n :data-size=\"size\"\n :data-interactive=\"isLink ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n :aria-busy=\"loading || undefined\"\n >\n <div v-if=\"$slots.media\" class=\"v-card-media\">\n <VSkeletonLoader v-if=\"loading\" shape=\"surface\" animation=\"pulse\" />\n <slot v-else name=\"media\" />\n </div>\n <div class=\"v-card-content\">\n <template v-if=\"loading\">\n <VSkeletonLoader v-if=\"title || subtitle || $slots.header\" width=\"60%\" />\n <VSkeletonLoader :lines=\"3\" :label=\"loadingText\" />\n </template>\n <template v-else>\n <div v-if=\"$slots.header || title || subtitle\" class=\"v-card-header\">\n <slot name=\"header\">\n <component :is=\"titleTag\" v-if=\"title\" class=\"v-card-title\">\n <a\n v-if=\"isLink\"\n class=\"v-card-link\"\n :aria-disabled=\"isInertLink ? 'true' : undefined\"\n v-bind=\"linkAttrs\"\n :href=\"linkHref\"\n >{{ title }}</a\n >\n <template v-else>{{ title }}</template>\n </component>\n <p v-if=\"subtitle\" class=\"v-card-subtitle\">{{ subtitle }}</p>\n </slot>\n </div>\n <div v-if=\"$slots.default\" class=\"v-card-body\">\n <slot />\n </div>\n <div v-if=\"$slots.footer\" class=\"v-card-footer\">\n <slot name=\"footer\" />\n </div>\n </template>\n </div>\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n .v-card {\n --card-padding: var(--vectis-space-4);\n --card-gap: var(--vectis-space-3);\n\n position: relative;\n display: flex;\n flex-direction: column;\n /* Clips the media to the rounded corners, whichever side it ends up on. */\n overflow: clip;\n /* Transparent rather than absent: forced colours paint it, and the card keeps an edge. */\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-surface);\n background: var(--vectis-color-surface);\n color: var(--vectis-color-text);\n font-family: var(--vectis-text-family);\n font-size: var(--vectis-text-body-md-size);\n font-weight: var(--vectis-text-body-md-weight);\n line-height: var(--vectis-text-body-md-leading);\n transition:\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n box-shadow var(--vectis-duration-fast) var(--vectis-ease-default),\n background-color var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-card[data-size='sm'] {\n --card-padding: var(--vectis-space-3);\n --card-gap: var(--vectis-space-2);\n }\n\n .v-card[data-size='lg'] {\n --card-padding: var(--vectis-space-6);\n --card-gap: var(--vectis-space-4);\n }\n\n .v-card[data-variant='flat'] {\n background: transparent;\n }\n\n .v-card[data-variant='outline'] {\n border-color: var(--vectis-color-border);\n }\n\n .v-card[data-variant='elevated'] {\n background: var(--vectis-color-surface-raised);\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-card[data-variant='filled'] {\n background: var(--vectis-color-surface-muted);\n }\n\n /*\n * Side by side without a container query: the content's flex basis is its narrowest\n * comfortable width, and its huge grow factor keeps the media at its own basis. When the card\n * cannot fit both bases on one line, the content wraps under the media and both take the\n * full width. A container query would need size containment on the card, which collapses it\n * to nothing in any parent that sizes it from its content.\n */\n .v-card[data-orientation='horizontal'] {\n flex-flow: row wrap;\n }\n\n .v-card[data-orientation='horizontal'] > .v-card-media {\n flex: 1 1 var(--vectis-control-size-card-media);\n }\n\n .v-card[data-orientation='horizontal'] > .v-card-content {\n flex: 999 1 var(--vectis-control-size-card-body-min);\n }\n\n .v-card-media > :is(img, video, picture, svg, iframe) {\n display: block;\n inline-size: 100%;\n block-size: 100%;\n object-fit: cover;\n }\n\n /*\n * The placeholder has no intrinsic height: the percentage fills a media stretched beside the\n * content, and the minimum gives it the skeleton's surface height everywhere else.\n */\n .v-card-media > .v-skeleton-loader {\n block-size: 100%;\n min-block-size: var(--vectis-control-size-skeleton-surface);\n }\n\n .v-card-content {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--card-gap);\n min-inline-size: 0;\n padding: var(--card-padding);\n }\n\n .v-card-header {\n display: flex;\n flex-direction: column;\n }\n\n .v-card-title {\n font-size: var(--vectis-text-heading-4-size);\n font-weight: var(--vectis-text-heading-4-weight);\n line-height: var(--vectis-text-heading-4-leading);\n }\n\n .v-card-subtitle {\n color: var(--vectis-color-text-muted);\n font-size: var(--vectis-text-body-sm-size);\n font-weight: var(--vectis-text-body-sm-weight);\n line-height: var(--vectis-text-body-sm-leading);\n }\n\n /* Pushed to the bottom, so that cards stretched to one height line their actions up. */\n .v-card-footer {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--vectis-space-2);\n margin-block-start: auto;\n }\n\n .v-card-link {\n color: inherit;\n text-decoration: none;\n outline: none;\n }\n\n /* The link's hit area: the whole card, which is its positioned ancestor. */\n .v-card-link::after {\n content: '';\n position: absolute;\n inset: 0;\n }\n\n /*\n * The controls inside a linked card are lifted above the stretched link so that they stay\n * their own targets. Consumer styles are unlayered and still override this positioning.\n */\n .v-card[data-interactive]\n :is(a[href], button, input, select, textarea, summary, label, [tabindex]):not(.v-card-link) {\n position: relative;\n z-index: 1;\n }\n\n /* The link draws no ring of its own; the card does, around the area that is clicked. */\n .v-card:has(.v-card-link:focus-visible) {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n .v-card[data-interactive][data-variant='flat']:not([data-disabled]):hover {\n background: var(--vectis-color-surface-muted);\n }\n\n .v-card[data-interactive][data-variant='outline']:not([data-disabled]):hover {\n border-color: var(--vectis-color-border-strong);\n }\n\n .v-card[data-interactive][data-variant='elevated']:not([data-disabled]):hover {\n box-shadow: var(--vectis-shadow-md);\n }\n\n .v-card[data-interactive][data-variant='filled']:not([data-disabled]):hover {\n background: color-mix(in oklab, var(--vectis-color-surface-muted), var(--vectis-color-text) 4%);\n }\n\n .v-card[data-interactive]:not([data-disabled]):hover .v-card-link {\n text-decoration: underline;\n }\n\n /*\n * Only the link, which carries `aria-disabled`, and the media are dimmed: the rest of the\n * text stays readable, since it is still content rather than a control.\n */\n .v-card[data-disabled] .v-card-link {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n .v-card[data-disabled] .v-card-media {\n opacity: 0.5;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-card {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"VCard.js","names":["_sfc_main","VCard_vue_vue_type_script_setup_true_lang_default","VCard_default"],"sources":["../../../src/components/VCard/VCard.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * A clickable card is one real link, its title, stretched over the card by a pseudo-element:\n * the reader hears one link named by the title, and the controls inside the card stay separate\n * targets. Wrapping the whole card in an `<a>` would name the link with all its text and forbid\n * the buttons it holds.\n */\n\nimport { computed, watchEffect } from 'vue'\n\nimport VSkeletonLoader from '../VSkeletonLoader/VSkeletonLoader.vue'\nimport { useInertLink } from '../../composables/useInertLink'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { isDev } from '../../utils/env'\n\n/** How the card is set off from the page: nothing, a border, a shadow, or a muted fill. */\nexport type CardVariant = 'flat' | 'outline' | 'elevated' | 'filled'\n/** Whether the media sits above the content or beside it. */\nexport type CardOrientation = 'vertical' | 'horizontal'\n/** How much room the card leaves around and between its parts. */\nexport type CardSize = 'sm' | 'md' | 'lg'\n/** The heading level of the title, when it should be part of the page outline. */\nexport type CardHeadingLevel = 1 | 2 | 3 | 4 | 5 | 6\n\ninterface CardProps {\n /**\n * How the card is set off from the page: nothing at all (`flat`), a border (`outline`, the\n * default), a shadow over a raised surface (`elevated`), or a muted fill with no border\n * (`filled`).\n */\n variant?: CardVariant\n /**\n * Where the `#media` slot sits: above the content (`vertical`, the default), or at its start\n * (`horizontal`). A horizontal card puts the media back above the content when it is too\n * narrow for both side by side.\n */\n orientation?: CardOrientation\n /** The padding of the card and the gaps between its parts. */\n size?: CardSize\n /** The title of the card. With `href`, it is the text of the card's link. */\n title?: string\n /** A line under the title. */\n subtitle?: string\n /**\n * Renders the title as a heading of this level, so that the card takes its place in the\n * page outline. Left out, the title is a paragraph.\n */\n headingLevel?: CardHeadingLevel\n /**\n * Makes the whole card a link to this address. The link is the title, whose clickable area\n * covers the card; buttons and links inside the card remain separate targets. It requires\n * `title`.\n */\n href?: string\n /**\n * Makes a linked card unusable: the link loses its address and is marked disabled, and the\n * title and media are dimmed. Controls in the slots are not affected.\n */\n disabled?: boolean\n /**\n * Replaces the content with placeholders, and the media too when there is one. The footer\n * is hidden until the content arrives.\n */\n loading?: boolean\n /**\n * Announces the loading to screen readers with this text. Left out, a loading card is\n * silent, so that a grid of them does not read out the same word a dozen times.\n */\n loadingText?: string\n /** The element the card is rendered as: `article`, `section` or `li` instead of a `div`. */\n as?: string\n}\n\nconst props = withDefaults(defineProps<CardProps>(), {\n variant: 'outline',\n orientation: 'vertical',\n size: 'md',\n title: undefined,\n subtitle: undefined,\n headingLevel: undefined,\n href: undefined,\n disabled: false,\n loading: false,\n loadingText: undefined,\n as: 'div',\n})\n\nconst slots = defineSlots<{\n /** The body of the card. */\n default?(): unknown\n /** An image, a video or an illustration, drawn edge to edge. */\n media?(): unknown\n /** Replaces the title and subtitle with content of your own. A linked card loses its link. */\n header?(): unknown\n /** Actions at the foot of the card, pushed to the bottom when the card is stretched. */\n footer?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\n/*\n * A linked card follows the wrapper-root pattern: the link is the element that acts, so it\n * receives the forwarded attributes (`target`, `rel`, a router's click handler) and the root\n * keeps only `class` and `style`. A card that is not a link is a plain container and keeps all\n * of them.\n */\nconst { attrs, rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\nconst {\n isLink,\n isInertLink,\n linkHref,\n attrs: linkAttrs,\n} = useInertLink({\n href: () => props.href,\n inert: () => props.disabled,\n attrs: () => forwardedAttrs.value,\n})\n\nconst rootAttrs = computed(() =>\n isLink.value ? { class: rootClass.value, style: rootStyle.value } : attrs,\n)\n\nconst titleTag = computed(() => (props.headingLevel ? `h${props.headingLevel}` : 'p'))\n\n// @devwarn\n// The link is the title: without one, the card would hold an empty link.\nif (isDev) {\n watchEffect(() => {\n if (props.href !== undefined && (!props.title || slots.header))\n console.warn(\n '[VCard] `href` needs the `title` prop, which becomes the text of the link; the `#header` slot replaces it and drops the link.',\n )\n })\n}\n</script>\n\n<template>\n <component\n :is=\"as\"\n v-bind=\"rootAttrs\"\n class=\"v-card\"\n :data-variant=\"variant\"\n :data-orientation=\"orientation\"\n :data-size=\"size\"\n :data-interactive=\"isLink ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n :aria-busy=\"loading || undefined\"\n >\n <div v-if=\"$slots.media\" class=\"v-card-media\">\n <VSkeletonLoader v-if=\"loading\" shape=\"surface\" animation=\"pulse\" />\n <slot v-else name=\"media\" />\n </div>\n <div class=\"v-card-content\">\n <template v-if=\"loading\">\n <VSkeletonLoader v-if=\"title || subtitle || $slots.header\" width=\"60%\" />\n <VSkeletonLoader :lines=\"3\" :label=\"loadingText\" />\n </template>\n <template v-else>\n <div v-if=\"$slots.header || title || subtitle\" class=\"v-card-header\">\n <slot name=\"header\">\n <component :is=\"titleTag\" v-if=\"title\" class=\"v-card-title\">\n <a\n v-if=\"isLink\"\n class=\"v-card-link\"\n :aria-disabled=\"isInertLink ? 'true' : undefined\"\n v-bind=\"linkAttrs\"\n :href=\"linkHref\"\n >{{ title }}</a\n >\n <template v-else>{{ title }}</template>\n </component>\n <p v-if=\"subtitle\" class=\"v-card-subtitle\">{{ subtitle }}</p>\n </slot>\n </div>\n <div v-if=\"$slots.default\" class=\"v-card-body\">\n <slot />\n </div>\n <div v-if=\"$slots.footer\" class=\"v-card-footer\">\n <slot name=\"footer\" />\n </div>\n </template>\n </div>\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n .v-card {\n --card-padding: var(--vectis-space-4);\n --card-gap: var(--vectis-space-3);\n\n position: relative;\n display: flex;\n flex-direction: column;\n /* Clips the media to the rounded corners, whichever side it ends up on. */\n overflow: clip;\n /* Transparent rather than absent: forced colours paint it, and the card keeps an edge. */\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-surface);\n background: var(--vectis-color-surface);\n color: var(--vectis-color-text);\n font-family: var(--vectis-text-family);\n font-size: var(--vectis-text-body-md-size);\n font-weight: var(--vectis-text-body-md-weight);\n line-height: var(--vectis-text-body-md-leading);\n transition:\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n box-shadow var(--vectis-duration-fast) var(--vectis-ease-default),\n background-color var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-card[data-size='sm'] {\n --card-padding: var(--vectis-space-3);\n --card-gap: var(--vectis-space-2);\n }\n\n .v-card[data-size='lg'] {\n --card-padding: var(--vectis-space-6);\n --card-gap: var(--vectis-space-4);\n }\n\n .v-card[data-variant='flat'] {\n background: transparent;\n }\n\n .v-card[data-variant='outline'] {\n border-color: var(--vectis-color-border);\n }\n\n .v-card[data-variant='elevated'] {\n background: var(--vectis-color-surface-raised);\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-card[data-variant='filled'] {\n background: var(--vectis-color-surface-muted);\n }\n\n /*\n * Side by side without a container query: the content's flex basis is its narrowest\n * comfortable width, and its huge grow factor keeps the media at its own basis. When the card\n * cannot fit both bases on one line, the content wraps under the media and both take the\n * full width. A container query would need size containment on the card, which collapses it\n * to nothing in any parent that sizes it from its content.\n */\n .v-card[data-orientation='horizontal'] {\n flex-flow: row wrap;\n }\n\n .v-card[data-orientation='horizontal'] > .v-card-media {\n flex: 1 1 var(--vectis-control-size-card-media);\n }\n\n .v-card[data-orientation='horizontal'] > .v-card-content {\n flex: 999 1 var(--vectis-control-size-card-body-min);\n }\n\n .v-card-media > :is(img, video, picture, svg, iframe) {\n display: block;\n inline-size: 100%;\n block-size: 100%;\n object-fit: cover;\n }\n\n /*\n * The placeholder has no intrinsic height: the percentage fills a media stretched beside the\n * content, and the minimum gives it the skeleton's surface height everywhere else.\n */\n .v-card-media > .v-skeleton-loader {\n block-size: 100%;\n min-block-size: var(--vectis-control-size-skeleton-surface);\n }\n\n .v-card-content {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--card-gap);\n min-inline-size: 0;\n padding: var(--card-padding);\n }\n\n .v-card-header {\n display: flex;\n flex-direction: column;\n }\n\n .v-card-title {\n font-size: var(--vectis-text-heading-4-size);\n font-weight: var(--vectis-text-heading-4-weight);\n line-height: var(--vectis-text-heading-4-leading);\n }\n\n .v-card-subtitle {\n color: var(--vectis-color-text-muted);\n font-size: var(--vectis-text-body-sm-size);\n font-weight: var(--vectis-text-body-sm-weight);\n line-height: var(--vectis-text-body-sm-leading);\n }\n\n /* Pushed to the bottom, so that cards stretched to one height line their actions up. */\n .v-card-footer {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--vectis-space-2);\n margin-block-start: auto;\n }\n\n .v-card-link {\n color: inherit;\n text-decoration: none;\n outline: none;\n }\n\n /* The link's hit area: the whole card, which is its positioned ancestor. */\n .v-card-link::after {\n content: '';\n position: absolute;\n inset: 0;\n }\n\n /*\n * The controls inside a linked card are lifted above the stretched link so that they stay\n * their own targets. Consumer styles are unlayered and still override this positioning.\n */\n .v-card[data-interactive]\n :is(a[href], button, input, select, textarea, summary, label, [tabindex]):not(.v-card-link) {\n position: relative;\n z-index: 1;\n }\n\n /* The link draws no ring of its own; the card does, around the area that is clicked. */\n .v-card:has(.v-card-link:focus-visible) {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n .v-card[data-interactive][data-variant='flat']:not([data-disabled]):hover {\n background: var(--vectis-color-surface-muted);\n }\n\n .v-card[data-interactive][data-variant='outline']:not([data-disabled]):hover {\n border-color: var(--vectis-color-border-strong);\n }\n\n .v-card[data-interactive][data-variant='elevated']:not([data-disabled]):hover {\n box-shadow: var(--vectis-shadow-md);\n }\n\n .v-card[data-interactive][data-variant='filled']:not([data-disabled]):hover {\n background: color-mix(in oklab, var(--vectis-color-surface-muted), var(--vectis-color-text) 4%);\n }\n\n .v-card[data-interactive]:not([data-disabled]):hover .v-card-link {\n text-decoration: underline;\n }\n\n /*\n * Only the link, which carries `aria-disabled`, and the media are dimmed: the rest of the\n * text stays readable, since it is still content rather than a control.\n */\n .v-card[data-disabled] .v-card-link {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n .v-card[data-disabled] .v-card-media {\n opacity: 0.5;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-card {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":""}
|