vectis-ui 0.10.1 → 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/tokens/css.d.ts +3 -16
- package/dist/tokens/css.js.map +1 -1
- package/dist/tokens/index.js.map +1 -1
- package/dist/tokens/primitives.js.map +1 -1
- package/dist/tokens/semantic.d.ts +1 -23
- 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/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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VTypography.js","names":[],"sources":["../../../src/components/VTypography/VTypography.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * The design system's text element. Rather than setting a size here and a weight\n * there, the writer names the ROLE the text plays — a heading, a caption, a label —\n * and `variant` applies the complete typographic recipe that goes with it: family,\n * size, weight, line height and letter spacing, all read from the semantic tokens.\n * `tone` does the same for colour.\n *\n * It renders a single element and adds no behaviour, so the attributes it is given\n * (id, for, aria-*…) land straight on that element. This is what lets it serve as a\n * real field label, with `as=\"label\"` and a `for`.\n */\nimport { computed } from 'vue'\n\n/** The role a text plays, each with its complete typographic recipe. */\nexport type TypographyVariant =\n | 'display'\n | 'heading-1'\n | 'heading-2'\n | 'heading-3'\n | 'heading-4'\n | 'subtitle'\n | 'body-xl'\n | 'body-lg'\n | 'body-md'\n | 'body-sm'\n | 'label'\n | 'caption'\n | 'overline'\n | 'code'\n\n/** The colour of a text; `default` sets none and inherits. */\nexport type TypographyTone =\n 'default' | 'muted' | 'subtle' | 'accent' | 'danger' | 'success' | 'warning' | 'on-inverse'\n\ninterface TypographyProps {\n /**\n * The role this text plays, which selects a complete recipe of typographic\n * tokens: size, weight, line height and, where the role calls for it, letter\n * spacing and a monospaced family.\n */\n variant?: TypographyVariant\n /**\n * The HTML tag to render. Each variant already has a sensible default (h1 to h4,\n * p, span, code), so this is for the cases where the meaning and the look differ:\n * a subtitle that is really an `h2`, or a label attached to a field.\n */\n as?: string\n /**\n * The colour of the text. `default` sets none at all, so the text inherits from\n * whatever surrounds it, which is what lets the same component be used on an\n * inverted surface or inside a coloured toast.\n */\n tone?: TypographyTone\n /**\n * Cuts the text to one line and ends it with an ellipsis. The element needs a\n * width to be cut against (being a block, or a flex item), otherwise there is\n * nothing to overflow and the text simply stays whole.\n */\n truncate?: boolean\n}\n\nconst props = withDefaults(defineProps<TypographyProps>(), {\n variant: 'body-md',\n as: undefined,\n tone: 'default',\n truncate: false,\n})\n\ndefineSlots<{\n /** Text content. */\n default(): unknown\n}>()\n\n/**\n * The tag each variant renders when `as` says nothing — the one whose meaning\n * matches the role.\n */\nconst DEFAULT_TAGS: Record<TypographyVariant, string> = {\n display: 'p',\n 'heading-1': 'h1',\n 'heading-2': 'h2',\n 'heading-3': 'h3',\n 'heading-4': 'h4',\n subtitle: 'p',\n 'body-xl': 'p',\n 'body-lg': 'p',\n 'body-md': 'p',\n 'body-sm': 'p',\n label: 'span',\n caption: 'p',\n overline: 'span',\n code: 'code',\n}\n\nconst tag = computed(() => props.as ?? DEFAULT_TAGS[props.variant])\n</script>\n\n<template>\n <component\n :is=\"tag\"\n class=\"v-typography\"\n :data-variant=\"variant\"\n :data-tone=\"tone === 'default' ? undefined : tone\"\n :data-truncate=\"truncate ? '' : undefined\"\n >\n <slot />\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n .v-typography {\n /* These two defaults are re-declared on every instance because custom properties\n INHERIT: nested inside a variant that sets letter spacing, or inside a `code`,\n a VTypography would otherwise pick up its ancestor's tracking and monospaced\n family. The [data-variant] blocks below, being more specific, still win.\n\n Size, weight and leading need no such reset, and that is an INVARIANT rather than\n an oversight: every variant block sets all three, so none can be inherited. A new\n variant that leaves one out takes it from whichever VTypography encloses it — the\n same bug these two lines exist to prevent, one step further out. */\n --typography-family: var(--vectis-text-family);\n --typography-tracking: normal;\n\n margin: 0; /* the space around a text block belongs to the layout holding it */\n font-family: var(--typography-family);\n font-size: var(--typography-size);\n font-weight: var(--typography-weight);\n line-height: var(--typography-leading);\n letter-spacing: var(--typography-tracking);\n /* With the `default` tone no colour variable is set, and the fallback makes the\n text inherit — which is what allows the component to be dropped inside an\n already coloured context, an inverted surface or a tinted toast. */\n color: var(--typography-color, inherit);\n overflow-wrap: break-word;\n }\n\n /* TRAP — the colour variable inherits like any other, so without this reset a `default`\n VTypography nested in a toned one took the OUTER tone's colour instead of the colour of\n whatever sits between them. `initial` makes it guaranteed-invalid, which is what lets\n the fallback above apply. At zero specificity, so the tone rules below and the sheets\n that route a colour through the variable (VDataTable's title, VFilePicker's subtitle)\n win over it whatever order the sheets land in. */\n :where(.v-typography) {\n --typography-color: initial;\n }\n\n .v-typography[data-variant='display'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-display-size);\n --typography-weight: var(--vectis-text-display-weight);\n --typography-leading: var(--vectis-text-display-leading);\n --typography-tracking: var(--vectis-text-display-tracking);\n }\n\n .v-typography[data-variant='heading-1'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-1-size);\n --typography-weight: var(--vectis-text-heading-1-weight);\n --typography-leading: var(--vectis-text-heading-1-leading);\n --typography-tracking: var(--vectis-text-heading-1-tracking);\n }\n\n .v-typography[data-variant='heading-2'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-2-size);\n --typography-weight: var(--vectis-text-heading-2-weight);\n --typography-leading: var(--vectis-text-heading-2-leading);\n --typography-tracking: var(--vectis-text-heading-2-tracking);\n }\n\n .v-typography[data-variant='heading-3'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-3-size);\n --typography-weight: var(--vectis-text-heading-3-weight);\n --typography-leading: var(--vectis-text-heading-3-leading);\n }\n\n .v-typography[data-variant='heading-4'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-4-size);\n --typography-weight: var(--vectis-text-heading-4-weight);\n --typography-leading: var(--vectis-text-heading-4-leading);\n }\n\n .v-typography[data-variant='subtitle'] {\n --typography-size: var(--vectis-text-subtitle-size);\n --typography-weight: var(--vectis-text-subtitle-weight);\n --typography-leading: var(--vectis-text-subtitle-leading);\n }\n\n .v-typography[data-variant='body-xl'] {\n --typography-size: var(--vectis-text-body-xl-size);\n --typography-weight: var(--vectis-text-body-xl-weight);\n --typography-leading: var(--vectis-text-body-xl-leading);\n }\n\n .v-typography[data-variant='body-lg'] {\n --typography-size: var(--vectis-text-body-lg-size);\n --typography-weight: var(--vectis-text-body-lg-weight);\n --typography-leading: var(--vectis-text-body-lg-leading);\n }\n\n .v-typography[data-variant='body-md'] {\n --typography-size: var(--vectis-text-body-md-size);\n --typography-weight: var(--vectis-text-body-md-weight);\n --typography-leading: var(--vectis-text-body-md-leading);\n }\n\n .v-typography[data-variant='body-sm'] {\n --typography-size: var(--vectis-text-body-sm-size);\n --typography-weight: var(--vectis-text-body-sm-weight);\n --typography-leading: var(--vectis-text-body-sm-leading);\n }\n\n .v-typography[data-variant='label'] {\n --typography-size: var(--vectis-text-label-size);\n --typography-weight: var(--vectis-text-label-weight);\n --typography-leading: var(--vectis-text-label-leading);\n }\n\n .v-typography[data-variant='caption'] {\n --typography-size: var(--vectis-text-caption-size);\n --typography-weight: var(--vectis-text-caption-weight);\n --typography-leading: var(--vectis-text-caption-leading);\n }\n\n .v-typography[data-variant='overline'] {\n --typography-size: var(--vectis-text-overline-size);\n --typography-weight: var(--vectis-text-overline-weight);\n --typography-leading: var(--vectis-text-overline-leading);\n --typography-tracking: var(--vectis-text-overline-tracking);\n\n text-transform: uppercase;\n }\n\n .v-typography[data-variant='code'] {\n --typography-family: var(--vectis-text-family-code);\n --typography-size: var(--vectis-text-code-size);\n --typography-weight: var(--vectis-text-code-weight);\n --typography-leading: var(--vectis-text-code-leading);\n }\n\n /* There is deliberately no block for the `default` tone: leaving the colour\n variable unset is precisely what makes the text inherit. */\n .v-typography[data-tone='muted'] {\n --typography-color: var(--vectis-color-text-muted);\n }\n\n .v-typography[data-tone='subtle'] {\n --typography-color: var(--vectis-color-text-subtle);\n }\n\n .v-typography[data-tone='accent'] {\n --typography-color: var(--vectis-color-accent-text);\n }\n\n .v-typography[data-tone='danger'] {\n --typography-color: var(--vectis-color-danger-text);\n }\n\n .v-typography[data-tone='success'] {\n --typography-color: var(--vectis-color-success-text);\n }\n\n .v-typography[data-tone='warning'] {\n --typography-color: var(--vectis-color-warning-text);\n }\n\n .v-typography[data-tone='on-inverse'] {\n --typography-color: var(--vectis-color-text-on-inverse);\n }\n\n .v-typography[data-truncate] {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"VTypography.js","names":[],"sources":["../../../src/components/VTypography/VTypography.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Apply complete semantic typography recipes by role; choose native heading levels unless as\n * explicitly overrides the element.\n */\nimport { computed } from 'vue'\n\n/** The role a text plays, each with its complete typographic recipe. */\nexport type TypographyVariant =\n | 'display'\n | 'heading-1'\n | 'heading-2'\n | 'heading-3'\n | 'heading-4'\n | 'subtitle'\n | 'body-xl'\n | 'body-lg'\n | 'body-md'\n | 'body-sm'\n | 'label'\n | 'caption'\n | 'overline'\n | 'code'\n\n/** The colour of a text; `default` sets none and inherits. */\nexport type TypographyTone =\n 'default' | 'muted' | 'subtle' | 'accent' | 'danger' | 'success' | 'warning' | 'on-inverse'\n\ninterface TypographyProps {\n /**\n * The role this text plays, which selects a complete recipe of typographic\n * tokens: size, weight, line height and, where the role calls for it, letter\n * spacing and a monospaced family.\n */\n variant?: TypographyVariant\n /**\n * The HTML tag to render. Each variant already has a sensible default (h1 to h4,\n * p, span, code), so this is for the cases where the meaning and the look differ:\n * a subtitle that is really an `h2`, or a label attached to a field.\n */\n as?: string\n /**\n * The colour of the text. `default` sets none at all, so the text inherits from whatever\n * surrounds it, which lets the same component be used on an inverted surface or inside a\n * coloured toast.\n */\n tone?: TypographyTone\n /** Cuts the text to one line and ends it with an ellipsis. */\n truncate?: boolean\n}\n\nconst props = withDefaults(defineProps<TypographyProps>(), {\n variant: 'body-md',\n as: undefined,\n tone: 'default',\n truncate: false,\n})\n\ndefineSlots<{\n /** Text content. */\n default(): unknown\n}>()\n\n/** The tag each variant renders when `as` says nothing; the one whose meaning matches the role. */\nconst DEFAULT_TAGS: Record<TypographyVariant, string> = {\n display: 'p',\n 'heading-1': 'h1',\n 'heading-2': 'h2',\n 'heading-3': 'h3',\n 'heading-4': 'h4',\n subtitle: 'p',\n 'body-xl': 'p',\n 'body-lg': 'p',\n 'body-md': 'p',\n 'body-sm': 'p',\n label: 'span',\n caption: 'p',\n overline: 'span',\n code: 'code',\n}\n\nconst tag = computed(() => props.as ?? DEFAULT_TAGS[props.variant])\n</script>\n\n<template>\n <component\n :is=\"tag\"\n class=\"v-typography\"\n :data-variant=\"variant\"\n :data-tone=\"tone === 'default' ? undefined : tone\"\n :data-truncate=\"truncate ? '' : undefined\"\n >\n <slot />\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n .v-typography {\n /*\n * These two defaults are re-declared on every instance because custom properties INHERIT:\n * nested inside a variant that sets letter spacing, or inside a `code`, a VTypography would\n * otherwise pick up its ancestor's tracking and monospaced family. The [data-variant]\n * blocks below, being more specific, still win.\n */\n --typography-family: var(--vectis-text-family);\n --typography-tracking: normal;\n\n margin: 0;\n font-family: var(--typography-family);\n font-size: var(--typography-size);\n font-weight: var(--typography-weight);\n line-height: var(--typography-leading);\n letter-spacing: var(--typography-tracking);\n /*\n * With the `default` tone no colour variable is set, and the fallback makes the text\n * inherit; which allows the component to be dropped inside an already coloured context, an\n * inverted surface or a tinted toast.\n */\n color: var(--typography-color, inherit);\n overflow-wrap: break-word;\n }\n\n /*\n * The colour variable inherits like any other, so without this reset a `default` VTypography\n * nested in a toned one took the OUTER tone's colour instead of the colour of whatever sits\n * between them. `initial` makes it guaranteed-invalid, which lets the fallback above apply.\n */\n :where(.v-typography) {\n --typography-color: initial;\n }\n\n .v-typography[data-variant='display'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-display-size);\n --typography-weight: var(--vectis-text-display-weight);\n --typography-leading: var(--vectis-text-display-leading);\n --typography-tracking: var(--vectis-text-display-tracking);\n }\n\n .v-typography[data-variant='heading-1'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-1-size);\n --typography-weight: var(--vectis-text-heading-1-weight);\n --typography-leading: var(--vectis-text-heading-1-leading);\n --typography-tracking: var(--vectis-text-heading-1-tracking);\n }\n\n .v-typography[data-variant='heading-2'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-2-size);\n --typography-weight: var(--vectis-text-heading-2-weight);\n --typography-leading: var(--vectis-text-heading-2-leading);\n --typography-tracking: var(--vectis-text-heading-2-tracking);\n }\n\n .v-typography[data-variant='heading-3'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-3-size);\n --typography-weight: var(--vectis-text-heading-3-weight);\n --typography-leading: var(--vectis-text-heading-3-leading);\n }\n\n .v-typography[data-variant='heading-4'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-4-size);\n --typography-weight: var(--vectis-text-heading-4-weight);\n --typography-leading: var(--vectis-text-heading-4-leading);\n }\n\n .v-typography[data-variant='subtitle'] {\n --typography-size: var(--vectis-text-subtitle-size);\n --typography-weight: var(--vectis-text-subtitle-weight);\n --typography-leading: var(--vectis-text-subtitle-leading);\n }\n\n .v-typography[data-variant='body-xl'] {\n --typography-size: var(--vectis-text-body-xl-size);\n --typography-weight: var(--vectis-text-body-xl-weight);\n --typography-leading: var(--vectis-text-body-xl-leading);\n }\n\n .v-typography[data-variant='body-lg'] {\n --typography-size: var(--vectis-text-body-lg-size);\n --typography-weight: var(--vectis-text-body-lg-weight);\n --typography-leading: var(--vectis-text-body-lg-leading);\n }\n\n .v-typography[data-variant='body-md'] {\n --typography-size: var(--vectis-text-body-md-size);\n --typography-weight: var(--vectis-text-body-md-weight);\n --typography-leading: var(--vectis-text-body-md-leading);\n }\n\n .v-typography[data-variant='body-sm'] {\n --typography-size: var(--vectis-text-body-sm-size);\n --typography-weight: var(--vectis-text-body-sm-weight);\n --typography-leading: var(--vectis-text-body-sm-leading);\n }\n\n .v-typography[data-variant='label'] {\n --typography-size: var(--vectis-text-label-size);\n --typography-weight: var(--vectis-text-label-weight);\n --typography-leading: var(--vectis-text-label-leading);\n }\n\n .v-typography[data-variant='caption'] {\n --typography-size: var(--vectis-text-caption-size);\n --typography-weight: var(--vectis-text-caption-weight);\n --typography-leading: var(--vectis-text-caption-leading);\n }\n\n .v-typography[data-variant='overline'] {\n --typography-size: var(--vectis-text-overline-size);\n --typography-weight: var(--vectis-text-overline-weight);\n --typography-leading: var(--vectis-text-overline-leading);\n --typography-tracking: var(--vectis-text-overline-tracking);\n\n text-transform: uppercase;\n }\n\n .v-typography[data-variant='code'] {\n --typography-family: var(--vectis-text-family-code);\n --typography-size: var(--vectis-text-code-size);\n --typography-weight: var(--vectis-text-code-weight);\n --typography-leading: var(--vectis-text-code-leading);\n }\n\n /* There is deliberately no block for the `default` tone: leaving the colour\n variable unset is precisely what makes the text inherit. */\n .v-typography[data-tone='muted'] {\n --typography-color: var(--vectis-color-text-muted);\n }\n\n .v-typography[data-tone='subtle'] {\n --typography-color: var(--vectis-color-text-subtle);\n }\n\n .v-typography[data-tone='accent'] {\n --typography-color: var(--vectis-color-accent-text);\n }\n\n .v-typography[data-tone='danger'] {\n --typography-color: var(--vectis-color-danger-text);\n }\n\n .v-typography[data-tone='success'] {\n --typography-color: var(--vectis-color-success-text);\n }\n\n .v-typography[data-tone='warning'] {\n --typography-color: var(--vectis-color-warning-text);\n }\n\n .v-typography[data-tone='on-inverse'] {\n --typography-color: var(--vectis-color-text-on-inverse);\n }\n\n .v-typography[data-truncate] {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n</style>\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VTypography.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VTypography/VTypography.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * The design system's text element. Rather than setting a size here and a weight\n * there, the writer names the ROLE the text plays — a heading, a caption, a label —\n * and `variant` applies the complete typographic recipe that goes with it: family,\n * size, weight, line height and letter spacing, all read from the semantic tokens.\n * `tone` does the same for colour.\n *\n * It renders a single element and adds no behaviour, so the attributes it is given\n * (id, for, aria-*…) land straight on that element. This is what lets it serve as a\n * real field label, with `as=\"label\"` and a `for`.\n */\nimport { computed } from 'vue'\n\n/** The role a text plays, each with its complete typographic recipe. */\nexport type TypographyVariant =\n | 'display'\n | 'heading-1'\n | 'heading-2'\n | 'heading-3'\n | 'heading-4'\n | 'subtitle'\n | 'body-xl'\n | 'body-lg'\n | 'body-md'\n | 'body-sm'\n | 'label'\n | 'caption'\n | 'overline'\n | 'code'\n\n/** The colour of a text; `default` sets none and inherits. */\nexport type TypographyTone =\n 'default' | 'muted' | 'subtle' | 'accent' | 'danger' | 'success' | 'warning' | 'on-inverse'\n\ninterface TypographyProps {\n /**\n * The role this text plays, which selects a complete recipe of typographic\n * tokens: size, weight, line height and, where the role calls for it, letter\n * spacing and a monospaced family.\n */\n variant?: TypographyVariant\n /**\n * The HTML tag to render. Each variant already has a sensible default (h1 to h4,\n * p, span, code), so this is for the cases where the meaning and the look differ:\n * a subtitle that is really an `h2`, or a label attached to a field.\n */\n as?: string\n /**\n * The colour of the text. `default` sets none at all, so the text inherits from\n * whatever surrounds it, which is what lets the same component be used on an\n * inverted surface or inside a coloured toast.\n */\n tone?: TypographyTone\n /**\n * Cuts the text to one line and ends it with an ellipsis. The element needs a\n * width to be cut against (being a block, or a flex item), otherwise there is\n * nothing to overflow and the text simply stays whole.\n */\n truncate?: boolean\n}\n\nconst props = withDefaults(defineProps<TypographyProps>(), {\n variant: 'body-md',\n as: undefined,\n tone: 'default',\n truncate: false,\n})\n\ndefineSlots<{\n /** Text content. */\n default(): unknown\n}>()\n\n/**\n * The tag each variant renders when `as` says nothing — the one whose meaning\n * matches the role.\n */\nconst DEFAULT_TAGS: Record<TypographyVariant, string> = {\n display: 'p',\n 'heading-1': 'h1',\n 'heading-2': 'h2',\n 'heading-3': 'h3',\n 'heading-4': 'h4',\n subtitle: 'p',\n 'body-xl': 'p',\n 'body-lg': 'p',\n 'body-md': 'p',\n 'body-sm': 'p',\n label: 'span',\n caption: 'p',\n overline: 'span',\n code: 'code',\n}\n\nconst tag = computed(() => props.as ?? DEFAULT_TAGS[props.variant])\n</script>\n\n<template>\n <component\n :is=\"tag\"\n class=\"v-typography\"\n :data-variant=\"variant\"\n :data-tone=\"tone === 'default' ? undefined : tone\"\n :data-truncate=\"truncate ? '' : undefined\"\n >\n <slot />\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n .v-typography {\n /* These two defaults are re-declared on every instance because custom properties\n INHERIT: nested inside a variant that sets letter spacing, or inside a `code`,\n a VTypography would otherwise pick up its ancestor's tracking and monospaced\n family. The [data-variant] blocks below, being more specific, still win.\n\n Size, weight and leading need no such reset, and that is an INVARIANT rather than\n an oversight: every variant block sets all three, so none can be inherited. A new\n variant that leaves one out takes it from whichever VTypography encloses it — the\n same bug these two lines exist to prevent, one step further out. */\n --typography-family: var(--vectis-text-family);\n --typography-tracking: normal;\n\n margin: 0; /* the space around a text block belongs to the layout holding it */\n font-family: var(--typography-family);\n font-size: var(--typography-size);\n font-weight: var(--typography-weight);\n line-height: var(--typography-leading);\n letter-spacing: var(--typography-tracking);\n /* With the `default` tone no colour variable is set, and the fallback makes the\n text inherit — which is what allows the component to be dropped inside an\n already coloured context, an inverted surface or a tinted toast. */\n color: var(--typography-color, inherit);\n overflow-wrap: break-word;\n }\n\n /* TRAP — the colour variable inherits like any other, so without this reset a `default`\n VTypography nested in a toned one took the OUTER tone's colour instead of the colour of\n whatever sits between them. `initial` makes it guaranteed-invalid, which is what lets\n the fallback above apply. At zero specificity, so the tone rules below and the sheets\n that route a colour through the variable (VDataTable's title, VFilePicker's subtitle)\n win over it whatever order the sheets land in. */\n :where(.v-typography) {\n --typography-color: initial;\n }\n\n .v-typography[data-variant='display'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-display-size);\n --typography-weight: var(--vectis-text-display-weight);\n --typography-leading: var(--vectis-text-display-leading);\n --typography-tracking: var(--vectis-text-display-tracking);\n }\n\n .v-typography[data-variant='heading-1'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-1-size);\n --typography-weight: var(--vectis-text-heading-1-weight);\n --typography-leading: var(--vectis-text-heading-1-leading);\n --typography-tracking: var(--vectis-text-heading-1-tracking);\n }\n\n .v-typography[data-variant='heading-2'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-2-size);\n --typography-weight: var(--vectis-text-heading-2-weight);\n --typography-leading: var(--vectis-text-heading-2-leading);\n --typography-tracking: var(--vectis-text-heading-2-tracking);\n }\n\n .v-typography[data-variant='heading-3'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-3-size);\n --typography-weight: var(--vectis-text-heading-3-weight);\n --typography-leading: var(--vectis-text-heading-3-leading);\n }\n\n .v-typography[data-variant='heading-4'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-4-size);\n --typography-weight: var(--vectis-text-heading-4-weight);\n --typography-leading: var(--vectis-text-heading-4-leading);\n }\n\n .v-typography[data-variant='subtitle'] {\n --typography-size: var(--vectis-text-subtitle-size);\n --typography-weight: var(--vectis-text-subtitle-weight);\n --typography-leading: var(--vectis-text-subtitle-leading);\n }\n\n .v-typography[data-variant='body-xl'] {\n --typography-size: var(--vectis-text-body-xl-size);\n --typography-weight: var(--vectis-text-body-xl-weight);\n --typography-leading: var(--vectis-text-body-xl-leading);\n }\n\n .v-typography[data-variant='body-lg'] {\n --typography-size: var(--vectis-text-body-lg-size);\n --typography-weight: var(--vectis-text-body-lg-weight);\n --typography-leading: var(--vectis-text-body-lg-leading);\n }\n\n .v-typography[data-variant='body-md'] {\n --typography-size: var(--vectis-text-body-md-size);\n --typography-weight: var(--vectis-text-body-md-weight);\n --typography-leading: var(--vectis-text-body-md-leading);\n }\n\n .v-typography[data-variant='body-sm'] {\n --typography-size: var(--vectis-text-body-sm-size);\n --typography-weight: var(--vectis-text-body-sm-weight);\n --typography-leading: var(--vectis-text-body-sm-leading);\n }\n\n .v-typography[data-variant='label'] {\n --typography-size: var(--vectis-text-label-size);\n --typography-weight: var(--vectis-text-label-weight);\n --typography-leading: var(--vectis-text-label-leading);\n }\n\n .v-typography[data-variant='caption'] {\n --typography-size: var(--vectis-text-caption-size);\n --typography-weight: var(--vectis-text-caption-weight);\n --typography-leading: var(--vectis-text-caption-leading);\n }\n\n .v-typography[data-variant='overline'] {\n --typography-size: var(--vectis-text-overline-size);\n --typography-weight: var(--vectis-text-overline-weight);\n --typography-leading: var(--vectis-text-overline-leading);\n --typography-tracking: var(--vectis-text-overline-tracking);\n\n text-transform: uppercase;\n }\n\n .v-typography[data-variant='code'] {\n --typography-family: var(--vectis-text-family-code);\n --typography-size: var(--vectis-text-code-size);\n --typography-weight: var(--vectis-text-code-weight);\n --typography-leading: var(--vectis-text-code-leading);\n }\n\n /* There is deliberately no block for the `default` tone: leaving the colour\n variable unset is precisely what makes the text inherit. */\n .v-typography[data-tone='muted'] {\n --typography-color: var(--vectis-color-text-muted);\n }\n\n .v-typography[data-tone='subtle'] {\n --typography-color: var(--vectis-color-text-subtle);\n }\n\n .v-typography[data-tone='accent'] {\n --typography-color: var(--vectis-color-accent-text);\n }\n\n .v-typography[data-tone='danger'] {\n --typography-color: var(--vectis-color-danger-text);\n }\n\n .v-typography[data-tone='success'] {\n --typography-color: var(--vectis-color-success-text);\n }\n\n .v-typography[data-tone='warning'] {\n --typography-color: var(--vectis-color-warning-text);\n }\n\n .v-typography[data-tone='on-inverse'] {\n --typography-color: var(--vectis-color-text-on-inverse);\n }\n\n .v-typography[data-truncate] {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;EA8DA,IAAM,IAAQ,GAgBR,IAAkD;GACtD,SAAS;GACT,aAAa;GACb,aAAa;GACb,aAAa;GACb,aAAa;GACb,UAAU;GACV,WAAW;GACX,WAAW;GACX,WAAW;GACX,WAAW;GACX,OAAO;GACP,SAAS;GACT,UAAU;GACV,MAAM;EACR,GAEM,IAAM,QAAe,EAAM,MAAM,EAAa,EAAM,QAAQ;yBAIhE,EAQY,EAPL,EAAA,KAAG,GAAA;GACR,OAAM;GACL,gBAAc,EAAA;GACd,aAAW,EAAA,SAAI,YAAiB,KAAA,IAAY,EAAA;GAC5C,iBAAe,EAAA,WAAQ,KAAQ,KAAA;;oBAExB,CAAR,EAAQ,EAAA,QAAA,SAAA,CAAA,CAAA"}
|
|
1
|
+
{"version":3,"file":"VTypography.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VTypography/VTypography.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Apply complete semantic typography recipes by role; choose native heading levels unless as\n * explicitly overrides the element.\n */\nimport { computed } from 'vue'\n\n/** The role a text plays, each with its complete typographic recipe. */\nexport type TypographyVariant =\n | 'display'\n | 'heading-1'\n | 'heading-2'\n | 'heading-3'\n | 'heading-4'\n | 'subtitle'\n | 'body-xl'\n | 'body-lg'\n | 'body-md'\n | 'body-sm'\n | 'label'\n | 'caption'\n | 'overline'\n | 'code'\n\n/** The colour of a text; `default` sets none and inherits. */\nexport type TypographyTone =\n 'default' | 'muted' | 'subtle' | 'accent' | 'danger' | 'success' | 'warning' | 'on-inverse'\n\ninterface TypographyProps {\n /**\n * The role this text plays, which selects a complete recipe of typographic\n * tokens: size, weight, line height and, where the role calls for it, letter\n * spacing and a monospaced family.\n */\n variant?: TypographyVariant\n /**\n * The HTML tag to render. Each variant already has a sensible default (h1 to h4,\n * p, span, code), so this is for the cases where the meaning and the look differ:\n * a subtitle that is really an `h2`, or a label attached to a field.\n */\n as?: string\n /**\n * The colour of the text. `default` sets none at all, so the text inherits from whatever\n * surrounds it, which lets the same component be used on an inverted surface or inside a\n * coloured toast.\n */\n tone?: TypographyTone\n /** Cuts the text to one line and ends it with an ellipsis. */\n truncate?: boolean\n}\n\nconst props = withDefaults(defineProps<TypographyProps>(), {\n variant: 'body-md',\n as: undefined,\n tone: 'default',\n truncate: false,\n})\n\ndefineSlots<{\n /** Text content. */\n default(): unknown\n}>()\n\n/** The tag each variant renders when `as` says nothing; the one whose meaning matches the role. */\nconst DEFAULT_TAGS: Record<TypographyVariant, string> = {\n display: 'p',\n 'heading-1': 'h1',\n 'heading-2': 'h2',\n 'heading-3': 'h3',\n 'heading-4': 'h4',\n subtitle: 'p',\n 'body-xl': 'p',\n 'body-lg': 'p',\n 'body-md': 'p',\n 'body-sm': 'p',\n label: 'span',\n caption: 'p',\n overline: 'span',\n code: 'code',\n}\n\nconst tag = computed(() => props.as ?? DEFAULT_TAGS[props.variant])\n</script>\n\n<template>\n <component\n :is=\"tag\"\n class=\"v-typography\"\n :data-variant=\"variant\"\n :data-tone=\"tone === 'default' ? undefined : tone\"\n :data-truncate=\"truncate ? '' : undefined\"\n >\n <slot />\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n .v-typography {\n /*\n * These two defaults are re-declared on every instance because custom properties INHERIT:\n * nested inside a variant that sets letter spacing, or inside a `code`, a VTypography would\n * otherwise pick up its ancestor's tracking and monospaced family. The [data-variant]\n * blocks below, being more specific, still win.\n */\n --typography-family: var(--vectis-text-family);\n --typography-tracking: normal;\n\n margin: 0;\n font-family: var(--typography-family);\n font-size: var(--typography-size);\n font-weight: var(--typography-weight);\n line-height: var(--typography-leading);\n letter-spacing: var(--typography-tracking);\n /*\n * With the `default` tone no colour variable is set, and the fallback makes the text\n * inherit; which allows the component to be dropped inside an already coloured context, an\n * inverted surface or a tinted toast.\n */\n color: var(--typography-color, inherit);\n overflow-wrap: break-word;\n }\n\n /*\n * The colour variable inherits like any other, so without this reset a `default` VTypography\n * nested in a toned one took the OUTER tone's colour instead of the colour of whatever sits\n * between them. `initial` makes it guaranteed-invalid, which lets the fallback above apply.\n */\n :where(.v-typography) {\n --typography-color: initial;\n }\n\n .v-typography[data-variant='display'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-display-size);\n --typography-weight: var(--vectis-text-display-weight);\n --typography-leading: var(--vectis-text-display-leading);\n --typography-tracking: var(--vectis-text-display-tracking);\n }\n\n .v-typography[data-variant='heading-1'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-1-size);\n --typography-weight: var(--vectis-text-heading-1-weight);\n --typography-leading: var(--vectis-text-heading-1-leading);\n --typography-tracking: var(--vectis-text-heading-1-tracking);\n }\n\n .v-typography[data-variant='heading-2'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-2-size);\n --typography-weight: var(--vectis-text-heading-2-weight);\n --typography-leading: var(--vectis-text-heading-2-leading);\n --typography-tracking: var(--vectis-text-heading-2-tracking);\n }\n\n .v-typography[data-variant='heading-3'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-3-size);\n --typography-weight: var(--vectis-text-heading-3-weight);\n --typography-leading: var(--vectis-text-heading-3-leading);\n }\n\n .v-typography[data-variant='heading-4'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-4-size);\n --typography-weight: var(--vectis-text-heading-4-weight);\n --typography-leading: var(--vectis-text-heading-4-leading);\n }\n\n .v-typography[data-variant='subtitle'] {\n --typography-size: var(--vectis-text-subtitle-size);\n --typography-weight: var(--vectis-text-subtitle-weight);\n --typography-leading: var(--vectis-text-subtitle-leading);\n }\n\n .v-typography[data-variant='body-xl'] {\n --typography-size: var(--vectis-text-body-xl-size);\n --typography-weight: var(--vectis-text-body-xl-weight);\n --typography-leading: var(--vectis-text-body-xl-leading);\n }\n\n .v-typography[data-variant='body-lg'] {\n --typography-size: var(--vectis-text-body-lg-size);\n --typography-weight: var(--vectis-text-body-lg-weight);\n --typography-leading: var(--vectis-text-body-lg-leading);\n }\n\n .v-typography[data-variant='body-md'] {\n --typography-size: var(--vectis-text-body-md-size);\n --typography-weight: var(--vectis-text-body-md-weight);\n --typography-leading: var(--vectis-text-body-md-leading);\n }\n\n .v-typography[data-variant='body-sm'] {\n --typography-size: var(--vectis-text-body-sm-size);\n --typography-weight: var(--vectis-text-body-sm-weight);\n --typography-leading: var(--vectis-text-body-sm-leading);\n }\n\n .v-typography[data-variant='label'] {\n --typography-size: var(--vectis-text-label-size);\n --typography-weight: var(--vectis-text-label-weight);\n --typography-leading: var(--vectis-text-label-leading);\n }\n\n .v-typography[data-variant='caption'] {\n --typography-size: var(--vectis-text-caption-size);\n --typography-weight: var(--vectis-text-caption-weight);\n --typography-leading: var(--vectis-text-caption-leading);\n }\n\n .v-typography[data-variant='overline'] {\n --typography-size: var(--vectis-text-overline-size);\n --typography-weight: var(--vectis-text-overline-weight);\n --typography-leading: var(--vectis-text-overline-leading);\n --typography-tracking: var(--vectis-text-overline-tracking);\n\n text-transform: uppercase;\n }\n\n .v-typography[data-variant='code'] {\n --typography-family: var(--vectis-text-family-code);\n --typography-size: var(--vectis-text-code-size);\n --typography-weight: var(--vectis-text-code-weight);\n --typography-leading: var(--vectis-text-code-leading);\n }\n\n /* There is deliberately no block for the `default` tone: leaving the colour\n variable unset is precisely what makes the text inherit. */\n .v-typography[data-tone='muted'] {\n --typography-color: var(--vectis-color-text-muted);\n }\n\n .v-typography[data-tone='subtle'] {\n --typography-color: var(--vectis-color-text-subtle);\n }\n\n .v-typography[data-tone='accent'] {\n --typography-color: var(--vectis-color-accent-text);\n }\n\n .v-typography[data-tone='danger'] {\n --typography-color: var(--vectis-color-danger-text);\n }\n\n .v-typography[data-tone='success'] {\n --typography-color: var(--vectis-color-success-text);\n }\n\n .v-typography[data-tone='warning'] {\n --typography-color: var(--vectis-color-warning-text);\n }\n\n .v-typography[data-tone='on-inverse'] {\n --typography-color: var(--vectis-color-text-on-inverse);\n }\n\n .v-typography[data-truncate] {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;EAmDA,IAAM,IAAQ,GAaR,IAAkD;GACtD,SAAS;GACT,aAAa;GACb,aAAa;GACb,aAAa;GACb,aAAa;GACb,UAAU;GACV,WAAW;GACX,WAAW;GACX,WAAW;GACX,WAAW;GACX,OAAO;GACP,SAAS;GACT,UAAU;GACV,MAAM;EACR,GAEM,IAAM,QAAe,EAAM,MAAM,EAAa,EAAM,QAAQ;yBAIhE,EAQY,EAPL,EAAA,KAAG,GAAA;GACR,OAAM;GACL,gBAAc,EAAA;GACd,aAAW,EAAA,SAAI,YAAiB,KAAA,IAAY,EAAA;GAC5C,iBAAe,EAAA,WAAQ,KAAQ,KAAA;;oBAExB,CAAR,EAAQ,EAAA,QAAA,SAAA,CAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAriaLabel.js","names":[],"sources":["../../src/composables/useAriaLabel.ts"],"sourcesContent":["// @a11y\n/**\n *
|
|
1
|
+
{"version":3,"file":"useAriaLabel.js","names":[],"sources":["../../src/composables/useAriaLabel.ts"],"sourcesContent":["// @a11y\n/**\n * Resolve accessible names in order: aria-labelledby, consumer aria-label, label fallback.\n * Suppress aria-label when aria-labelledby names the element.\n */\n\nimport { computed, useAttrs, type ComputedRef } from 'vue'\n\nexport function useAriaLabel(label: () => string | undefined): ComputedRef<string | undefined> {\n const attrs = useAttrs()\n return computed(() =>\n attrs['aria-labelledby'] != null\n ? undefined\n : ((attrs['aria-label'] as string | undefined) ?? label()),\n )\n}\n"],"mappings":";;AAQA,SAAgB,EAAa,GAAkE;CAC7F,IAAM,IAAQ,EAAS;CACvB,OAAO,QACL,EAAM,sBAAsB,OAEtB,EAAM,iBAAwC,EAAM,IADtD,KAAA,CAEN;AACF"}
|
|
@@ -5,7 +5,7 @@ interface ClearableOptions {
|
|
|
5
5
|
/**
|
|
6
6
|
* The consumer's EXPLICIT answer to "is there anything to clear?", which holds even on a
|
|
7
7
|
* read-only field: the value of a read-only date or time picker changes through its panel
|
|
8
|
-
* rather than by typing.
|
|
8
|
+
* rather than by typing.
|
|
9
9
|
*/
|
|
10
10
|
clearVisible: () => boolean | undefined;
|
|
11
11
|
/** Whether the field is unusable, the row it sits in taken into account. */
|
|
@@ -23,13 +23,7 @@ interface ClearableOptions {
|
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* Whether a field offers its cross: it asks for one, it can be used and changed, and it holds
|
|
26
|
-
* something.
|
|
27
|
-
* VFileInput, hold their value somewhere other than the text, so they work out `filled`
|
|
28
|
-
* themselves and hand VInput the result as `clearVisible`; the other terms are the same for
|
|
29
|
-
* every field and are written here once.
|
|
30
|
-
*
|
|
31
|
-
* `disabled` is passed apart from the props because it is the value RESOLVED against the row
|
|
32
|
-
* the field sits in, which the prop alone is not.
|
|
26
|
+
* something.
|
|
33
27
|
*/
|
|
34
28
|
export declare function canClear(props: {
|
|
35
29
|
clearable: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useClearable.js","names":[],"sources":["../../src/composables/useClearable.ts"],"sourcesContent":["/**\n *
|
|
1
|
+
{"version":3,"file":"useClearable.js","names":[],"sources":["../../src/composables/useClearable.ts"],"sourcesContent":["/**\n * Share clear-button visibility and activation. Restore field focus because clearing removes\n * the button currently holding it.\n */\nimport { computed } from 'vue'\nimport type { ComputedRef, Ref } from 'vue'\n\ninterface ClearableOptions {\n /** Whether the field offers a cross at all. */\n clearable: () => boolean\n /**\n * The consumer's EXPLICIT answer to \"is there anything to clear?\", which holds even on a\n * read-only field: the value of a read-only date or time picker changes through its panel\n * rather than by typing.\n */\n clearVisible: () => boolean | undefined\n /** Whether the field is unusable, the row it sits in taken into account. */\n disabled: () => boolean\n /** Whether the field refuses to be typed into. */\n readonly: () => boolean\n /** What the field currently holds, as text. */\n text: () => string\n /** The real control, which the focus goes back to. */\n controlEl: Ref<{ focus: () => void } | null>\n /** Empties the value and tells the consumer. */\n onCleared: () => void\n}\n\n/**\n * Whether a field offers its cross: it asks for one, it can be used and changed, and it holds\n * something.\n */\nexport function canClear(\n props: { clearable: boolean; readonly: boolean },\n disabled: boolean,\n filled: boolean,\n): boolean {\n return props.clearable && !disabled && !props.readonly && filled\n}\n\nexport function useClearable(options: ClearableOptions): {\n showClear: ComputedRef<boolean>\n onClear: () => void\n} {\n // An explicit `clearVisible` replaces the read-only and the content terms together: a\n // composed field has already asked both through `canClear`.\n const showClear = computed(() => {\n if (!options.clearable() || options.disabled()) return false\n return options.clearVisible() ?? (!options.readonly() && options.text().length > 0)\n })\n\n function onClear() {\n options.onCleared()\n options.controlEl.value?.focus()\n }\n\n return { showClear, onClear }\n}\n"],"mappings":";;AAgCA,SAAgB,EACd,GACA,GACA,GACS;CACT,OAAO,EAAM,aAAa,CAAC,KAAY,CAAC,EAAM,YAAY;AAC5D;AAEA,SAAgB,EAAa,GAG3B;CAGA,IAAM,IAAY,QACZ,CAAC,EAAQ,UAAU,KAAK,EAAQ,SAAS,IAAU,KAChD,EAAQ,aAAa,MAAM,CAAC,EAAQ,SAAS,KAAK,EAAQ,KAAK,CAAC,CAAC,SAAS,EAClF;CAED,SAAS,IAAU;EAEjB,AADA,EAAQ,UAAU,GAClB,EAAQ,UAAU,OAAO,MAAM;CACjC;CAEA,OAAO;EAAE;EAAW;CAAQ;AAC9B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useControlShape.js","names":[],"sources":["../../src/composables/useControlShape.ts"],"sourcesContent":["/**\n *
|
|
1
|
+
{"version":3,"file":"useControlShape.js","names":[],"sources":["../../src/composables/useControlShape.ts"],"sourcesContent":["/**\n * Row size and compact override control shape; disabled is cumulative so no child can\n * reactivate a disabled row.\n */\nimport { computed } from 'vue'\nimport type { ComputedRef } from 'vue'\n\n/** The three members every group context shares, whatever else it carries. */\ninterface ShapeContext<S> {\n readonly size?: S\n readonly compact?: boolean\n readonly disabled?: boolean\n}\n\ninterface ShapeProps<S> {\n readonly size: S\n readonly compact: boolean\n readonly disabled: boolean\n}\n\nexport interface ResolvedShape<S> {\n /** The height, the row's if it named one. */\n size: ComputedRef<S>\n /** The reduced density, the row's if it named one. */\n compact: ComputedRef<boolean>\n /** Unusable as soon as EITHER the row or the control says so. */\n disabled: ComputedRef<boolean>\n}\n\nexport function useControlShape<S>(\n props: ShapeProps<S>,\n group: ShapeContext<S> | null,\n): ResolvedShape<S> {\n return {\n size: computed(() => group?.size ?? props.size),\n compact: computed(() => group?.compact ?? props.compact),\n disabled: computed(() => group?.disabled || props.disabled),\n }\n}\n"],"mappings":";;AA6BA,SAAgB,EACd,GACA,GACkB;CAClB,OAAO;EACL,MAAM,QAAe,GAAO,QAAQ,EAAM,IAAI;EAC9C,SAAS,QAAe,GAAO,WAAW,EAAM,OAAO;EACvD,UAAU,QAAe,GAAO,YAAY,EAAM,QAAQ;CAC5D;AACF"}
|
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
import { Ref } from 'vue';
|
|
2
2
|
/**
|
|
3
3
|
* The open state of a native `<details>`, shared by the design system's two consumers of one:
|
|
4
|
-
* VAccordionItem and VSideNavigationItem.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* The element is the source of truth and the `v-model:open` is fed BY it. Left unbound, the
|
|
8
|
-
* model follows the element, so Vue never has anything to patch back and the native toggling
|
|
9
|
-
* stays sovereign.
|
|
10
|
-
*
|
|
11
|
-
* `defaultOpen` is read ONCE, here, and never again: it is an initial state, so a prop changed
|
|
12
|
-
* later must not close a section the reader has opened. Read inside the computed instead, a
|
|
13
|
-
* parent re-rendering with another value would silently fold it.
|
|
4
|
+
* VAccordionItem and VSideNavigationItem. Read inside the computed instead, a parent
|
|
5
|
+
* re-rendering with another value would silently fold it.
|
|
14
6
|
*/
|
|
15
7
|
export declare function useDetailsOpen(open: Ref<boolean | null>, options: {
|
|
16
8
|
defaultOpen: () => boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDetailsOpen.js","names":[],"sources":["../../src/composables/useDetailsOpen.ts"],"sourcesContent":["import { computed, nextTick } from 'vue'\nimport type { Ref } from 'vue'\n\n/**\n * The open state of a native `<details>`, shared by the design system's two consumers of one:\n * VAccordionItem and VSideNavigationItem.
|
|
1
|
+
{"version":3,"file":"useDetailsOpen.js","names":[],"sources":["../../src/composables/useDetailsOpen.ts"],"sourcesContent":["import { computed, nextTick } from 'vue'\nimport type { Ref } from 'vue'\n\n/**\n * The open state of a native `<details>`, shared by the design system's two consumers of one:\n * VAccordionItem and VSideNavigationItem. Read inside the computed instead, a parent\n * re-rendering with another value would silently fold it.\n */\nexport function useDetailsOpen(\n open: Ref<boolean | null>,\n options: { defaultOpen: () => boolean; disabled: () => boolean },\n) {\n const initial = options.defaultOpen()\n const openAttr = computed(() => (open.value ?? initial) || undefined)\n\n // @core\n // The toggle event does not bubble, which is convenient: listening on the element itself\n // cannot pick up a nested `<details>` opening inside it.\n function onToggle(event: Event) {\n const element = event.target as HTMLDetailsElement\n const value = element.open\n if (open.value === value) return\n open.value = value\n /*\n * A controlled model may refuse the change (`:open=\"false\"` and no handler, or one that\n * says no). Nothing then moves in the props, so Vue has nothing to patch, and the element\n * would stay open under a model that says closed.\n */\n void nextTick(() => {\n if (open.value !== null && element.open !== open.value) element.open = open.value\n })\n }\n\n // @a11y @core\n /*\n * A `<summary>` has no `disabled` attribute, so cancelling the click is the only way to stop\n * a disabled section from folding. The keyboard needs nothing: the component takes the\n * summary out of the tab order.\n */\n function onSummaryClick(event: MouseEvent) {\n if (options.disabled()) event.preventDefault()\n }\n\n return { openAttr, onToggle, onSummaryClick }\n}\n"],"mappings":";;AAQA,SAAgB,EACd,GACA,GACA;CACA,IAAM,IAAU,EAAQ,YAAY,GAC9B,IAAW,SAAgB,EAAK,SAAS,MAAY,KAAA,CAAS;CAKpE,SAAS,EAAS,GAAc;EAC9B,IAAM,IAAU,EAAM,QAChB,IAAQ,EAAQ;EAClB,EAAK,UAAU,MACnB,EAAK,QAAQ,GAMb,QAAoB;GAClB,AAAI,EAAK,UAAU,QAAQ,EAAQ,SAAS,EAAK,UAAO,EAAQ,OAAO,EAAK;EAC9E,CAAC;CACH;CAQA,SAAS,EAAe,GAAmB;EACzC,AAAI,EAAQ,SAAS,KAAG,EAAM,eAAe;CAC/C;CAEA,OAAO;EAAE;EAAU;EAAU;CAAe;AAC9C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFieldIds.js","names":[],"sources":["../../src/composables/useFieldIds.ts"],"sourcesContent":["// @a11y @ssr\n/**\n *
|
|
1
|
+
{"version":3,"file":"useFieldIds.js","names":[],"sources":["../../src/composables/useFieldIds.ts"],"sourcesContent":["// @a11y @ssr\n/**\n * Use hydration-stable IDs and append hint/counter references to consumer aria-describedby\n * rather than replacing it.\n */\n\nimport { computed, useId, type ComputedRef } from 'vue'\n\nimport { joinIds } from '../utils/ids'\n\nexport function useFieldIds(\n attrs: Record<string, unknown>,\n hasHint: () => boolean,\n hasCounter: () => boolean = () => false,\n): {\n fieldId: ComputedRef<string>\n hintId: string\n counterId: string\n describedBy: ComputedRef<string | undefined>\n} {\n const uid = useId()\n const hintId = useId()\n const counterId = useId()\n\n return {\n fieldId: computed(() => (attrs.id as string | undefined) ?? uid),\n hintId,\n counterId,\n describedBy: computed(() =>\n joinIds(\n attrs['aria-describedby'] as string | undefined,\n hasHint() && hintId,\n hasCounter() && counterId,\n ),\n ),\n }\n}\n"],"mappings":";;;AAUA,SAAgB,EACd,GACA,GACA,UAAkC,IAMlC;CACA,IAAM,IAAM,EAAM,GACZ,IAAS,EAAM,GACf,IAAY,EAAM;CAExB,OAAO;EACL,SAAS,QAAgB,EAAM,MAA6B,CAAG;EAC/D;EACA;EACA,aAAa,QACX,EACE,EAAM,qBACN,EAAQ,KAAK,GACb,EAAW,KAAK,CAClB,CACF;CACF;AACF"}
|
|
@@ -26,26 +26,13 @@ export interface UseFieldPanelOptions {
|
|
|
26
26
|
onOpen?: () => void;
|
|
27
27
|
/** What it needs to do as the panel closes: clear an announcement, for instance. */
|
|
28
28
|
onClose?: () => void;
|
|
29
|
-
/**
|
|
30
|
-
* Whether focusing the field opens the panel. It does not by default.
|
|
31
|
-
*
|
|
32
|
-
* A field one TYPES into says yes, and its panel then opens WITHOUT taking the caret, so
|
|
33
|
-
* typing carries on and the down arrow remains the one explicit way in. A click on such a
|
|
34
|
-
* field leaves the focus where it is for the same reason.
|
|
35
|
-
*/
|
|
29
|
+
/** Whether focusing the field opens the panel. It does not by default. */
|
|
36
30
|
openOnFocus?: () => boolean;
|
|
37
31
|
}
|
|
38
32
|
/**
|
|
39
|
-
* The
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* return. This is exactly that minimum; what is particular to either component arrives
|
|
43
|
-
* through `onOpen`/`onClose`.
|
|
44
|
-
*
|
|
45
|
-
* TRAP: the panel is WRITTEN imperatively and READ back by model. Bind `open` as
|
|
46
|
-
* `v-model:open` so the DOM feeds it, but the write must stay synchronous: the `rAF` that
|
|
47
|
-
* moves focus assumes the panel is already open when it is armed, and the model would put
|
|
48
|
-
* a tick in between.
|
|
33
|
+
* The panel is written imperatively and read back by model. Bind `open` as `v-model:open` so
|
|
34
|
+
* the DOM feeds it, but the write must stay synchronous: the `rAF` that moves focus assumes the
|
|
35
|
+
* panel is already open when it is armed, and the model would put a tick in between.
|
|
49
36
|
*/
|
|
50
37
|
export declare function useFieldPanel(options: UseFieldPanelOptions): {
|
|
51
38
|
open: Ref<boolean, boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFieldPanel.js","names":[],"sources":["../../src/composables/useFieldPanel.ts"],"sourcesContent":["import { onBeforeUnmount, provide, ref, useId, watch } from 'vue'\nimport type { Ref } from 'vue'\n\nimport { NO_BUTTON_GROUP, buttonGroupKey } from '../components/VButton/context'\nimport { useFocusoutDismiss } from './useFocusoutDismiss'\n\n/** The least this needs of the field: something it can put the focus back on. */\ninterface FocusableField {\n focus: () => void\n}\n\n/** The least this needs of the panel: something it can open and close. */\ninterface PanelControl {\n show: () => void\n close: () => void\n}\n\nexport interface UseFieldPanelOptions {\n /** The component's root, against which \"the focus has left\" is judged. */\n rootEl: Ref<HTMLElement | null>\n /** The panel to open and close. */\n panelRef: Ref<PanelControl | null>\n /** The field the focus returns to. */\n field: Ref<FocusableField | null>\n /**\n * Whether opening is refused. It is the SINGLE cut-off point: every route in passes\n * through it, so a component never has to repeat the condition per handler.\n */\n disabled: () => boolean\n /** Moves the focus INTO the open panel: where in it is the component's business. */\n focusInPanel: () => void\n /** What the component needs to do as the panel opens: prepare a draft, reset a step. */\n onOpen?: () => void\n /** What it needs to do as the panel closes: clear an announcement, for instance. */\n onClose?: () => void\n /**\n * Whether focusing the field opens the panel. It does not by default.\n *\n * A field one TYPES into says yes, and its panel then opens WITHOUT taking the caret, so\n * typing carries on and the down arrow remains the one explicit way in. A click on such a\n * field leaves the focus where it is for the same reason.\n */\n openOnFocus?: () => boolean\n}\n\n// @a11y @keyboard @core\n/**\n * The \"field + `VPopover` in `mode=\"manual\"`\" shell of VDateInput and VTimeInput.\n *\n * A `manual` popover does nothing on its own: no light dismiss, no focus move, no focus\n * return. This is exactly that minimum; what is particular to either component arrives\n * through `onOpen`/`onClose`.\n *\n * TRAP: the panel is WRITTEN imperatively and READ back by model. Bind `open` as\n * `v-model:open` so the DOM feeds it, but the write must stay synchronous: the `rAF` that\n * moves focus assumes the panel is already open when it is armed, and the model would put\n * a tick in between.\n */\nexport function useFieldPanel(options: UseFieldPanelOptions) {\n const open = ref(false)\n const openOnFocus = () => options.openOnFocus?.() ?? false\n\n /** The id the panel carries, for the field's `aria-controls`. */\n const panelId = useId()\n\n // @core\n // TRAP — this stops a VButtonGroup's or a VInputGroup's row context at the field's\n // boundary. The picker in the panel sizes its own buttons — VDatePicker writes `size=\"sm\"`\n // on its navigation, VTimePicker `size=\"lg\"` on its hour and minute cells — and a group\n // WINS over a button's prop: without this line a VInputGroup would resize them to its own\n // height, which only shows once the panel is open and nothing in the sheet or the template\n // would say why. It is the JS counterpart of the `.v-overlay` guard every VButtonGroup\n // selector carries — a floating panel is not a segment of the row that opened it.\n provide(buttonGroupKey, NO_BUTTON_GROUP)\n\n /*\n * The frame the focus is moved in, kept so it can be called off. A panel closed inside that\n * frame (Escape at once, the field disabled, a consumer closing it) would otherwise have the\n * focus pulled into it once shut, taken from the field it had just been handed back to.\n */\n let focusFrame = 0\n\n function cancelFocusFrame() {\n if (focusFrame) cancelAnimationFrame(focusFrame)\n focusFrame = 0\n }\n\n function openPanel(moveFocus = !openOnFocus()) {\n if (options.disabled() || open.value) return\n options.onOpen?.()\n options.panelRef.value?.show()\n // @a11y\n // A `manual` popover moves focus nowhere, so it is moved by hand — a frame later,\n // the panel not being painted yet, and nothing invisible can take focus.\n if (moveFocus) {\n cancelFocusFrame()\n focusFrame = requestAnimationFrame(() => {\n focusFrame = 0\n options.focusInPanel()\n })\n }\n }\n\n /** Closes the panel and leaves the focus where it is: the focus has already gone elsewhere. */\n function closePanel() {\n cancelFocusFrame()\n if (!open.value) return\n options.panelRef.value?.close()\n options.onClose?.()\n }\n\n onBeforeUnmount(cancelFocusFrame)\n\n // @core\n // TRAP — a panel refused WHILE it is open (the field turned read-only or disabled, the\n // picker switched off) is unmounted by its `v-if`, and an unmounted popover sends no\n // `toggle` to bring `open` back down. The model then stays true, and the next time the\n // panel is mounted VPopover's mount replay shows it again with nobody having asked.\n watch(options.disabled, (refused) => {\n if (!refused || !open.value) return\n closePanel()\n open.value = false\n })\n\n // @a11y\n /*\n * TRAP — handing the focus back to the field is what makes a field that opens ON FOCUS\n * reopen the panel that was just closed: the two would chase each other and the panel\n * would never close. The lock covers the focus call, which is synchronous.\n *\n * EVERY focus this shell or a component gives the field must go through `focusField`,\n * `closeAndFocus` included. Focusing the field directly brings the loop straight back.\n */\n let refocusing = false\n\n /**\n * Puts the focus on the field without opening the panel.\n *\n * A component emptying its value calls it BEFORE the field focuses itself, and that order\n * is the point: focusing an element that already has the focus emits no event at all, so\n * the field's own call that follows is inert and the panel stays shut.\n */\n function focusField() {\n refocusing = true\n options.field.value?.focus()\n refocusing = false\n }\n\n /** Closes the panel and hands the focus back to the field, under the lock above. */\n function closeAndFocus() {\n if (!open.value) return\n closePanel()\n focusField()\n }\n\n /** The field's focus handler: opens the panel on a field that asks for it. */\n function onFieldFocus() {\n if (refocusing || !openOnFocus()) return\n openPanel(false)\n }\n\n /**\n * The icon at the end of the field. Clicking it is an explicit request for the panel, and\n * the focus has already left the field for the button — so carrying it into the panel is\n * right whatever `openOnFocus` says.\n */\n function toggleFromIcon() {\n if (open.value) closeAndFocus()\n else openPanel(true)\n }\n\n /**\n * Whether an event started on one of the controls the field holds: the clear cross, the\n * AM/PM word, a clickable icon, or a button of the consumer's own in a slot. What happens\n * there is that control's business, for the pointer and the keyboard alike.\n */\n const fromOwnControl = (event: Event) =>\n !!(event.target as HTMLElement | null)?.closest?.('button, a[href]')\n\n function onControlClick(event: MouseEvent) {\n // Reacting here too would open the panel the clear cross has just given a reason to close.\n if (fromOwnControl(event)) return\n openPanel()\n }\n\n // @a11y\n const onFocusout = useFocusoutDismiss(options.rootEl, closePanel)\n\n // @a11y\n /*\n * TRAP — clicking a non-focusable pixel of the panel (padding, the gutter between cells)\n * hands focus back to `<body>`. `onFocusout` above then fires with a null `relatedTarget`,\n * reads it as an exit — rightly — and closes a panel the reader has just clicked.\n *\n * The filter is essential: VCombobox's unconditional `preventDefault` is safe there\n * because focus never leaves its field, but here it would rob the days and the navigation\n * arrows of focus and desynchronize VDatePicker's roving.\n *\n * Invisible in jsdom, where clicking moves no focus — the `ClicDansLeVide` play functions\n * of both components cover it.\n */\n function onPanelMousedown(event: MouseEvent) {\n const target = event.target as HTMLElement | null\n if (!target?.closest('button, a, input, select, textarea, [tabindex]')) event.preventDefault()\n }\n\n // @keyboard @a11y\n function onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape') {\n if (open.value) {\n event.preventDefault()\n closeAndFocus()\n }\n return\n }\n // TRAP — a key already consumed INSIDE the panel is ignored. An Enter that selected a\n // day has just CLOSED the panel, and without the guard would reopen it as it bubbles.\n // TRAP — the same goes for a key typed on one of the field's own buttons. Its Enter is\n // what becomes the click that clears the field or flips the half of the day: cancelled\n // here to open the panel, the button would never be pressed from the keyboard at all.\n if (\n (event.key === 'ArrowDown' || event.key === 'Enter') &&\n !open.value &&\n !event.defaultPrevented &&\n !fromOwnControl(event)\n ) {\n event.preventDefault()\n // Opened from the KEYBOARD the panel always takes focus, whatever `openOnFocus` says:\n // otherwise ArrowDown opens a panel the keyboard cannot reach.\n openPanel(true)\n }\n }\n\n return {\n open,\n panelId,\n openPanel,\n closePanel,\n closeAndFocus,\n focusField,\n onFieldFocus,\n toggleFromIcon,\n onControlClick,\n onFocusout,\n onKeydown,\n onPanelMousedown,\n }\n}\n"],"mappings":";;;;AA0DA,SAAgB,EAAc,GAA+B;CAC3D,IAAM,IAAO,EAAI,EAAK,GAChB,UAAoB,EAAQ,cAAc,KAAK,IAG/C,IAAU,EAAM;CAUtB,EAAQ,GAAgB,CAAe;CAOvC,IAAI,IAAa;CAEjB,SAAS,IAAmB;EAE1B,AADI,KAAY,qBAAqB,CAAU,GAC/C,IAAa;CACf;CAEA,SAAS,EAAU,IAAY,CAAC,EAAY,GAAG;EACzC,EAAQ,SAAS,KAAK,EAAK,UAC/B,EAAQ,SAAS,GACjB,EAAQ,SAAS,OAAO,KAAK,GAIzB,MACF,EAAiB,GACjB,IAAa,4BAA4B;GAEvC,AADA,IAAa,GACb,EAAQ,aAAa;EACvB,CAAC;CAEL;CAGA,SAAS,IAAa;EACpB,EAAiB,GACZ,EAAK,UACV,EAAQ,SAAS,OAAO,MAAM,GAC9B,EAAQ,UAAU;CACpB;CASA,AAPA,EAAgB,CAAgB,GAOhC,EAAM,EAAQ,WAAW,MAAY;EAC/B,CAAC,KAAW,CAAC,EAAK,UACtB,EAAW,GACX,EAAK,QAAQ;CACf,CAAC;CAWD,IAAI,IAAa;CASjB,SAAS,IAAa;EAGpB,AAFA,IAAa,IACb,EAAQ,MAAM,OAAO,MAAM,GAC3B,IAAa;CACf;CAGA,SAAS,IAAgB;EAClB,EAAK,UACV,EAAW,GACX,EAAW;CACb;CAGA,SAAS,IAAe;EAClB,KAAc,CAAC,EAAY,KAC/B,EAAU,EAAK;CACjB;CAOA,SAAS,IAAiB;EACxB,AAAI,EAAK,QAAO,EAAc,IACzB,EAAU,EAAI;CACrB;CAOA,IAAM,KAAkB,MACtB,CAAC,CAAE,EAAM,QAA+B,UAAU,iBAAiB;CAErE,SAAS,EAAe,GAAmB;EAErC,EAAe,CAAK,KACxB,EAAU;CACZ;CAGA,IAAM,IAAa,EAAmB,EAAQ,QAAQ,CAAU;CAehE,SAAS,EAAiB,GAAmB;EAE3C,AADe,EAAM,QACR,QAAQ,gDAAgD,KAAG,EAAM,eAAe;CAC/F;CAGA,SAAS,EAAU,GAAsB;EACvC,IAAI,EAAM,QAAQ,UAAU;GAC1B,AAAI,EAAK,UACP,EAAM,eAAe,GACrB,EAAc;GAEhB;EACF;EAMA,CACG,EAAM,QAAQ,eAAe,EAAM,QAAQ,YAC5C,CAAC,EAAK,SACN,CAAC,EAAM,oBACP,CAAC,EAAe,CAAK,MAErB,EAAM,eAAe,GAGrB,EAAU,EAAI;CAElB;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"useFieldPanel.js","names":[],"sources":["../../src/composables/useFieldPanel.ts"],"sourcesContent":["import { onBeforeUnmount, provide, ref, useId, watch } from 'vue'\nimport type { Ref } from 'vue'\n\nimport { NO_BUTTON_GROUP, buttonGroupKey } from '../components/VButton/context'\nimport { useFocusoutDismiss } from './useFocusoutDismiss'\n\n/** The least this needs of the field: something it can put the focus back on. */\ninterface FocusableField {\n focus: () => void\n}\n\n/** The least this needs of the panel: something it can open and close. */\ninterface PanelControl {\n show: () => void\n close: () => void\n}\n\nexport interface UseFieldPanelOptions {\n /** The component's root, against which \"the focus has left\" is judged. */\n rootEl: Ref<HTMLElement | null>\n /** The panel to open and close. */\n panelRef: Ref<PanelControl | null>\n /** The field the focus returns to. */\n field: Ref<FocusableField | null>\n /**\n * Whether opening is refused. It is the SINGLE cut-off point: every route in passes\n * through it, so a component never has to repeat the condition per handler.\n */\n disabled: () => boolean\n /** Moves the focus INTO the open panel: where in it is the component's business. */\n focusInPanel: () => void\n /** What the component needs to do as the panel opens: prepare a draft, reset a step. */\n onOpen?: () => void\n /** What it needs to do as the panel closes: clear an announcement, for instance. */\n onClose?: () => void\n /** Whether focusing the field opens the panel. It does not by default. */\n openOnFocus?: () => boolean\n}\n\n// @a11y @keyboard @core\n/**\n * The panel is written imperatively and read back by model. Bind `open` as `v-model:open` so\n * the DOM feeds it, but the write must stay synchronous: the `rAF` that moves focus assumes the\n * panel is already open when it is armed, and the model would put a tick in between.\n */\nexport function useFieldPanel(options: UseFieldPanelOptions) {\n const open = ref(false)\n const openOnFocus = () => options.openOnFocus?.() ?? false\n\n /** The id the panel carries, for the field's `aria-controls`. */\n const panelId = useId()\n\n // @core\n // Stop row contexts at the field boundary so the picker keeps its own button sizes instead of\n // inheriting the enclosing input group's shape.\n provide(buttonGroupKey, NO_BUTTON_GROUP)\n\n /*\n * The frame the focus is moved in, kept so it can be called off. A panel closed inside that\n * frame (Escape at once, the field disabled, a consumer closing it) would otherwise have the\n * focus pulled into it once shut, taken from the field it had just been handed back to.\n */\n let focusFrame = 0\n\n function cancelFocusFrame() {\n if (focusFrame) cancelAnimationFrame(focusFrame)\n focusFrame = 0\n }\n\n function openPanel(moveFocus = !openOnFocus()) {\n if (options.disabled() || open.value) return\n options.onOpen?.()\n options.panelRef.value?.show()\n // @a11y\n // A `manual` popover moves focus nowhere, so it is moved by hand; a frame later, the panel\n // not being painted yet, and nothing invisible can take focus.\n if (moveFocus) {\n cancelFocusFrame()\n focusFrame = requestAnimationFrame(() => {\n focusFrame = 0\n options.focusInPanel()\n })\n }\n }\n\n /** Closes the panel and leaves the focus where it is: the focus has already gone elsewhere. */\n function closePanel() {\n cancelFocusFrame()\n if (!open.value) return\n options.panelRef.value?.close()\n options.onClose?.()\n }\n\n onBeforeUnmount(cancelFocusFrame)\n\n // @core\n // A panel refused WHILE it is open (the field turned read-only or disabled, the picker\n // switched off) is unmounted by its `v-if`, and an unmounted popover sends no `toggle` to\n // bring `open` back down. The model then stays true, and the next time the panel is mounted\n // VPopover's mount replay shows it again with nobody having asked.\n watch(options.disabled, (refused) => {\n if (!refused || !open.value) return\n closePanel()\n open.value = false\n })\n\n // @a11y\n /*\n * Handing the focus back to the field is what makes a field that opens on FOCUS reopen the\n * panel that was just closed: the two would chase each other and the panel would never close.\n * The lock covers the focus call, which is synchronous.\n */\n let refocusing = false\n\n /** Puts the focus on the field without opening the panel. */\n function focusField() {\n refocusing = true\n options.field.value?.focus()\n refocusing = false\n }\n\n /** Closes the panel and hands the focus back to the field, under the lock above. */\n function closeAndFocus() {\n if (!open.value) return\n closePanel()\n focusField()\n }\n\n /** The field's focus handler: opens the panel on a field that asks for it. */\n function onFieldFocus() {\n if (refocusing || !openOnFocus()) return\n openPanel(false)\n }\n\n /** The icon at the end of the field. */\n function toggleFromIcon() {\n if (open.value) closeAndFocus()\n else openPanel(true)\n }\n\n /**\n * Whether an event started on one of the controls the field holds: the clear cross, the AM/PM\n * word, a clickable icon, or a button of the consumer's own in a slot.\n */\n const fromOwnControl = (event: Event) =>\n !!(event.target as HTMLElement | null)?.closest?.('button, a[href]')\n\n function onControlClick(event: MouseEvent) {\n if (fromOwnControl(event)) return\n openPanel()\n }\n\n // @a11y\n const onFocusout = useFocusoutDismiss(options.rootEl, closePanel)\n\n // @a11y\n /*\n * Clicking a non-focusable pixel of the panel (padding, the gutter between cells) hands focus\n * back to `<body>`. `onFocusout` above then fires with a null `relatedTarget`, reads it as an\n * exit; rightly; and closes a panel the reader has just clicked.\n */\n function onPanelMousedown(event: MouseEvent) {\n const target = event.target as HTMLElement | null\n if (!target?.closest('button, a, input, select, textarea, [tabindex]')) event.preventDefault()\n }\n\n // @keyboard @a11y\n function onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape') {\n if (open.value) {\n event.preventDefault()\n closeAndFocus()\n }\n return\n }\n // A key already consumed INSIDE the panel is ignored. An Enter that selected a day has just\n // CLOSED the panel, and without the guard would reopen it as it bubbles.\n if (\n (event.key === 'ArrowDown' || event.key === 'Enter') &&\n !open.value &&\n !event.defaultPrevented &&\n !fromOwnControl(event)\n ) {\n event.preventDefault()\n // Opened from the KEYBOARD the panel always takes focus, whatever `openOnFocus` says:\n // otherwise ArrowDown opens a panel the keyboard cannot reach.\n openPanel(true)\n }\n }\n\n return {\n open,\n panelId,\n openPanel,\n closePanel,\n closeAndFocus,\n focusField,\n onFieldFocus,\n toggleFromIcon,\n onControlClick,\n onFocusout,\n onKeydown,\n onPanelMousedown,\n }\n}\n"],"mappings":";;;;AA6CA,SAAgB,EAAc,GAA+B;CAC3D,IAAM,IAAO,EAAI,EAAK,GAChB,UAAoB,EAAQ,cAAc,KAAK,IAG/C,IAAU,EAAM;CAKtB,EAAQ,GAAgB,CAAe;CAOvC,IAAI,IAAa;CAEjB,SAAS,IAAmB;EAE1B,AADI,KAAY,qBAAqB,CAAU,GAC/C,IAAa;CACf;CAEA,SAAS,EAAU,IAAY,CAAC,EAAY,GAAG;EACzC,EAAQ,SAAS,KAAK,EAAK,UAC/B,EAAQ,SAAS,GACjB,EAAQ,SAAS,OAAO,KAAK,GAIzB,MACF,EAAiB,GACjB,IAAa,4BAA4B;GAEvC,AADA,IAAa,GACb,EAAQ,aAAa;EACvB,CAAC;CAEL;CAGA,SAAS,IAAa;EACpB,EAAiB,GACZ,EAAK,UACV,EAAQ,SAAS,OAAO,MAAM,GAC9B,EAAQ,UAAU;CACpB;CASA,AAPA,EAAgB,CAAgB,GAOhC,EAAM,EAAQ,WAAW,MAAY;EAC/B,CAAC,KAAW,CAAC,EAAK,UACtB,EAAW,GACX,EAAK,QAAQ;CACf,CAAC;CAQD,IAAI,IAAa;CAGjB,SAAS,IAAa;EAGpB,AAFA,IAAa,IACb,EAAQ,MAAM,OAAO,MAAM,GAC3B,IAAa;CACf;CAGA,SAAS,IAAgB;EAClB,EAAK,UACV,EAAW,GACX,EAAW;CACb;CAGA,SAAS,IAAe;EAClB,KAAc,CAAC,EAAY,KAC/B,EAAU,EAAK;CACjB;CAGA,SAAS,IAAiB;EACxB,AAAI,EAAK,QAAO,EAAc,IACzB,EAAU,EAAI;CACrB;CAMA,IAAM,KAAkB,MACtB,CAAC,CAAE,EAAM,QAA+B,UAAU,iBAAiB;CAErE,SAAS,EAAe,GAAmB;EACrC,EAAe,CAAK,KACxB,EAAU;CACZ;CAGA,IAAM,IAAa,EAAmB,EAAQ,QAAQ,CAAU;CAQhE,SAAS,EAAiB,GAAmB;EAE3C,AADe,EAAM,QACR,QAAQ,gDAAgD,KAAG,EAAM,eAAe;CAC/F;CAGA,SAAS,EAAU,GAAsB;EACvC,IAAI,EAAM,QAAQ,UAAU;GAC1B,AAAI,EAAK,UACP,EAAM,eAAe,GACrB,EAAc;GAEhB;EACF;EAGA,CACG,EAAM,QAAQ,eAAe,EAAM,QAAQ,YAC5C,CAAC,EAAK,SACN,CAAC,EAAM,oBACP,CAAC,EAAe,CAAK,MAErB,EAAM,eAAe,GAGrB,EAAU,EAAI;CAElB;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFileDrop.js","names":[],"sources":["../../src/composables/useFileDrop.ts"],"sourcesContent":["// @core\n/**\n *
|
|
1
|
+
{"version":3,"file":"useFileDrop.js","names":[],"sources":["../../src/composables/useFileDrop.ts"],"sourcesContent":["// @core\n/**\n * Cancel dragover to permit drops and avoid browser navigation. Track nested drag depth so\n * entering a child does not clear the active drop state.\n */\n\nimport { computed, ref, type ComputedRef } from 'vue'\n\nexport function useFileDrop(\n enabled: () => boolean,\n onFiles: (files: File[]) => void,\n): {\n dragging: ComputedRef<boolean>\n onDragEnter: (event: DragEvent) => void\n onDragOver: (event: DragEvent) => void\n onDragLeave: () => void\n onDrop: (event: DragEvent) => void\n} {\n const depth = ref(0)\n\n function onDragEnter(event: DragEvent) {\n if (!enabled()) return\n event.preventDefault()\n depth.value += 1\n }\n\n function onDragOver(event: DragEvent) {\n if (!enabled()) return\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'copy'\n }\n\n // The depth comes back down whether or not files are accepted: a component disabled mid-drag\n // that ignored the leave would keep counting one enter too many, and show the highlight\n // again, for good, the day it was switched back on.\n function onDragLeave() {\n depth.value = Math.max(0, depth.value - 1)\n }\n\n function onDrop(event: DragEvent) {\n depth.value = 0\n if (!enabled()) return\n event.preventDefault()\n onFiles([...(event.dataTransfer?.files ?? [])])\n }\n\n return {\n dragging: computed(() => depth.value > 0 && enabled()),\n onDragEnter,\n onDragOver,\n onDragLeave,\n onDrop,\n }\n}\n"],"mappings":";;AAQA,SAAgB,EACd,GACA,GAOA;CACA,IAAM,IAAQ,EAAI,CAAC;CAEnB,SAAS,EAAY,GAAkB;EAChC,EAAQ,MACb,EAAM,eAAe,GACrB,EAAM,SAAS;CACjB;CAEA,SAAS,EAAW,GAAkB;EAC/B,EAAQ,MACb,EAAM,eAAe,GACjB,EAAM,iBAAc,EAAM,aAAa,aAAa;CAC1D;CAKA,SAAS,IAAc;EACrB,EAAM,QAAQ,KAAK,IAAI,GAAG,EAAM,QAAQ,CAAC;CAC3C;CAEA,SAAS,EAAO,GAAkB;EAChC,EAAM,QAAQ,GACT,EAAQ,MACb,EAAM,eAAe,GACrB,EAAQ,CAAC,GAAI,EAAM,cAAc,SAAS,CAAC,CAAE,CAAC;CAChD;CAEA,OAAO;EACL,UAAU,QAAe,EAAM,QAAQ,KAAK,EAAQ,CAAC;EACrD;EACA;EACA;EACA;CACF;AACF"}
|
|
@@ -28,11 +28,7 @@ export interface FileFieldOptions {
|
|
|
28
28
|
* `disabled` prop answers alone.
|
|
29
29
|
*/
|
|
30
30
|
disabled?: Ref<boolean>;
|
|
31
|
-
/**
|
|
32
|
-
* Attributes to withhold from the visible control too. VFileInput withholds
|
|
33
|
-
* `aria-describedby`, which it re-aggregates itself: a binding written after the spread
|
|
34
|
-
* would replace the consumer's rather than merge with it.
|
|
35
|
-
*/
|
|
31
|
+
/** Attributes to withhold from the visible control too. */
|
|
36
32
|
excludeFromControl?: readonly string[];
|
|
37
33
|
/** Called once the selection has been emptied by `clear`, before `change` is emitted. */
|
|
38
34
|
onClear?: () => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFileField.js","names":[],"sources":["../../src/composables/useFileField.ts"],"sourcesContent":["// @core\n/**\n * The hidden `<input type=\"file\">` gate of VFileInput and VFilePicker.\n *\n * The JS here is imposed by the platform, not chosen: a dialog opens ONLY from a real click\n * on a file input, and a `FileList` cannot be written from a template. So what the reader\n * clicks can never BE the file input, and the file input can only ever be a SOURCE of files.\n *\n * Hence `.click()` and not `showPicker()` — both need a transient user activation, but\n * `showPicker()` THROWS without one where `.click()` is merely inert — and hence\n * `syncNative()` on every path, which writes the selection back into the input so the form\n * submits it, and so re-picking a file that left the selection is a change again.\n *\n * Everything the two components did identically lives here, so the two cannot drift: the\n * attributes of the hidden input, the screening of what comes in, the removal of one file and\n * the emptying of all of them, and the development warnings both of them owe the integrator.\n */\nimport {\n computed,\n getCurrentInstance,\n ref,\n watch,\n watchEffect,\n type ComputedRef,\n type Ref,\n} from 'vue'\n\nimport { isDev } from '../utils/env'\nimport { screenFiles, type FileRejection } from '../utils/file'\n\n// @a11y\n/**\n * The attributes belonging on the hidden file input rather than on the visible control.\n *\n * The wrapper-root pattern assumes ONE functional element and there are two here, so the\n * attrs split in three: `class`/`style` on the root, these four on the input because they\n * are what the FORM reads, everything else on the element the READER focuses. The buckets\n * must not overlap — a duplicated `id` breaks a consumer's `<label for>`, a duplicated\n * `name` gets the field announced twice.\n */\nconst NATIVE_ONLY = ['name', 'required', 'form', 'capture']\n\n/** The props both components declare under the same names, read at the moment they matter. */\nexport interface FileFieldProps {\n multiple: boolean\n accept?: string\n maxSize?: number\n maxFiles?: number\n maxTotalSize?: number\n disabled: boolean\n readonly: boolean\n}\n\n/** The events both components declare under the same names and with the same payloads. */\nexport interface FileFieldEmit {\n (event: 'change', files: File[]): void\n (event: 'reject', rejection: FileRejection): void\n (event: 'remove', file: File, index: number): void\n}\n\nexport interface FileFieldOptions {\n /** The component's value: always a list of files, whether one or several are allowed. */\n model: Ref<File[]>\n props: FileFieldProps\n emit: FileFieldEmit\n /** The attributes the consumer wrote, minus styling, still to be split in two. */\n forwardedAttrs: ComputedRef<Record<string, unknown>>\n /**\n * Whether the component is disabled, the row it sits in taken into account. Left out, the\n * `disabled` prop answers alone.\n */\n disabled?: Ref<boolean>\n /**\n * Attributes to withhold from the visible control too. VFileInput withholds\n * `aria-describedby`, which it re-aggregates itself: a binding written after the spread\n * would replace the consumer's rather than merge with it.\n */\n excludeFromControl?: readonly string[]\n /** Called once the selection has been emptied by `clear`, before `change` is emitted. */\n onClear?: () => void\n /**\n * The component's own development warnings, one message per condition that holds. They\n * follow the same rule as the shared ones: each is given once per instance.\n */\n warnings?: () => (string | false)[]\n}\n\nexport function useFileField(options: FileFieldOptions) {\n const { model, props, emit } = options\n\n const fileEl = ref<HTMLInputElement | null>(null)\n\n const disabled = () => options.disabled?.value ?? props.disabled\n\n /** Whether files may come in or go out — no dialog, no drop and no removal otherwise. */\n const enabled = computed(() => !disabled() && !props.readonly)\n\n const controlAttrs = computed(() => {\n const excluded = options.excludeFromControl ?? []\n return Object.fromEntries(\n Object.entries(options.forwardedAttrs.value).filter(\n ([key]) => !NATIVE_ONLY.includes(key) && !excluded.includes(key),\n ),\n )\n })\n\n // @core\n /**\n * Writes the selection back into the hidden input, which is what the FORM reads: `name`,\n * `form` and `required` land there, and an input left holding the last batch the dialog\n * produced (or emptied) would submit that rather than the selection, or nothing at all.\n * A `FileList` cannot be built, but a `DataTransfer` hands one out.\n *\n * TRAP — it also keeps every file that LEFT the selection pickable again. The input holds\n * exactly the selection, so choosing a file that was cleared, removed or refused differs\n * from what it holds and fires `change`; left holding it, the dialog would answer the\n * same file with no event at all.\n */\n function syncNative() {\n const el = fileEl.value\n if (!el) return\n // @fallback\n // An environment without `DataTransfer` (jsdom) can only empty the input,\n // which keeps a file pickable again and leaves the form without it.\n if (typeof DataTransfer === 'undefined') {\n el.value = ''\n return\n }\n const transfer = new DataTransfer()\n for (const file of model.value) transfer.items.add(file)\n el.files = transfer.files\n }\n\n // A value set from outside, a consumer emptying the list after an upload, reaches the\n // input the same way.\n watch(model, syncNative, { flush: 'post' })\n\n /**\n * The single entry into the model: the dialog and a drop both arrive here.\n *\n * A refused file never enters the model at all, and every refusal is reported so the\n * component can say why. The screening is a pure function in `utils/file`, which is what\n * makes the ORDER of the reasons testable without a mount.\n */\n function acceptFiles(incoming: File[]) {\n const current = props.multiple ? model.value : []\n const { accepted, rejected } = screenFiles(incoming, current, {\n accept: props.accept,\n maxSize: props.maxSize,\n // A single-file field is a list capped at one, so extra files are refused for the\n // same reason as anywhere else: too many.\n maxFiles: props.multiple ? props.maxFiles : 1,\n maxTotalSize: props.maxTotalSize,\n })\n\n for (const rejection of rejected) emit('reject', rejection)\n\n // The dialog has just written ITS batch into the input, whatever happens to it here.\n syncNative()\n if (accepted.length === 0) return\n\n // A single file replaced by another has left the selection as surely as one taken out\n // by its cross, and `remove` is how an upload already under way for it is cancelled.\n const replaced = props.multiple ? undefined : model.value[0]\n model.value = [...current, ...accepted]\n if (replaced && replaced !== model.value[0]) emit('remove', replaced, 0)\n emit('change', model.value)\n }\n\n function onNativeChange(event: Event) {\n acceptFiles([...((event.target as HTMLInputElement).files ?? [])])\n }\n\n /**\n * Everything the hidden input is bound with, the consumer's form attributes included. It\n * is taken out of the tab order and hidden from screen readers, which leaves the visible\n * control as the single stop and the single announcement; it stays disabled only when the\n * component is, since a read-only one still submits what it holds (`syncNative`).\n */\n const nativeInputAttrs = computed(() => ({\n ...Object.fromEntries(\n Object.entries(options.forwardedAttrs.value).filter(([key]) => NATIVE_ONLY.includes(key)),\n ),\n type: 'file',\n tabindex: -1,\n 'aria-hidden': 'true' as const,\n accept: props.accept,\n multiple: props.multiple || undefined,\n disabled: disabled() || undefined,\n onChange: onNativeChange,\n }))\n\n // @fallback\n /**\n * `.click()` and not `showPicker()`. Both need a transient user activation, but\n * `showPicker()` THROWS without one — and inside a cross-origin iframe — where a click is\n * simply inert. On a file input there is nothing to gain in exchange.\n */\n function openPicker() {\n if (!enabled.value) return\n fileEl.value?.click()\n }\n\n /**\n * Takes ONE file out of the selection, and says which: `remove` with the file and the\n * position it held, then `change` with what is left. It returns the file removed, or\n * `undefined` when nothing was — so the component knows whether it has a focus to catch.\n */\n function removeAt(index: number): File | undefined {\n const file = model.value[index]\n if (!file || !enabled.value) return undefined\n\n model.value = model.value.filter((_, i) => i !== index)\n syncNative()\n emit('remove', file, index)\n emit('change', model.value)\n return file\n }\n\n /**\n * Empties the selection, and the hidden input with it. Refused where `removeAt` is, and\n * silent when there is nothing to empty: no `change` for a selection that did not change.\n */\n function clear() {\n if (!enabled.value || model.value.length === 0) return\n model.value = []\n syncNative()\n options.onClear?.()\n emit('change', model.value)\n }\n\n // @devwarn\n /*\n * Both shared guards describe something that fails SILENTLY at runtime. Each message is given\n * once per instance: they sit in an effect, which re-runs on every change of what it reads,\n * and the same sentence repeated at every keystroke is how a warning stops being read.\n */\n if (isDev) {\n // The prefix is the component's own name, read off the instance rather than passed in:\n // this whole block is dropped from a production build, and the name with it.\n const name = getCurrentInstance()?.type.__name\n const warned = new Set<string>()\n const warn = (message: string) => {\n if (warned.has(message)) return\n warned.add(message)\n console.warn(`[${name}] ${message}`)\n }\n watchEffect(() => {\n if (props.maxFiles !== undefined && !props.multiple)\n warn('`maxFiles` ignored without `multiple`: single mode already caps at one file.')\n if (options.forwardedAttrs.value.required !== undefined)\n warn(\n '`required` lands on the hidden file input: the browser does block an empty submission, but its message points at an input nobody can see. Validate the v-model yourself and use the `invalid` prop.',\n )\n for (const message of options.warnings?.() ?? []) if (message) warn(message)\n })\n }\n\n return {\n fileEl,\n enabled,\n nativeInputAttrs,\n controlAttrs,\n acceptFiles,\n openPicker,\n removeAt,\n clear,\n }\n}\n"],"mappings":";;;;AAwCA,IAAM,IAAc;CAAC;CAAQ;CAAY;CAAQ;AAAS;AA+C1D,SAAgB,EAAa,GAA2B;CACtD,IAAM,EAAE,UAAO,UAAO,YAAS,GAEzB,IAAS,EAA6B,IAAI,GAE1C,UAAiB,EAAQ,UAAU,SAAS,EAAM,UAGlD,IAAU,QAAe,CAAC,EAAS,KAAK,CAAC,EAAM,QAAQ,GAEvD,IAAe,QAAe;EAClC,IAAM,IAAW,EAAQ,sBAAsB,CAAC;EAChD,OAAO,OAAO,YACZ,OAAO,QAAQ,EAAQ,eAAe,KAAK,CAAC,CAAC,QAC1C,CAAC,OAAS,CAAC,EAAY,SAAS,CAAG,KAAK,CAAC,EAAS,SAAS,CAAG,CACjE,CACF;CACF,CAAC;CAcD,SAAS,IAAa;EACpB,IAAM,IAAK,EAAO;EAClB,IAAI,CAAC,GAAI;EAIT,IAAI,OAAO,eAAiB,KAAa;GACvC,EAAG,QAAQ;GACX;EACF;EACA,IAAM,IAAW,IAAI,aAAa;EAClC,KAAK,IAAM,KAAQ,EAAM,OAAO,EAAS,MAAM,IAAI,CAAI;EACvD,EAAG,QAAQ,EAAS;CACtB;CAIA,EAAM,GAAO,GAAY,EAAE,OAAO,OAAO,CAAC;CAS1C,SAAS,EAAY,GAAkB;EACrC,IAAM,IAAU,EAAM,WAAW,EAAM,QAAQ,CAAC,GAC1C,EAAE,aAAU,gBAAa,EAAY,GAAU,GAAS;GAC5D,QAAQ,EAAM;GACd,SAAS,EAAM;GAGf,UAAU,EAAM,WAAW,EAAM,WAAW;GAC5C,cAAc,EAAM;EACtB,CAAC;EAED,KAAK,IAAM,KAAa,GAAU,EAAK,UAAU,CAAS;EAI1D,IADA,EAAW,GACP,EAAS,WAAW,GAAG;EAI3B,IAAM,IAAW,EAAM,WAAW,KAAA,IAAY,EAAM,MAAM;EAG1D,AAFA,EAAM,QAAQ,CAAC,GAAG,GAAS,GAAG,CAAQ,GAClC,KAAY,MAAa,EAAM,MAAM,MAAI,EAAK,UAAU,GAAU,CAAC,GACvE,EAAK,UAAU,EAAM,KAAK;CAC5B;CAEA,SAAS,EAAe,GAAc;EACpC,EAAY,CAAC,GAAK,EAAM,OAA4B,SAAS,CAAC,CAAE,CAAC;CACnE;CAQA,IAAM,IAAmB,SAAgB;EACvC,GAAG,OAAO,YACR,OAAO,QAAQ,EAAQ,eAAe,KAAK,CAAC,CAAC,QAAQ,CAAC,OAAS,EAAY,SAAS,CAAG,CAAC,CAC1F;EACA,MAAM;EACN,UAAU;EACV,eAAe;EACf,QAAQ,EAAM;EACd,UAAU,EAAM,YAAY,KAAA;EAC5B,UAAU,EAAS,KAAK,KAAA;EACxB,UAAU;CACZ,EAAE;CAQF,SAAS,IAAa;EACf,EAAQ,SACb,EAAO,OAAO,MAAM;CACtB;CAOA,SAAS,EAAS,GAAiC;EACjD,IAAM,IAAO,EAAM,MAAM;EACrB,OAAC,KAAQ,CAAC,EAAQ,QAMtB,OAJA,EAAM,QAAQ,EAAM,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAK,GACtD,EAAW,GACX,EAAK,UAAU,GAAM,CAAK,GAC1B,EAAK,UAAU,EAAM,KAAK,GACnB;CACT;CAMA,SAAS,IAAQ;EACX,CAAC,EAAQ,SAAS,EAAM,MAAM,WAAW,MAC7C,EAAM,QAAQ,CAAC,GACf,EAAW,GACX,EAAQ,UAAU,GAClB,EAAK,UAAU,EAAM,KAAK;CAC5B;CAQA,IAAI,GAAO;EAGT,IAAM,IAAO,EAAmB,CAAC,EAAE,KAAK,QAClC,oBAAS,IAAI,IAAY,GACzB,KAAQ,MAAoB;GAC5B,EAAO,IAAI,CAAO,MACtB,EAAO,IAAI,CAAO,GAClB,QAAQ,KAAK,IAAI,EAAK,IAAI,GAAS;EACrC;EACA,QAAkB;GAGhB,AAFI,EAAM,aAAa,KAAA,KAAa,CAAC,EAAM,YACzC,EAAK,8EAA8E,GACjF,EAAQ,eAAe,MAAM,aAAa,KAAA,KAC5C,EACE,qMACF;GACF,KAAK,IAAM,KAAW,EAAQ,WAAW,KAAK,CAAC,GAAG,AAAI,KAAS,EAAK,CAAO;EAC7E,CAAC;CACH;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"useFileField.js","names":[],"sources":["../../src/composables/useFileField.ts"],"sourcesContent":["// @core\n/**\n * Bridge native file selection, screening and form state for both file components. Synchronize\n * the accepted File[] through DataTransfer where supported.\n */\nimport {\n computed,\n getCurrentInstance,\n ref,\n watch,\n watchEffect,\n type ComputedRef,\n type Ref,\n} from 'vue'\n\nimport { isDev } from '../utils/env'\nimport { screenFiles, type FileRejection } from '../utils/file'\n\n// @a11y\n/** The attributes belonging on the hidden file input rather than on the visible control. */\nconst NATIVE_ONLY = ['name', 'required', 'form', 'capture']\n\n/** The props both components declare under the same names, read at the moment they matter. */\nexport interface FileFieldProps {\n multiple: boolean\n accept?: string\n maxSize?: number\n maxFiles?: number\n maxTotalSize?: number\n disabled: boolean\n readonly: boolean\n}\n\n/** The events both components declare under the same names and with the same payloads. */\nexport interface FileFieldEmit {\n (event: 'change', files: File[]): void\n (event: 'reject', rejection: FileRejection): void\n (event: 'remove', file: File, index: number): void\n}\n\nexport interface FileFieldOptions {\n /** The component's value: always a list of files, whether one or several are allowed. */\n model: Ref<File[]>\n props: FileFieldProps\n emit: FileFieldEmit\n /** The attributes the consumer wrote, minus styling, still to be split in two. */\n forwardedAttrs: ComputedRef<Record<string, unknown>>\n /**\n * Whether the component is disabled, the row it sits in taken into account. Left out, the\n * `disabled` prop answers alone.\n */\n disabled?: Ref<boolean>\n /** Attributes to withhold from the visible control too. */\n excludeFromControl?: readonly string[]\n /** Called once the selection has been emptied by `clear`, before `change` is emitted. */\n onClear?: () => void\n /**\n * The component's own development warnings, one message per condition that holds. They\n * follow the same rule as the shared ones: each is given once per instance.\n */\n warnings?: () => (string | false)[]\n}\n\nexport function useFileField(options: FileFieldOptions) {\n const { model, props, emit } = options\n\n const fileEl = ref<HTMLInputElement | null>(null)\n\n const disabled = () => options.disabled?.value ?? props.disabled\n\n /** Whether files may come in or go out; no dialog, no drop and no removal otherwise. */\n const enabled = computed(() => !disabled() && !props.readonly)\n\n const controlAttrs = computed(() => {\n const excluded = options.excludeFromControl ?? []\n return Object.fromEntries(\n Object.entries(options.forwardedAttrs.value).filter(\n ([key]) => !NATIVE_ONLY.includes(key) && !excluded.includes(key),\n ),\n )\n })\n\n // @core\n /**\n * It also keeps every file that LEFT the selection pickable again. The input holds exactly\n * the selection, so choosing a file that was cleared, removed or refused differs from what it\n * holds and fires `change`; left holding it, the dialog would answer the same file with no\n * event at all.\n */\n function syncNative() {\n const el = fileEl.value\n if (!el) return\n // @fallback\n // An environment without `DataTransfer` (jsdom) can only empty the input,\n // which keeps a file pickable again and leaves the form without it.\n if (typeof DataTransfer === 'undefined') {\n el.value = ''\n return\n }\n const transfer = new DataTransfer()\n for (const file of model.value) transfer.items.add(file)\n el.files = transfer.files\n }\n\n // A value set from outside, a consumer emptying the list after an upload, reaches the\n // input the same way.\n watch(model, syncNative, { flush: 'post' })\n\n /** The single entry into the model: the dialog and a drop both arrive here. */\n function acceptFiles(incoming: File[]) {\n const current = props.multiple ? model.value : []\n const { accepted, rejected } = screenFiles(incoming, current, {\n accept: props.accept,\n maxSize: props.maxSize,\n // A single-file field is a list capped at one, so extra files are refused for the\n // same reason as anywhere else: too many.\n maxFiles: props.multiple ? props.maxFiles : 1,\n maxTotalSize: props.maxTotalSize,\n })\n\n for (const rejection of rejected) emit('reject', rejection)\n\n syncNative()\n if (accepted.length === 0) return\n\n // A single file replaced by another has left the selection as surely as one taken out\n // by its cross, and `remove` is how an upload already under way for it is cancelled.\n const replaced = props.multiple ? undefined : model.value[0]\n model.value = [...current, ...accepted]\n if (replaced && replaced !== model.value[0]) emit('remove', replaced, 0)\n emit('change', model.value)\n }\n\n function onNativeChange(event: Event) {\n acceptFiles([...((event.target as HTMLInputElement).files ?? [])])\n }\n\n /**\n * Everything the hidden input is bound with, the consumer's form attributes included. It\n * is taken out of the tab order and hidden from screen readers, which leaves the visible\n * control as the single stop and the single announcement; it stays disabled only when the\n * component is, since a read-only one still submits what it holds (`syncNative`).\n */\n const nativeInputAttrs = computed(() => ({\n ...Object.fromEntries(\n Object.entries(options.forwardedAttrs.value).filter(([key]) => NATIVE_ONLY.includes(key)),\n ),\n type: 'file',\n tabindex: -1,\n 'aria-hidden': 'true' as const,\n accept: props.accept,\n multiple: props.multiple || undefined,\n disabled: disabled() || undefined,\n onChange: onNativeChange,\n }))\n\n // @fallback\n /** `.click()` and not `showPicker()`. */\n function openPicker() {\n if (!enabled.value) return\n fileEl.value?.click()\n }\n\n /**\n * Takes ONE file out of the selection, and says which: `remove` with the file and the\n * position it held, then `change` with what is left. It returns the file removed, or\n * `undefined` when nothing was; so the component knows whether it has a focus to catch.\n */\n function removeAt(index: number): File | undefined {\n const file = model.value[index]\n if (!file || !enabled.value) return undefined\n\n model.value = model.value.filter((_, i) => i !== index)\n syncNative()\n emit('remove', file, index)\n emit('change', model.value)\n return file\n }\n\n /**\n * Empties the selection, and the hidden input with it. Refused where `removeAt` is, and\n * silent when there is nothing to empty: no `change` for a selection that did not change.\n */\n function clear() {\n if (!enabled.value || model.value.length === 0) return\n model.value = []\n syncNative()\n options.onClear?.()\n emit('change', model.value)\n }\n\n // @devwarn\n /*\n * Both shared guards describe something that fails SILENTLY at runtime. Each message is given\n * once per instance: they sit in an effect, which re-runs on every change of what it reads,\n * and the same sentence repeated at every keystroke is how a warning stops being read.\n */\n if (isDev) {\n // The prefix is the component's own name, read off the instance rather than passed in:\n // this whole block is dropped from a production build, and the name with it.\n const name = getCurrentInstance()?.type.__name\n const warned = new Set<string>()\n const warn = (message: string) => {\n if (warned.has(message)) return\n warned.add(message)\n console.warn(`[${name}] ${message}`)\n }\n watchEffect(() => {\n if (props.maxFiles !== undefined && !props.multiple)\n warn('`maxFiles` ignored without `multiple`: single mode already caps at one file.')\n if (options.forwardedAttrs.value.required !== undefined)\n warn(\n '`required` lands on the hidden file input: the browser does block an empty submission, but its message points at an input nobody can see. Validate the v-model yourself and use the `invalid` prop.',\n )\n for (const message of options.warnings?.() ?? []) if (message) warn(message)\n })\n }\n\n return {\n fileEl,\n enabled,\n nativeInputAttrs,\n controlAttrs,\n acceptFiles,\n openPicker,\n removeAt,\n clear,\n }\n}\n"],"mappings":";;;;AAoBA,IAAM,IAAc;CAAC;CAAQ;CAAY;CAAQ;AAAS;AA2C1D,SAAgB,EAAa,GAA2B;CACtD,IAAM,EAAE,UAAO,UAAO,YAAS,GAEzB,IAAS,EAA6B,IAAI,GAE1C,UAAiB,EAAQ,UAAU,SAAS,EAAM,UAGlD,IAAU,QAAe,CAAC,EAAS,KAAK,CAAC,EAAM,QAAQ,GAEvD,IAAe,QAAe;EAClC,IAAM,IAAW,EAAQ,sBAAsB,CAAC;EAChD,OAAO,OAAO,YACZ,OAAO,QAAQ,EAAQ,eAAe,KAAK,CAAC,CAAC,QAC1C,CAAC,OAAS,CAAC,EAAY,SAAS,CAAG,KAAK,CAAC,EAAS,SAAS,CAAG,CACjE,CACF;CACF,CAAC;CASD,SAAS,IAAa;EACpB,IAAM,IAAK,EAAO;EAClB,IAAI,CAAC,GAAI;EAIT,IAAI,OAAO,eAAiB,KAAa;GACvC,EAAG,QAAQ;GACX;EACF;EACA,IAAM,IAAW,IAAI,aAAa;EAClC,KAAK,IAAM,KAAQ,EAAM,OAAO,EAAS,MAAM,IAAI,CAAI;EACvD,EAAG,QAAQ,EAAS;CACtB;CAIA,EAAM,GAAO,GAAY,EAAE,OAAO,OAAO,CAAC;CAG1C,SAAS,EAAY,GAAkB;EACrC,IAAM,IAAU,EAAM,WAAW,EAAM,QAAQ,CAAC,GAC1C,EAAE,aAAU,gBAAa,EAAY,GAAU,GAAS;GAC5D,QAAQ,EAAM;GACd,SAAS,EAAM;GAGf,UAAU,EAAM,WAAW,EAAM,WAAW;GAC5C,cAAc,EAAM;EACtB,CAAC;EAED,KAAK,IAAM,KAAa,GAAU,EAAK,UAAU,CAAS;EAG1D,IADA,EAAW,GACP,EAAS,WAAW,GAAG;EAI3B,IAAM,IAAW,EAAM,WAAW,KAAA,IAAY,EAAM,MAAM;EAG1D,AAFA,EAAM,QAAQ,CAAC,GAAG,GAAS,GAAG,CAAQ,GAClC,KAAY,MAAa,EAAM,MAAM,MAAI,EAAK,UAAU,GAAU,CAAC,GACvE,EAAK,UAAU,EAAM,KAAK;CAC5B;CAEA,SAAS,EAAe,GAAc;EACpC,EAAY,CAAC,GAAK,EAAM,OAA4B,SAAS,CAAC,CAAE,CAAC;CACnE;CAQA,IAAM,IAAmB,SAAgB;EACvC,GAAG,OAAO,YACR,OAAO,QAAQ,EAAQ,eAAe,KAAK,CAAC,CAAC,QAAQ,CAAC,OAAS,EAAY,SAAS,CAAG,CAAC,CAC1F;EACA,MAAM;EACN,UAAU;EACV,eAAe;EACf,QAAQ,EAAM;EACd,UAAU,EAAM,YAAY,KAAA;EAC5B,UAAU,EAAS,KAAK,KAAA;EACxB,UAAU;CACZ,EAAE;CAIF,SAAS,IAAa;EACf,EAAQ,SACb,EAAO,OAAO,MAAM;CACtB;CAOA,SAAS,EAAS,GAAiC;EACjD,IAAM,IAAO,EAAM,MAAM;EACrB,OAAC,KAAQ,CAAC,EAAQ,QAMtB,OAJA,EAAM,QAAQ,EAAM,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAK,GACtD,EAAW,GACX,EAAK,UAAU,GAAM,CAAK,GAC1B,EAAK,UAAU,EAAM,KAAK,GACnB;CACT;CAMA,SAAS,IAAQ;EACX,CAAC,EAAQ,SAAS,EAAM,MAAM,WAAW,MAC7C,EAAM,QAAQ,CAAC,GACf,EAAW,GACX,EAAQ,UAAU,GAClB,EAAK,UAAU,EAAM,KAAK;CAC5B;CAQA,IAAI,GAAO;EAGT,IAAM,IAAO,EAAmB,CAAC,EAAE,KAAK,QAClC,oBAAS,IAAI,IAAY,GACzB,KAAQ,MAAoB;GAC5B,EAAO,IAAI,CAAO,MACtB,EAAO,IAAI,CAAO,GAClB,QAAQ,KAAK,IAAI,EAAK,IAAI,GAAS;EACrC;EACA,QAAkB;GAGhB,AAFI,EAAM,aAAa,KAAA,KAAa,CAAC,EAAM,YACzC,EAAK,8EAA8E,GACjF,EAAQ,eAAe,MAAM,aAAa,KAAA,KAC5C,EACE,qMACF;GACF,KAAK,IAAM,KAAW,EAAQ,WAAW,KAAK,CAAC,GAAG,AAAI,KAAS,EAAK,CAAO;EAC7E,CAAC;CACH;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFocusoutDismiss.js","names":[],"sources":["../../src/composables/useFocusoutDismiss.ts"],"sourcesContent":["// @a11y\n/**\n *
|
|
1
|
+
{"version":3,"file":"useFocusoutDismiss.js","names":[],"sources":["../../src/composables/useFocusoutDismiss.ts"],"sourcesContent":["// @a11y\n/**\n * Close when focus leaves the owner and panel. Top-layer popovers remain DOM descendants, so\n * contains covers both without document listeners.\n */\n\nimport type { Ref } from 'vue'\n\nexport function useFocusoutDismiss(\n root: Ref<HTMLElement | null>,\n close: () => void,\n): (event: FocusEvent) => void {\n return (event: FocusEvent) => {\n const next = event.relatedTarget as Node | null\n if (!next || !root.value?.contains(next)) close()\n }\n}\n"],"mappings":";AAQA,SAAgB,EACd,GACA,GAC6B;CAC7B,QAAQ,MAAsB;EAC5B,IAAM,IAAO,EAAM;EACnB,CAAI,CAAC,KAAQ,CAAC,EAAK,OAAO,SAAS,CAAI,MAAG,EAAM;CAClD;AACF"}
|
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Declared emits are removed from `$attrs`, so the only place left to look is
|
|
6
|
-
* `vnode.props` — in BOTH spellings, a template writing them with dashes and a render
|
|
7
|
-
* function in camel case. Read ONCE and never revised: a listener attached later is not
|
|
8
|
-
* picked up, which is marginal enough to accept.
|
|
9
|
-
*
|
|
10
|
-
* The missing-name warning lives here rather than in each field: a button nothing can
|
|
11
|
-
* announce is a flaw of the arrangement, not of the component using it.
|
|
2
|
+
* Detect declared icon-click listeners from vnode props because emits removes them from attrs.
|
|
3
|
+
* Require accessible names before turning decorative icons into buttons.
|
|
12
4
|
*/
|
|
13
5
|
/**
|
|
14
6
|
* Whether the component being set up was handed a `@click:icon-start` / `@click:icon-end`
|
|
@@ -19,15 +11,8 @@ export declare function iconClickHandlers(): {
|
|
|
19
11
|
end: boolean;
|
|
20
12
|
};
|
|
21
13
|
/**
|
|
22
|
-
* The start-icon listener a field COMPOSED on top of VInput: VCombobox, VDateInput,
|
|
23
|
-
*
|
|
24
|
-
* consumer's attributes. Call it during setup: it reads the component's own vnode.
|
|
25
|
-
*
|
|
26
|
-
* Each of those fields declares `click:icon-start` as its own event, which puts it in the API
|
|
27
|
-
* tables and takes it out of `$attrs`, hence out of what is forwarded: the listener has to be
|
|
28
|
-
* bound by hand. It has to follow what the consumer actually wrote: bound unconditionally,
|
|
29
|
-
* VInput would read a handler on every instance and turn a decorative icon into a focusable
|
|
30
|
-
* button nobody asked for. So it is `undefined` when there is nothing to relay.
|
|
14
|
+
* The start-icon listener a field COMPOSED on top of VInput: VCombobox, VDateInput, VTimeInput,
|
|
15
|
+
* VFileInput, binds onto that inner VInput, ready to spread beside the consumer's attributes.
|
|
31
16
|
*/
|
|
32
17
|
export declare function iconStartListener(relay: (event: MouseEvent) => void): {
|
|
33
18
|
'onClick:icon-start': (event: MouseEvent) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useIconClickHandlers.js","names":[],"sources":["../../src/composables/useIconClickHandlers.ts"],"sourcesContent":["// @core\n/**\n *
|
|
1
|
+
{"version":3,"file":"useIconClickHandlers.js","names":[],"sources":["../../src/composables/useIconClickHandlers.ts"],"sourcesContent":["// @core\n/**\n * Detect declared icon-click listeners from vnode props because emits removes them from attrs.\n * Require accessible names before turning decorative icons into buttons.\n */\n\nimport { getCurrentInstance } from 'vue'\n\nimport { isDev } from '../utils/env'\n\n/**\n * Whether the component being set up was handed a `@click:icon-start` / `@click:icon-end`\n * listener, in either spelling.\n */\nexport function iconClickHandlers(): { start: boolean; end: boolean } {\n const vnodeProps = getCurrentInstance()?.vnode.props ?? {}\n return {\n start: 'onClick:iconStart' in vnodeProps || 'onClick:icon-start' in vnodeProps,\n end: 'onClick:iconEnd' in vnodeProps || 'onClick:icon-end' in vnodeProps,\n }\n}\n\n/**\n * The start-icon listener a field COMPOSED on top of VInput: VCombobox, VDateInput, VTimeInput,\n * VFileInput, binds onto that inner VInput, ready to spread beside the consumer's attributes.\n */\nexport function iconStartListener(\n relay: (event: MouseEvent) => void,\n): { 'onClick:icon-start': (event: MouseEvent) => void } | undefined {\n return iconClickHandlers().start ? { 'onClick:icon-start': relay } : undefined\n}\n\nexport function useIconClickHandlers(options: {\n name: string\n iconStartLabel?: string\n iconEndLabel?: string\n}): { hasIconStartHandler: boolean; hasIconEndHandler: boolean } {\n const { start: hasIconStartHandler, end: hasIconEndHandler } = iconClickHandlers()\n\n // @a11y @devwarn\n if (isDev) {\n if (hasIconStartHandler && !options.iconStartLabel)\n console.warn(\n `[${options.name}] clickable start icon without iconStartLabel — provide an accessible label.`,\n )\n if (hasIconEndHandler && !options.iconEndLabel)\n console.warn(\n `[${options.name}] clickable end icon without iconEndLabel — provide an accessible label.`,\n )\n }\n\n return { hasIconStartHandler, hasIconEndHandler }\n}\n"],"mappings":";;;AAcA,SAAgB,IAAsD;CACpE,IAAM,IAAa,EAAmB,CAAC,EAAE,MAAM,SAAS,CAAC;CACzD,OAAO;EACL,OAAO,uBAAuB,KAAc,wBAAwB;EACpE,KAAK,qBAAqB,KAAc,sBAAsB;CAChE;AACF;AAMA,SAAgB,EACd,GACmE;CACnE,OAAO,EAAkB,CAAC,CAAC,QAAQ,EAAE,sBAAsB,EAAM,IAAI,KAAA;AACvE;AAEA,SAAgB,EAAqB,GAI4B;CAC/D,IAAM,EAAE,OAAO,GAAqB,KAAK,MAAsB,EAAkB;CAcjF,OAXI,MACE,KAAuB,CAAC,EAAQ,kBAClC,QAAQ,KACN,IAAI,EAAQ,KAAK,6EACnB,GACE,KAAqB,CAAC,EAAQ,gBAChC,QAAQ,KACN,IAAI,EAAQ,KAAK,yEACnB,IAGG;EAAE;EAAqB;CAAkB;AAClD"}
|
|
@@ -10,7 +10,7 @@ export interface InertLinkOptions {
|
|
|
10
10
|
export interface InertLink {
|
|
11
11
|
/** Whether the component renders an `<a>`. */
|
|
12
12
|
isLink: ComputedRef<boolean>;
|
|
13
|
-
/** Whether that `<a>` is inert, which
|
|
13
|
+
/** Whether that `<a>` is inert, which `aria-disabled` is bound to. */
|
|
14
14
|
isInertLink: ComputedRef<boolean>;
|
|
15
15
|
/** The `href` to bind: the address, or nothing on an inert link. */
|
|
16
16
|
linkHref: ComputedRef<string | undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useInertLink.js","names":[],"sources":["../../src/composables/useInertLink.ts"],"sourcesContent":["// @a11y\n/**\n *
|
|
1
|
+
{"version":3,"file":"useInertLink.js","names":[],"sources":["../../src/composables/useInertLink.ts"],"sourcesContent":["// @a11y\n/**\n * Anchors have no disabled state: remove href, mark the link disabled and filter every\n * click-listener modifier. Keep role=link when href is absent.\n */\nimport { computed } from 'vue'\nimport type { ComputedRef } from 'vue'\n\nexport interface InertLinkOptions {\n /** The address the component would link to, or `undefined` when it is not a link. */\n href: () => string | undefined\n /** Whether the component is unusable right now. */\n inert: () => boolean\n /** The attributes the component forwards to the element that acts. */\n attrs: () => Record<string, unknown>\n}\n\nexport interface InertLink {\n /** Whether the component renders an `<a>`. */\n isLink: ComputedRef<boolean>\n /** Whether that `<a>` is inert, which `aria-disabled` is bound to. */\n isInertLink: ComputedRef<boolean>\n /** The `href` to bind: the address, or nothing on an inert link. */\n linkHref: ComputedRef<string | undefined>\n /** The attributes to bind, without the click handler on an inert link. */\n attrs: ComputedRef<Record<string, unknown>>\n}\n\n/** `onClick` and the keys Vue compiles its event modifiers to, in any combination. */\nconst CLICK_LISTENER = /^onClick(?:Once|Capture|Passive)*$/\n\nexport function useInertLink(options: InertLinkOptions): InertLink {\n const isLink = computed(() => options.href() !== undefined)\n const isInertLink = computed(() => isLink.value && options.inert())\n return {\n isLink,\n isInertLink,\n linkHref: computed(() => (isInertLink.value ? undefined : options.href())),\n attrs: computed(() => {\n const attrs = options.attrs()\n if (!isInertLink.value) return attrs\n const rest: Record<string, unknown> = { role: 'link', ...attrs }\n for (const key of Object.keys(rest)) if (CLICK_LISTENER.test(key)) delete rest[key]\n return rest\n }),\n }\n}\n"],"mappings":";;AA6BA,IAAM,IAAiB;AAEvB,SAAgB,EAAa,GAAsC;CACjE,IAAM,IAAS,QAAe,EAAQ,KAAK,MAAM,KAAA,CAAS,GACpD,IAAc,QAAe,EAAO,SAAS,EAAQ,MAAM,CAAC;CAClE,OAAO;EACL;EACA;EACA,UAAU,QAAgB,EAAY,QAAQ,KAAA,IAAY,EAAQ,KAAK,CAAE;EACzE,OAAO,QAAe;GACpB,IAAM,IAAQ,EAAQ,MAAM;GAC5B,IAAI,CAAC,EAAY,OAAO,OAAO;GAC/B,IAAM,IAAgC;IAAE,MAAM;IAAQ,GAAG;GAAM;GAC/D,KAAK,IAAM,KAAO,OAAO,KAAK,CAAI,GAAG,AAAI,EAAe,KAAK,CAAG,KAAG,OAAO,EAAK;GAC/E,OAAO;EACT,CAAC;CACH;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLiveAnnouncer.js","names":[],"sources":["../../src/composables/useLiveAnnouncer.ts"],"sourcesContent":["// @a11y\n/**\n *
|
|
1
|
+
{"version":3,"file":"useLiveAnnouncer.js","names":[],"sources":["../../src/composables/useLiveAnnouncer.ts"],"sourcesContent":["// @a11y\n/**\n * Mount live regions before writing messages. Clear before rewriting identical text and batch\n * same-tick announcements so none overwrite each other.\n */\nimport { nextTick, ref } from 'vue'\n\nexport function useLiveAnnouncer() {\n const polite = ref('')\n const assertive = ref('')\n const pending = { polite: [] as string[], assertive: [] as string[] }\n\n /** Says `text`, interrupting what is being read when `urgent`, waiting for a pause otherwise. */\n function announce(text: string, urgent: boolean) {\n const region = urgent ? assertive : polite\n const queue = urgent ? pending.assertive : pending.polite\n queue.push(text)\n if (queue.length > 1) return\n region.value = ''\n void nextTick(() => {\n region.value = queue.join('\\n')\n queue.length = 0\n })\n }\n\n return { polite, assertive, announce }\n}\n"],"mappings":";;AAOA,SAAgB,IAAmB;CACjC,IAAM,IAAS,EAAI,EAAE,GACf,IAAY,EAAI,EAAE,GAClB,IAAU;EAAE,QAAQ,CAAC;EAAe,WAAW,CAAC;CAAc;CAGpE,SAAS,EAAS,GAAc,GAAiB;EAC/C,IAAM,IAAS,IAAS,IAAY,GAC9B,IAAQ,IAAS,EAAQ,YAAY,EAAQ;EACnD,EAAM,KAAK,CAAI,GACX,IAAM,SAAS,OACnB,EAAO,QAAQ,IACf,QAAoB;GAElB,AADA,EAAO,QAAQ,EAAM,KAAK,IAAI,GAC9B,EAAM,SAAS;EACjB,CAAC;CACH;CAEA,OAAO;EAAE;EAAQ;EAAW;CAAS;AACvC"}
|
|
@@ -4,7 +4,7 @@ export interface MaskedFieldOptions<T extends string> {
|
|
|
4
4
|
fieldEl: Ref<HTMLInputElement | null>;
|
|
5
5
|
/** Whether the field can be typed into at all. Everything here is inert when it cannot. */
|
|
6
6
|
typing: () => boolean;
|
|
7
|
-
/** What the field shows when it is
|
|
7
|
+
/** What the field shows when it is not being typed into: the value, written out in full. */
|
|
8
8
|
displayText: () => string;
|
|
9
9
|
/** The current value, or nothing. */
|
|
10
10
|
readValue: () => T | null;
|
|
@@ -15,20 +15,13 @@ export interface MaskedFieldOptions<T extends string> {
|
|
|
15
15
|
/** Lays a run of digits out as masked text. */
|
|
16
16
|
format: (digits: string) => string;
|
|
17
17
|
/**
|
|
18
|
-
* Where the caret goes so as to sit after a given number of digits.
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* would step over it instead of erasing, and the key would appear to do nothing.
|
|
18
|
+
* Where the caret goes so as to sit after a given number of digits. The flag says whether
|
|
19
|
+
* something was INSERTED rather than deleted, and it matters: on a deletion the caret must
|
|
20
|
+
* stay in FRONT of the separator, or the next press of the key would step over it instead of
|
|
21
|
+
* erasing, and the key would appear to do nothing.
|
|
23
22
|
*/
|
|
24
23
|
caret: (text: string, digitsBefore: number, inserting: boolean) => number;
|
|
25
|
-
/**
|
|
26
|
-
* Reads masked text back into a value, or nothing when it is not one yet.
|
|
27
|
-
*
|
|
28
|
-
* The flag marks the FINAL reading, when the reader leaves the field. A date expands a
|
|
29
|
-
* two-digit year only there: "26" must not be committed as 2026 while it may still be on
|
|
30
|
-
* its way to becoming it.
|
|
31
|
-
*/
|
|
24
|
+
/** Reads masked text back into a value, or nothing when it is not one yet. */
|
|
32
25
|
parse: (text: string, final: boolean) => T | null;
|
|
33
26
|
/** Writes a value as masked text, and nothing at all for no value. */
|
|
34
27
|
toMask: (value: T | null) => string;
|
|
@@ -40,11 +33,7 @@ export interface MaskedFieldOptions<T extends string> {
|
|
|
40
33
|
}
|
|
41
34
|
/** What a component adds to the mask's own keys. */
|
|
42
35
|
export interface MaskedKeyHooks {
|
|
43
|
-
/**
|
|
44
|
-
* A key that is not a digit was typed: a separator, most often. Its character never
|
|
45
|
-
* reaches the field; what it MEANS is the component's: a date pads the field being typed
|
|
46
|
-
* and moves on to the next, a time pads the hour.
|
|
47
|
-
*/
|
|
36
|
+
/** A key that is not a digit was typed: a separator, most often. */
|
|
48
37
|
onSeparator: (el: HTMLInputElement) => void;
|
|
49
38
|
/**
|
|
50
39
|
* The down arrow, which a component with a panel turns into the way into it. It answers
|
|
@@ -59,10 +48,10 @@ export interface MaskedField<T extends string> {
|
|
|
59
48
|
/** The text currently in the field while it is being typed into. */
|
|
60
49
|
draft: Ref<string>;
|
|
61
50
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
51
|
+
* Bind with `v-model` and never `:model-value`. Without an `onUpdate:modelValue` listener,
|
|
52
|
+
* `useModel` keeps an internal copy of the RAW typed text and rewrites it on the next patch,
|
|
53
|
+
* erasing the mask exactly when the masked text did not change: a rejected character, or a
|
|
54
|
+
* digit past the last.
|
|
66
55
|
*/
|
|
67
56
|
fieldModel: WritableComputedRef<string | number>;
|
|
68
57
|
/**
|
|
@@ -78,11 +67,7 @@ export interface MaskedField<T extends string> {
|
|
|
78
67
|
onFieldInput: (event: Event) => void;
|
|
79
68
|
/** The handler for the field's keydown event, with what the component adds to it. */
|
|
80
69
|
onKeydown: (event: KeyboardEvent, hooks: MaskedKeyHooks) => void;
|
|
81
|
-
/**
|
|
82
|
-
* The handler for the field's paste event. `recognize` reads the pasted text as a WHOLE
|
|
83
|
-
* value when it is one (a date in ISO form or a canonical time) and answers nothing
|
|
84
|
-
* otherwise, in which case only the digits of what was pasted are taken.
|
|
85
|
-
*/
|
|
70
|
+
/** The handler for the field's paste event. */
|
|
86
71
|
onPaste: (event: ClipboardEvent, recognize: (pasted: string) => T | null) => void;
|
|
87
72
|
}
|
|
88
73
|
export declare function useMaskedField<T extends string>(options: MaskedFieldOptions<T>): MaskedField<T>;
|