@rotki/ui-library 2.26.1 → 3.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -15
- package/dist/components/accordions/accordion/RuiAccordion.js.map +1 -1
- package/dist/components/accordions/accordion/RuiAccordion.vue.d.ts +0 -4
- package/dist/components/accordions/accordion/RuiAccordion.vue_vue_type_script_setup_true_lang.js +6 -7
- package/dist/components/accordions/accordion/RuiAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/accordions/accordions/RuiAccordions.js.map +1 -1
- package/dist/components/accordions/accordions/RuiAccordions.vue_vue_type_script_setup_true_lang.js +15 -2
- package/dist/components/accordions/accordions/RuiAccordions.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/alerts/RuiAlert.js.map +1 -1
- package/dist/components/alerts/RuiAlert.vue_vue_type_script_setup_true_lang.js +43 -212
- package/dist/components/alerts/RuiAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/avatars/RuiAvatar.js.map +1 -1
- package/dist/components/avatars/RuiAvatar.vue_vue_type_script_setup_true_lang.js +21 -31
- package/dist/components/avatars/RuiAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/avatars/RuiAvatarGroup.js.map +1 -1
- package/dist/components/avatars/RuiAvatarGroup.vue.d.ts +6 -0
- package/dist/components/avatars/RuiAvatarGroup.vue_vue_type_script_setup_true_lang.js +16 -12
- package/dist/components/avatars/RuiAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/avatars/avatar-props.d.ts +16 -3
- package/dist/components/avatars/avatar-props.js +42 -5
- package/dist/components/avatars/avatar-props.js.map +1 -1
- package/dist/components/buttons/button/RuiButton.js.map +1 -1
- package/dist/components/buttons/button/RuiButton.vue.d.ts +0 -1
- package/dist/components/buttons/button/RuiButton.vue_vue_type_script_setup_true_lang.js +29 -16
- package/dist/components/buttons/button/RuiButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/buttons/button/button-props.d.ts +0 -2
- package/dist/components/buttons/button/button-props.js +5 -5
- package/dist/components/buttons/button/button-props.js.map +1 -1
- package/dist/components/buttons/button/button-styles.d.ts +4 -1
- package/dist/components/buttons/button/button-styles.js +127 -54
- package/dist/components/buttons/button/button-styles.js.map +1 -1
- package/dist/components/buttons/button-group/RuiButtonGroup.js.map +1 -1
- package/dist/components/buttons/button-group/RuiButtonGroup.vue.d.ts +8 -2
- package/dist/components/buttons/button-group/RuiButtonGroup.vue_vue_type_script_setup_true_lang.js +65 -96
- package/dist/components/buttons/button-group/RuiButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/buttons/button-group/button-group-context.d.ts +28 -0
- package/dist/components/buttons/button-group/button-group-context.js +22 -0
- package/dist/components/buttons/button-group/button-group-context.js.map +1 -0
- package/dist/components/calendar/RuiCalendar.js.map +1 -1
- package/dist/components/calendar/RuiCalendar.vue_vue_type_script_setup_true_lang.js +5 -2
- package/dist/components/calendar/RuiCalendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/calendar/RuiCalendarGrid.js.map +1 -1
- package/dist/components/calendar/RuiCalendarGrid.vue_vue_type_script_setup_true_lang.js +11 -7
- package/dist/components/calendar/RuiCalendarGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/calendar/RuiCalendarHeader.js.map +1 -1
- package/dist/components/calendar/RuiCalendarHeader.vue_vue_type_script_setup_true_lang.js +20 -21
- package/dist/components/calendar/RuiCalendarHeader.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/calendar/RuiCalendarMenu.js.map +1 -1
- package/dist/components/calendar/RuiCalendarMenu.vue_vue_type_script_setup_true_lang.js +16 -20
- package/dist/components/calendar/RuiCalendarMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/cards/RuiCard.js.map +1 -1
- package/dist/components/cards/RuiCard.vue.d.ts +0 -3
- package/dist/components/cards/RuiCard.vue_vue_type_script_setup_true_lang.js +31 -19
- package/dist/components/cards/RuiCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/cards/RuiCardHeader.js.map +1 -1
- package/dist/components/cards/RuiCardHeader.vue_vue_type_script_setup_true_lang.js +13 -25
- package/dist/components/cards/RuiCardHeader.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/chips/RuiChip.js.map +1 -1
- package/dist/components/chips/RuiChip.vue.d.ts +5 -2
- package/dist/components/chips/RuiChip.vue_vue_type_script_setup_true_lang.js +127 -78
- package/dist/components/chips/RuiChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/chips/chip-props.d.ts +2 -0
- package/dist/components/chips/chip-props.js +5 -3
- package/dist/components/chips/chip-props.js.map +1 -1
- package/dist/components/color-picker/RuiColorBoard.js.map +1 -1
- package/dist/components/color-picker/RuiColorBoard.vue_vue_type_script_setup_true_lang.js +4 -4
- package/dist/components/color-picker/RuiColorBoard.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/color-picker/RuiColorHue.js.map +1 -1
- package/dist/components/color-picker/RuiColorHue.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/color-picker/RuiColorHue.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/color-picker/RuiColorInput.js.map +1 -1
- package/dist/components/color-picker/RuiColorInput.vue_vue_type_script_setup_true_lang.js +38 -27
- package/dist/components/color-picker/RuiColorInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/color-picker/RuiColorPicker.js.map +1 -1
- package/dist/components/color-picker/RuiColorPicker.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/color-picker/RuiColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/date-time-picker/RuiDateTimePicker.js.map +1 -1
- package/dist/components/date-time-picker/RuiDateTimePicker.vue.d.ts +3 -2
- package/dist/components/date-time-picker/RuiDateTimePicker.vue_vue_type_script_setup_true_lang.js +49 -47
- package/dist/components/date-time-picker/RuiDateTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/date-time-picker/RuiDateTimePickerMenu.js.map +1 -1
- package/dist/components/date-time-picker/RuiDateTimePickerMenu.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/date-time-picker/RuiDateTimePickerMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/date-time-picker/RuiTimezoneSelect.js.map +1 -1
- package/dist/components/date-time-picker/RuiTimezoneSelect.vue.d.ts +4 -3
- package/dist/components/date-time-picker/RuiTimezoneSelect.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/date-time-picker/RuiTimezoneSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/date-time-picker/date-time-picker-styles.d.ts +128 -219
- package/dist/components/date-time-picker/date-time-picker-styles.js +1 -2
- package/dist/components/date-time-picker/date-time-picker-styles.js.map +1 -1
- package/dist/components/date-time-picker/dayjs-setup.d.ts +12 -1
- package/dist/components/date-time-picker/dayjs-setup.js +19 -4
- package/dist/components/date-time-picker/dayjs-setup.js.map +1 -1
- package/dist/components/date-time-picker/segment-utils.d.ts +1 -2
- package/dist/components/date-time-picker/segment-utils.js +1 -2
- package/dist/components/date-time-picker/segment-utils.js.map +1 -1
- package/dist/components/date-time-picker/use-date-bounds.d.ts +1 -1
- package/dist/components/date-time-picker/use-date-bounds.js +1 -1
- package/dist/components/date-time-picker/use-date-bounds.js.map +1 -1
- package/dist/components/date-time-picker/use-date-time-selection.d.ts +1 -2
- package/dist/components/date-time-picker/use-date-time-selection.js +1 -2
- package/dist/components/date-time-picker/use-date-time-selection.js.map +1 -1
- package/dist/components/date-time-picker/utils.d.ts +0 -1
- package/dist/components/date-time-picker/utils.js +1 -2
- package/dist/components/date-time-picker/utils.js.map +1 -1
- package/dist/components/forms/auto-complete/RuiAutoComplete.js.map +1 -1
- package/dist/components/forms/auto-complete/RuiAutoComplete.vue.d.ts +1 -68
- package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js +184 -180
- package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/auto-complete/RuiAutoCompleteOption.js.map +1 -1
- package/dist/components/forms/auto-complete/RuiAutoCompleteOption.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/components/forms/auto-complete/RuiAutoCompleteOption.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/auto-complete/RuiAutoCompleteOptionList.js.map +1 -1
- package/dist/components/forms/auto-complete/RuiAutoCompleteOptionList.vue.d.ts +2 -0
- package/dist/components/forms/auto-complete/RuiAutoCompleteOptionList.vue_vue_type_script_setup_true_lang.js +8 -3
- package/dist/components/forms/auto-complete/RuiAutoCompleteOptionList.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/auto-complete/RuiAutoCompleteSelection.js.map +1 -1
- package/dist/components/forms/auto-complete/RuiAutoCompleteSelection.vue.d.ts +4 -0
- package/dist/components/forms/auto-complete/RuiAutoCompleteSelection.vue_vue_type_script_setup_true_lang.js +24 -10
- package/dist/components/forms/auto-complete/RuiAutoCompleteSelection.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/auto-complete/auto-complete-props.d.ts +66 -0
- package/dist/components/forms/auto-complete/auto-complete-styles.d.ts +128 -218
- package/dist/components/forms/auto-complete/auto-complete-styles.js +4 -2
- package/dist/components/forms/auto-complete/auto-complete-styles.js.map +1 -1
- package/dist/components/forms/category-picker/RuiCategoryPicker.js.map +1 -1
- package/dist/components/forms/category-picker/RuiCategoryPicker.vue.d.ts +3 -6
- package/dist/components/forms/category-picker/RuiCategoryPicker.vue_vue_type_script_setup_true_lang.js +106 -105
- package/dist/components/forms/category-picker/RuiCategoryPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/category-picker/category-picker-styles.d.ts +83 -232
- package/dist/components/forms/category-picker/category-picker-styles.js +20 -26
- package/dist/components/forms/category-picker/category-picker-styles.js.map +1 -1
- package/dist/components/forms/check-control-styles.d.ts +61 -2
- package/dist/components/forms/check-control-styles.js +53 -46
- package/dist/components/forms/check-control-styles.js.map +1 -1
- package/dist/components/forms/checkbox/RuiCheckbox.js.map +1 -1
- package/dist/components/forms/checkbox/RuiCheckbox.vue_vue_type_script_setup_true_lang.js +31 -18
- package/dist/components/forms/checkbox/RuiCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.js.map +1 -1
- package/dist/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/field-defaults/RuiFieldDefaults.js +7 -0
- package/dist/components/forms/field-defaults/RuiFieldDefaults.js.map +1 -0
- package/dist/components/forms/field-defaults/RuiFieldDefaults.vue.d.ts +16 -0
- package/dist/components/forms/field-defaults/RuiFieldDefaults.vue_vue_type_script_setup_true_lang.js +18 -0
- package/dist/components/forms/field-defaults/RuiFieldDefaults.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/forms/field-label/RuiFieldLabel.js +7 -0
- package/dist/components/forms/field-label/RuiFieldLabel.js.map +1 -0
- package/dist/components/forms/field-label/RuiFieldLabel.vue.d.ts +17 -0
- package/dist/components/forms/field-label/RuiFieldLabel.vue_vue_type_script_setup_true_lang.js +57 -0
- package/dist/components/forms/field-label/RuiFieldLabel.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/forms/file-upload/RuiFileUpload.js.map +1 -1
- package/dist/components/forms/file-upload/RuiFileUpload.vue.d.ts +1 -1
- package/dist/components/forms/file-upload/RuiFileUpload.vue_vue_type_script_setup_true_lang.js +39 -20
- package/dist/components/forms/file-upload/RuiFileUpload.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/radio-button/radio/RuiRadio.js.map +1 -1
- package/dist/components/forms/radio-button/radio/RuiRadio.vue_vue_type_script_setup_true_lang.js +13 -14
- package/dist/components/forms/radio-button/radio/RuiRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/radio-button/radio-group/RuiRadioGroup.js.map +1 -1
- package/dist/components/forms/radio-button/radio-group/RuiRadioGroup.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/forms/radio-button/radio-group/RuiRadioGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/revealable-text-field/RuiRevealableTextField.js.map +1 -1
- package/dist/components/forms/revealable-text-field/RuiRevealableTextField.vue_vue_type_script_setup_true_lang.js +6 -6
- package/dist/components/forms/revealable-text-field/RuiRevealableTextField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/select/RuiMenuSelect.js.map +1 -1
- package/dist/components/forms/select/RuiMenuSelect.vue.d.ts +3 -12
- package/dist/components/forms/select/RuiMenuSelect.vue_vue_type_script_setup_true_lang.js +50 -45
- package/dist/components/forms/select/RuiMenuSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/select/RuiSimpleSelect.js.map +1 -1
- package/dist/components/forms/select/RuiSimpleSelect.vue.d.ts +0 -1
- package/dist/components/forms/select/RuiSimpleSelect.vue_vue_type_script_setup_true_lang.js +13 -20
- package/dist/components/forms/select/RuiSimpleSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/select/menu-select-styles.d.ts +99 -221
- package/dist/components/forms/select/menu-select-styles.js +1 -4
- package/dist/components/forms/select/menu-select-styles.js.map +1 -1
- package/dist/components/forms/slider/RuiSlider.js.map +1 -1
- package/dist/components/forms/slider/RuiSlider.vue.d.ts +0 -4
- package/dist/components/forms/slider/RuiSlider.vue_vue_type_script_setup_true_lang.js +113 -90
- package/dist/components/forms/slider/RuiSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/slider/slider-styles.d.ts +40 -40
- package/dist/components/forms/slider/slider-styles.js +67 -109
- package/dist/components/forms/slider/slider-styles.js.map +1 -1
- package/dist/components/forms/switch/RuiSwitch.js.map +1 -1
- package/dist/components/forms/switch/RuiSwitch.vue_vue_type_script_setup_true_lang.js +32 -44
- package/dist/components/forms/switch/RuiSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/text-area/RuiTextArea.js.map +1 -1
- package/dist/components/forms/text-area/RuiTextArea.vue.d.ts +3 -2
- package/dist/components/forms/text-area/RuiTextArea.vue_vue_type_script_setup_true_lang.js +120 -99
- package/dist/components/forms/text-area/RuiTextArea.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/text-area/text-area-styles.d.ts +41 -115
- package/dist/components/forms/text-area/text-area-styles.js +34 -194
- package/dist/components/forms/text-area/text-area-styles.js.map +1 -1
- package/dist/components/forms/text-field/RuiTextField.js.map +1 -1
- package/dist/components/forms/text-field/RuiTextField.vue.d.ts +3 -2
- package/dist/components/forms/text-field/RuiTextField.vue_vue_type_script_setup_true_lang.js +93 -90
- package/dist/components/forms/text-field/RuiTextField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/text-field/text-field-styles.d.ts +37 -145
- package/dist/components/forms/text-field/text-field-styles.js +24 -267
- package/dist/components/forms/text-field/text-field-styles.js.map +1 -1
- package/dist/components/forms/text-input-styles.d.ts +98 -229
- package/dist/components/forms/text-input-styles.js +63 -193
- package/dist/components/forms/text-input-styles.js.map +1 -1
- package/dist/components/helpers/RuiFormTextDetail.js.map +1 -1
- package/dist/components/helpers/RuiFormTextDetail.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/helpers/RuiFormTextDetail.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/icons/RuiIcon.js.map +1 -1
- package/dist/components/icons/RuiIcon.vue_vue_type_script_setup_true_lang.js +6 -3
- package/dist/components/icons/RuiIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/index.d.ts +5 -3
- package/dist/components/index.js +4 -3
- package/dist/components/loaders/RuiSkeletonBase.js.map +1 -1
- package/dist/components/loaders/RuiSkeletonBase.vue_vue_type_script_setup_true_lang.js +6 -5
- package/dist/components/loaders/RuiSkeletonBase.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/loaders/RuiSkeletonLoader.js.map +1 -1
- package/dist/components/loaders/RuiSkeletonLoader.vue_vue_type_script_setup_true_lang.js +8 -8
- package/dist/components/loaders/RuiSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/logos/RuiLogo.js.map +1 -1
- package/dist/components/logos/RuiLogo.vue.d.ts +8 -10
- package/dist/components/logos/RuiLogo.vue_vue_type_script_setup_true_lang.js +42 -43
- package/dist/components/logos/RuiLogo.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/logos/verified-logo.d.ts +17 -0
- package/dist/components/logos/verified-logo.js +36 -0
- package/dist/components/logos/verified-logo.js.map +1 -0
- package/dist/components/overlays/badge/RuiBadge.js.map +1 -1
- package/dist/components/overlays/badge/RuiBadge.vue_vue_type_script_setup_true_lang.js +75 -47
- package/dist/components/overlays/badge/RuiBadge.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/overlays/dialog/RuiDialog.js.map +1 -1
- package/dist/components/overlays/dialog/RuiDialog.vue.d.ts +3 -2
- package/dist/components/overlays/dialog/RuiDialog.vue_vue_type_script_setup_true_lang.js +16 -12
- package/dist/components/overlays/dialog/RuiDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/overlays/menu/RuiMenu.js.map +1 -1
- package/dist/components/overlays/menu/RuiMenu.vue.d.ts +15 -10
- package/dist/components/overlays/menu/RuiMenu.vue_vue_type_script_setup_true_lang.js +29 -22
- package/dist/components/overlays/menu/RuiMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/overlays/menu/activator-handlers.d.ts +11 -0
- package/dist/components/overlays/menu/activator-handlers.js +18 -0
- package/dist/components/overlays/menu/activator-handlers.js.map +1 -0
- package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.js.map +1 -1
- package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.vue.d.ts +6 -2
- package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.vue_vue_type_script_setup_true_lang.js +40 -16
- package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/overlays/notification/RuiNotification.js.map +1 -1
- package/dist/components/overlays/notification/RuiNotification.vue_vue_type_script_setup_true_lang.js +4 -4
- package/dist/components/overlays/notification/RuiNotification.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/overlays/tooltip/RuiTooltip.js.map +1 -1
- package/dist/components/overlays/tooltip/RuiTooltip.vue.d.ts +0 -5
- package/dist/components/overlays/tooltip/RuiTooltip.vue_vue_type_script_setup_true_lang.js +16 -10
- package/dist/components/overlays/tooltip/RuiTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/overlays/tooltip/tooltip-styles.d.ts +5 -12
- package/dist/components/overlays/tooltip/tooltip-styles.js +11 -18
- package/dist/components/overlays/tooltip/tooltip-styles.js.map +1 -1
- package/dist/components/progress/RuiProgress.js.map +1 -1
- package/dist/components/progress/RuiProgress.vue.d.ts +4 -0
- package/dist/components/progress/RuiProgress.vue_vue_type_script_setup_true_lang.js +72 -46
- package/dist/components/progress/RuiProgress.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/steppers/RuiFooterStepper.js.map +1 -1
- package/dist/components/steppers/RuiFooterStepper.vue_vue_type_script_setup_true_lang.js +27 -28
- package/dist/components/steppers/RuiFooterStepper.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/steppers/RuiStepper.js.map +1 -1
- package/dist/components/steppers/RuiStepper.vue.d.ts +0 -4
- package/dist/components/steppers/RuiStepper.vue_vue_type_script_setup_true_lang.js +25 -23
- package/dist/components/steppers/RuiStepper.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/steppers/RuiStepperIcon.js.map +1 -1
- package/dist/components/steppers/RuiStepperIcon.vue.d.ts +2 -0
- package/dist/components/steppers/RuiStepperIcon.vue_vue_type_script_setup_true_lang.js +47 -47
- package/dist/components/steppers/RuiStepperIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/RuiDataTable.js.map +1 -1
- package/dist/components/tables/RuiDataTable.vue.d.ts +3 -6
- package/dist/components/tables/RuiDataTable.vue_vue_type_script_setup_true_lang.js +65 -47
- package/dist/components/tables/RuiDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/RuiExpandButton.js.map +1 -1
- package/dist/components/tables/RuiExpandButton.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/components/tables/RuiExpandButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/RuiTable.js.map +1 -1
- package/dist/components/tables/RuiTable.vue_vue_type_script_setup_true_lang.js +8 -5
- package/dist/components/tables/RuiTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/RuiTableEmptyState.js.map +1 -1
- package/dist/components/tables/RuiTableEmptyState.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/tables/RuiTableEmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/RuiTableHead.js.map +1 -1
- package/dist/components/tables/RuiTableHead.vue_vue_type_script_setup_true_lang.js +88 -66
- package/dist/components/tables/RuiTableHead.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/RuiTablePagination.js.map +1 -1
- package/dist/components/tables/RuiTablePagination.vue_vue_type_script_setup_true_lang.js +8 -11
- package/dist/components/tables/RuiTablePagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableExpandedRow.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableExpandedRow.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/tables/data-table/RuiDataTableExpandedRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableGroupRow.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableGroupRow.vue_vue_type_script_setup_true_lang.js +7 -3
- package/dist/components/tables/data-table/RuiDataTableGroupRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableMobileSort.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableMobileSort.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/tables/data-table/RuiDataTableMobileSort.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableRow.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableRow.vue_vue_type_script_setup_true_lang.js +58 -19
- package/dist/components/tables/data-table/RuiDataTableRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/data-table-styles.d.ts +24 -0
- package/dist/components/tables/data-table-styles.js +35 -20
- package/dist/components/tables/data-table-styles.js.map +1 -1
- package/dist/components/tables/table_no_data_placeholder.js +1 -1
- package/dist/components/tables/table_no_data_placeholder.js.map +1 -1
- package/dist/components/tables/table_no_data_placeholder_dark.js +1 -1
- package/dist/components/tables/table_no_data_placeholder_dark.js.map +1 -1
- package/dist/components/tabs/tab/RuiTab.js.map +1 -1
- package/dist/components/tabs/tab/RuiTab.vue.d.ts +2 -5
- package/dist/components/tabs/tab/RuiTab.vue_vue_type_script_setup_true_lang.js +94 -68
- package/dist/components/tabs/tab/RuiTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tabs/tab-item/RuiTabItem.js.map +1 -1
- package/dist/components/tabs/tab-item/RuiTabItem.vue.d.ts +0 -1
- package/dist/components/tabs/tab-item/RuiTabItem.vue_vue_type_script_setup_true_lang.js +5 -11
- package/dist/components/tabs/tab-item/RuiTabItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tabs/tab-items/RuiTabItems.js.map +1 -1
- package/dist/components/tabs/tab-items/RuiTabItems.vue_vue_type_script_setup_true_lang.js +3 -8
- package/dist/components/tabs/tab-items/RuiTabItems.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tabs/tab-props.d.ts +7 -1
- package/dist/components/tabs/tab-props.js +7 -1
- package/dist/components/tabs/tab-props.js.map +1 -1
- package/dist/components/tabs/tabs/RuiTabs.js.map +1 -1
- package/dist/components/tabs/tabs/RuiTabs.vue.d.ts +2 -1
- package/dist/components/tabs/tabs/RuiTabs.vue_vue_type_script_setup_true_lang.js +178 -32
- package/dist/components/tabs/tabs/RuiTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tabs/tabs/use-tab-indicator.d.ts +27 -0
- package/dist/components/tabs/tabs/use-tab-indicator.js +67 -0
- package/dist/components/tabs/tabs/use-tab-indicator.js.map +1 -0
- package/dist/components/tabs/tabs/use-tab-keyboard.d.ts +17 -0
- package/dist/components/tabs/tabs/use-tab-keyboard.js +57 -0
- package/dist/components/tabs/tabs/use-tab-keyboard.js.map +1 -0
- package/dist/components/tabs/tabs/use-tab-routing.js +1 -2
- package/dist/components/tabs/tabs/use-tab-routing.js.map +1 -1
- package/dist/components/time-picker/RuiTimePicker.js.map +1 -1
- package/dist/components/time-picker/RuiTimePicker.vue_vue_type_script_setup_true_lang.js +35 -32
- package/dist/components/time-picker/RuiTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/time-picker/time-picker-styles.d.ts +18 -0
- package/dist/components/time-picker/time-picker-styles.js +16 -10
- package/dist/components/time-picker/time-picker-styles.js.map +1 -1
- package/dist/composables/defaults/field.d.ts +22 -0
- package/dist/composables/defaults/field.js +29 -0
- package/dist/composables/defaults/field.js.map +1 -0
- package/dist/composables/defaults/logo.d.ts +18 -0
- package/dist/composables/defaults/logo.js +13 -0
- package/dist/composables/defaults/logo.js.map +1 -0
- package/dist/composables/dropdown-menu.d.ts +14 -2
- package/dist/composables/dropdown-menu.js +12 -2
- package/dist/composables/dropdown-menu.js.map +1 -1
- package/dist/composables/floating.d.ts +0 -2
- package/dist/composables/floating.js +5 -2
- package/dist/composables/floating.js.map +1 -1
- package/dist/composables/forms/auto-complete/chips.d.ts +22 -0
- package/dist/composables/forms/auto-complete/chips.js +42 -0
- package/dist/composables/forms/auto-complete/chips.js.map +1 -0
- package/dist/composables/forms/auto-complete/combobox.d.ts +25 -0
- package/dist/composables/forms/auto-complete/combobox.js +35 -0
- package/dist/composables/forms/auto-complete/combobox.js.map +1 -0
- package/dist/composables/forms/auto-complete/focus.d.ts +5 -2
- package/dist/composables/forms/auto-complete/focus.js +36 -10
- package/dist/composables/forms/auto-complete/focus.js.map +1 -1
- package/dist/composables/forms/auto-complete/index.d.ts +3 -0
- package/dist/composables/forms/auto-complete/keyboard-navigation.d.ts +8 -1
- package/dist/composables/forms/auto-complete/keyboard-navigation.js +48 -20
- package/dist/composables/forms/auto-complete/keyboard-navigation.js.map +1 -1
- package/dist/composables/forms/auto-complete/selection.d.ts +37 -0
- package/dist/composables/forms/auto-complete/selection.js +60 -0
- package/dist/composables/forms/auto-complete/selection.js.map +1 -0
- package/dist/composables/forms/auto-complete/value.js +19 -0
- package/dist/composables/forms/auto-complete/value.js.map +1 -1
- package/dist/composables/keyboard-scroll.d.ts +17 -0
- package/dist/composables/keyboard-scroll.js +58 -0
- package/dist/composables/keyboard-scroll.js.map +1 -0
- package/dist/composables/sticky-header.d.ts +8 -0
- package/dist/composables/sticky-header.js +13 -2
- package/dist/composables/sticky-header.js.map +1 -1
- package/dist/composables/tables/data-table/page-scroll.d.ts +30 -0
- package/dist/composables/tables/data-table/page-scroll.js +113 -0
- package/dist/composables/tables/data-table/page-scroll.js.map +1 -0
- package/dist/consts/colors.d.ts +4 -0
- package/dist/consts/colors.js +1 -37
- package/dist/consts/colors.js.map +1 -1
- package/dist/consts/tokens.d.ts +56 -0
- package/dist/consts/tokens.js +46 -0
- package/dist/consts/tokens.js.map +1 -0
- package/dist/consts/typography.d.ts +51 -18
- package/dist/index.d.ts +13 -0
- package/dist/index.js +12 -7
- package/dist/index.js.map +1 -1
- package/dist/library-icons.json +0 -10
- package/dist/style.css +5644 -4677
- package/dist/styles/colors.css +315 -0
- package/dist/styles/tokens.css +50 -0
- package/dist/tailwind.css +145 -0
- package/dist/theme/theme.css +403 -0
- package/dist/theme.css +403 -0
- package/dist/utils/helpers.d.ts +0 -8
- package/dist/utils/helpers.js +1 -17
- package/dist/utils/helpers.js.map +1 -1
- package/dist/utils/tv.js +13 -21
- package/dist/utils/tv.js.map +1 -1
- package/dist/web-types.json +271 -413
- package/package.json +17 -16
- package/dist/components/icons/LazyIcon.vue.d.ts +0 -8
- package/dist/components/logos/use-logo-sources.d.ts +0 -29
- package/dist/components/logos/use-logo-sources.js +0 -90
- package/dist/components/logos/use-logo-sources.js.map +0 -1
- package/dist/components/steppers/RuiStepperCustomIcon.js +0 -7
- package/dist/components/steppers/RuiStepperCustomIcon.js.map +0 -1
- package/dist/components/steppers/RuiStepperCustomIcon.vue.d.ts +0 -8
- package/dist/components/steppers/RuiStepperCustomIcon.vue_vue_type_script_setup_true_lang.js +0 -43
- package/dist/components/steppers/RuiStepperCustomIcon.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/composables/colors/common.d.ts +0 -20
- package/dist/composables/colors/index.d.ts +0 -3
- package/dist/composables/colors/text-input.d.ts +0 -27
- package/dist/composables/forms/auto-complete/index.js +0 -5
- package/dist/composables/forms/use-label-with-quote.d.ts +0 -2
- package/dist/composables/popper.d.ts +0 -16
- package/dist/composables/popper.js +0 -23
- package/dist/composables/popper.js.map +0 -1
- package/dist/consts/forms/auto-complete/index.d.ts +0 -4
- package/dist/theme/index.d.ts +0 -5
- package/dist/theme/index.js +0 -162
- package/dist/theme/index.js.map +0 -1
- package/dist/utils/forms/auto-complete/index.d.ts +0 -2
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { computed, nextTick, toValue, watch } from "vue";
|
|
1
|
+
import { computed, nextTick, shallowRef, toValue, watch } from "vue";
|
|
2
2
|
import { watchDebounced } from "@vueuse/core";
|
|
3
3
|
import { get as get$1, set as set$1 } from "@vueuse/shared";
|
|
4
4
|
//#region src/composables/forms/auto-complete/focus.ts
|
|
5
5
|
function useAutoCompleteFocus(options, deps) {
|
|
6
|
+
/**
|
|
7
|
+
* The input holds focus after a pick, as the combobox, but shows the picked value rather than an
|
|
8
|
+
* empty search until the user types, clicks or opens the list again.
|
|
9
|
+
*/
|
|
10
|
+
const settled = shallowRef(false);
|
|
6
11
|
const anyFocused = computed(() => get$1(deps.activatorFocusedWithin) || get$1(deps.menuWrapperFocusedWithin));
|
|
12
|
+
const editing = computed(() => get$1(deps.searchInputFocused) && !get$1(settled));
|
|
7
13
|
const inputClass = computed(() => {
|
|
8
14
|
const isFocused = get$1(anyFocused);
|
|
9
15
|
const isDisabled = toValue(options.disabled);
|
|
@@ -13,19 +19,34 @@ function useAutoCompleteFocus(options, deps) {
|
|
|
13
19
|
if (hasSearch) return "flex-1 min-w-[4rem]";
|
|
14
20
|
return "flex-1 min-w-0";
|
|
15
21
|
});
|
|
22
|
+
function unsettle() {
|
|
23
|
+
set$1(settled, false);
|
|
24
|
+
}
|
|
25
|
+
/** Puts focus on the input, as the combobox, while it keeps showing the value just picked. */
|
|
26
|
+
function settleOnInput() {
|
|
27
|
+
set$1(settled, true);
|
|
28
|
+
set$1(deps.searchInputFocused, true);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The first character typed after a pick replaces the shown value, as it would after tabbing in,
|
|
32
|
+
* rather than adding to it.
|
|
33
|
+
*
|
|
34
|
+
* @param event - the key press on the input
|
|
35
|
+
*/
|
|
36
|
+
function onSettledKeydown(event) {
|
|
37
|
+
if (!get$1(settled) || event.key.length !== 1 || event.ctrlKey || event.metaKey || event.altKey) return;
|
|
38
|
+
get$1(deps.textInput)?.select();
|
|
39
|
+
unsettle();
|
|
40
|
+
}
|
|
16
41
|
async function setInputFocus() {
|
|
42
|
+
unsettle();
|
|
17
43
|
await nextTick(() => {
|
|
18
44
|
set$1(deps.searchInputFocused, true);
|
|
19
45
|
});
|
|
20
46
|
}
|
|
21
|
-
async function onActivatorFocused() {
|
|
22
|
-
await nextTick(() => {
|
|
23
|
-
if (!get$1(deps.activatorFocused)) set$1(deps.searchInputFocused, true);
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
47
|
function onInputFocused() {
|
|
27
48
|
set$1(deps.focusedValueIndex, -1);
|
|
28
|
-
if (toValue(options.shouldApplyValueAsSearch)) get$1(deps.textInput)?.select();
|
|
49
|
+
if (toValue(options.shouldApplyValueAsSearch) && !get$1(settled)) get$1(deps.textInput)?.select();
|
|
29
50
|
if (!get$1(deps.isOpen)) set$1(deps.justOpened, true);
|
|
30
51
|
}
|
|
31
52
|
/**
|
|
@@ -35,7 +56,9 @@ function useAutoCompleteFocus(options, deps) {
|
|
|
35
56
|
*/
|
|
36
57
|
let pendingCustomSearch = "";
|
|
37
58
|
watch(anyFocused, (focused) => {
|
|
38
|
-
if (
|
|
59
|
+
if (focused) return;
|
|
60
|
+
unsettle();
|
|
61
|
+
if (toValue(options.customValue)) pendingCustomSearch = get$1(deps.internalSearch);
|
|
39
62
|
});
|
|
40
63
|
watchDebounced(anyFocused, (focused) => {
|
|
41
64
|
if (!focused) {
|
|
@@ -55,10 +78,13 @@ function useAutoCompleteFocus(options, deps) {
|
|
|
55
78
|
});
|
|
56
79
|
return {
|
|
57
80
|
anyFocused,
|
|
81
|
+
editing,
|
|
58
82
|
inputClass,
|
|
59
|
-
onActivatorFocused,
|
|
60
83
|
onInputFocused,
|
|
61
|
-
|
|
84
|
+
onSettledKeydown,
|
|
85
|
+
setInputFocus,
|
|
86
|
+
settleOnInput,
|
|
87
|
+
unsettle
|
|
62
88
|
};
|
|
63
89
|
}
|
|
64
90
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"focus.js","names":[],"sources":["../../../../src/composables/forms/auto-complete/focus.ts"],"sourcesContent":["import type { ComputedRef, MaybeRefOrGetter, Ref, TemplateRef } from 'vue';\n\nexport interface UseAutoCompleteFocusOptions {\n /** Whether custom (free-text) values are allowed. */\n customValue: MaybeRefOrGetter<boolean>;\n /** Whether to apply the current value as the search text when focused. */\n shouldApplyValueAsSearch: MaybeRefOrGetter<boolean>;\n /** Whether the autocomplete is disabled. */\n disabled: MaybeRefOrGetter<boolean>;\n}\n\nexport interface UseAutoCompleteFocusDeps {\n
|
|
1
|
+
{"version":3,"file":"focus.js","names":[],"sources":["../../../../src/composables/forms/auto-complete/focus.ts"],"sourcesContent":["import type { ComputedRef, MaybeRefOrGetter, Ref, TemplateRef } from 'vue';\n\nexport interface UseAutoCompleteFocusOptions {\n /** Whether custom (free-text) values are allowed. */\n customValue: MaybeRefOrGetter<boolean>;\n /** Whether to apply the current value as the search text when focused. */\n shouldApplyValueAsSearch: MaybeRefOrGetter<boolean>;\n /** Whether the autocomplete is disabled. */\n disabled: MaybeRefOrGetter<boolean>;\n}\n\nexport interface UseAutoCompleteFocusDeps {\n activatorFocusedWithin: Ref<boolean>;\n focusedValueIndex: Ref<number>;\n internalSearch: Ref<string>;\n isOpen: Ref<boolean>;\n justOpened: Ref<boolean>;\n menuWrapperFocusedWithin: Ref<boolean>;\n searchInputFocused: Ref<boolean>;\n textInput: Ref<HTMLInputElement | undefined> | TemplateRef<HTMLInputElement>;\n setSearchAsValue: () => void;\n updateInternalSearch: (value?: string) => void;\n}\n\nexport interface UseAutoCompleteFocusReturn {\n anyFocused: ComputedRef<boolean>;\n /** Whether the user is searching: the input has focus and is not just showing a picked value. */\n editing: ComputedRef<boolean>;\n inputClass: ComputedRef<string>;\n onInputFocused: () => void;\n onSettledKeydown: (event: KeyboardEvent) => void;\n setInputFocus: () => Promise<void>;\n settleOnInput: () => void;\n unsettle: () => void;\n}\n\nexport function useAutoCompleteFocus(\n options: UseAutoCompleteFocusOptions,\n deps: UseAutoCompleteFocusDeps,\n): UseAutoCompleteFocusReturn {\n /**\n * The input holds focus after a pick, as the combobox, but shows the picked value rather than an\n * empty search until the user types, clicks or opens the list again.\n */\n const settled = shallowRef<boolean>(false);\n\n const anyFocused = computed<boolean>(\n () => get(deps.activatorFocusedWithin) || get(deps.menuWrapperFocusedWithin),\n );\n\n const editing = computed<boolean>(() => get(deps.searchInputFocused) && !get(settled));\n\n const inputClass = computed<string>(() => {\n const isFocused = get(anyFocused);\n const isDisabled = toValue(options.disabled);\n const shouldApply = toValue(options.shouldApplyValueAsSearch);\n const hasSearch = get(deps.internalSearch);\n\n if ((!isFocused || isDisabled) && !shouldApply)\n return 'w-0 h-0';\n if (hasSearch)\n return 'flex-1 min-w-[4rem]';\n return 'flex-1 min-w-0';\n });\n\n function unsettle(): void {\n set(settled, false);\n }\n\n /** Puts focus on the input, as the combobox, while it keeps showing the value just picked. */\n function settleOnInput(): void {\n set(settled, true);\n set(deps.searchInputFocused, true);\n }\n\n /**\n * The first character typed after a pick replaces the shown value, as it would after tabbing in,\n * rather than adding to it.\n *\n * @param event - the key press on the input\n */\n function onSettledKeydown(event: KeyboardEvent): void {\n if (!get(settled) || event.key.length !== 1 || event.ctrlKey || event.metaKey || event.altKey)\n return;\n get(deps.textInput)?.select();\n unsettle();\n }\n\n async function setInputFocus(): Promise<void> {\n unsettle();\n await nextTick(() => {\n set(deps.searchInputFocused, true);\n });\n }\n\n function onInputFocused(): void {\n set(deps.focusedValueIndex, -1);\n\n // focus coming back after a pick keeps the value as it is, unselected\n const shouldApply = toValue(options.shouldApplyValueAsSearch);\n if (shouldApply && !get(settled)) {\n const textInput = get(deps.textInput);\n textInput?.select();\n }\n\n if (!get(deps.isOpen)) {\n set(deps.justOpened, true);\n }\n }\n\n /**\n * The search text as it stood when focus was lost, kept for the custom-value\n * blur flow: it is captured before the debounced handler runs, because the\n * watchers in value.ts clear the search when the menu closes.\n */\n let pendingCustomSearch = '';\n\n watch(anyFocused, (focused) => {\n if (focused)\n return;\n unsettle();\n if (toValue(options.customValue))\n pendingCustomSearch = get(deps.internalSearch);\n });\n\n // Debounced, so the menu does not close for a moment while focus moves between its own elements\n watchDebounced(\n anyFocused,\n (focused) => {\n if (!focused) {\n set(deps.isOpen, false);\n\n const customValue = toValue(options.customValue);\n const searchToUse = pendingCustomSearch;\n pendingCustomSearch = '';\n\n if (customValue && searchToUse) {\n deps.updateInternalSearch(searchToUse);\n deps.setSearchAsValue();\n }\n\n const shouldApply = toValue(options.shouldApplyValueAsSearch);\n if (!shouldApply) {\n deps.updateInternalSearch();\n }\n }\n },\n {\n debounce: 100,\n maxWait: 200,\n },\n );\n\n return {\n anyFocused,\n editing,\n inputClass,\n onInputFocused,\n onSettledKeydown,\n setInputFocus,\n settleOnInput,\n unsettle,\n };\n}\n"],"mappings":";;;;AAoCA,SAAgB,qBACd,SACA,MAC4B;;;;;CAK5B,MAAM,UAAU,WAAoB,KAAK;CAEzC,MAAM,aAAa,eACX,MAAI,KAAK,sBAAsB,KAAK,MAAI,KAAK,wBAAwB,CAC7E;CAEA,MAAM,UAAU,eAAwB,MAAI,KAAK,kBAAkB,KAAK,CAAC,MAAI,OAAO,CAAC;CAErF,MAAM,aAAa,eAAuB;EACxC,MAAM,YAAY,MAAI,UAAU;EAChC,MAAM,aAAa,QAAQ,QAAQ,QAAQ;EAC3C,MAAM,cAAc,QAAQ,QAAQ,wBAAwB;EAC5D,MAAM,YAAY,MAAI,KAAK,cAAc;EAEzC,KAAK,CAAC,aAAa,eAAe,CAAC,aACjC,OAAO;EACT,IAAI,WACF,OAAO;EACT,OAAO;CACT,CAAC;CAED,SAAS,WAAiB;EACxB,MAAI,SAAS,KAAK;CACpB;;CAGA,SAAS,gBAAsB;EAC7B,MAAI,SAAS,IAAI;EACjB,MAAI,KAAK,oBAAoB,IAAI;CACnC;;;;;;;CAQA,SAAS,iBAAiB,OAA4B;EACpD,IAAI,CAAC,MAAI,OAAO,KAAK,MAAM,IAAI,WAAW,KAAK,MAAM,WAAW,MAAM,WAAW,MAAM,QACrF;EACF,MAAI,KAAK,SAAS,CAAC,EAAE,OAAO;EAC5B,SAAS;CACX;CAEA,eAAe,gBAA+B;EAC5C,SAAS;EACT,MAAM,eAAe;GACnB,MAAI,KAAK,oBAAoB,IAAI;EACnC,CAAC;CACH;CAEA,SAAS,iBAAuB;EAC9B,MAAI,KAAK,mBAAmB,EAAE;EAI9B,IADoB,QAAQ,QAAQ,wBAChC,KAAe,CAAC,MAAI,OAAO,GAE7B,MADsB,KAAK,SAC3B,CAAA,EAAW,OAAO;EAGpB,IAAI,CAAC,MAAI,KAAK,MAAM,GAClB,MAAI,KAAK,YAAY,IAAI;CAE7B;;;;;;CAOA,IAAI,sBAAsB;CAE1B,MAAM,aAAa,YAAY;EAC7B,IAAI,SACF;EACF,SAAS;EACT,IAAI,QAAQ,QAAQ,WAAW,GAC7B,sBAAsB,MAAI,KAAK,cAAc;CACjD,CAAC;CAGD,eACE,aACC,YAAY;EACX,IAAI,CAAC,SAAS;GACZ,MAAI,KAAK,QAAQ,KAAK;GAEtB,MAAM,cAAc,QAAQ,QAAQ,WAAW;GAC/C,MAAM,cAAc;GACpB,sBAAsB;GAEtB,IAAI,eAAe,aAAa;IAC9B,KAAK,qBAAqB,WAAW;IACrC,KAAK,iBAAiB;GACxB;GAGA,IAAI,CADgB,QAAQ,QAAQ,wBAC/B,GACH,KAAK,qBAAqB;EAE9B;CACF,GACA;EACE,UAAU;EACV,SAAS;CACX,CACF;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
|
|
@@ -20,14 +20,21 @@ export interface UseAutoCompleteKeyboardNavigationDeps<TItem> {
|
|
|
20
20
|
removeValue: (item: TItem) => void;
|
|
21
21
|
setSearchAsValue: () => void;
|
|
22
22
|
getText: (item: TItem) => string | undefined;
|
|
23
|
+
updateInternalSearch: (value?: string) => void;
|
|
23
24
|
activator: Ref<HTMLElement | undefined> | TemplateRef<HTMLElement>;
|
|
24
25
|
}
|
|
25
26
|
export interface UseAutoCompleteKeyboardNavigationReturn {
|
|
27
|
+
/**
|
|
28
|
+
* The selected value (a chip) that Backspace or Delete would remove, -1 for none. It is only
|
|
29
|
+
* drawn as selected: focus stays in the input, where no browser shortcut takes the key.
|
|
30
|
+
*/
|
|
26
31
|
focusedValueIndex: Ref<number>;
|
|
27
32
|
moveSelectedValueHighlight: (event: KeyboardEvent, next: boolean) => void;
|
|
28
33
|
onEnter: (event: KeyboardEvent) => void;
|
|
29
34
|
onTab: (event: KeyboardEvent) => void;
|
|
30
|
-
onInputDeletePressed: () => void;
|
|
35
|
+
onInputDeletePressed: (event: KeyboardEvent) => void;
|
|
36
|
+
/** Drops the value selection on any key that does not act on it. */
|
|
37
|
+
onInputKeydown: (event: KeyboardEvent) => void;
|
|
31
38
|
setValueFocus: (index: number) => void;
|
|
32
39
|
}
|
|
33
40
|
export declare function useAutoCompleteKeyboardNavigation<TItem>(options: UseAutoCompleteKeyboardNavigationOptions, deps: UseAutoCompleteKeyboardNavigationDeps<TItem>): UseAutoCompleteKeyboardNavigationReturn;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ref, toValue, watch } from "vue";
|
|
2
2
|
import { get, set } from "@vueuse/shared";
|
|
3
3
|
//#region src/composables/forms/auto-complete/keyboard-navigation.ts
|
|
4
4
|
function useAutoCompleteKeyboardNavigation(options, deps) {
|
|
@@ -88,36 +88,64 @@ function useAutoCompleteKeyboardNavigation(options, deps) {
|
|
|
88
88
|
event.preventDefault();
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Removes the selected value. Alt with it, where custom values are accepted, puts the value's
|
|
93
|
+
* text back in the search instead of dropping it.
|
|
94
|
+
*
|
|
95
|
+
* @param event - the Backspace or Delete press
|
|
96
|
+
* @param item - the value the highlighted chip stands for
|
|
97
|
+
*/
|
|
98
|
+
function removeSelectedValue(event, item) {
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
set(focusedValueIndex, -1);
|
|
101
|
+
deps.removeValue(item);
|
|
102
|
+
if (event.altKey && toValue(options.customValue)) deps.updateInternalSearch(deps.getText(item) ?? "");
|
|
103
|
+
}
|
|
104
|
+
/** Backspace or Delete with nothing typed: steps onto the last value, or drops it outright. */
|
|
105
|
+
function deleteFromEmptySearch(value) {
|
|
106
|
+
if (!toValue(options.multiple)) {
|
|
107
|
+
deps.clear();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (toValue(options.chips)) {
|
|
111
|
+
set(focusedValueIndex, value.length - 1);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const lastItem = value.at(-1);
|
|
115
|
+
if (lastItem !== void 0) deps.removeValue(lastItem);
|
|
116
|
+
}
|
|
117
|
+
function onInputDeletePressed(event) {
|
|
93
118
|
const value = get(deps.value);
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (toValue(options.chips)) set(focusedValueIndex, total - 1);
|
|
99
|
-
else {
|
|
100
|
-
const lastItem = value[total - 1];
|
|
101
|
-
if (lastItem !== void 0) deps.removeValue(lastItem);
|
|
102
|
-
}
|
|
103
|
-
} else deps.clear();
|
|
119
|
+
const selected = value[get(focusedValueIndex)];
|
|
120
|
+
if (selected !== void 0) {
|
|
121
|
+
removeSelectedValue(event, selected);
|
|
122
|
+
return;
|
|
104
123
|
}
|
|
124
|
+
if (!get(deps.internalSearch) && value.length > 0) deleteFromEmptySearch(value);
|
|
125
|
+
}
|
|
126
|
+
function onInputKeydown(event) {
|
|
127
|
+
if (get(focusedValueIndex) === -1) return;
|
|
128
|
+
if (![
|
|
129
|
+
"Backspace",
|
|
130
|
+
"Delete",
|
|
131
|
+
"ArrowLeft",
|
|
132
|
+
"ArrowRight",
|
|
133
|
+
"Alt",
|
|
134
|
+
"AltGraph",
|
|
135
|
+
"Control",
|
|
136
|
+
"Meta",
|
|
137
|
+
"Shift"
|
|
138
|
+
].includes(event.key)) set(focusedValueIndex, -1);
|
|
105
139
|
}
|
|
106
140
|
watch(deps.value, () => {
|
|
107
141
|
setValueFocus(-1);
|
|
108
142
|
});
|
|
109
|
-
watch(focusedValueIndex, async (index) => {
|
|
110
|
-
const multiple = toValue(options.multiple);
|
|
111
|
-
if (index === -1 || !multiple) return;
|
|
112
|
-
await nextTick(() => {
|
|
113
|
-
(get(deps.activator)?.querySelector(`[data-index="${index}"]`))?.focus();
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
143
|
return {
|
|
117
144
|
focusedValueIndex,
|
|
118
145
|
moveSelectedValueHighlight,
|
|
119
146
|
onEnter,
|
|
120
147
|
onInputDeletePressed,
|
|
148
|
+
onInputKeydown,
|
|
121
149
|
onTab,
|
|
122
150
|
setValueFocus
|
|
123
151
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyboard-navigation.js","names":[],"sources":["../../../../src/composables/forms/auto-complete/keyboard-navigation.ts"],"sourcesContent":["import type { MaybeRefOrGetter, Ref, TemplateRef } from 'vue';\nimport { get, set } from '@vueuse/shared';\n\nexport interface UseAutoCompleteKeyboardNavigationOptions {\n /** Whether multiple items can be selected. */\n multiple: MaybeRefOrGetter<boolean>;\n /** Whether custom (free-text) values are allowed. */\n customValue: MaybeRefOrGetter<boolean>;\n /** Whether selected values are displayed as chips. */\n chips: MaybeRefOrGetter<boolean>;\n}\n\nexport interface UseAutoCompleteKeyboardNavigationDeps<TItem> {\n internalSearch: Ref<string>;\n value: Ref<TItem[]>;\n filteredOptions: Ref<TItem[]>;\n isOpen: Ref<boolean>;\n highlightedIndex: Ref<number>;\n userNavigated: Ref<boolean>;\n searchInputFocused: Ref<boolean>;\n applyHighlighted: () => void;\n clear: () => void;\n removeValue: (item: TItem) => void;\n setSearchAsValue: () => void;\n getText: (item: TItem) => string | undefined;\n activator: Ref<HTMLElement | undefined> | TemplateRef<HTMLElement>;\n}\n\nexport interface UseAutoCompleteKeyboardNavigationReturn {\n focusedValueIndex: Ref<number>;\n moveSelectedValueHighlight: (event: KeyboardEvent, next: boolean) => void;\n onEnter: (event: KeyboardEvent) => void;\n onTab: (event: KeyboardEvent) => void;\n onInputDeletePressed: () => void;\n setValueFocus: (index: number) => void;\n}\n\nexport function useAutoCompleteKeyboardNavigation<TItem>(\n options: UseAutoCompleteKeyboardNavigationOptions,\n deps: UseAutoCompleteKeyboardNavigationDeps<TItem>,\n): UseAutoCompleteKeyboardNavigationReturn {\n const focusedValueIndex = ref<number>(-1);\n\n function setValueFocus(index: number): void {\n set(focusedValueIndex, index);\n }\n\n function moveSelectedValueHighlight(event: KeyboardEvent, next: boolean): void {\n const multiple = toValue(options.multiple);\n const internalSearchValue = get(deps.internalSearch);\n\n if (!multiple || internalSearchValue.length > 0)\n return;\n\n event.preventDefault();\n const total = get(deps.value).length;\n\n let current = get(focusedValueIndex);\n\n if (current === -1) {\n set(focusedValueIndex, next ? 0 : total - 1);\n return;\n }\n\n const move = next ? 1 : -1;\n current += move;\n\n if (current < 0 || current >= total) {\n set(focusedValueIndex, -1);\n set(deps.searchInputFocused, true);\n }\n else {\n set(focusedValueIndex, current);\n }\n }\n\n function highlightedMatchesSearch(filteredOptions: TItem[], highlightedIndex: number, search: string): boolean {\n if (highlightedIndex < 0 || highlightedIndex >= filteredOptions.length)\n return false;\n\n const option = filteredOptions[highlightedIndex];\n if (typeof option === 'string')\n return option === search;\n\n if (option && typeof option === 'object')\n return (deps.getText(option) ?? '') === search;\n\n return false;\n }\n\n function applyCustomValue(event: KeyboardEvent, multiple: boolean): void {\n deps.setSearchAsValue();\n if (!multiple)\n set(deps.isOpen, false);\n event.preventDefault();\n }\n\n function submitClosestForm(): void {\n const activator = get(deps.activator);\n const form = activator?.closest('form');\n form?.dispatchEvent(new Event('submit'));\n }\n\n function tryApplyHighlight(event: KeyboardEvent): boolean {\n const filteredOptions = get(deps.filteredOptions);\n const highlightedIndex = get(deps.highlightedIndex);\n if (highlightedIndex <= -1 || filteredOptions.length === 0)\n return false;\n\n deps.applyHighlighted();\n event.preventDefault();\n return true;\n }\n\n function tryApplyCustom(event: KeyboardEvent, requireEmptyOptions: boolean): boolean {\n const customValue = toValue(options.customValue);\n const internalSearch = get(deps.internalSearch);\n if (!customValue || !internalSearch)\n return false;\n\n const filteredOptions = get(deps.filteredOptions);\n if (requireEmptyOptions) {\n if (filteredOptions.length > 0)\n return false;\n }\n else {\n const highlightedIndex = get(deps.highlightedIndex);\n if (highlightedMatchesSearch(filteredOptions, highlightedIndex, internalSearch))\n return false;\n }\n\n applyCustomValue(event, toValue(options.multiple));\n return true;\n }\n\n /**\n * Enter with the menu open, which takes the option the user navigated to\n * before anything the component highlighted on its own.\n */\n function handleOpenMenuEnter(event: KeyboardEvent): boolean {\n if (get(deps.userNavigated) && tryApplyHighlight(event))\n return true;\n\n // Fall back to a typed custom value when it differs from the highlight.\n if (tryApplyCustom(event, false))\n return true;\n\n // Use the (auto-)highlighted option if there is one.\n if (tryApplyHighlight(event))\n return true;\n\n // No options matched but custom values are allowed — accept the typed text.\n return tryApplyCustom(event, true);\n }\n\n function onEnter(event: KeyboardEvent): void {\n if (get(deps.isOpen)) {\n if (handleOpenMenuEnter(event))\n return;\n\n // Open with nothing to act on, so Enter is swallowed rather than submitting a surrounding form\n event.preventDefault();\n return;\n }\n\n // Nothing selected, menu closed: open menu\n if (get(deps.value).length === 0) {\n set(deps.isOpen, true);\n return event.preventDefault();\n }\n\n // Menu closed with a value selected: let Enter submit the parent form.\n submitClosestForm();\n }\n\n function onTab(event: KeyboardEvent): void {\n const isOpen = get(deps.isOpen);\n const filteredOptions = get(deps.filteredOptions);\n const highlightedIndex = get(deps.highlightedIndex);\n const multiple = toValue(options.multiple);\n\n if (isOpen && filteredOptions.length > 0 && highlightedIndex > -1 && !multiple) {\n deps.applyHighlighted();\n event.preventDefault();\n }\n }\n\n function onInputDeletePressed(): void {\n const internalSearch = get(deps.internalSearch);\n const value = get(deps.value);\n const total = value.length;\n const multiple = toValue(options.multiple);\n\n if (!internalSearch && total > 0) {\n if (multiple) {\n if (toValue(options.chips)) {\n set(focusedValueIndex, total - 1);\n }\n else {\n const lastItem = value[total - 1];\n if (lastItem !== undefined)\n deps.removeValue(lastItem);\n }\n }\n else {\n deps.clear();\n }\n }\n }\n\n // Watch value changes to reset focused index\n watch(deps.value, () => {\n setValueFocus(-1);\n });\n\n // Watch focused value index to handle chip focus\n watch(focusedValueIndex, async (index) => {\n const multiple = toValue(options.multiple);\n if (index === -1 || !multiple)\n return;\n\n await nextTick(() => {\n const activator = get(deps.activator);\n const element = activator?.querySelector<HTMLElement>(`[data-index=\"${index}\"]`);\n element?.focus();\n });\n });\n\n return {\n // eslint-disable-next-line @rotki/composable-return-readonly -- written by focus.ts onInputFocused\n focusedValueIndex,\n moveSelectedValueHighlight,\n onEnter,\n onInputDeletePressed,\n onTab,\n setValueFocus,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"keyboard-navigation.js","names":[],"sources":["../../../../src/composables/forms/auto-complete/keyboard-navigation.ts"],"sourcesContent":["import type { MaybeRefOrGetter, Ref, TemplateRef } from 'vue';\nimport { get, set } from '@vueuse/shared';\n\nexport interface UseAutoCompleteKeyboardNavigationOptions {\n /** Whether multiple items can be selected. */\n multiple: MaybeRefOrGetter<boolean>;\n /** Whether custom (free-text) values are allowed. */\n customValue: MaybeRefOrGetter<boolean>;\n /** Whether selected values are displayed as chips. */\n chips: MaybeRefOrGetter<boolean>;\n}\n\nexport interface UseAutoCompleteKeyboardNavigationDeps<TItem> {\n internalSearch: Ref<string>;\n value: Ref<TItem[]>;\n filteredOptions: Ref<TItem[]>;\n isOpen: Ref<boolean>;\n highlightedIndex: Ref<number>;\n userNavigated: Ref<boolean>;\n searchInputFocused: Ref<boolean>;\n applyHighlighted: () => void;\n clear: () => void;\n removeValue: (item: TItem) => void;\n setSearchAsValue: () => void;\n getText: (item: TItem) => string | undefined;\n updateInternalSearch: (value?: string) => void;\n activator: Ref<HTMLElement | undefined> | TemplateRef<HTMLElement>;\n}\n\nexport interface UseAutoCompleteKeyboardNavigationReturn {\n /**\n * The selected value (a chip) that Backspace or Delete would remove, -1 for none. It is only\n * drawn as selected: focus stays in the input, where no browser shortcut takes the key.\n */\n focusedValueIndex: Ref<number>;\n moveSelectedValueHighlight: (event: KeyboardEvent, next: boolean) => void;\n onEnter: (event: KeyboardEvent) => void;\n onTab: (event: KeyboardEvent) => void;\n onInputDeletePressed: (event: KeyboardEvent) => void;\n /** Drops the value selection on any key that does not act on it. */\n onInputKeydown: (event: KeyboardEvent) => void;\n setValueFocus: (index: number) => void;\n}\n\nexport function useAutoCompleteKeyboardNavigation<TItem>(\n options: UseAutoCompleteKeyboardNavigationOptions,\n deps: UseAutoCompleteKeyboardNavigationDeps<TItem>,\n): UseAutoCompleteKeyboardNavigationReturn {\n const focusedValueIndex = ref<number>(-1);\n\n function setValueFocus(index: number): void {\n set(focusedValueIndex, index);\n }\n\n function moveSelectedValueHighlight(event: KeyboardEvent, next: boolean): void {\n const multiple = toValue(options.multiple);\n const internalSearchValue = get(deps.internalSearch);\n\n if (!multiple || internalSearchValue.length > 0)\n return;\n\n event.preventDefault();\n const total = get(deps.value).length;\n\n let current = get(focusedValueIndex);\n\n if (current === -1) {\n set(focusedValueIndex, next ? 0 : total - 1);\n return;\n }\n\n const move = next ? 1 : -1;\n current += move;\n\n if (current < 0 || current >= total) {\n set(focusedValueIndex, -1);\n set(deps.searchInputFocused, true);\n }\n else {\n set(focusedValueIndex, current);\n }\n }\n\n function highlightedMatchesSearch(filteredOptions: TItem[], highlightedIndex: number, search: string): boolean {\n if (highlightedIndex < 0 || highlightedIndex >= filteredOptions.length)\n return false;\n\n const option = filteredOptions[highlightedIndex];\n if (typeof option === 'string')\n return option === search;\n\n if (option && typeof option === 'object')\n return (deps.getText(option) ?? '') === search;\n\n return false;\n }\n\n function applyCustomValue(event: KeyboardEvent, multiple: boolean): void {\n deps.setSearchAsValue();\n if (!multiple)\n set(deps.isOpen, false);\n event.preventDefault();\n }\n\n function submitClosestForm(): void {\n const activator = get(deps.activator);\n const form = activator?.closest('form');\n form?.dispatchEvent(new Event('submit'));\n }\n\n function tryApplyHighlight(event: KeyboardEvent): boolean {\n const filteredOptions = get(deps.filteredOptions);\n const highlightedIndex = get(deps.highlightedIndex);\n if (highlightedIndex <= -1 || filteredOptions.length === 0)\n return false;\n\n deps.applyHighlighted();\n event.preventDefault();\n return true;\n }\n\n function tryApplyCustom(event: KeyboardEvent, requireEmptyOptions: boolean): boolean {\n const customValue = toValue(options.customValue);\n const internalSearch = get(deps.internalSearch);\n if (!customValue || !internalSearch)\n return false;\n\n const filteredOptions = get(deps.filteredOptions);\n if (requireEmptyOptions) {\n if (filteredOptions.length > 0)\n return false;\n }\n else {\n const highlightedIndex = get(deps.highlightedIndex);\n if (highlightedMatchesSearch(filteredOptions, highlightedIndex, internalSearch))\n return false;\n }\n\n applyCustomValue(event, toValue(options.multiple));\n return true;\n }\n\n /**\n * Enter with the menu open, which takes the option the user navigated to\n * before anything the component highlighted on its own.\n */\n function handleOpenMenuEnter(event: KeyboardEvent): boolean {\n if (get(deps.userNavigated) && tryApplyHighlight(event))\n return true;\n\n // Fall back to a typed custom value when it differs from the highlight.\n if (tryApplyCustom(event, false))\n return true;\n\n // Use the (auto-)highlighted option if there is one.\n if (tryApplyHighlight(event))\n return true;\n\n // No options matched but custom values are allowed — accept the typed text.\n return tryApplyCustom(event, true);\n }\n\n function onEnter(event: KeyboardEvent): void {\n if (get(deps.isOpen)) {\n if (handleOpenMenuEnter(event))\n return;\n\n // Open with nothing to act on, so Enter is swallowed rather than submitting a surrounding form\n event.preventDefault();\n return;\n }\n\n // Nothing selected, menu closed: open menu\n if (get(deps.value).length === 0) {\n set(deps.isOpen, true);\n return event.preventDefault();\n }\n\n // Menu closed with a value selected: let Enter submit the parent form.\n submitClosestForm();\n }\n\n function onTab(event: KeyboardEvent): void {\n const isOpen = get(deps.isOpen);\n const filteredOptions = get(deps.filteredOptions);\n const highlightedIndex = get(deps.highlightedIndex);\n const multiple = toValue(options.multiple);\n\n if (isOpen && filteredOptions.length > 0 && highlightedIndex > -1 && !multiple) {\n deps.applyHighlighted();\n event.preventDefault();\n }\n }\n\n /**\n * Removes the selected value. Alt with it, where custom values are accepted, puts the value's\n * text back in the search instead of dropping it.\n *\n * @param event - the Backspace or Delete press\n * @param item - the value the highlighted chip stands for\n */\n function removeSelectedValue(event: KeyboardEvent, item: TItem): void {\n event.preventDefault();\n set(focusedValueIndex, -1);\n deps.removeValue(item);\n if (event.altKey && toValue(options.customValue))\n deps.updateInternalSearch(deps.getText(item) ?? '');\n }\n\n /** Backspace or Delete with nothing typed: steps onto the last value, or drops it outright. */\n function deleteFromEmptySearch(value: TItem[]): void {\n if (!toValue(options.multiple)) {\n deps.clear();\n return;\n }\n if (toValue(options.chips)) {\n set(focusedValueIndex, value.length - 1);\n return;\n }\n const lastItem = value.at(-1);\n if (lastItem !== undefined)\n deps.removeValue(lastItem);\n }\n\n function onInputDeletePressed(event: KeyboardEvent): void {\n const value = get(deps.value);\n const selected = value[get(focusedValueIndex)];\n if (selected !== undefined) {\n removeSelectedValue(event, selected);\n return;\n }\n\n if (!get(deps.internalSearch) && value.length > 0)\n deleteFromEmptySearch(value);\n }\n\n function onInputKeydown(event: KeyboardEvent): void {\n if (get(focusedValueIndex) === -1)\n return;\n // a modifier pressed on its own (the Alt of Alt + Backspace) keeps the selection for the key it modifies\n const keepsSelection = ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Alt', 'AltGraph', 'Control', 'Meta', 'Shift'].includes(event.key);\n if (!keepsSelection)\n set(focusedValueIndex, -1);\n }\n\n // Watch value changes to reset focused index\n watch(deps.value, () => {\n setValueFocus(-1);\n });\n\n return {\n // eslint-disable-next-line @rotki/composable-return-readonly -- written by focus.ts onInputFocused\n focusedValueIndex,\n moveSelectedValueHighlight,\n onEnter,\n onInputDeletePressed,\n onInputKeydown,\n onTab,\n setValueFocus,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ComputedRef, Ref } from 'vue';
|
|
2
|
+
export interface UseAutoCompleteSelectionDeps<TItem> {
|
|
3
|
+
value: Ref<TItem[]>;
|
|
4
|
+
multiple: ComputedRef<boolean>;
|
|
5
|
+
/** Whether a single pick shows its text in the input, rather than a slot or chip drawing it. */
|
|
6
|
+
shouldApplyValueAsSearch: ComputedRef<boolean>;
|
|
7
|
+
isOpen: Ref<boolean>;
|
|
8
|
+
searchInputFocused: Ref<boolean>;
|
|
9
|
+
internalSearch: Ref<string>;
|
|
10
|
+
/** The item's place in the selection, -1 when it is not picked. */
|
|
11
|
+
itemIndexInValue: (item: TItem) => number;
|
|
12
|
+
getText: (item: TItem) => string | undefined;
|
|
13
|
+
textValueToProperValue: (text: string) => TItem;
|
|
14
|
+
updateInternalSearch: (value?: string) => void;
|
|
15
|
+
/** Keeps focus on the input after a pick, showing the value rather than an empty search. */
|
|
16
|
+
settleOnInput: () => void;
|
|
17
|
+
/** Empties the model: an empty list when several values are picked, undefined otherwise. */
|
|
18
|
+
resetModel: () => void;
|
|
19
|
+
}
|
|
20
|
+
export interface UseAutoCompleteSelectionReturn<TItem> {
|
|
21
|
+
/**
|
|
22
|
+
* Picks an item, or drops it when several values are picked and it already is one.
|
|
23
|
+
* `skipRefocused` leaves focus where it is, for a value set from typed text.
|
|
24
|
+
*/
|
|
25
|
+
setValue: (item: TItem, skipRefocused?: boolean) => Promise<void>;
|
|
26
|
+
/** Turns the typed search into a value, where custom values are accepted. */
|
|
27
|
+
setSearchAsValue: () => void;
|
|
28
|
+
clear: () => void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The changes to the auto-complete's selection: picking or dropping an item, taking the typed text
|
|
32
|
+
* as a value, and clearing, with the search and focus that follow each.
|
|
33
|
+
*
|
|
34
|
+
* @param deps - the selection state and the operations it calls
|
|
35
|
+
* @returns the selection operations
|
|
36
|
+
*/
|
|
37
|
+
export declare function useAutoCompleteSelection<TItem>(deps: UseAutoCompleteSelectionDeps<TItem>): UseAutoCompleteSelectionReturn<TItem>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { nextTick } from "vue";
|
|
2
|
+
import { get, set } from "@vueuse/shared";
|
|
3
|
+
//#region src/composables/forms/auto-complete/selection.ts
|
|
4
|
+
/**
|
|
5
|
+
* The changes to the auto-complete's selection: picking or dropping an item, taking the typed text
|
|
6
|
+
* as a value, and clearing, with the search and focus that follow each.
|
|
7
|
+
*
|
|
8
|
+
* @param deps - the selection state and the operations it calls
|
|
9
|
+
* @returns the selection operations
|
|
10
|
+
*/
|
|
11
|
+
function useAutoCompleteSelection(deps) {
|
|
12
|
+
function toggleInSelection(item) {
|
|
13
|
+
const newValue = [...get(deps.value)];
|
|
14
|
+
const indexInValue = deps.itemIndexInValue(item);
|
|
15
|
+
if (indexInValue === -1) {
|
|
16
|
+
deps.updateInternalSearch();
|
|
17
|
+
newValue.push(item);
|
|
18
|
+
} else newValue.splice(indexInValue, 1);
|
|
19
|
+
set(deps.value, newValue);
|
|
20
|
+
}
|
|
21
|
+
function replaceSelection(item) {
|
|
22
|
+
if (get(deps.shouldApplyValueAsSearch)) deps.updateInternalSearch(deps.getText(item));
|
|
23
|
+
else deps.updateInternalSearch();
|
|
24
|
+
set(deps.value, [item]);
|
|
25
|
+
}
|
|
26
|
+
async function setValue(item, skipRefocused = false) {
|
|
27
|
+
if (get(deps.multiple)) {
|
|
28
|
+
toggleInSelection(item);
|
|
29
|
+
if (!skipRefocused) set(deps.searchInputFocused, true);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
replaceSelection(item);
|
|
33
|
+
if (skipRefocused) {
|
|
34
|
+
set(deps.isOpen, false);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
deps.settleOnInput();
|
|
38
|
+
await nextTick(() => {
|
|
39
|
+
set(deps.isOpen, false);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function setSearchAsValue() {
|
|
43
|
+
const searchToBeValue = get(deps.internalSearch);
|
|
44
|
+
if (!searchToBeValue) return;
|
|
45
|
+
setValue(deps.textValueToProperValue(searchToBeValue), true);
|
|
46
|
+
}
|
|
47
|
+
function clear() {
|
|
48
|
+
deps.updateInternalSearch();
|
|
49
|
+
deps.resetModel();
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
clear,
|
|
53
|
+
setSearchAsValue,
|
|
54
|
+
setValue
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { useAutoCompleteSelection };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=selection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection.js","names":[],"sources":["../../../../src/composables/forms/auto-complete/selection.ts"],"sourcesContent":["import type { ComputedRef, Ref } from 'vue';\n\nexport interface UseAutoCompleteSelectionDeps<TItem> {\n value: Ref<TItem[]>;\n multiple: ComputedRef<boolean>;\n /** Whether a single pick shows its text in the input, rather than a slot or chip drawing it. */\n shouldApplyValueAsSearch: ComputedRef<boolean>;\n isOpen: Ref<boolean>;\n searchInputFocused: Ref<boolean>;\n internalSearch: Ref<string>;\n /** The item's place in the selection, -1 when it is not picked. */\n itemIndexInValue: (item: TItem) => number;\n getText: (item: TItem) => string | undefined;\n textValueToProperValue: (text: string) => TItem;\n updateInternalSearch: (value?: string) => void;\n /** Keeps focus on the input after a pick, showing the value rather than an empty search. */\n settleOnInput: () => void;\n /** Empties the model: an empty list when several values are picked, undefined otherwise. */\n resetModel: () => void;\n}\n\nexport interface UseAutoCompleteSelectionReturn<TItem> {\n /**\n * Picks an item, or drops it when several values are picked and it already is one.\n * `skipRefocused` leaves focus where it is, for a value set from typed text.\n */\n setValue: (item: TItem, skipRefocused?: boolean) => Promise<void>;\n /** Turns the typed search into a value, where custom values are accepted. */\n setSearchAsValue: () => void;\n clear: () => void;\n}\n\n/**\n * The changes to the auto-complete's selection: picking or dropping an item, taking the typed text\n * as a value, and clearing, with the search and focus that follow each.\n *\n * @param deps - the selection state and the operations it calls\n * @returns the selection operations\n */\nexport function useAutoCompleteSelection<TItem>(deps: UseAutoCompleteSelectionDeps<TItem>): UseAutoCompleteSelectionReturn<TItem> {\n function toggleInSelection(item: TItem): void {\n const newValue = [...get(deps.value)];\n const indexInValue = deps.itemIndexInValue(item);\n if (indexInValue === -1) {\n deps.updateInternalSearch();\n newValue.push(item);\n }\n else {\n newValue.splice(indexInValue, 1);\n }\n set(deps.value, newValue);\n }\n\n function replaceSelection(item: TItem): void {\n if (get(deps.shouldApplyValueAsSearch))\n deps.updateInternalSearch(deps.getText(item));\n else deps.updateInternalSearch();\n set(deps.value, [item]);\n }\n\n async function setValue(item: TItem, skipRefocused = false): Promise<void> {\n if (get(deps.multiple)) {\n toggleInSelection(item);\n if (!skipRefocused)\n set(deps.searchInputFocused, true);\n return;\n }\n\n replaceSelection(item);\n if (skipRefocused) {\n set(deps.isOpen, false);\n return;\n }\n // focus stays on the combobox, which keeps showing the value just picked\n deps.settleOnInput();\n await nextTick(() => {\n set(deps.isOpen, false);\n });\n }\n\n function setSearchAsValue(): void {\n const searchToBeValue = get(deps.internalSearch);\n if (!searchToBeValue)\n return;\n\n setValue(deps.textValueToProperValue(searchToBeValue), true);\n }\n\n function clear(): void {\n deps.updateInternalSearch();\n deps.resetModel();\n }\n\n return { clear, setSearchAsValue, setValue };\n}\n"],"mappings":";;;;;;;;;;AAuCA,SAAgB,yBAAgC,MAAkF;CAChI,SAAS,kBAAkB,MAAmB;EAC5C,MAAM,WAAW,CAAC,GAAG,IAAI,KAAK,KAAK,CAAC;EACpC,MAAM,eAAe,KAAK,iBAAiB,IAAI;EAC/C,IAAI,iBAAiB,IAAI;GACvB,KAAK,qBAAqB;GAC1B,SAAS,KAAK,IAAI;EACpB,OAEE,SAAS,OAAO,cAAc,CAAC;EAEjC,IAAI,KAAK,OAAO,QAAQ;CAC1B;CAEA,SAAS,iBAAiB,MAAmB;EAC3C,IAAI,IAAI,KAAK,wBAAwB,GACnC,KAAK,qBAAqB,KAAK,QAAQ,IAAI,CAAC;OACzC,KAAK,qBAAqB;EAC/B,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC;CACxB;CAEA,eAAe,SAAS,MAAa,gBAAgB,OAAsB;EACzE,IAAI,IAAI,KAAK,QAAQ,GAAG;GACtB,kBAAkB,IAAI;GACtB,IAAI,CAAC,eACH,IAAI,KAAK,oBAAoB,IAAI;GACnC;EACF;EAEA,iBAAiB,IAAI;EACrB,IAAI,eAAe;GACjB,IAAI,KAAK,QAAQ,KAAK;GACtB;EACF;EAEA,KAAK,cAAc;EACnB,MAAM,eAAe;GACnB,IAAI,KAAK,QAAQ,KAAK;EACxB,CAAC;CACH;CAEA,SAAS,mBAAyB;EAChC,MAAM,kBAAkB,IAAI,KAAK,cAAc;EAC/C,IAAI,CAAC,iBACH;EAEF,SAAS,KAAK,uBAAuB,eAAe,GAAG,IAAI;CAC7D;CAEA,SAAS,QAAc;EACrB,KAAK,qBAAqB;EAC1B,KAAK,WAAW;CAClB;CAEA,OAAO;EAAE;EAAO;EAAkB;CAAS;AAC7C"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assert } from "../../../utils/assert.js";
|
|
2
|
+
import { isEqual } from "../../../utils/is-equal.js";
|
|
2
3
|
import { computed, toValue, watch } from "vue";
|
|
3
4
|
import { get, set } from "@vueuse/shared";
|
|
4
5
|
//#region src/composables/forms/auto-complete/value.ts
|
|
@@ -71,6 +72,24 @@ function useAutoCompleteValue(modelValue, options, opts, deps) {
|
|
|
71
72
|
deps.updateInternalSearch(deps.getText(firstItem));
|
|
72
73
|
}
|
|
73
74
|
}, { immediate: true });
|
|
75
|
+
/**
|
|
76
|
+
* Reconciles the selection against a new set of options, comparing by
|
|
77
|
+
* reference before by value so an unchanged list costs nothing.
|
|
78
|
+
*
|
|
79
|
+
* Only a selection the previous options could resolve is reconciled: a value
|
|
80
|
+
* that was already unresolvable is one the consumer set before its options
|
|
81
|
+
* arrived, since an async list starts empty, and clearing it here would
|
|
82
|
+
* discard a legitimate value.
|
|
83
|
+
*
|
|
84
|
+
* @param curr - the new options
|
|
85
|
+
* @param old - the options before them
|
|
86
|
+
*/
|
|
87
|
+
function onOptionsChanged(curr, old) {
|
|
88
|
+
if (curr === old || toValue(opts.customValue) || isEqual(curr, old)) return;
|
|
89
|
+
if (!toValue(deps.multiple) && resolveIn(old).length === 0) return;
|
|
90
|
+
setSelected(get(value));
|
|
91
|
+
}
|
|
92
|
+
watch(() => toValue(options), onOptionsChanged);
|
|
74
93
|
return {
|
|
75
94
|
resolveIn,
|
|
76
95
|
setSelected,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"value.js","names":[],"sources":["../../../../src/composables/forms/auto-complete/value.ts"],"sourcesContent":["import type { ComputedRef, MaybeRefOrGetter, Ref } from 'vue';\nimport { get, set } from '@vueuse/shared';\nimport { assert } from '@/utils/assert';\n\nexport interface UseAutoCompleteValueOptions<TItem> {\n /** The property used as the unique identifier for each item. */\n keyAttr?: MaybeRefOrGetter<keyof TItem | undefined>;\n /** Whether to return the full item object instead of just the key. */\n returnObject?: MaybeRefOrGetter<boolean>;\n /** Whether custom (free-text) values are allowed. */\n customValue?: MaybeRefOrGetter<boolean>;\n}\n\nexport interface UseAutoCompleteValueReturn<TItem> {\n value: ComputedRef<TItem[]>;\n setSelected: (selected: TItem[]) => void;\n /** Resolves the current model value against an arbitrary options list. */\n resolveIn: (options: TItem[]) => TItem[];\n}\n\nexport interface UseAutoCompleteValueDeps<TItem> {\n getIdentifier: (item: TItem) => any;\n getText: (item: TItem) => string | undefined;\n textValueToProperValue: (val: any, returnObject?: boolean) => TItem;\n shouldApplyValueAsSearch: MaybeRefOrGetter<boolean>;\n isOpen: MaybeRefOrGetter<boolean>;\n multiple: MaybeRefOrGetter<boolean>;\n updateInternalSearch: (value?: string) => void;\n}\n\nexport function useAutoCompleteValue<TValue, TItem>(\n modelValue: Ref<TValue | undefined>,\n options: MaybeRefOrGetter<TItem[]>,\n opts: UseAutoCompleteValueOptions<TItem>,\n deps: UseAutoCompleteValueDeps<TItem>,\n): UseAutoCompleteValueReturn<TItem> {\n function buildOptionsMap(optionsData: TItem[]): Map<any, TItem> {\n const map = new Map<any, TItem>();\n for (const item of optionsData) {\n map.set(deps.getIdentifier(item), item);\n }\n return map;\n }\n\n // Create maps for O(1) lookup performance\n const optionsMap = computed<Map<any, TItem>>(() => buildOptionsMap(toValue(options)));\n\n function convertModelValueToArray(modelValueData: TValue | undefined): any[] {\n if (modelValueData === null || modelValueData === undefined)\n return [];\n return Array.isArray(modelValueData) ? modelValueData : [modelValueData];\n }\n\n function filterValues(\n valueArray: any[],\n optionsMapData: Map<any, TItem>,\n customValue: boolean,\n returnObject: boolean,\n getKey: (val: any) => any,\n ): TItem[] {\n const filtered: TItem[] = [];\n for (const val of valueArray) {\n const inOptions = optionsMapData.get(getKey(val));\n if (inOptions !== undefined)\n filtered.push(inOptions);\n else if (customValue)\n filtered.push(deps.textValueToProperValue(val, returnObject));\n }\n return filtered;\n }\n\n function processReturnObjectValue(\n modelValueData: TValue | undefined,\n optionsMapData: Map<any, TItem>,\n returnObject: boolean,\n customValue: boolean,\n ): TItem[] {\n const multipleValue = Array.isArray(modelValueData);\n const valueArray = convertModelValueToArray(modelValueData);\n const filtered = filterValues(valueArray, optionsMapData, customValue, returnObject, val => deps.getIdentifier(val));\n\n if (multipleValue || filtered.length === 0)\n return filtered;\n\n const val = filtered[0];\n assert(val !== undefined && val !== null);\n return [val];\n }\n\n function processStandardValue(\n modelValueData: TValue | undefined,\n optionsMapData: Map<any, TItem>,\n returnObject: boolean,\n customValue: boolean,\n ): TItem[] {\n const valueArray = convertModelValueToArray(modelValueData);\n return filterValues(valueArray, optionsMapData, customValue, returnObject, val => val);\n }\n\n function onUpdateModelValue(value: TValue | undefined): void {\n set(modelValue, value);\n }\n\n function setSelected(selected: TItem[]): void {\n const keyAttr = toValue(opts.keyAttr);\n const returnObject = toValue(opts.returnObject);\n const selection = keyAttr && !returnObject ? selected.map(item => item[keyAttr]) : selected;\n\n if (toValue(deps.multiple))\n return onUpdateModelValue(selection as TValue);\n\n if (selection.length === 0)\n return onUpdateModelValue(undefined);\n\n return onUpdateModelValue(selection[0] as TValue);\n }\n\n function resolveAgainst(optionsMapData: Map<any, TItem>): TItem[] {\n const modelValueData = get(modelValue);\n const keyAttr = toValue(opts.keyAttr);\n const returnObject = toValue(opts.returnObject) ?? false;\n const customValue = toValue(opts.customValue) ?? false;\n\n if (keyAttr && returnObject)\n return processReturnObjectValue(modelValueData, optionsMapData, returnObject, customValue);\n\n return processStandardValue(modelValueData, optionsMapData, returnObject, customValue);\n }\n\n function resolveIn(optionsData: TItem[]): TItem[] {\n return resolveAgainst(buildOptionsMap(optionsData));\n }\n\n const value = computed<TItem[]>({\n get: () => resolveAgainst(get(optionsMap)),\n set: (selected: TItem[]): void => {\n setSelected(selected);\n },\n });\n\n // Watches `value` rather than `modelValue`, so the search text resyncs once late `options` resolve it\n watch([value, () => toValue(deps.isOpen)], () => {\n if (toValue(deps.isOpen) || !toValue(deps.shouldApplyValueAsSearch))\n return;\n\n const currentValue = get(value);\n if (toValue(deps.multiple) || currentValue.length === 0) {\n deps.updateInternalSearch();\n }\n else {\n const firstItem = currentValue[0];\n assert(firstItem !== undefined && firstItem !== null);\n deps.updateInternalSearch(deps.getText(firstItem));\n }\n }, { immediate: true });\n\n return {\n resolveIn,\n setSelected,\n value,\n };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"value.js","names":[],"sources":["../../../../src/composables/forms/auto-complete/value.ts"],"sourcesContent":["import type { ComputedRef, MaybeRefOrGetter, Ref } from 'vue';\nimport { get, set } from '@vueuse/shared';\nimport { assert } from '@/utils/assert';\nimport { isEqual } from '@/utils/is-equal';\n\nexport interface UseAutoCompleteValueOptions<TItem> {\n /** The property used as the unique identifier for each item. */\n keyAttr?: MaybeRefOrGetter<keyof TItem | undefined>;\n /** Whether to return the full item object instead of just the key. */\n returnObject?: MaybeRefOrGetter<boolean>;\n /** Whether custom (free-text) values are allowed. */\n customValue?: MaybeRefOrGetter<boolean>;\n}\n\nexport interface UseAutoCompleteValueReturn<TItem> {\n value: ComputedRef<TItem[]>;\n setSelected: (selected: TItem[]) => void;\n /** Resolves the current model value against an arbitrary options list. */\n resolveIn: (options: TItem[]) => TItem[];\n}\n\nexport interface UseAutoCompleteValueDeps<TItem> {\n getIdentifier: (item: TItem) => any;\n getText: (item: TItem) => string | undefined;\n textValueToProperValue: (val: any, returnObject?: boolean) => TItem;\n shouldApplyValueAsSearch: MaybeRefOrGetter<boolean>;\n isOpen: MaybeRefOrGetter<boolean>;\n multiple: MaybeRefOrGetter<boolean>;\n updateInternalSearch: (value?: string) => void;\n}\n\nexport function useAutoCompleteValue<TValue, TItem>(\n modelValue: Ref<TValue | undefined>,\n options: MaybeRefOrGetter<TItem[]>,\n opts: UseAutoCompleteValueOptions<TItem>,\n deps: UseAutoCompleteValueDeps<TItem>,\n): UseAutoCompleteValueReturn<TItem> {\n function buildOptionsMap(optionsData: TItem[]): Map<any, TItem> {\n const map = new Map<any, TItem>();\n for (const item of optionsData) {\n map.set(deps.getIdentifier(item), item);\n }\n return map;\n }\n\n // Create maps for O(1) lookup performance\n const optionsMap = computed<Map<any, TItem>>(() => buildOptionsMap(toValue(options)));\n\n function convertModelValueToArray(modelValueData: TValue | undefined): any[] {\n if (modelValueData === null || modelValueData === undefined)\n return [];\n return Array.isArray(modelValueData) ? modelValueData : [modelValueData];\n }\n\n function filterValues(\n valueArray: any[],\n optionsMapData: Map<any, TItem>,\n customValue: boolean,\n returnObject: boolean,\n getKey: (val: any) => any,\n ): TItem[] {\n const filtered: TItem[] = [];\n for (const val of valueArray) {\n const inOptions = optionsMapData.get(getKey(val));\n if (inOptions !== undefined)\n filtered.push(inOptions);\n else if (customValue)\n filtered.push(deps.textValueToProperValue(val, returnObject));\n }\n return filtered;\n }\n\n function processReturnObjectValue(\n modelValueData: TValue | undefined,\n optionsMapData: Map<any, TItem>,\n returnObject: boolean,\n customValue: boolean,\n ): TItem[] {\n const multipleValue = Array.isArray(modelValueData);\n const valueArray = convertModelValueToArray(modelValueData);\n const filtered = filterValues(valueArray, optionsMapData, customValue, returnObject, val => deps.getIdentifier(val));\n\n if (multipleValue || filtered.length === 0)\n return filtered;\n\n const val = filtered[0];\n assert(val !== undefined && val !== null);\n return [val];\n }\n\n function processStandardValue(\n modelValueData: TValue | undefined,\n optionsMapData: Map<any, TItem>,\n returnObject: boolean,\n customValue: boolean,\n ): TItem[] {\n const valueArray = convertModelValueToArray(modelValueData);\n return filterValues(valueArray, optionsMapData, customValue, returnObject, val => val);\n }\n\n function onUpdateModelValue(value: TValue | undefined): void {\n set(modelValue, value);\n }\n\n function setSelected(selected: TItem[]): void {\n const keyAttr = toValue(opts.keyAttr);\n const returnObject = toValue(opts.returnObject);\n const selection = keyAttr && !returnObject ? selected.map(item => item[keyAttr]) : selected;\n\n if (toValue(deps.multiple))\n return onUpdateModelValue(selection as TValue);\n\n if (selection.length === 0)\n return onUpdateModelValue(undefined);\n\n return onUpdateModelValue(selection[0] as TValue);\n }\n\n function resolveAgainst(optionsMapData: Map<any, TItem>): TItem[] {\n const modelValueData = get(modelValue);\n const keyAttr = toValue(opts.keyAttr);\n const returnObject = toValue(opts.returnObject) ?? false;\n const customValue = toValue(opts.customValue) ?? false;\n\n if (keyAttr && returnObject)\n return processReturnObjectValue(modelValueData, optionsMapData, returnObject, customValue);\n\n return processStandardValue(modelValueData, optionsMapData, returnObject, customValue);\n }\n\n function resolveIn(optionsData: TItem[]): TItem[] {\n return resolveAgainst(buildOptionsMap(optionsData));\n }\n\n const value = computed<TItem[]>({\n get: () => resolveAgainst(get(optionsMap)),\n set: (selected: TItem[]): void => {\n setSelected(selected);\n },\n });\n\n // Watches `value` rather than `modelValue`, so the search text resyncs once late `options` resolve it\n watch([value, () => toValue(deps.isOpen)], () => {\n if (toValue(deps.isOpen) || !toValue(deps.shouldApplyValueAsSearch))\n return;\n\n const currentValue = get(value);\n if (toValue(deps.multiple) || currentValue.length === 0) {\n deps.updateInternalSearch();\n }\n else {\n const firstItem = currentValue[0];\n assert(firstItem !== undefined && firstItem !== null);\n deps.updateInternalSearch(deps.getText(firstItem));\n }\n }, { immediate: true });\n\n /**\n * Reconciles the selection against a new set of options, comparing by\n * reference before by value so an unchanged list costs nothing.\n *\n * Only a selection the previous options could resolve is reconciled: a value\n * that was already unresolvable is one the consumer set before its options\n * arrived, since an async list starts empty, and clearing it here would\n * discard a legitimate value.\n *\n * @param curr - the new options\n * @param old - the options before them\n */\n function onOptionsChanged(curr: TItem[], old: TItem[]): void {\n if (curr === old || toValue(opts.customValue) || isEqual(curr, old))\n return;\n\n if (!toValue(deps.multiple) && resolveIn(old).length === 0)\n return;\n\n setSelected(get(value));\n }\n\n watch(() => toValue(options), onOptionsChanged);\n\n return {\n resolveIn,\n setSelected,\n value,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ComputedRef, Ref } from 'vue';
|
|
2
|
+
/**
|
|
3
|
+
* Whether a box scrolls while holding nothing a keyboard user can reach, the case WCAG 2.1.1 and
|
|
4
|
+
* axe's scrollable-region-focusable rule cover: the arrow keys only scroll what has focus.
|
|
5
|
+
*
|
|
6
|
+
* @param element - the scrolling box
|
|
7
|
+
* @returns true when the box overflows and has no focusable content
|
|
8
|
+
*/
|
|
9
|
+
export declare function needsOwnFocus(element: HTMLElement): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Makes a scrolling box a tab stop while, and only while, it overflows with nothing focusable
|
|
12
|
+
* inside, so keyboard users can scroll it without a stray tab stop on every box that fits.
|
|
13
|
+
*
|
|
14
|
+
* @param target - the scrolling box
|
|
15
|
+
* @returns the `tabindex` to bind on the box: 0 when it needs focus, otherwise undefined
|
|
16
|
+
*/
|
|
17
|
+
export declare function useKeyboardScroll(target: Readonly<Ref<HTMLElement | null | undefined>>): ComputedRef<0 | undefined>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { computed, onMounted, shallowRef } from "vue";
|
|
2
|
+
import { useMutationObserver, useResizeObserver } from "@vueuse/core";
|
|
3
|
+
import { get as get$1, set as set$1 } from "@vueuse/shared";
|
|
4
|
+
//#region src/composables/keyboard-scroll.ts
|
|
5
|
+
/** What a keyboard user can tab to inside a scrolling box, and so scroll it by. */
|
|
6
|
+
var FOCUSABLE = [
|
|
7
|
+
"a[href]",
|
|
8
|
+
"button:not([disabled])",
|
|
9
|
+
"input:not([disabled]):not([type=hidden])",
|
|
10
|
+
"select:not([disabled])",
|
|
11
|
+
"textarea:not([disabled])",
|
|
12
|
+
"[tabindex]:not([tabindex=\"-1\"])"
|
|
13
|
+
].join(",");
|
|
14
|
+
/**
|
|
15
|
+
* Whether a box scrolls while holding nothing a keyboard user can reach, the case WCAG 2.1.1 and
|
|
16
|
+
* axe's scrollable-region-focusable rule cover: the arrow keys only scroll what has focus.
|
|
17
|
+
*
|
|
18
|
+
* @param element - the scrolling box
|
|
19
|
+
* @returns true when the box overflows and has no focusable content
|
|
20
|
+
*/
|
|
21
|
+
function needsOwnFocus(element) {
|
|
22
|
+
return (element.scrollWidth > element.clientWidth + 1 || element.scrollHeight > element.clientHeight + 1) && element.querySelector(FOCUSABLE) === null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Makes a scrolling box a tab stop while, and only while, it overflows with nothing focusable
|
|
26
|
+
* inside, so keyboard users can scroll it without a stray tab stop on every box that fits.
|
|
27
|
+
*
|
|
28
|
+
* @param target - the scrolling box
|
|
29
|
+
* @returns the `tabindex` to bind on the box: 0 when it needs focus, otherwise undefined
|
|
30
|
+
*/
|
|
31
|
+
function useKeyboardScroll(target) {
|
|
32
|
+
const focusable = shallowRef(false);
|
|
33
|
+
let scheduled = false;
|
|
34
|
+
function update() {
|
|
35
|
+
scheduled = false;
|
|
36
|
+
const element = get$1(target);
|
|
37
|
+
set$1(focusable, element ? needsOwnFocus(element) : false);
|
|
38
|
+
}
|
|
39
|
+
/** Reading the scroll sizes forces layout, so a burst of resizes and mutations is checked once a frame. */
|
|
40
|
+
function schedule() {
|
|
41
|
+
if (scheduled) return;
|
|
42
|
+
scheduled = true;
|
|
43
|
+
requestAnimationFrame(update);
|
|
44
|
+
}
|
|
45
|
+
onMounted(update);
|
|
46
|
+
useResizeObserver(target, schedule);
|
|
47
|
+
useMutationObserver(target, schedule, {
|
|
48
|
+
childList: true,
|
|
49
|
+
subtree: true,
|
|
50
|
+
attributes: true,
|
|
51
|
+
attributeFilter: ["disabled", "tabindex"]
|
|
52
|
+
});
|
|
53
|
+
return computed(() => get$1(focusable) ? 0 : void 0);
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { needsOwnFocus, useKeyboardScroll };
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=keyboard-scroll.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard-scroll.js","names":[],"sources":["../../src/composables/keyboard-scroll.ts"],"sourcesContent":["import type { ComputedRef, Ref } from 'vue';\nimport { useMutationObserver, useResizeObserver } from '@vueuse/core';\n\n/** What a keyboard user can tab to inside a scrolling box, and so scroll it by. */\nconst FOCUSABLE = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled]):not([type=hidden])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n].join(',');\n\n/**\n * Whether a box scrolls while holding nothing a keyboard user can reach, the case WCAG 2.1.1 and\n * axe's scrollable-region-focusable rule cover: the arrow keys only scroll what has focus.\n *\n * @param element - the scrolling box\n * @returns true when the box overflows and has no focusable content\n */\nexport function needsOwnFocus(element: HTMLElement): boolean {\n const overflows = element.scrollWidth > element.clientWidth + 1 || element.scrollHeight > element.clientHeight + 1;\n return overflows && element.querySelector(FOCUSABLE) === null;\n}\n\n/**\n * Makes a scrolling box a tab stop while, and only while, it overflows with nothing focusable\n * inside, so keyboard users can scroll it without a stray tab stop on every box that fits.\n *\n * @param target - the scrolling box\n * @returns the `tabindex` to bind on the box: 0 when it needs focus, otherwise undefined\n */\nexport function useKeyboardScroll(target: Readonly<Ref<HTMLElement | null | undefined>>): ComputedRef<0 | undefined> {\n const focusable = shallowRef<boolean>(false);\n let scheduled = false;\n\n function update(): void {\n scheduled = false;\n const element = get(target);\n set(focusable, element ? needsOwnFocus(element) : false);\n }\n\n /** Reading the scroll sizes forces layout, so a burst of resizes and mutations is checked once a frame. */\n function schedule(): void {\n if (scheduled)\n return;\n scheduled = true;\n requestAnimationFrame(update);\n }\n\n // the first check runs at mount, so the box is reachable from its first render, not a frame later\n onMounted(update);\n useResizeObserver(target, schedule);\n // the box keeps its size while its content grows or swaps, which changes whether it overflows\n useMutationObserver(target, schedule, { childList: true, subtree: true, attributes: true, attributeFilter: ['disabled', 'tabindex'] });\n\n return computed<0 | undefined>(() => (get(focusable) ? 0 : undefined));\n}\n"],"mappings":";;;;;AAIA,IAAM,YAAY;CAChB;CACA;CACA;CACA;CACA;CACA;AACF,CAAC,CAAC,KAAK,GAAG;;;;;;;;AASV,SAAgB,cAAc,SAA+B;CAE3D,QADkB,QAAQ,cAAc,QAAQ,cAAc,KAAK,QAAQ,eAAe,QAAQ,eAAe,MAC7F,QAAQ,cAAc,SAAS,MAAM;AAC3D;;;;;;;;AASA,SAAgB,kBAAkB,QAAmF;CACnH,MAAM,YAAY,WAAoB,KAAK;CAC3C,IAAI,YAAY;CAEhB,SAAS,SAAe;EACtB,YAAY;EACZ,MAAM,UAAU,MAAI,MAAM;EAC1B,MAAI,WAAW,UAAU,cAAc,OAAO,IAAI,KAAK;CACzD;;CAGA,SAAS,WAAiB;EACxB,IAAI,WACF;EACF,YAAY;EACZ,sBAAsB,MAAM;CAC9B;CAGA,UAAU,MAAM;CAChB,kBAAkB,QAAQ,QAAQ;CAElC,oBAAoB,QAAQ,UAAU;EAAE,WAAW;EAAM,SAAS;EAAM,YAAY;EAAM,iBAAiB,CAAC,YAAY,UAAU;CAAE,CAAC;CAErI,OAAO,eAA+B,MAAI,SAAS,IAAI,IAAI,KAAA,CAAU;AACvE"}
|
|
@@ -5,6 +5,14 @@ export interface UseStickyTableHeaderRefs {
|
|
|
5
5
|
interface UseStickyTableHeaderReturn {
|
|
6
6
|
stick: Readonly<Ref<boolean>>;
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Whether an element becomes the containing block of its `position: fixed`
|
|
10
|
+
* descendants. The individual `translate`, `rotate` and `scale` properties
|
|
11
|
+
* count like `transform`: Tailwind 4 writes its translate utilities to
|
|
12
|
+
* `translate`, so a dialog centred with `-translate-x-1/2` leaves `transform`
|
|
13
|
+
* at `none`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createsFixedContainingBlock(style: CSSStyleDeclaration): boolean;
|
|
8
16
|
/**
|
|
9
17
|
* Setup sticky table header
|
|
10
18
|
*/
|
|
@@ -50,8 +50,18 @@ function findScrollParent(element) {
|
|
|
50
50
|
parent = parent.parentElement;
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
function isSet(value) {
|
|
54
|
+
return value !== void 0 && value !== "" && value !== "none";
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Whether an element becomes the containing block of its `position: fixed`
|
|
58
|
+
* descendants. The individual `translate`, `rotate` and `scale` properties
|
|
59
|
+
* count like `transform`: Tailwind 4 writes its translate utilities to
|
|
60
|
+
* `translate`, so a dialog centred with `-translate-x-1/2` leaves `transform`
|
|
61
|
+
* at `none`.
|
|
62
|
+
*/
|
|
53
63
|
function createsFixedContainingBlock(style) {
|
|
54
|
-
return style.transform
|
|
64
|
+
return isSet(style.transform) || isSet(style.translate) || isSet(style.rotate) || isSet(style.scale) || isSet(style.perspective) || isSet(style.filter) || isSet(style.backdropFilter) || /transform|translate|rotate|scale|perspective|filter/.test(style.willChange) || /paint|layout|strict|content/.test(style.contain);
|
|
55
65
|
}
|
|
56
66
|
/**
|
|
57
67
|
* Where a `position: fixed` descendant's `top: 0; left: 0` lands in the viewport. That is the
|
|
@@ -101,6 +111,7 @@ function useStickyTableHeader(sticky = shallowRef(false), offsetTop, tableRefs)
|
|
|
101
111
|
syncRafId = requestAnimationFrame(() => {
|
|
102
112
|
syncRafId = null;
|
|
103
113
|
applyColumnWidths(mainColumns, readColumnWidths(cloneColumns));
|
|
114
|
+
toggleStickyClass();
|
|
104
115
|
});
|
|
105
116
|
}
|
|
106
117
|
function observeColumnResizes(cloneColumns, mainColumns) {
|
|
@@ -195,6 +206,6 @@ function useStickyTableHeader(sticky = shallowRef(false), offsetTop, tableRefs)
|
|
|
195
206
|
return { stick: readonly(stick) };
|
|
196
207
|
}
|
|
197
208
|
//#endregion
|
|
198
|
-
export { useStickyTableHeader };
|
|
209
|
+
export { createsFixedContainingBlock, useStickyTableHeader };
|
|
199
210
|
|
|
200
211
|
//# sourceMappingURL=sticky-header.js.map
|