vectis-ui 0.10.0 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/VAccordion/VAccordion.js.map +1 -1
- package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAccordion/VAccordionItem.d.ts +6 -21
- 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/VAvatar/VAvatar.d.ts +2 -12
- package/dist/components/VAvatar/VAvatar.js.map +1 -1
- package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAvatar/VAvatarGroup.d.ts +2 -2
- package/dist/components/VAvatar/VAvatarGroup.js.map +1 -1
- package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js +6 -6
- package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAvatar/context.d.ts +1 -5
- package/dist/components/VAvatar/context.js.map +1 -1
- package/dist/components/VBadge/VBadge.d.ts +13 -27
- package/dist/components/VBadge/VBadge.js.map +1 -1
- package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VBreadcrumb/VBreadcrumb.d.ts +3 -9
- 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 +10 -17
- package/dist/components/VButton/VButton.js.map +1 -1
- package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VButton/VButtonGroup.d.ts +6 -15
- package/dist/components/VButton/VButtonGroup.js.map +1 -1
- package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VButton/context.d.ts +1 -9
- package/dist/components/VButton/context.js.map +1 -1
- package/dist/components/VCalendar/VCalendar.d.ts +9 -24
- 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.d.ts +4 -13
- 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.map +1 -1
- package/dist/components/VCalendar/VCalendarTimeGrid.d.ts +1 -5
- package/dist/components/VCalendar/VCalendarTimeGrid.js.map +1 -1
- package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCalendar/VCalendarYear.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.d.ts +1 -7
- package/dist/components/VCalendar/color.js.map +1 -1
- package/dist/components/VCalendar/edgeStep.d.ts +5 -15
- package/dist/components/VCalendar/edgeStep.js.map +1 -1
- package/dist/components/VCalendar/gesture.d.ts +12 -36
- package/dist/components/VCalendar/gesture.js.map +1 -1
- package/dist/components/VCalendar/keyboard.d.ts +5 -42
- package/dist/components/VCalendar/keyboard.js.map +1 -1
- package/dist/components/VCalendar/layout.d.ts +38 -199
- package/dist/components/VCalendar/layout.js.map +1 -1
- package/dist/components/VCalendar/timeGrid.d.ts +3 -25
- package/dist/components/VCalendar/timeGrid.js.map +1 -1
- package/dist/components/VCalendar/types.d.ts +6 -23
- package/dist/components/VCarousel/VCarousel.d.ts +25 -96
- package/dist/components/VCarousel/VCarousel.js.map +1 -1
- package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js +6 -6
- package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCarousel/VCarouselItem.d.ts +1 -6
- 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/VCheckbox.d.ts +1 -5
- 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 +2 -8
- package/dist/components/VChip/VChip.js.map +1 -1
- package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCombobox/VCombobox.d.ts +21 -59
- package/dist/components/VCombobox/VCombobox.js.map +1 -1
- package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +117 -119
- package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCombobox/VComboboxGroup.js.map +1 -1
- package/dist/components/VCombobox/VComboboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCombobox/VComboboxOption.js.map +1 -1
- package/dist/components/VCombobox/VComboboxOption.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCombobox/VComboboxSeparator.js.map +1 -1
- package/dist/components/VCombobox/VComboboxSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCombobox/infiniteScroll.d.ts +4 -10
- package/dist/components/VCombobox/infiniteScroll.js.map +1 -1
- package/dist/components/VDataTable/VDataTable.d.ts +11 -36
- package/dist/components/VDataTable/VDataTable.js.map +1 -1
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +23 -20
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDateInput/VDateInput.d.ts +12 -21
- package/dist/components/VDateInput/VDateInput.js.map +1 -1
- package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDateInput/mask.d.ts +14 -59
- package/dist/components/VDateInput/mask.js.map +1 -1
- package/dist/components/VDatePicker/VDatePicker.d.ts +6 -12
- package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
- package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDatePicker/keyboard.d.ts +10 -42
- package/dist/components/VDatePicker/keyboard.js.map +1 -1
- package/dist/components/VDatePicker/names.d.ts +8 -22
- package/dist/components/VDatePicker/names.js.map +1 -1
- package/dist/components/VDialog/VDialog.d.ts +5 -10
- package/dist/components/VDialog/VDialog.js.map +1 -1
- package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js +51 -53
- package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDialog/VDialogAlert.d.ts +2 -3
- 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/VFileInput/VFileInput.d.ts +11 -37
- package/dist/components/VFileInput/VFileInput.js.map +1 -1
- package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VFileInput/truncate.d.ts +3 -23
- package/dist/components/VFileInput/truncate.js.map +1 -1
- package/dist/components/VFilePicker/VFilePicker.d.ts +15 -65
- package/dist/components/VFilePicker/VFilePicker.js.map +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 +6 -15
- package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
- package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VHotkeys/platform.d.ts +10 -49
- package/dist/components/VHotkeys/platform.js.map +1 -1
- package/dist/components/VIcon/VIcon.d.ts +7 -19
- package/dist/components/VIcon/VIcon.js.map +1 -1
- package/dist/components/VIcon/VIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VIcon/iconProps.d.ts +5 -19
- package/dist/components/VIcon/iconProps.js.map +1 -1
- package/dist/components/VIcon/resolver.d.ts +15 -43
- package/dist/components/VIcon/resolver.js.map +1 -1
- package/dist/components/VIcon/types.d.ts +7 -34
- package/dist/components/VIconButton/VIconButton.d.ts +2 -3
- package/dist/components/VIconButton/VIconButton.js.map +1 -1
- package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/VInput.d.ts +14 -29
- package/dist/components/VInput/VInput.js.map +1 -1
- package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/VInputGroup.js.map +1 -1
- package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js +34 -50
- package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/context.js.map +1 -1
- package/dist/components/VInputOTP/VInputOTP.d.ts +8 -16
- package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
- package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +55 -63
- package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenu.js.map +1 -1
- package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenuGroup.d.ts +1 -6
- 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.d.ts +0 -5
- package/dist/components/VMenu/VMenuItem.js.map +1 -1
- package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenuPanel.d.ts +1 -6
- package/dist/components/VMenu/VMenuPanel.js.map +1 -1
- package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +2 -2
- 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.d.ts +3 -11
- package/dist/components/VMenu/context.js.map +1 -1
- package/dist/components/VPagination/VPagination.d.ts +9 -20
- package/dist/components/VPagination/VPagination.js.map +1 -1
- package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VPopover/VPopover.d.ts +7 -18
- package/dist/components/VPopover/VPopover.js.map +1 -1
- package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js +23 -27
- package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VProgressCircular/VProgressCircular.d.ts +2 -6
- 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.d.ts +4 -15
- 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.d.ts +1 -5
- 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/VSeparator/VSeparator.d.ts +2 -15
- 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 +1 -1
- package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationGroup.d.ts +1 -5
- 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.d.ts +1 -5
- package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js +80 -75
- 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 +6 -19
- package/dist/components/VSkeletonLoader/VSkeletonLoader.js.map +1 -1
- package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSlider/VSlider.d.ts +5 -13
- package/dist/components/VSlider/VSlider.js.map +1 -1
- 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.d.ts +4 -17
- package/dist/components/VSnackbar/state.js.map +1 -1
- package/dist/components/VSpinner/VSpinner.d.ts +3 -5
- package/dist/components/VSpinner/VSpinner.js.map +1 -1
- package/dist/components/VSpinner/VSpinner.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSwitch/VSwitch.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.d.ts +1 -5
- package/dist/components/VTabs/VTab.js.map +1 -1
- package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/VTabPanel.js.map +1 -1
- package/dist/components/VTabs/VTabPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/VTabs.d.ts +5 -17
- package/dist/components/VTabs/VTabs.js.map +1 -1
- package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/context.js.map +1 -1
- package/dist/components/VTextarea/VTextarea.d.ts +13 -20
- package/dist/components/VTextarea/VTextarea.js.map +1 -1
- package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeInput/VTimeInput.d.ts +8 -21
- package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
- package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeInput/search.d.ts +3 -34
- package/dist/components/VTimeInput/search.js.map +1 -1
- package/dist/components/VTimePicker/VTimePicker.d.ts +9 -31
- package/dist/components/VTimePicker/VTimePicker.js.map +1 -1
- package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimePicker/limits.d.ts +18 -45
- package/dist/components/VTimePicker/limits.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.map +1 -1
- package/dist/components/VToast/state.d.ts +2 -7
- package/dist/components/VToast/state.js.map +1 -1
- package/dist/components/VToggle/VToggle.d.ts +5 -9
- package/dist/components/VToggle/VToggle.js.map +1 -1
- package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToggle/VToggleItem.d.ts +1 -9
- 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.d.ts +3 -16
- package/dist/components/VTooltip/VTooltip.js.map +1 -1
- package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTypography/VTypography.d.ts +4 -8
- package/dist/components/VTypography/VTypography.js.map +1 -1
- package/dist/components/VTypography/VTypography.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/composables/useAriaLabel.js.map +1 -1
- package/dist/composables/useClearable.d.ts +2 -8
- package/dist/composables/useClearable.js.map +1 -1
- package/dist/composables/useControlShape.js.map +1 -1
- package/dist/composables/useDetailsOpen.d.ts +2 -10
- package/dist/composables/useDetailsOpen.js.map +1 -1
- package/dist/composables/useFieldIds.js.map +1 -1
- package/dist/composables/useFieldPanel.d.ts +4 -17
- package/dist/composables/useFieldPanel.js.map +1 -1
- package/dist/composables/useFileDrop.js.map +1 -1
- package/dist/composables/useFileField.d.ts +1 -5
- package/dist/composables/useFileField.js.map +1 -1
- package/dist/composables/useFocusoutDismiss.js.map +1 -1
- package/dist/composables/useIconClickHandlers.d.ts +4 -19
- package/dist/composables/useIconClickHandlers.js.map +1 -1
- package/dist/composables/useInertLink.d.ts +1 -1
- package/dist/composables/useInertLink.js.map +1 -1
- package/dist/composables/useLiveAnnouncer.js.map +1 -1
- package/dist/composables/useMaskedField.d.ts +12 -27
- package/dist/composables/useMaskedField.js.map +1 -1
- package/dist/composables/usePopover.d.ts +3 -11
- package/dist/composables/usePopover.js.map +1 -1
- package/dist/composables/useProgressValue.js.map +1 -1
- package/dist/composables/useRootAttrs.d.ts +0 -9
- package/dist/composables/useRootAttrs.js +11 -4
- package/dist/composables/useRootAttrs.js.map +1 -1
- package/dist/composables/useSlotNodes.d.ts +3 -14
- package/dist/composables/useSlotNodes.js.map +1 -1
- package/dist/composables/useTextLimit.d.ts +4 -16
- package/dist/composables/useTextLimit.js.map +1 -1
- package/dist/composables/useTimer.d.ts +2 -15
- package/dist/composables/useTimer.js.map +1 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/fr.js.map +1 -1
- package/dist/i18n/state.d.ts +9 -38
- package/dist/i18n/state.js.map +1 -1
- package/dist/i18n/types.d.ts +19 -81
- package/dist/icons.d.ts +2 -12
- package/dist/styles.css +1 -1
- package/dist/tokens/css.d.ts +3 -16
- package/dist/tokens/css.js.map +1 -1
- package/dist/tokens/index.d.ts +5 -0
- package/dist/tokens/index.js.map +1 -1
- package/dist/tokens/primitives.js.map +1 -1
- package/dist/tokens/semantic.d.ts +6 -23
- package/dist/tokens/semantic.js +5 -0
- package/dist/tokens/semantic.js.map +1 -1
- package/dist/tokens/themes/dark.js.map +1 -1
- package/dist/tokens/types.d.ts +2 -12
- package/dist/tokens/types.js.map +1 -1
- package/dist/tokens.json +14 -0
- package/dist/types.d.ts +3 -21
- package/dist/utils/array.d.ts +2 -4
- package/dist/utils/array.js.map +1 -1
- package/dist/utils/arrowNav.d.ts +2 -26
- package/dist/utils/arrowNav.js.map +1 -1
- package/dist/utils/chip.d.ts +1 -8
- package/dist/utils/chip.js.map +1 -1
- package/dist/utils/clock.d.ts +16 -39
- package/dist/utils/clock.js.map +1 -1
- package/dist/utils/css.d.ts +5 -16
- package/dist/utils/css.js.map +1 -1
- package/dist/utils/date.d.ts +17 -60
- package/dist/utils/date.js.map +1 -1
- package/dist/utils/direction.d.ts +2 -9
- package/dist/utils/direction.js.map +1 -1
- package/dist/utils/env.d.ts +2 -15
- package/dist/utils/env.js.map +1 -1
- package/dist/utils/file.d.ts +8 -46
- package/dist/utils/file.js.map +1 -1
- package/dist/utils/focus.d.ts +1 -14
- package/dist/utils/focus.js +1 -6
- package/dist/utils/focus.js.map +1 -1
- package/dist/utils/hostWarns.d.ts +3 -8
- package/dist/utils/hostWarns.js.map +1 -1
- package/dist/utils/ids.d.ts +2 -7
- package/dist/utils/ids.js.map +1 -1
- package/dist/utils/matcher.d.ts +1 -8
- package/dist/utils/matcher.js.map +1 -1
- package/dist/utils/memo.d.ts +1 -11
- package/dist/utils/memo.js.map +1 -1
- package/dist/utils/number.d.ts +1 -7
- package/dist/utils/number.js.map +1 -1
- package/dist/utils/text.d.ts +4 -23
- package/dist/utils/text.js.map +1 -1
- package/dist/utils/time.d.ts +4 -12
- package/dist/utils/time.js.map +1 -1
- package/dist/utils/vnode.d.ts +0 -10
- package/dist/utils/vnode.js.map +1 -1
- package/package.json +1 -1
|
@@ -4,14 +4,7 @@ import { CalendarEvent, CalendarEventId, CalendarEventTimes, CalendarView } from
|
|
|
4
4
|
export declare const MINUTES_PER_HOUR = 60;
|
|
5
5
|
/** How many minutes a day holds. The upper bound of every time computed here. */
|
|
6
6
|
export declare const MINUTES_PER_DAY: number;
|
|
7
|
-
/**
|
|
8
|
-
* How far the pointer must travel before a press becomes a drag rather than a click.
|
|
9
|
-
*
|
|
10
|
-
* It is what keeps a click a click, on a card as on an empty cell: a press that never moves
|
|
11
|
-
* this far writes nothing on release and is handled as a plain activation. Without it, the
|
|
12
|
-
* hand's natural tremor during a click would register as a one-pixel drag and every click
|
|
13
|
-
* would silently move its event.
|
|
14
|
-
*/
|
|
7
|
+
/** How far the pointer must travel before a press becomes a drag rather than a click. */
|
|
15
8
|
export declare const DRAG_THRESHOLD = 3;
|
|
16
9
|
/** The slice of the day a calendar is showing, as minutes since midnight. */
|
|
17
10
|
export interface TimeWindow {
|
|
@@ -21,87 +14,42 @@ export interface TimeWindow {
|
|
|
21
14
|
/** Turns the `dayStart`/`dayEnd` props, given in hours, into the window the rest works in. */
|
|
22
15
|
export declare function windowOf(startHour: number, endHour: number): TimeWindow;
|
|
23
16
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* A day's last moment is 1440 in this arithmetic, but `24:00` is not a time any of the
|
|
28
|
-
* design system's helpers accept (`isValidTime` rejects it). Writing
|
|
29
|
-
* `00:00` on the following day would turn an ordinary evening appointment into one that
|
|
30
|
-
* runs past midnight, which every gesture then treats differently. One minute is invisible at
|
|
31
|
-
* every zoom this component offers; an event that changed kind under the pointer is not.
|
|
17
|
+
* The value is held one minute short of midnight rather than allowed to reach it. A day's last
|
|
18
|
+
* moment is 1440 in this arithmetic, but `24:00` is not a time any of the design system's
|
|
19
|
+
* helpers accept (`isValidTime` rejects it).
|
|
32
20
|
*/
|
|
33
21
|
export declare function timeOf(minutes: number): string;
|
|
34
22
|
/** An `HH:mm` string into minutes since midnight; `fallback` when it is not one. */
|
|
35
23
|
export declare function minutesAt(time: string | undefined, fallback: number): number;
|
|
36
|
-
/**
|
|
37
|
-
* The weekdays a calendar shows, in reading order.
|
|
38
|
-
*
|
|
39
|
-
* The array does double duty: it says which days are VISIBLE: `[1,2,3,4,5]` hides the
|
|
40
|
-
* weekend everywhere, and its first entry is the day a week starts on. Given nothing, the
|
|
41
|
-
* seven days rotated to `firstDayOfWeek`, which the calendar takes from its prop of that
|
|
42
|
-
* name or from the locale.
|
|
43
|
-
*/
|
|
24
|
+
/** The weekdays a calendar shows, in reading order. */
|
|
44
25
|
export declare function normalizeWeekdays(weekdays: readonly number[] | undefined, firstDayOfWeek: number): number[];
|
|
45
26
|
/** Whether a date falls on one of the visible weekdays. */
|
|
46
27
|
export declare function isVisibleDay(iso: string, weekdays: readonly number[]): boolean;
|
|
47
|
-
/**
|
|
48
|
-
* The first visible day at or after `iso` (or at or before it, going backwards).
|
|
49
|
-
*
|
|
50
|
-
* The loop is bounded at seven because a non-empty weekday list always contains one of any
|
|
51
|
-
* seven consecutive days: the bound is a guard against an empty list, never a real limit.
|
|
52
|
-
*/
|
|
28
|
+
/** The first visible day at or after `iso` (or at or before it, going backwards). */
|
|
53
29
|
export declare function snapToVisibleDay(iso: string, weekdays: readonly number[], direction?: -1 | 1): string;
|
|
54
30
|
/** Moves `count` VISIBLE days from `iso`, skipping over the hidden ones. */
|
|
55
31
|
export declare function advanceVisibleDays(iso: string, count: number, weekdays: readonly number[]): string;
|
|
56
32
|
/** How many day columns a view shows, before the visible-weekday filter applies. */
|
|
57
33
|
export declare function columnCount(view: CalendarView, customDays: number): number;
|
|
58
|
-
/**
|
|
59
|
-
* The days a view shows, in reading order.
|
|
60
|
-
*
|
|
61
|
-
* `day`, `4days` and `custom` run forward from the anchor over the visible days alone, so
|
|
62
|
-
* a four-day view of a Monday-to-Friday calendar shows four WORKING days rather than four
|
|
63
|
-
* calendar ones. `week` shows the week the anchor falls in, starting on `weekdays[0]`.
|
|
64
|
-
* `month` returns every day of the anchor's month that is visible: the grid that renders
|
|
65
|
-
* them cuts them back into weeks itself.
|
|
66
|
-
*
|
|
67
|
-
* `year` returns an empty list on purpose: that view is twelve month names, not a row of
|
|
68
|
-
* days, and giving it a 365-entry list nothing reads would be an invitation to read it.
|
|
69
|
-
*/
|
|
34
|
+
/** The days a view shows, in reading order. */
|
|
70
35
|
export declare function visibleDays(anchor: string, view: CalendarView, weekdays: readonly number[], customDays: number): string[];
|
|
71
36
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* grid does not draw; the view would then show the same week it already showed, and the
|
|
77
|
-
* button would look broken with nothing in the console to say why.
|
|
37
|
+
* The day-shaped views step over VISIBLE days, not calendar ones. Stepping by one calendar day
|
|
38
|
+
* from a Friday in a Monday-to-Friday calendar would land on a Saturday the grid does not draw;
|
|
39
|
+
* the view would then show the same week it already showed, and the button would look broken
|
|
40
|
+
* with nothing in the console to say why.
|
|
78
41
|
*/
|
|
79
42
|
export declare function stepAnchor(anchor: string, view: CalendarView, direction: -1 | 1, weekdays: readonly number[], customDays: number): string;
|
|
80
|
-
/** The first and last day a view covers, which
|
|
43
|
+
/** The first and last day a view covers, which names it in the toolbar. */
|
|
81
44
|
export declare function visibleRange(anchor: string, view: CalendarView, weekdays: readonly number[], customDays: number): {
|
|
82
45
|
start: string;
|
|
83
46
|
end: string;
|
|
84
47
|
};
|
|
85
|
-
/**
|
|
86
|
-
* How long an event lasts, in minutes, counting the days between its dates.
|
|
87
|
-
*
|
|
88
|
-
* A time that is not one reads as the far end of its day: midnight for a start, the next
|
|
89
|
-
* midnight for an end, so a malformed event errs towards being long, and towards the band.
|
|
90
|
-
*/
|
|
48
|
+
/** How long an event lasts, in minutes, counting the days between its dates. */
|
|
91
49
|
export declare function durationOf(times: CalendarEventTimes): number;
|
|
92
50
|
/** Whether an event runs from one day into the next without lasting a whole day. */
|
|
93
51
|
export declare function spansMidnight(times: CalendarEventTimes): boolean;
|
|
94
|
-
/**
|
|
95
|
-
* Whether an event belongs in the band above the grid rather than in a day's column.
|
|
96
|
-
*
|
|
97
|
-
* A day's column holds up to twenty-four hours, so an event running past midnight for less
|
|
98
|
-
* than that is drawn as two cards, the evening in one column and the early morning in the next.
|
|
99
|
-
* Only one lasting a whole day or more becomes a bar. Exactly twenty-four hours is a bar:
|
|
100
|
-
* two cards covering the same hours of two columns would read as two events.
|
|
101
|
-
*
|
|
102
|
-
* An event whose end comes before its start has no column to go in either, so it goes to the
|
|
103
|
-
* band, where `packAllDay` finds no day it covers and draws nothing.
|
|
104
|
-
*/
|
|
52
|
+
/** Whether an event belongs in the band above the grid rather than in a day's column. */
|
|
105
53
|
export declare function isAllDayEvent(event: CalendarEvent): boolean;
|
|
106
54
|
/**
|
|
107
55
|
* Which piece of its event a box is: the whole of it, or one side of midnight for an event
|
|
@@ -123,19 +71,14 @@ export interface EventSegment {
|
|
|
123
71
|
clippedEnd: boolean;
|
|
124
72
|
/**
|
|
125
73
|
* `head` is the evening of an event running past midnight and `tail` its morning. Only a
|
|
126
|
-
* `whole` or a `tail` holds the event's real end, which
|
|
74
|
+
* `whole` or a `tail` holds the event's real end, which its resize strip drags.
|
|
127
75
|
*/
|
|
128
76
|
part: SegmentPart;
|
|
129
77
|
}
|
|
130
78
|
/**
|
|
131
|
-
* Cuts the timed events into the boxes the day columns draw.
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
* `minDuration` is applied HERE, by stretching the end, so that `packDayColumn` below sees
|
|
136
|
-
* ordinary intervals and can stay a plain interval algorithm. Were the minimum applied
|
|
137
|
-
* during packing instead, two events fifteen minutes apart would be laid out as though
|
|
138
|
-
* they did not overlap and then drawn as though they did.
|
|
79
|
+
* Cuts the timed events into the boxes the day columns draw. Were the minimum applied during
|
|
80
|
+
* packing instead, two events fifteen minutes apart would be laid out as though they did not
|
|
81
|
+
* overlap and then drawn as though they did.
|
|
139
82
|
*/
|
|
140
83
|
export declare function timedSegments(events: readonly CalendarEvent[], days: readonly string[], timeWindow: TimeWindow, minDuration: number): EventSegment[];
|
|
141
84
|
/** A segment once it knows how much of its column's width it gets. */
|
|
@@ -148,56 +91,18 @@ export interface PlacedSegment extends EventSegment {
|
|
|
148
91
|
columns: number;
|
|
149
92
|
}
|
|
150
93
|
/**
|
|
151
|
-
* Shares one day's width out between the events happening at the same time.
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
* event starting after everything before it has finished begins a new one. That is what
|
|
155
|
-
* stops a crowded morning from squeezing a lone afternoon meeting into a sliver. Then,
|
|
156
|
-
* inside a cluster, each event takes the first column whose last occupant has already
|
|
157
|
-
* ended, which is the ordinary greedy colouring of an interval graph and uses as few
|
|
158
|
-
* columns as the cluster can be drawn in. Finally each event grows sideways for as long as
|
|
159
|
-
* nothing in the next column overlaps it, so a single event never leaves a gap it could
|
|
160
|
-
* have filled.
|
|
161
|
-
*
|
|
162
|
-
* The order is fixed by start, then longest first, then by id, so the same list always
|
|
163
|
-
* produces the same picture. Without the id to break the last tie, two events at the same
|
|
164
|
-
* time for the same length would swap places whenever the array was re-sorted, and the
|
|
165
|
-
* whole day would appear to shuffle for no reason.
|
|
166
|
-
*
|
|
167
|
-
* Overlap is strictly `a.start < b.end && b.start < a.end`, so events that merely touch
|
|
168
|
-
* (one ending exactly as the next begins) share a column instead of splitting the width.
|
|
94
|
+
* Shares one day's width out between the events happening at the same time. Overlap is strictly
|
|
95
|
+
* `a.start < b.end && b.start < a.end`, so events that merely touch (one ending exactly as the
|
|
96
|
+
* next begins) share a column instead of splitting the width.
|
|
169
97
|
*/
|
|
170
98
|
export declare function packDayColumn(segments: readonly EventSegment[]): PlacedSegment[];
|
|
171
|
-
/**
|
|
172
|
-
* One square of the month view: a day, and whether it belongs to the month on show.
|
|
173
|
-
*
|
|
174
|
-
* It is `utils/date`'s own type, re-exported rather than restated: `monthWeeks` below builds
|
|
175
|
-
* its cells by calling `buildMonthGrid` from that module, so a second declaration here could
|
|
176
|
-
* only ever drift away from the shape it is actually handed. The re-export keeps
|
|
177
|
-
* `import { type MonthCell } from './layout.js'` working for the two views that read it.
|
|
178
|
-
*/
|
|
99
|
+
/** One square of the month view: a day, and whether it belongs to the month on show. */
|
|
179
100
|
export type { MonthCell };
|
|
180
|
-
/**
|
|
181
|
-
* The month view, cut into weeks.
|
|
182
|
-
*
|
|
183
|
-
* It keeps the fixed six rows `buildMonthGrid` produces: padded with the neighbouring
|
|
184
|
-
* months, so the grid is the same height whichever month is on show and the page does not
|
|
185
|
-
* jump as one steps through the year. Each row is then filtered down to the visible
|
|
186
|
-
* weekdays, which is what makes a Monday-to-Friday month five columns wide rather than
|
|
187
|
-
* seven with two blanks.
|
|
188
|
-
*/
|
|
101
|
+
/** The month view, cut into weeks. */
|
|
189
102
|
export declare function monthWeeks(anchor: string, weekdays: readonly number[]): MonthCell[][];
|
|
190
|
-
/** The first day of each month of the anchor's year, which
|
|
103
|
+
/** The first day of each month of the anchor's year, which the year view lays out. */
|
|
191
104
|
export declare function monthsOfYear(anchor: string): string[];
|
|
192
|
-
/**
|
|
193
|
-
* The last day an event puts anything on.
|
|
194
|
-
*
|
|
195
|
-
* Its `end` date, except for a timed event ending at midnight EXACTLY: it has nothing on
|
|
196
|
-
* that day, which is why `timedSegments` draws it whole on its first day. Every other view
|
|
197
|
-
* has to agree, or twenty-four hours from one midnight to the next becomes a two-day bar and
|
|
198
|
-
* an evening ending at 00:00 is listed on the following day too. An all-day event's dates
|
|
199
|
-
* are whole days, so its end is kept as it stands.
|
|
200
|
-
*/
|
|
105
|
+
/** The last day an event puts anything on. */
|
|
201
106
|
export declare function lastDayOf(event: CalendarEvent): string;
|
|
202
107
|
/** Whether an event covers a given day at all, all-day or timed. */
|
|
203
108
|
export declare function coversDay(event: CalendarEvent, iso: string): boolean;
|
|
@@ -205,16 +110,6 @@ export declare function coversDay(event: CalendarEvent, iso: string): boolean;
|
|
|
205
110
|
* Every visible day's events, in the order a summary lists them: the all-day ones first, since
|
|
206
111
|
* they frame the day rather than sit inside it, then the rest by when they start, the last tie
|
|
207
112
|
* broken by id so the list cannot reshuffle for no reason.
|
|
208
|
-
*
|
|
209
|
-
* WHY ONE PASS RATHER THAN A FILTER PER DAY. The month view needs all 42 squares filled, and
|
|
210
|
-
* filtering and sorting the list once per square walks the whole event list 42 times and sorts
|
|
211
|
-
* it 42 times: `cells × events`, with a sort each. Measured on the bench: 7.8 ms per render at
|
|
212
|
-
* 2000 events, and the month view rebuilds it every time a drag carries a chip onto another
|
|
213
|
-
* day, so that is roughly half a frame spent rebuilding lists the gesture left alone.
|
|
214
|
-
*
|
|
215
|
-
* Here each event is placed once, into the days it actually covers, and each day is sorted
|
|
216
|
-
* once: `events × span + cells × k log k`. The walk is bounded to the grid on both ends, so
|
|
217
|
-
* an event running from last year costs its visible part and nothing more.
|
|
218
113
|
*/
|
|
219
114
|
export declare function eventsByDay<T extends CalendarEvent>(events: readonly T[], days: readonly string[]): Map<string, T[]>;
|
|
220
115
|
/** One bar in the all-day band: the columns it covers, and the row it sits on. */
|
|
@@ -230,19 +125,7 @@ export interface AllDaySpan {
|
|
|
230
125
|
/** Which row of the band it sits on. */
|
|
231
126
|
lane: number;
|
|
232
127
|
}
|
|
233
|
-
/**
|
|
234
|
-
* Stacks the all-day bars into as few rows as they fit in.
|
|
235
|
-
*
|
|
236
|
-
* A bar covers the run from the FIRST to the LAST visible day it touches, which is what
|
|
237
|
-
* makes a hidden weekday in the middle of a range simply not there rather than a gap to
|
|
238
|
-
* account for: an event running Friday to Monday in a Monday-to-Friday calendar shows as
|
|
239
|
-
* one day on the Friday and one on the following Monday, because those are the two days it
|
|
240
|
-
* actually covers on screen.
|
|
241
|
-
*
|
|
242
|
-
* The rows are the same greedy pass the day columns use: each bar takes the first row
|
|
243
|
-
* whose last bar has already ended, and the ordering is fixed, longest first at equal
|
|
244
|
-
* starts, so the band cannot reshuffle when the array is re-sorted.
|
|
245
|
-
*/
|
|
128
|
+
/** Stacks the all-day bars into as few rows as they fit in. */
|
|
246
129
|
export declare function packAllDay(events: readonly CalendarEvent[], days: readonly string[]): AllDaySpan[];
|
|
247
130
|
/** Where a moment sits in the visible window, as a unitless fraction the stylesheet scales. */
|
|
248
131
|
export declare function fractionOf(minutes: number, timeWindow: TimeWindow): number;
|
|
@@ -256,12 +139,9 @@ export interface GridGeometry {
|
|
|
256
139
|
columns: number;
|
|
257
140
|
}
|
|
258
141
|
/**
|
|
259
|
-
* Turns a point on the screen into a point on the calendar: which day column it fell in,
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
* Both answers are brought back inside the grid, so a pointer dragged past an edge keeps
|
|
263
|
-
* producing the nearest sensible value rather than none. That is what makes a drag that
|
|
264
|
-
* wanders off the component behave as the user expects instead of stopping dead.
|
|
142
|
+
* Turns a point on the screen into a point on the calendar: which day column it fell in, and
|
|
143
|
+
* what moment that height stands for. That is what makes a drag that wanders off the component
|
|
144
|
+
* behave as the user expects instead of stopping dead.
|
|
265
145
|
*/
|
|
266
146
|
export declare function pointToCell(point: {
|
|
267
147
|
x: number;
|
|
@@ -279,28 +159,13 @@ export declare function floorToSlot(minutes: number, step: number): number;
|
|
|
279
159
|
* on the same day, two for a trip from Monday to Wednesday.
|
|
280
160
|
*/
|
|
281
161
|
export declare function daySpan(times: CalendarEventTimes): number;
|
|
282
|
-
/**
|
|
283
|
-
* Moves an event to another day, KEEPING its length in days as well as its times.
|
|
284
|
-
*
|
|
285
|
-
* This is the counterpart of `timeGrid.ts`'s `moveEvent`, and the two must not be confused: that one is for
|
|
286
|
-
* a timed move INSIDE a single day and deliberately collapses `end` onto `start`, so putting
|
|
287
|
-
* a three-day trip through it would silently squash it into one Tuesday. This one is for the
|
|
288
|
-
* whole-day gestures (an all-day bar dragged along the band, a chip dragged across a month),
|
|
289
|
-
* where the times are carried along untouched and only the dates move.
|
|
290
|
-
*/
|
|
162
|
+
/** Moves an event to another day, KEEPING its length in days as well as its times. */
|
|
291
163
|
export declare function moveEventToDay(origin: CalendarEventTimes, target: string): CalendarEventTimes;
|
|
292
164
|
/** The measurements of a month grid: the same struct, with rows where a time window was. */
|
|
293
165
|
export interface MonthGeometry extends GridGeometry {
|
|
294
166
|
rows: number;
|
|
295
167
|
}
|
|
296
|
-
/**
|
|
297
|
-
* Turns a point on the screen into a square of the month.
|
|
298
|
-
*
|
|
299
|
-
* The block axis is a ROW here rather than a moment, which is the whole difference from
|
|
300
|
-
* `pointToCell`: the inline half is the same arithmetic, RTL flip included. It is sound
|
|
301
|
-
* because every week of a month is drawn the same height: the rows share what space there is
|
|
302
|
-
* and a day too full for its box clips rather than growing.
|
|
303
|
-
*/
|
|
168
|
+
/** Turns a point on the screen into a square of the month. */
|
|
304
169
|
export declare function pointToMonthCell(point: {
|
|
305
170
|
x: number;
|
|
306
171
|
y: number;
|
|
@@ -311,47 +176,23 @@ export declare function pointToMonthCell(point: {
|
|
|
311
176
|
/**
|
|
312
177
|
* Which inline edge a point is resting against, as a direction to page in: -1 for the start
|
|
313
178
|
* edge, 1 for the end one, nothing in between.
|
|
314
|
-
*
|
|
315
|
-
* It answers in READING order, so the start edge is the left one normally and the right one
|
|
316
|
-
* in a right-to-left page. That makes "hold at the edge to go back" mean the same
|
|
317
|
-
* thing in both.
|
|
318
179
|
*/
|
|
319
180
|
export declare function inlineEdgeAt(x: number, geometry: {
|
|
320
181
|
inlineStart: number;
|
|
321
182
|
inlineSize: number;
|
|
322
183
|
}, band: number, rtl: boolean): -1 | 1 | null;
|
|
323
184
|
/**
|
|
324
|
-
* How hard a point near the top or bottom of a box is asking it to scroll: -1 at the very
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
* A FRACTION and not a direction, so the speed can rise as the pointer nears the edge. At a
|
|
328
|
-
* constant speed there is no setting that works: fast enough to be useful at the boundary is
|
|
329
|
-
* uncontrollable a few pixels in, and gentle enough to be controllable never gets anywhere.
|
|
185
|
+
* How hard a point near the top or bottom of a box is asking it to scroll: -1 at the very top,
|
|
186
|
+
* 1 at the very bottom, 0 anywhere in the middle.
|
|
330
187
|
*/
|
|
331
188
|
export declare function blockEdgeAt(y: number, geometry: {
|
|
332
189
|
top: number;
|
|
333
190
|
height: number;
|
|
334
191
|
}, band: number): number;
|
|
335
192
|
/**
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
* It takes the box as the browser MEASURED it, and never the `GridGeometry` the rest of this file
|
|
340
|
-
* works in. Containment is a physical question with no reading direction, where `inlineStart` is
|
|
341
|
-
* the box's RIGHT edge in a right-to-left page: bracketing with it would walk off the far side of
|
|
342
|
-
* the screen and make every right-to-left drop read as outside. `columns` says nothing about
|
|
343
|
-
* whether a point is in a box either, so asking for a `GridGeometry` would ask every caller to
|
|
344
|
-
* build a thing this needs no part of.
|
|
345
|
-
*
|
|
346
|
-
* The edges COUNT as inside: a rect is a pair of floats on a screen that may be scaled by a
|
|
347
|
-
* fraction, and an exclusive boundary would refuse a drop that looked, and was, on target.
|
|
348
|
-
*
|
|
349
|
-
* TRAP: a box that measures nothing contains EVERYTHING. That is the same answer `inlineEdgeAt`
|
|
350
|
-
* and `blockEdgeAt` give a box of no size, and for the same reason: with no measurement there is
|
|
351
|
-
* no outside to be on. Take a zero-size box literally instead and the calendar refuses every drop
|
|
352
|
-
* wherever nothing has been laid out: in a hidden tab, before the first frame, and in jsdom,
|
|
353
|
-
* which measures nothing at all and where all but a handful of the drag tests would go red at
|
|
354
|
-
* once.
|
|
193
|
+
* A box that measures nothing contains EVERYTHING. That is the same answer `inlineEdgeAt` and
|
|
194
|
+
* `blockEdgeAt` give a box of no size, and for the same reason: with no measurement there is no
|
|
195
|
+
* outside to be on.
|
|
355
196
|
*/
|
|
356
197
|
export declare function pointWithin(point: {
|
|
357
198
|
x: number;
|
|
@@ -365,11 +206,9 @@ export declare function pointWithin(point: {
|
|
|
365
206
|
/** The times of an event, as the shape the move and resize events carry. */
|
|
366
207
|
export declare function timesOf(event: CalendarEvent): CalendarEventTimes;
|
|
367
208
|
/**
|
|
368
|
-
* Whether two placements are the same, field for field.
|
|
369
|
-
*
|
|
370
|
-
* It is what a drag asks before it writes its preview. A slot is a quarter of an hour, some
|
|
209
|
+
* Whether two placements are the same, field for field. A slot is a quarter of an hour, some
|
|
371
210
|
* sixteen pixels at the default hour height, so most pointer moves land on the times already
|
|
372
|
-
* shown, and the gesture state is deeply reactive, so an
|
|
211
|
+
* shown, and the gesture state is deeply reactive, so an equal but new object would still
|
|
373
212
|
* re-run the whole layout and re-render every cell of the grid for a picture that did not
|
|
374
213
|
* change.
|
|
375
214
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.js","names":[],"sources":["../../../src/components/VCalendar/layout.ts"],"sourcesContent":["// @core\n/**\n * Everything VCalendar has to work out rather than render: which days a view shows, where\n * an event's box goes, how a day's width is shared between events happening at once, and\n * what point on the screen corresponds to what moment in the day.\n *\n * It sits in VCalendar's own folder rather than in `utils/` because it has exactly one\n * consumer, which fails the admission rule for shared code, and because VCalendar owns\n * the contract (the same reasoning as `VDatePicker/keyboard.ts` and\n * `VHotkeys/platform.ts`).\n *\n * Being pure is not tidiness here, it is the only way any of this is testable. The test\n * environment lays nothing out — every element it renders measures zero — so a function\n * that took an Element could never be checked. NOTHING below receives one: the single\n * `getBoundingClientRect()` in the whole component builds the plain `GridGeometry` struct\n * that `pointToCell` takes, and every consequence of that measurement is an ordinary unit\n * test over numbers.\n *\n * Times are handled throughout as MINUTES SINCE MIDNIGHT, converted at the edges from the\n * `HH:mm` strings the public API speaks. That is what lets the arithmetic be plain\n * addition instead of a string dance at every step.\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/**\n * A day in milliseconds, for counting the days between two dates.\n *\n * It is safe here because both ends come from `parseISO`, which builds LOCAL midnights: the\n * difference between two of them is a whole number of days everywhere except across a\n * daylight-saving boundary, and `Math.round` absorbs the hour that gains or loses there.\n */\nconst MS_PER_DAY = 24 * 60 * 60 * 1000\n\n/**\n * How far the pointer must travel before a press becomes a drag rather than a click.\n *\n * It is what keeps a click a click, on a card as on an empty cell: a press that never moves\n * this far writes nothing on release and is handled as a plain activation. Without it, the\n * hand's natural tremor during a click would register as a one-pixel drag and every click\n * would silently move its event.\n */\nexport const DRAG_THRESHOLD = 3\n\n/**\n * The last tie-break of every ordering below: a stable total order over event ids.\n *\n * TRAP — this must NOT be `localeCompare`. An id is an opaque key, never user-facing text,\n * so there is nothing here to collate; and `localeCompare` with no locale resolves against\n * the RUNTIME's default, which differs between Node and the browser. The three orderings it\n * settles all decide rendered markup — `packDayColumn` assigns columns, `packAllDay`\n * assigns lanes, `eventsByDay` sets the order chips are listed in — so a divergent order is\n * a hydration mismatch, silent in dev and visible only as a card in the wrong place. It is\n * the same hazard `VDataTable` avoids by passing its locale explicitly.\n *\n * Comparing by code point is also allocation-free, which matters because all three run again\n * every time a drag carries its event into another slot.\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 // The start stops an hour short of midnight, so the hour kept on screen fits in the day.\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 * Minutes since midnight back into an `HH:mm` string.\n *\n * TRAP: the value is held one minute short of midnight rather than allowed to reach it.\n * A day's last moment is 1440 in this arithmetic, but `24:00` is not a time any of the\n * design system's helpers accept (`isValidTime` rejects it). Writing\n * `00:00` on the following day would turn an ordinary evening appointment into one that\n * runs past midnight, which every gesture then treats differently. One minute is invisible at\n * every zoom this component offers; an event that changed kind under the pointer is not.\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/**\n * The weekdays a calendar shows, in reading order.\n *\n * The array does double duty: it says which days are VISIBLE: `[1,2,3,4,5]` hides the\n * weekend everywhere, and its first entry is the day a week starts on. Given nothing, the\n * seven days rotated to `firstDayOfWeek`, which the calendar takes from its prop of that\n * name or from the locale.\n */\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/**\n * The first visible day at or after `iso` (or at or before it, going backwards).\n *\n * The loop is bounded at seven because a non-empty weekday list always contains one of any\n * seven consecutive days: the bound is a guard against an empty list, never a real limit.\n */\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/**\n * The days a view shows, in reading order.\n *\n * `day`, `4days` and `custom` run forward from the anchor over the visible days alone, so\n * a four-day view of a Monday-to-Friday calendar shows four WORKING days rather than four\n * calendar ones. `week` shows the week the anchor falls in, starting on `weekdays[0]`.\n * `month` returns every day of the anchor's month that is visible: the grid that renders\n * them cuts them back into weeks itself.\n *\n * `year` returns an empty list on purpose: that view is twelve month names, not a row of\n * days, and giving it a 365-entry list nothing reads would be an invitation to read it.\n */\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 * Where Previous and Next land.\n *\n * TRAP: the day-shaped views step over VISIBLE days, not calendar ones. Stepping by one\n * calendar day from a Friday in a Monday-to-Friday calendar would land on a Saturday the\n * grid does not draw; the view would then show the same week it already showed, and the\n * button would look broken 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 is what 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/**\n * How long an event lasts, in minutes, counting the days between its dates.\n *\n * A time that is not one reads as the far end of its day: midnight for a start, the next\n * midnight for an end, so a malformed event errs towards being long, and towards the band.\n */\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/**\n * Whether an event belongs in the band above the grid rather than in a day's column.\n *\n * A day's column holds up to twenty-four hours, so an event running past midnight for less\n * than that is drawn as two cards, the evening in one column and the early morning in the next.\n * Only one lasting a whole day or more becomes a bar. Exactly twenty-four hours is a bar:\n * two cards covering the same hours of two columns would read as two events.\n *\n * An event whose end comes before its start has no column to go in either, so it goes to the\n * band, where `packAllDay` finds no day it covers and draws nothing.\n */\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 is what its resize strip drags.\n */\n part: SegmentPart\n}\n\n/**\n * Cuts the timed events into the boxes the day columns draw. All-day events produce none;\n * they belong in the band. Nor does an event whose day is not on show or whose hours fall\n * entirely outside the window.\n *\n * `minDuration` is applied HERE, by stretching the end, so that `packDayColumn` below sees\n * ordinary intervals and can stay a plain interval algorithm. Were the minimum applied\n * during packing instead, two events fifteen minutes apart would be laid out as though\n * they did not 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 * Running past midnight: cut at midnight into two boxes, each in its own day's column. An\n * event ending at midnight exactly has nothing on its second day, so it is drawn whole — a\n * 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.\n *\n * Three passes. First the segments are cut into CLUSTERS: runs joined by overlap, where an\n * event starting after everything before it has finished begins a new one. That is what\n * stops a crowded morning from squeezing a lone afternoon meeting into a sliver. Then,\n * inside a cluster, each event takes the first column whose last occupant has already\n * ended, which is the ordinary greedy colouring of an interval graph and uses as few\n * columns as the cluster can be drawn in. Finally each event grows sideways for as long as\n * nothing in the next column overlaps it, so a single event never leaves a gap it could\n * have filled.\n *\n * The order is fixed by start, then longest first, then by id, so the same list always\n * produces the same picture. Without the id to break the last tie, two events at the same\n * time for the same length would swap places whenever the array was re-sorted, and the\n * whole day would appear to shuffle for no reason.\n *\n * Overlap is strictly `a.start < b.end && b.start < a.end`, so events that merely touch\n * (one ending exactly as the 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/**\n * One square of the month view: a day, and whether it belongs to the month on show.\n *\n * It is `utils/date`'s own type, re-exported rather than restated: `monthWeeks` below builds\n * its cells by calling `buildMonthGrid` from that module, so a second declaration here could\n * only ever drift away from the shape it is actually handed. The re-export keeps\n * `import { type MonthCell } from './layout'` working for the two views that read it.\n */\nexport type { MonthCell }\n\n/**\n * The month view, cut into weeks.\n *\n * It keeps the fixed six rows `buildMonthGrid` produces: padded with the neighbouring\n * months, so the grid is the same height whichever month is on show and the page does not\n * jump as one steps through the year. Each row is then filtered down to the visible\n * weekdays, which is what makes a Monday-to-Friday month five columns wide rather than\n * seven with two blanks.\n */\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 is what 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/**\n * The last day an event puts anything on.\n *\n * Its `end` date, except for a timed event ending at midnight EXACTLY: it has nothing on\n * that day, which is why `timedSegments` draws it whole on its first day. Every other view\n * has to agree, or twenty-four hours from one midnight to the next becomes a two-day bar and\n * an evening ending at 00:00 is listed on the following day too. An all-day event's dates\n * are whole days, so its end is kept as it stands.\n */\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 *\n * WHY ONE PASS RATHER THAN A FILTER PER DAY. The month view needs all 42 squares filled, and\n * filtering and sorting the list once per square walks the whole event list 42 times and sorts\n * it 42 times: `cells × events`, with a sort each. Measured on the bench: 7.8 ms per render at\n * 2000 events, and the month view rebuilds it every time a drag carries a chip onto another\n * day, so that is roughly half a frame spent rebuilding lists the gesture left alone.\n *\n * Here each event is placed once, into the days it actually covers, and each day is sorted\n * once: `events × span + cells × k log k`. The walk is bounded to the grid on both ends, so\n * an event running from last year costs its visible part and nothing more.\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\n // is what keeps 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 * TRAP: the walk below steps from one date to the next through `addDays`, which hands\n * back a string that is not a date unchanged. A datetime slipped into `start` would never\n * be 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\n * event 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\n * 2000 events sorts some 17 000 pairs. Measured at 9.97 ms\n * against 4.19 ms with the key hoisted.\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/**\n * Stacks the all-day bars into as few rows as they fit in.\n *\n * A bar covers the run from the FIRST to the LAST visible day it touches, which is what\n * makes a hidden weekday in the middle of a range simply not there rather than a gap to\n * account for: an event running Friday to Monday in a Monday-to-Friday calendar shows as\n * one day on the Friday and one on the following Monday, because those are the two days it\n * actually covers on screen.\n *\n * The rows are the same greedy pass the day columns use: each bar takes the first row\n * whose last bar has already ended, and the ordering is fixed, longest first at equal\n * starts, so the band cannot reshuffle when the array is re-sorted.\n */\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,\n * and what moment that height stands for.\n *\n * Both answers are brought back inside the grid, so a pointer dragged past an edge keeps\n * producing the nearest sensible value rather than none. That is what makes a drag that\n * wanders off the component 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/**\n * Moves an event to another day, KEEPING its length in days as well as its times.\n *\n * This is the counterpart of `timeGrid.ts`'s `moveEvent`, and the two must not be confused: that one is for\n * a timed move INSIDE a single day and deliberately collapses `end` onto `start`, so putting\n * a three-day trip through it would silently squash it into one Tuesday. This one is for the\n * whole-day gestures (an all-day bar dragged along the band, a chip dragged across a month),\n * where the times are carried along untouched and only the dates move.\n */\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/**\n * Turns a point on the screen into a square of the month.\n *\n * The block axis is a ROW here rather than a moment, which is the whole difference from\n * `pointToCell`: the inline half is the same arithmetic, RTL flip included. It is sound\n * because every week of a month is drawn the same height: the rows share what space there is\n * and a day too full for its box clips rather than growing.\n */\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 *\n * It answers in READING order, so the start edge is the left one normally and the right one\n * in a right-to-left page. That makes \"hold at the edge to go back\" mean the same\n * thing in both.\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\n * top, 1 at the very bottom, 0 anywhere in the middle.\n *\n * A FRACTION and not a direction, so the speed can rise as the pointer nears the edge. At a\n * constant speed there is no setting that works: fast enough to be useful at the boundary is\n * uncontrollable a few pixels in, and gentle enough to be controllable never gets anywhere.\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 * Whether a point falls inside a box: how a drag decides, on release, whether it is over the\n * calendar at all or over the toolbar, or over the page beside it.\n *\n * It takes the box as the browser MEASURED it, and never the `GridGeometry` the rest of this file\n * works in. Containment is a physical question with no reading direction, where `inlineStart` is\n * the box's RIGHT edge in a right-to-left page: bracketing with it would walk off the far side of\n * the screen and make every right-to-left drop read as outside. `columns` says nothing about\n * whether a point is in a box either, so asking for a `GridGeometry` would ask every caller to\n * build a thing this needs no part of.\n *\n * The edges COUNT as inside: a rect is a pair of floats on a screen that may be scaled by a\n * fraction, and an exclusive boundary would refuse a drop that looked, and was, on target.\n *\n * TRAP: a box that measures nothing contains EVERYTHING. That is the same answer `inlineEdgeAt`\n * and `blockEdgeAt` give a box of no size, and for the same reason: with no measurement there is\n * no outside to be on. Take a zero-size box literally instead and the calendar refuses every drop\n * wherever nothing has been laid out: in a hidden tab, before the first frame, and in jsdom,\n * which measures nothing at all and where all but a handful of the drag tests would go red at\n * once.\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.\n *\n * It is what a drag asks before it writes its preview. 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":[],"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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"}
|
|
@@ -7,12 +7,7 @@ import { CalendarEventTimes } from './types.js';
|
|
|
7
7
|
export declare function roundToSlot(minutes: number, step: number): number;
|
|
8
8
|
/**
|
|
9
9
|
* Where an event lands when it is dragged or nudged along its day: it keeps its length and
|
|
10
|
-
* moves by `minuteDelta`.
|
|
11
|
-
* show rather than counted here, so a hidden weekday is never landed on.
|
|
12
|
-
*
|
|
13
|
-
* An event pushed past the end of the window is held against it rather than having its tail
|
|
14
|
-
* cut off: moving something must never change how long it is, which is the other gesture's
|
|
15
|
-
* job.
|
|
10
|
+
* moves by `minuteDelta`.
|
|
16
11
|
*/
|
|
17
12
|
export declare function moveEvent(origin: CalendarEventTimes, minuteDelta: number, timeWindow: TimeWindow): CalendarEventTimes;
|
|
18
13
|
/**
|
|
@@ -23,33 +18,16 @@ export declare function resizeEvent(origin: CalendarEventTimes, endMinutes: numb
|
|
|
23
18
|
/**
|
|
24
19
|
* Where a timed event lands when it is dragged or nudged: it starts at `startMinutes` counted
|
|
25
20
|
* from the midnight that begins `day`, and keeps its length.
|
|
26
|
-
*
|
|
27
|
-
* `crossMidnight` says which of two rules applies, and it is read off the event as the gesture
|
|
28
|
-
* FOUND it rather than off the times being written. An event that ran past midnight when it was
|
|
29
|
-
* taken hold of may go on doing so, or come back inside one day, or cross again: its start may
|
|
30
|
-
* be pushed into the day before or after `day`, and its end lands wherever its length takes it.
|
|
31
|
-
* Any other event is `moveEvent`'s, held inside `day`, so a drag never turns an ordinary
|
|
32
|
-
* appointment into an overnight one by accident.
|
|
33
21
|
*/
|
|
34
22
|
export declare function moveTimedEvent(times: CalendarEventTimes, day: string, startMinutes: number, timeWindow: TimeWindow, crossMidnight: boolean): CalendarEventTimes;
|
|
35
23
|
/**
|
|
36
24
|
* Where a timed event's end lands when it is dragged or nudged: `endMinutes` counted from the
|
|
37
|
-
* midnight that begins `endDay`.
|
|
38
|
-
*
|
|
39
|
-
* With `crossMidnight` false this is `resizeEvent`, and `endDay` is ignored. With it true: an
|
|
40
|
-
* event that ran past midnight when the gesture began: the end may sit on the event's first
|
|
41
|
-
* day or on the next one, whichever `endDay` names (anything else is held to the nearer of the
|
|
42
|
-
* two), and it stays at least `minDuration` after the start and short of a whole day after it,
|
|
43
|
-
* so the event never turns into a bar under the pointer.
|
|
25
|
+
* midnight that begins `endDay`. With `crossMidnight` false this is `resizeEvent`, and `endDay`
|
|
26
|
+
* is ignored.
|
|
44
27
|
*/
|
|
45
28
|
export declare function resizeTimedEvent(times: CalendarEventTimes, endDay: string, endMinutes: number, minDuration: number, timeWindow: TimeWindow, crossMidnight: boolean): CalendarEventTimes;
|
|
46
29
|
/**
|
|
47
30
|
* The stretch of a day drawn out by a press at `anchor` and a pointer now at `pointer`, both in
|
|
48
31
|
* minutes since midnight.
|
|
49
|
-
*
|
|
50
|
-
* It covers every slot from the one pressed to the one under the pointer, both included, so it
|
|
51
|
-
* reads the same whichever way the pointer went: down from 10:00 to 11:10 is 10:00–11:15, up
|
|
52
|
-
* from 10:00 to 9:10 is 9:00–10:15. The pressed slot is never given up, which is what keeps the
|
|
53
|
-
* box from flipping over as the pointer crosses its own starting point.
|
|
54
32
|
*/
|
|
55
33
|
export declare function drawnSlot(day: string, anchor: number, pointer: number, step: number, timeWindow: TimeWindow): CalendarEventTimes;
|