@rotki/ui-library 2.24.0 → 2.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/dist/components/accordions/accordion/RuiAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/accordions/accordions/RuiAccordions.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/alerts/RuiAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/avatars/RuiAvatar.vue.d.ts +1 -1
- 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 +1 -1
- package/dist/components/avatars/RuiAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/avatars/use-avatar-group.d.ts +1 -1
- package/dist/components/buttons/button/RuiButton.vue.d.ts +1 -1
- package/dist/components/buttons/button/RuiButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/buttons/button/button-styles.d.ts +40 -2
- package/dist/components/buttons/button/button-styles.js +38 -0
- 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 +1 -1
- package/dist/components/buttons/button-group/RuiButtonGroup.vue_vue_type_script_setup_true_lang.js +26 -14
- package/dist/components/buttons/button-group/RuiButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/calendar/RuiCalendar.js.map +1 -1
- 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 +7 -0
- package/dist/components/calendar/RuiCalendarGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/calendar/RuiCalendarHeader.vue.d.ts +1 -1
- 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.d.ts +1 -1
- package/dist/components/calendar/RuiCalendarMenu.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/components/calendar/RuiCalendarMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/cards/RuiCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/cards/RuiCardHeader.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/chips/RuiChip.vue.d.ts +1 -1
- package/dist/components/chips/RuiChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/color-picker/RuiColorBoard.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/color-picker/RuiColorDisplay.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/color-picker/RuiColorHue.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/components/color-picker/RuiColorHue.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/color-picker/RuiColorInput.vue_vue_type_script_setup_true_lang.js.map +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 +2 -2
- package/dist/components/date-time-picker/RuiDateTimePicker.vue_vue_type_script_setup_true_lang.js +54 -26
- 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.vue.d.ts +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.vue.d.ts +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 +48 -3
- package/dist/components/date-time-picker/partial-time.d.ts +1 -1
- package/dist/components/date-time-picker/partial-time.js +17 -6
- package/dist/components/date-time-picker/partial-time.js.map +1 -1
- package/dist/components/date-time-picker/segment-config.d.ts +1 -1
- package/dist/components/date-time-picker/segment-utils.d.ts +2 -2
- package/dist/components/date-time-picker/segment-utils.js +17 -5
- package/dist/components/date-time-picker/segment-utils.js.map +1 -1
- package/dist/components/date-time-picker/timezones.js.map +1 -1
- package/dist/components/date-time-picker/use-date-bounds.d.ts +2 -1
- package/dist/components/date-time-picker/use-date-bounds.js +7 -3
- 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 +13 -13
- package/dist/components/date-time-picker/use-date-time-selection.js +96 -86
- package/dist/components/date-time-picker/use-date-time-selection.js.map +1 -1
- package/dist/components/date-time-picker/use-input-handler.d.ts +1 -1
- package/dist/components/date-time-picker/use-keyboard-handler.d.ts +2 -2
- package/dist/components/date-time-picker/use-keyboard-handler.js +52 -13
- package/dist/components/date-time-picker/use-keyboard-handler.js.map +1 -1
- package/dist/components/date-time-picker/utils.d.ts +1 -1
- package/dist/components/date-time-picker/utils.js.map +1 -1
- package/dist/components/divider/RuiDivider.vue_vue_type_script_setup_true_lang.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 +2 -2
- package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js +136 -158
- 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 +7 -0
- package/dist/components/forms/auto-complete/RuiAutoCompleteOption.js.map +1 -0
- package/dist/components/forms/auto-complete/RuiAutoCompleteOption.vue.d.ts +45 -0
- package/dist/components/forms/auto-complete/RuiAutoCompleteOption.vue_vue_type_script_setup_true_lang.js +58 -0
- package/dist/components/forms/auto-complete/RuiAutoCompleteOption.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/forms/auto-complete/RuiAutoCompleteOptionList.js +7 -0
- package/dist/components/forms/auto-complete/RuiAutoCompleteOptionList.js.map +1 -0
- package/dist/components/forms/auto-complete/RuiAutoCompleteOptionList.vue.d.ts +70 -0
- package/dist/components/forms/auto-complete/RuiAutoCompleteOptionList.vue_vue_type_script_setup_true_lang.js +140 -0
- package/dist/components/forms/auto-complete/RuiAutoCompleteOptionList.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/forms/auto-complete/RuiAutoCompleteSelection.js +7 -0
- package/dist/components/forms/auto-complete/RuiAutoCompleteSelection.js.map +1 -0
- package/dist/components/forms/auto-complete/RuiAutoCompleteSelection.vue.d.ts +39 -0
- package/dist/components/forms/auto-complete/RuiAutoCompleteSelection.vue_vue_type_script_setup_true_lang.js +56 -0
- package/dist/components/forms/auto-complete/RuiAutoCompleteSelection.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/forms/auto-complete/auto-complete-styles.d.ts +48 -3
- package/dist/components/forms/category-picker/RuiCategoryPicker.js.map +1 -1
- package/dist/components/forms/category-picker/RuiCategoryPicker.vue.d.ts +2 -2
- package/dist/components/forms/category-picker/RuiCategoryPicker.vue_vue_type_script_setup_true_lang.js +49 -5
- 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 +69 -5
- package/dist/components/forms/category-picker/category-picker-styles.js +19 -0
- package/dist/components/forms/category-picker/category-picker-styles.js.map +1 -1
- package/dist/components/forms/check-control-styles.d.ts +2 -2
- 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.vue_vue_type_script_setup_true_lang.js.map +1 -1
- 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.map +1 -1
- 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.map +1 -1
- package/dist/components/forms/revealable-text-field/RuiRevealableTextField.vue.d.ts +1 -1
- 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 +2 -2
- package/dist/components/forms/select/RuiMenuSelect.vue_vue_type_script_setup_true_lang.js +10 -8
- package/dist/components/forms/select/RuiMenuSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
- 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 +48 -3
- package/dist/components/forms/slider/RuiSlider.js.map +1 -1
- package/dist/components/forms/slider/RuiSlider.vue_vue_type_script_setup_true_lang.js +22 -12
- package/dist/components/forms/slider/RuiSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/slider/RuiSliderTicks.js +7 -0
- package/dist/components/forms/slider/RuiSliderTicks.js.map +1 -0
- package/dist/components/forms/slider/RuiSliderTicks.vue.d.ts +15 -0
- package/dist/components/forms/slider/RuiSliderTicks.vue_vue_type_script_setup_true_lang.js +59 -0
- package/dist/components/forms/slider/RuiSliderTicks.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/forms/slider/slider-styles.d.ts +2 -2
- package/dist/components/forms/switch/RuiSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/text-area/RuiTextArea.vue.d.ts +1 -1
- 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 +3 -3
- package/dist/components/forms/text-area/text-area-styles.js.map +1 -1
- package/dist/components/forms/text-field/RuiTextField.vue.d.ts +3 -3
- 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 -4
- package/dist/components/forms/text-field/text-field-styles.js +34 -1
- package/dist/components/forms/text-field/text-field-styles.js.map +1 -1
- package/dist/components/forms/text-input-styles.d.ts +20 -5
- package/dist/components/forms/text-input-styles.js +16 -1
- package/dist/components/forms/text-input-styles.js.map +1 -1
- 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 +26 -0
- package/dist/components/icons/RuiIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/index.d.ts +57 -53
- package/dist/components/index.js +5 -1
- package/dist/components/loaders/RuiSkeletonBase.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/loaders/RuiSkeletonLoader.vue.d.ts +2 -2
- package/dist/components/loaders/RuiSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/logos/RuiLogo.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/logos/use-logo-sources.d.ts +1 -1
- package/dist/components/logos/use-logo-sources.js +6 -1
- package/dist/components/logos/use-logo-sources.js.map +1 -1
- package/dist/components/overlays/badge/RuiBadge.vue.d.ts +1 -1
- package/dist/components/overlays/badge/RuiBadge.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/overlays/bottom-sheet/RuiBottomSheet.js.map +1 -1
- package/dist/components/overlays/bottom-sheet/RuiBottomSheet.vue.d.ts +1 -0
- package/dist/components/overlays/bottom-sheet/RuiBottomSheet.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 +2 -0
- package/dist/components/overlays/dialog/RuiDialog.vue_vue_type_script_setup_true_lang.js +15 -1
- 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_vue_type_script_setup_true_lang.js +20 -0
- package/dist/components/overlays/menu/RuiMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.js.map +1 -1
- package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/overlays/notification/RuiNotification.vue_vue_type_script_setup_true_lang.js.map +1 -1
- 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 +2 -2
- package/dist/components/progress/RuiProgress.js.map +1 -1
- package/dist/components/progress/RuiProgress.vue.d.ts +3 -3
- package/dist/components/progress/RuiProgress.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/steppers/RuiFooterStepper.vue.d.ts +1 -1
- 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_vue_type_script_setup_true_lang.js +8 -0
- package/dist/components/steppers/RuiStepper.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/steppers/RuiStepperCustomIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
- 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 +5 -6
- package/dist/components/tables/RuiDataTable.vue_vue_type_script_setup_true_lang.js +42 -22
- package/dist/components/tables/RuiDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/RuiExpandButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/RuiTable.js +7 -0
- package/dist/components/tables/RuiTable.js.map +1 -0
- package/dist/components/tables/RuiTable.vue.d.ts +40 -0
- package/dist/components/tables/RuiTable.vue_vue_type_script_setup_true_lang.js +95 -0
- package/dist/components/tables/RuiTable.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/tables/RuiTableEmptyState.js.map +1 -1
- package/dist/components/tables/RuiTableEmptyState.vue.d.ts +5 -0
- package/dist/components/tables/RuiTableEmptyState.vue_vue_type_script_setup_true_lang.js +25 -10
- package/dist/components/tables/RuiTableEmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/RuiTableErrorState.js +7 -0
- package/dist/components/tables/RuiTableErrorState.js.map +1 -0
- package/dist/components/tables/RuiTableErrorState.vue.d.ts +28 -0
- package/dist/components/tables/RuiTableErrorState.vue_vue_type_script_setup_true_lang.js +44 -0
- package/dist/components/tables/RuiTableErrorState.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/tables/RuiTableHead.js.map +1 -1
- package/dist/components/tables/RuiTableHead.vue.d.ts +5 -9
- package/dist/components/tables/RuiTableHead.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/RuiTableLoadingState.js +7 -0
- package/dist/components/tables/RuiTableLoadingState.js.map +1 -0
- package/dist/components/tables/RuiTableLoadingState.vue.d.ts +13 -0
- package/dist/components/tables/RuiTableLoadingState.vue_vue_type_script_setup_true_lang.js +40 -0
- package/dist/components/tables/RuiTableLoadingState.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/tables/RuiTablePagination.vue.d.ts +1 -1
- package/dist/components/tables/RuiTablePagination.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/components/tables/RuiTablePagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableBody.vue.d.ts +1 -1
- package/dist/components/tables/data-table/RuiDataTableBody.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableCell.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableCell.vue.d.ts +1 -1
- package/dist/components/tables/data-table/RuiDataTableCell.vue_vue_type_script_setup_true_lang.js +6 -0
- package/dist/components/tables/data-table/RuiDataTableCell.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableEmptyRow.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 +6 -0
- 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 +5 -0
- package/dist/components/tables/data-table/RuiDataTableGroupRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableLoadingRow.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableLoadingRow.vue_vue_type_script_setup_true_lang.js +2 -6
- package/dist/components/tables/data-table/RuiDataTableLoadingRow.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.d.ts +2 -2
- package/dist/components/tables/data-table/RuiDataTableMobileSort.vue_vue_type_script_setup_true_lang.js +4 -0
- 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.d.ts +1 -1
- package/dist/components/tables/data-table/RuiDataTableRow.vue_vue_type_script_setup_true_lang.js +25 -0
- package/dist/components/tables/data-table/RuiDataTableRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/data-table/context.d.ts +2 -3
- package/dist/components/tables/data-table/context.js.map +1 -1
- package/dist/components/tables/data-table-styles.d.ts +2 -5
- package/dist/components/tables/data-table-styles.js +1 -2
- package/dist/components/tables/data-table-styles.js.map +1 -1
- package/dist/components/tabs/tab/RuiTab.js.map +1 -1
- package/dist/components/tabs/tab/RuiTab.vue.d.ts +1 -1
- package/dist/components/tabs/tab/RuiTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
- 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.d.ts +9 -0
- package/dist/components/tabs/tab-items/RuiTabItems.vue_vue_type_script_setup_true_lang.js +16 -0
- package/dist/components/tabs/tab-items/RuiTabItems.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tabs/tabs/RuiTabs.js.map +1 -1
- package/dist/components/tabs/tabs/RuiTabs.vue.d.ts +6 -1
- package/dist/components/tabs/tabs/RuiTabs.vue_vue_type_script_setup_true_lang.js +12 -0
- package/dist/components/tabs/tabs/RuiTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tabs/tabs/use-tab-routing.d.ts +1 -1
- 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 +2 -2
- package/dist/composables/colors/index.d.ts +3 -3
- package/dist/composables/dropdown-menu.d.ts +2 -2
- package/dist/composables/dropdown-menu.js +26 -23
- package/dist/composables/dropdown-menu.js.map +1 -1
- package/dist/composables/floating.d.ts +7 -8
- package/dist/composables/floating.js +19 -14
- package/dist/composables/floating.js.map +1 -1
- package/dist/composables/forms/auto-complete/focus.js +5 -0
- package/dist/composables/forms/auto-complete/focus.js.map +1 -1
- package/dist/composables/forms/auto-complete/keyboard-navigation.js +12 -5
- package/dist/composables/forms/auto-complete/keyboard-navigation.js.map +1 -1
- package/dist/composables/forms/auto-complete/search.d.ts +1 -1
- package/dist/composables/forms/auto-complete/search.js +5 -0
- package/dist/composables/forms/auto-complete/search.js.map +1 -1
- package/dist/composables/forms/auto-complete/value.js.map +1 -1
- package/dist/composables/forms/category-picker/use-category-picker.d.ts +1 -1
- package/dist/composables/forms/category-picker/use-category-picker.js +1 -0
- package/dist/composables/forms/category-picker/use-category-picker.js.map +1 -1
- package/dist/composables/forms/use-prepend-append-width.d.ts +18 -3
- package/dist/composables/forms/use-prepend-append-width.js +18 -3
- package/dist/composables/forms/use-prepend-append-width.js.map +1 -1
- package/dist/composables/index.d.ts +4 -3
- package/dist/composables/index.js +2 -1
- package/dist/composables/overlay-stack.d.ts +52 -0
- package/dist/composables/overlay-stack.js +93 -0
- package/dist/composables/overlay-stack.js.map +1 -0
- package/dist/composables/sticky-header.js.map +1 -1
- package/dist/composables/tables/data-table/expansion.js +7 -0
- package/dist/composables/tables/data-table/expansion.js.map +1 -1
- package/dist/composables/tables/data-table/grouping.d.ts +1 -1
- package/dist/composables/tables/data-table/pagination.d.ts +2 -2
- package/dist/composables/tables/data-table/selection.d.ts +1 -1
- package/dist/composables/tables/data-table/selection.js +37 -34
- package/dist/composables/tables/data-table/selection.js.map +1 -1
- package/dist/composables/theme.d.ts +4 -2
- package/dist/composables/theme.js +18 -24
- package/dist/composables/theme.js.map +1 -1
- package/dist/i18n/keys.d.ts +1 -1
- package/dist/i18n/keys.js +1 -1
- package/dist/i18n/keys.js.map +1 -1
- package/dist/icons/icons_1.d.ts +8 -8
- package/dist/icons/icons_1.js +246 -223
- package/dist/icons/icons_1.js.map +1 -1
- package/dist/icons/icons_2.d.ts +26 -26
- package/dist/icons/icons_2.js +320 -400
- package/dist/icons/icons_2.js.map +1 -1
- package/dist/icons/icons_3.d.ts +46 -46
- package/dist/icons/icons_3.js +661 -706
- package/dist/icons/icons_3.js.map +1 -1
- package/dist/icons/icons_4.d.ts +63 -63
- package/dist/icons/icons_4.js +4347 -4480
- package/dist/icons/icons_4.js.map +1 -1
- package/dist/icons/icons_5.d.ts +60 -1
- package/dist/icons/icons_5.js +774 -31
- package/dist/icons/icons_5.js.map +1 -1
- package/dist/icons/index.d.ts +1 -1
- package/dist/icons/index.js +84 -25
- package/dist/icons/index.js.map +1 -1
- package/dist/index.js +11 -6
- package/dist/index.js.map +1 -1
- package/dist/style.css +83 -10
- package/dist/theme/index.js +1 -0
- package/dist/theme/index.js.map +1 -1
- package/dist/types/theme.d.ts +1 -1
- package/dist/utils/assert.js +2 -2
- package/dist/utils/assert.js.map +1 -1
- package/dist/utils/blockie.d.ts +4 -0
- package/dist/utils/blockie.js +8 -0
- package/dist/utils/blockie.js.map +1 -1
- package/dist/utils/helpers.d.ts +24 -18
- package/dist/utils/helpers.js +28 -21
- package/dist/utils/helpers.js.map +1 -1
- package/dist/utils/is-equal.d.ts +9 -0
- package/dist/utils/is-equal.js +22 -15
- package/dist/utils/is-equal.js.map +1 -1
- package/dist/utils/tv.d.ts +6 -2
- package/dist/utils/tv.js +30 -14
- package/dist/utils/tv.js.map +1 -1
- package/dist/vite-plugin/client.d.ts +1 -1
- package/dist/vite-plugin/index.js +29 -21
- package/dist/vite-plugin/index.js.map +1 -1
- package/dist/vite-plugin/scanner.d.ts +5 -2
- package/dist/vite-plugin/scanner.js +5 -2
- package/dist/vite-plugin/scanner.js.map +1 -1
- package/dist/vite-plugin/types.d.ts +4 -8
- package/dist/web-types.json +863 -50
- package/package.json +32 -33
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface AutoCompleteSelectionProps<TItem> {
|
|
2
|
+
items: TItem[];
|
|
3
|
+
chips: boolean;
|
|
4
|
+
dense: boolean;
|
|
5
|
+
multiple: boolean;
|
|
6
|
+
searchInputFocused: boolean;
|
|
7
|
+
hideSelectionWrapper: boolean;
|
|
8
|
+
getIdentifier: (item: TItem) => unknown;
|
|
9
|
+
getText: (item: TItem) => string | undefined;
|
|
10
|
+
chipAttrs: (item: TItem, index: number) => Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
declare const __VLS_export: <TItem>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
13
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<AutoCompleteSelectionProps<TItem>> & (typeof globalThis extends {
|
|
14
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
15
|
+
} ? P : {});
|
|
16
|
+
expose: (exposed: {}) => void;
|
|
17
|
+
attrs: any;
|
|
18
|
+
slots: {
|
|
19
|
+
prepend?: (props: {
|
|
20
|
+
index: number;
|
|
21
|
+
item: TItem;
|
|
22
|
+
}) => any;
|
|
23
|
+
default?: (props: {
|
|
24
|
+
index: number;
|
|
25
|
+
item: TItem;
|
|
26
|
+
chipAttrs: Record<string, unknown>;
|
|
27
|
+
}) => any;
|
|
28
|
+
};
|
|
29
|
+
emit: {};
|
|
30
|
+
}>) => import("vue").VNode & {
|
|
31
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
32
|
+
};
|
|
33
|
+
declare const _default: typeof __VLS_export;
|
|
34
|
+
export default _default;
|
|
35
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
36
|
+
[K in keyof T]: T[K];
|
|
37
|
+
} : {
|
|
38
|
+
[K in keyof T as K]: T[K];
|
|
39
|
+
}) & {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import RuiChip_default from "../../chips/RuiChip.js";
|
|
2
|
+
import { getTextToken } from "../../../utils/helpers.js";
|
|
3
|
+
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, defineComponent, mergeProps, normalizeClass, openBlock, renderList, renderSlot, toDisplayString, unref, useSlots, withCtx } from "vue";
|
|
4
|
+
//#region src/components/forms/auto-complete/RuiAutoCompleteSelection.vue?vue&type=script&setup=true&lang.ts
|
|
5
|
+
var _hoisted_1 = { class: "flex" };
|
|
6
|
+
var RuiAutoCompleteSelection_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
7
|
+
__name: "RuiAutoCompleteSelection",
|
|
8
|
+
props: {
|
|
9
|
+
items: {},
|
|
10
|
+
chips: { type: Boolean },
|
|
11
|
+
dense: { type: Boolean },
|
|
12
|
+
multiple: { type: Boolean },
|
|
13
|
+
searchInputFocused: { type: Boolean },
|
|
14
|
+
hideSelectionWrapper: { type: Boolean },
|
|
15
|
+
getIdentifier: { type: Function },
|
|
16
|
+
getText: { type: Function },
|
|
17
|
+
chipAttrs: { type: Function }
|
|
18
|
+
},
|
|
19
|
+
setup(__props) {
|
|
20
|
+
const slots = useSlots();
|
|
21
|
+
/**
|
|
22
|
+
* Without a chip, a selected value is only drawn when it cannot be read off the
|
|
23
|
+
* search input: while picking several values, or while the input is unfocused
|
|
24
|
+
* and a slot renders the selection itself.
|
|
25
|
+
*/
|
|
26
|
+
const showPlainSelection = computed(() => __props.multiple || !__props.searchInputFocused && (!!slots.prepend || !!slots.default));
|
|
27
|
+
return (_ctx, _cache) => {
|
|
28
|
+
return openBlock(true), createElementBlock(Fragment, null, renderList(__props.items, (item, i) => {
|
|
29
|
+
return openBlock(), createElementBlock(Fragment, { key: __props.getIdentifier(item)?.toString() }, [__props.chips ? (openBlock(), createBlock(RuiChip_default, mergeProps({
|
|
30
|
+
key: unref(getTextToken)(__props.getIdentifier(item)),
|
|
31
|
+
tabindex: "-1",
|
|
32
|
+
size: __props.dense ? "sm" : "md",
|
|
33
|
+
closeable: "",
|
|
34
|
+
class: { "leading-3": __props.dense },
|
|
35
|
+
clickable: ""
|
|
36
|
+
}, { ref_for: true }, __props.chipAttrs(item, i)), {
|
|
37
|
+
default: withCtx(() => [createElementVNode("div", _hoisted_1, [renderSlot(_ctx.$slots, "prepend", mergeProps({ index: i }, { ref_for: true }, { item })), renderSlot(_ctx.$slots, "default", mergeProps({ index: i }, { ref_for: true }, {
|
|
38
|
+
item,
|
|
39
|
+
chipAttrs: __props.chipAttrs(item, i)
|
|
40
|
+
}), () => [createTextVNode(toDisplayString(__props.getText(item)), 1)])])]),
|
|
41
|
+
_: 2
|
|
42
|
+
}, 1040, ["size", "class"])) : unref(showPlainSelection) ? (openBlock(), createElementBlock("div", {
|
|
43
|
+
key: 1,
|
|
44
|
+
class: normalizeClass(__props.hideSelectionWrapper ? "contents" : "flex")
|
|
45
|
+
}, [renderSlot(_ctx.$slots, "prepend", mergeProps({ index: i }, { ref_for: true }, { item })), __props.multiple || !!slots.default ? renderSlot(_ctx.$slots, "default", mergeProps({ index: i }, { ref_for: true }, {
|
|
46
|
+
item,
|
|
47
|
+
chipAttrs: __props.chipAttrs(item, i)
|
|
48
|
+
}), () => [createTextVNode(toDisplayString(__props.getText(item)), 1)], void 0, 0) : createCommentVNode("", true)], 2)) : createCommentVNode("", true)], 64);
|
|
49
|
+
}), 128);
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
//#endregion
|
|
54
|
+
export { RuiAutoCompleteSelection_vue_vue_type_script_setup_true_lang_default as default };
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=RuiAutoCompleteSelection.vue_vue_type_script_setup_true_lang.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RuiAutoCompleteSelection.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../../src/components/forms/auto-complete/RuiAutoCompleteSelection.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TItem\">\nimport RuiChip from '@/components/chips/RuiChip.vue';\nimport { getTextToken } from '@/utils/helpers';\n\nexport interface AutoCompleteSelectionProps<TItem> {\n items: TItem[];\n chips: boolean;\n dense: boolean;\n multiple: boolean;\n searchInputFocused: boolean;\n hideSelectionWrapper: boolean;\n getIdentifier: (item: TItem) => unknown;\n getText: (item: TItem) => string | undefined;\n chipAttrs: (item: TItem, index: number) => Record<string, unknown>;\n}\n\nconst {\n chipAttrs,\n chips,\n dense,\n getIdentifier,\n getText,\n hideSelectionWrapper,\n items,\n multiple,\n searchInputFocused,\n} = defineProps<AutoCompleteSelectionProps<TItem>>();\n\nconst slots = defineSlots<{\n prepend?: (props: { index: number; item: TItem }) => any;\n default?: (props: { index: number; item: TItem; chipAttrs: Record<string, unknown> }) => any;\n}>();\n\n/**\n * Without a chip, a selected value is only drawn when it cannot be read off the\n * search input: while picking several values, or while the input is unfocused\n * and a slot renders the selection itself.\n */\nconst showPlainSelection = computed<boolean>(() =>\n multiple || (!searchInputFocused && (!!slots.prepend || !!slots.default)),\n);\n</script>\n\n<template>\n <template\n v-for=\"(item, i) in items\"\n :key=\"getIdentifier(item)?.toString()\"\n >\n <RuiChip\n v-if=\"chips\"\n :key=\"getTextToken(getIdentifier(item))\"\n tabindex=\"-1\"\n :size=\"dense ? 'sm' : 'md'\"\n closeable\n :class=\"{ 'leading-3': dense }\"\n clickable\n v-bind=\"chipAttrs(item, i)\"\n >\n <div class=\"flex\">\n <slot\n name=\"prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n :index=\"i\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </RuiChip>\n <div\n v-else-if=\"showPlainSelection\"\n :class=\"hideSelectionWrapper ? 'contents' : 'flex'\"\n >\n <slot\n name=\"prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n v-if=\"multiple || !!slots.default\"\n :index=\"i\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </template>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;EA4BA,MAAM,QAAQ,SAAA;;;;;;EAUd,MAAM,qBAAqB,eACzB,QAAA,YAAa,CAAC,QAAA,uBAAuB,CAAC,CAAC,MAAM,WAAW,CAAC,CAAC,MAAM,QAClE;;GAIE,OAAA,UAAA,IAAA,GAAA,mBA6CW,UAAA,MAAA,WA5CW,QAAA,QAAZ,MAAM,MAAC;IACT,OAAA,UAAA,GAAA,mBAAA,UAAA,EAAA,KAAA,QAAA,cAAc,IAAI,CAAA,EAAG,SAAQ,EAAA,GAAA,CAG3B,QAAA,SADR,UAAA,GAAA,YAuBU,iBAvBV,WAuBU;KArBP,KAAK,MAAA,YAAA,CAAY,CAAC,QAAA,cAAc,IAAI,CAAA;KACrC,UAAS;KACR,MAAM,QAAA,QAAK,OAAA;KACZ,WAAA;KACC,OAAK,EAAA,aAAiB,QAAA,MAAK;KAC5B,WAAA;IACQ,GAAA,EAAA,SAAA,KAAA,GAAA,QAAA,UAAU,MAAM,CAAC,CAAA,GAAA;KAEzB,SAAA,cAYM,CAZN,mBAYM,OAZN,YAYM,CAXJ,WAIE,KAAA,QAAA,WAJF,WAIE,EAFC,OAAO,EAAC,GAAA,EAAA,SAAA,KAAA,GAAA,EACC,KAAI,CAAA,CAAA,GAEhB,WAKO,KAAA,QAAA,WALP,WAKO,EAJJ,OAAO,EAAC,GAAA,EAAA,SAAA,KAAA,GAAA;MACC;MAAI,WAAa,QAAA,UAAU,MAAM,CAAC;KAAA,CAAA,SAGvC,CADF,gBAAA,gBAAA,QAAA,QAAQ,IAAI,CAAA,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;;IAKR,GAAA,MAAA,CAAA,QAAA,OAAA,CAAA,KAAA,MAAA,kBAAA,KADb,UAAA,GAAA,mBAgBM,OAAA;;KAdH,OAAK,eAAE,QAAA,uBAAoB,aAAA,MAAA;IAE5B,GAAA,CAAA,WAIE,KAAA,QAAA,WAJF,WAIE,EAFC,OAAO,EAAC,GAAA,EAAA,SAAA,KAAA,GAAA,EACC,KAAI,CAAA,CAAA,GAGR,QAAA,YAAQ,CAAA,CAAM,MAAM,UAD5B,WAMO,KAAA,QAAA,WANP,WAMO,EAJJ,OAAO,EAAC,GAAA,EAAA,SAAA,KAAA,GAAA;KACC;KAAI,WAAa,QAAA,UAAU,MAAM,CAAC;IAAA,CAAA,SAGvC,CADF,gBAAA,gBAAA,QAAA,QAAQ,IAAI,CAAA,GAAA,CAAA,CAAA,GAAA,KAAA,GAAA,CAAA,IAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,EAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type TextInputVariant } from '
|
|
1
|
+
import { type TextInputVariant } from '../text-input-styles.js';
|
|
2
2
|
export type AutoCompleteVariant = TextInputVariant;
|
|
3
3
|
/**
|
|
4
4
|
* Overrides the `value` slot for flex-wrap chip layout.
|
|
@@ -64,6 +64,47 @@ export declare const autoCompleteStyles: import("tailwind-variants").TVReturnTyp
|
|
|
64
64
|
hovered: {
|
|
65
65
|
true: {};
|
|
66
66
|
};
|
|
67
|
+
} & {
|
|
68
|
+
focused: {
|
|
69
|
+
true: {
|
|
70
|
+
fieldset: string;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
hovered: {
|
|
74
|
+
true: {
|
|
75
|
+
fieldset: string;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
disabled: {
|
|
79
|
+
true: {
|
|
80
|
+
fieldset: string;
|
|
81
|
+
label: string;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
active: {
|
|
85
|
+
true: {};
|
|
86
|
+
};
|
|
87
|
+
showLabel: {
|
|
88
|
+
true: {};
|
|
89
|
+
};
|
|
90
|
+
validation: {
|
|
91
|
+
error: {
|
|
92
|
+
fieldset: string;
|
|
93
|
+
label: string;
|
|
94
|
+
};
|
|
95
|
+
success: {
|
|
96
|
+
fieldset: string;
|
|
97
|
+
label: string;
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
color: {
|
|
101
|
+
primary: {};
|
|
102
|
+
secondary: {};
|
|
103
|
+
error: {};
|
|
104
|
+
warning: {};
|
|
105
|
+
info: {};
|
|
106
|
+
success: {};
|
|
107
|
+
};
|
|
67
108
|
}, {
|
|
68
109
|
fieldset: string;
|
|
69
110
|
legend: string;
|
|
@@ -78,6 +119,10 @@ export declare const autoCompleteStyles: import("tailwind-variants").TVReturnTyp
|
|
|
78
119
|
icon: string;
|
|
79
120
|
iconWrapper: string;
|
|
80
121
|
required: string;
|
|
122
|
+
} & {
|
|
123
|
+
label: string;
|
|
124
|
+
fieldset: string;
|
|
125
|
+
legend: string;
|
|
81
126
|
}, import("tailwind-variants").TVReturnType<{
|
|
82
127
|
dense: {
|
|
83
128
|
true: {
|
|
@@ -282,7 +327,7 @@ export declare const autoCompleteStyles: import("tailwind-variants").TVReturnTyp
|
|
|
282
327
|
label: string;
|
|
283
328
|
fieldset: string;
|
|
284
329
|
legend: string;
|
|
285
|
-
}, import("tailwind-variants").
|
|
330
|
+
}, import("tailwind-variants").TVReturnTypeLike<{
|
|
286
331
|
focused: {
|
|
287
332
|
true: {
|
|
288
333
|
fieldset: string;
|
|
@@ -327,4 +372,4 @@ export declare const autoCompleteStyles: import("tailwind-variants").TVReturnTyp
|
|
|
327
372
|
label: string;
|
|
328
373
|
fieldset: string;
|
|
329
374
|
legend: string;
|
|
330
|
-
}
|
|
375
|
+
}>>>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiCategoryPicker.js","names":[],"sources":["../../../../src/components/forms/category-picker/RuiCategoryPicker.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue, TItem\">\nimport type { KeyOfType } from '@/composables/dropdown-menu';\nimport type { VueClassValue } from '@/types/class-value';\nimport { breakpointsTailwind, useBreakpoints } from '@vueuse/core';\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport { categoryPickerActivatorStyles, categoryPickerStyles, type CategoryPickerVariant } from '@/components/forms/category-picker/category-picker-styles';\nimport RuiTextField from '@/components/forms/text-field/RuiTextField.vue';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiDialog, { type DialogProps } from '@/components/overlays/dialog/RuiDialog.vue';\nimport RuiMenu from '@/components/overlays/menu/RuiMenu.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { Placement } from '@/composables/floating';\nimport { useCategoryPicker } from '@/composables/forms/category-picker/use-category-picker';\nimport { useFormTextDetail } from '@/utils/form-text-detail';\nimport { cn } from '@/utils/tv';\n\nexport interface RuiCategoryPickerClassNames {\n root?: VueClassValue;\n rail?: VueClassValue;\n detail?: VueClassValue;\n search?: VueClassValue;\n}\n\nexport type CategoryPickerBreakpoint = 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\nexport interface RuiCategoryPickerProps<TValue, TItem> {\n /** The full catalogue of selectable items. */\n items: TItem[];\n /** Resolve an item's category label. Takes precedence over `categoryAttr`. */\n categoryOf?: (item: TItem) => string;\n /** Resolve an item's category from one of its own properties. */\n categoryAttr?: keyof TItem;\n /** Property whose value becomes the model value. Omit to use the item itself. */\n keyAttr?: KeyOfType<TItem, TValue extends (infer U)[] ? U : TValue>;\n /** Property that produces an item's display text. */\n textAttr?: keyof TItem;\n /** Field label, and the title of the mobile sheet. */\n label?: string;\n /** Placeholder shown in the field while it is empty or being searched. */\n placeholder?: string;\n /** Allow filtering: type in the field (desktop) or the sheet's search box (mobile). */\n searchable?: boolean;\n /**\n * Custom predicate. Receives the resolved category label as the third\n * argument, matching the `RuiAutoComplete` filter signature.\n */\n filter?: (item: TItem, query: string, category: string) => boolean;\n /** Show the synthetic \"All\" pseudo-category as the first rail entry. */\n showAll?: boolean;\n /** Label for the \"All\" pseudo-category. */\n allLabel?: string;\n /** Text shown when no item matches the current search. */\n emptyText?: string;\n disabled?: boolean;\n readOnly?: boolean;\n dense?: boolean;\n clearable?: boolean;\n loading?: boolean;\n /** Trigger field style: underline (`default`), `outlined`, or `filled`. */\n variant?: CategoryPickerVariant;\n /** Marks the trigger field as required (renders the asterisk). */\n required?: boolean;\n /** Validation errors rendered under the trigger field. */\n errorMessages?: string | string[];\n /** Success messages rendered under the trigger field. */\n successMessages?: string | string[];\n /** Hint rendered under the trigger field. */\n hint?: string;\n /** Hide the details row (errors/success/hint) under the trigger field. */\n hideDetails?: boolean;\n /** Below this breakpoint the picker collapses to a single-pane drill-in. */\n mobileBreakpoint?: CategoryPickerBreakpoint;\n /** Forwarded to the mobile bottom-sheet (`RuiDialog`). */\n dialogOptions?: DialogProps;\n classNames?: RuiCategoryPickerClassNames;\n}\n\ndefineOptions({\n name: 'RuiCategoryPicker',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<TValue>();\nconst searchInput = defineModel<string>('search', { default: '' });\n\nconst {\n items,\n categoryOf = undefined,\n categoryAttr = undefined,\n keyAttr = undefined,\n textAttr = undefined,\n label = 'Select',\n placeholder = 'Search',\n searchable = true,\n filter = undefined,\n showAll = true,\n allLabel = 'All',\n emptyText = 'No results',\n disabled = false,\n readOnly = false,\n dense = false,\n clearable = false,\n loading = false,\n variant = 'outlined',\n required = false,\n errorMessages = [],\n successMessages = [],\n hint = '',\n hideDetails = false,\n mobileBreakpoint = 'md',\n dialogOptions = undefined,\n classNames = undefined,\n} = defineProps<RuiCategoryPickerProps<TValue, TItem>>();\n\nconst emit = defineEmits<{\n select: [item: TItem];\n}>();\n\ndefineSlots<{\n 'activator'?: (props: { attrs: { onClick?: () => void; onMouseover?: () => void; onMouseleave?: () => void }; isOpen: boolean; open: () => void; item: TItem | undefined; text: string | undefined }) => any;\n 'activator.label'?: (props: { item: TItem | undefined }) => any;\n 'selection'?: (props: { item: TItem }) => any;\n 'selection.prepend'?: (props: { item: TItem }) => any;\n 'category'?: (props: { category: string | null; label: string; active: boolean; count: number }) => any;\n 'item'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'item.prepend'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'item.append'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'empty'?: () => any;\n 'header'?: () => any;\n 'footer'?: (props: { close: () => void }) => any;\n}>();\n\nconst isOpen = ref<boolean>(false);\nconst focusedPane = ref<'rail' | 'detail'>('rail');\nconst highlightedItemIndex = ref<number>(0);\nconst drillStep = ref<'category' | 'detail'>('category');\n\nconst railRef = useTemplateRef<HTMLDivElement>('railRef');\nconst detailRef = useTemplateRef<HTMLDivElement>('detailRef');\nconst activatorRef = useTemplateRef<HTMLInputElement>('activatorRef');\nconst fieldRef = useTemplateRef<HTMLElement>('fieldRef');\nconst isHovered = ref<boolean>(false);\n\nconst { focused } = useFocus(activatorRef);\nconst { hasError, hasSuccess } = useFormTextDetail(() => errorMessages, () => successMessages);\n\nconst baseId = useId();\nconst breakpoints = useBreakpoints(breakpointsTailwind);\n\nfunction resolveCategory(item: TItem): string {\n if (categoryOf)\n return categoryOf(item);\n if (categoryAttr !== undefined)\n return String(item[categoryAttr] ?? '');\n return '';\n}\n\nconst {\n activeCategory,\n detailItems,\n detailRows,\n getIdentifier,\n getText,\n isEmpty,\n isSelected,\n railEntries,\n setActiveCategory,\n syncActiveToSelection,\n} = useCategoryPicker<TValue, TItem>({\n allLabel: () => allLabel,\n categoryOf: resolveCategory,\n filter: () => filter,\n items: () => items,\n keyAttr,\n modelValue,\n search: searchInput,\n searchable: () => searchable,\n showAll: () => showAll,\n textAttr,\n});\n\nconst isMobile = computed<boolean>(() => get(breakpoints.smaller(mobileBreakpoint)));\n\nconst mode = computed<'twoPane' | 'drill'>(() => (get(isMobile) ? 'drill' : 'twoPane'));\n\n// Desktop anchors an outside-click popover under the field (RuiMenu); narrow\n// width shows a full-width bottom sheet (RuiDialog). Each branch returns only\n// its own shell's props so the other component's props never leak as attrs.\nconst shell = computed<typeof RuiMenu | typeof RuiDialog>(() => (get(isMobile) ? RuiDialog : RuiMenu));\n\nconst shellProps = computed<Record<string, unknown>>(() => {\n if (get(isMobile)) {\n return {\n ariaLabel: dialogOptions?.ariaLabel ?? label,\n bottomSheet: dialogOptions?.bottomSheet ?? true,\n maxWidth: dialogOptions?.maxWidth,\n persistent: dialogOptions?.persistent,\n width: dialogOptions?.width ?? '100%',\n };\n }\n return {\n // Anchor to the field box, not the wrapper, so the popover sits directly\n // under the input instead of below the reserved details row.\n anchorEl: get(fieldRef) ?? undefined,\n // Drop the menu's default vertical padding so the popover box equals our\n // panel: the size middleware caps the panel to the available height, and\n // any extra chrome padding would push the popover past the viewport edge.\n classNames: { content: 'py-0', menu: 'z-[9999]' },\n closeOnContentClick: false,\n disableAutoFocus: true,\n fullWidth: true,\n options: { placement: Placement.bottomStart, size: true },\n persistent: dialogOptions?.persistent,\n persistOnActivatorClick: true,\n };\n});\n\nconst ui = computed<ReturnType<typeof categoryPickerStyles>>(() => categoryPickerStyles({\n dense,\n mode: get(mode),\n}));\n\nconst selectedItem = computed<TItem | undefined>(() => {\n const value = get(modelValue);\n if (value === undefined)\n return undefined;\n return items.find(item => getIdentifier(item) === value);\n});\n\nconst selectedText = computed<string | undefined>(() => {\n const selected = get(selectedItem);\n return selected ? getText(selected) : undefined;\n});\n\nconst outlined = computed<boolean>(() => variant === 'outlined');\n\n// Desktop lets the user type in the field itself (no separate search box);\n// narrow width keeps a read-only trigger that opens the sheet's own search.\nconst canType = computed<boolean>(() => searchable && !get(isMobile) && !readOnly && !disabled);\n\nconst isTyping = computed<boolean>(() => get(isOpen) && get(canType));\n\nconst float = computed<boolean>(() => (get(isOpen) || get(selectedItem) !== undefined || get(focused)) && get(outlined));\n\nconst legendText = computed<string>(() => {\n if (!get(float) || !label)\n return '';\n return required ? `${label} ﹡` : label;\n});\n\n// Drives both the clear button itself and the room the selection overlay has\n// to leave for it, so the two can never disagree.\nconst showClear = computed<boolean>(() =>\n clearable && get(selectedItem) !== undefined && !disabled && !readOnly);\n\nconst activatorUi = computed<ReturnType<typeof categoryPickerActivatorStyles>>(() => categoryPickerActivatorStyles({\n dense,\n disabled,\n filled: variant === 'filled',\n float: get(float),\n hasError: get(hasError),\n hasSuccess: get(hasSuccess) && !get(hasError),\n hovered: get(isHovered),\n opened: get(isOpen),\n outlined: get(outlined),\n readonly: readOnly,\n withClear: get(showClear),\n}));\n\nconst activeRailIndex = computed<number>(() =>\n get(railEntries).findIndex(entry => entry.category === get(activeCategory)));\n\nconst showRail = computed<boolean>(() => get(mode) === 'twoPane' || get(drillStep) === 'category');\nconst showDetail = computed<boolean>(() => get(mode) === 'twoPane' || get(drillStep) === 'detail');\n\nfunction clamp(value: number, max: number): number {\n if (value < 0)\n return 0;\n if (value > max)\n return max;\n return value;\n}\n\nfunction open(): void {\n if (disabled)\n return;\n set(isOpen, true);\n}\n\nfunction close(): void {\n set(isOpen, false);\n}\n\nfunction onInput(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement))\n return;\n set(searchInput, target.value);\n if (!get(isOpen))\n open();\n}\n\nfunction selectItem(item: TItem): void {\n if (readOnly)\n return;\n set(modelValue, getIdentifier(item));\n emit('select', item);\n close();\n}\n\nfunction clearSelection(): void {\n set(modelValue, undefined);\n}\n\nfunction onCategoryClick(category: string | null): void {\n setActiveCategory(category);\n if (get(mode) === 'drill')\n set(drillStep, 'detail');\n else\n focusPane('detail');\n}\n\nfunction focusPane(pane: 'rail' | 'detail'): void {\n set(focusedPane, pane);\n nextTick(() => {\n // preventScroll: focus moves between panes inside a teleported popover;\n // without it the browser scrolls the page behind to reveal the pane.\n (pane === 'rail' ? get(railRef) : get(detailRef))?.focus({ preventScroll: true });\n });\n}\n\nfunction moveRail(direction: 1 | -1): void {\n const entries = get(railEntries);\n if (entries.length === 0)\n return;\n const next = clamp(get(activeRailIndex) + direction, entries.length - 1);\n setActiveCategory(entries[next]?.category ?? null);\n}\n\nfunction moveItem(direction: 1 | -1): void {\n const total = get(detailItems).length;\n if (total === 0)\n return;\n set(highlightedItemIndex, clamp(get(highlightedItemIndex) + direction, total - 1));\n}\n\nfunction enterDetail(): void {\n if (get(detailItems).length === 0)\n return;\n resetHighlightToSelection();\n if (get(mode) === 'drill')\n set(drillStep, 'detail');\n focusPane('detail');\n}\n\nfunction selectHighlighted(): void {\n const item = get(detailItems)[get(highlightedItemIndex)];\n if (item !== undefined)\n selectItem(item);\n}\n\n// Enter from the field commits the best match for the current query, so a\n// type-to-pick flow (type a few letters, press Enter) works without leaving the\n// input. Does nothing when the field is empty.\nfunction selectTopResult(): void {\n if (!get(searchInput).trim())\n return;\n const item = get(detailItems)[0];\n if (item !== undefined)\n selectItem(item);\n}\n\nfunction resetHighlightToSelection(): void {\n const index = get(detailItems).findIndex(item => isSelected(item));\n set(highlightedItemIndex, index >= 0 ? index : 0);\n}\n\nfunction backToRail(): void {\n if (get(mode) === 'drill')\n set(drillStep, 'category');\n else\n focusPane('rail');\n}\n\nfunction scrollActiveDescendantIntoView(container: HTMLElement | null, id: string): void {\n if (!container)\n return;\n nextTick(() => {\n container.querySelector(`#${id}`)?.scrollIntoView({ block: 'nearest' });\n });\n}\n\n// Reset the item highlight whenever the active category changes.\nwatch(activeCategory, () => {\n resetHighlightToSelection();\n});\n\nwatch(highlightedItemIndex, (index) => {\n scrollActiveDescendantIntoView(get(detailRef), `${baseId}-opt-${index}`);\n});\n\nwatch(activeRailIndex, (index) => {\n if (index >= 0)\n scrollActiveDescendantIntoView(get(railRef), `${baseId}-cat-${index}`);\n});\n\nwatch(isOpen, (value) => {\n if (value) {\n syncActiveToSelection();\n set(focusedPane, 'rail');\n set(drillStep, 'category');\n resetHighlightToSelection();\n // Desktop is a combobox: keep focus in the field so the user can type\n // straight away (ArrowDown moves into the panes). The mobile sheet has no\n // typing field, so focus the rail instead.\n nextTick(() => (get(canType) ? get(activatorRef) : get(railRef))?.focus({ preventScroll: true }));\n }\n else {\n // Reset the query so the field shows the selection again next time, and\n // return focus to the trigger (APG dialog contract).\n set(searchInput, '');\n nextTick(() => get(activatorRef)?.focus({ preventScroll: true }));\n }\n});\n</script>\n\n<template>\n <component\n :is=\"shell\"\n v-model=\"isOpen\"\n v-bind=\"{ ...$attrs, ...shellProps }\"\n >\n <template #activator=\"{ attrs }\">\n <slot\n name=\"activator\"\n v-bind=\"{ attrs, isOpen, item: selectedItem, open, text: selectedText }\"\n >\n <div :class=\"activatorUi.wrapper()\">\n <div\n ref=\"fieldRef\"\n role=\"combobox\"\n data-id=\"activator\"\n :class=\"activatorUi.activator()\"\n aria-haspopup=\"dialog\"\n :aria-controls=\"isOpen ? `${baseId}-panel` : undefined\"\n :aria-expanded=\"isOpen\"\n :aria-disabled=\"disabled || undefined\"\n :aria-readonly=\"(readOnly || !canType) || undefined\"\n :aria-required=\"required || undefined\"\n :aria-invalid=\"hasError || undefined\"\n :aria-busy=\"loading || undefined\"\n v-bind=\"readOnly ? {} : attrs\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n >\n <span\n v-if=\"outlined || (!selectedItem && !isTyping)\"\n :class=\"activatorUi.label()\"\n >\n <slot\n name=\"activator.label\"\n v-bind=\"{ item: selectedItem }\"\n >\n {{ label }}\n </slot>\n <span\n v-if=\"required\"\n :class=\"activatorUi.required()\"\n >\n ﹡\n </span>\n </span>\n <span\n v-if=\"selectedItem && !isTyping && $slots.selection\"\n data-id=\"selection\"\n :class=\"activatorUi.selection()\"\n >\n <slot\n name=\"selection.prepend\"\n v-bind=\"{ item: selectedItem }\"\n />\n <slot\n name=\"selection\"\n v-bind=\"{ item: selectedItem }\"\n />\n </span>\n <input\n ref=\"activatorRef\"\n data-id=\"search-input\"\n :value=\"isTyping ? searchInput : (($slots.selection && selectedItem) ? '' : (selectedText ?? ''))\"\n :placeholder=\"isTyping ? placeholder : ''\"\n :readonly=\"!canType\"\n :disabled=\"disabled\"\n :aria-invalid=\"hasError || undefined\"\n class=\"bg-transparent outline-none\"\n :class=\"[activatorUi.value(), !canType && 'cursor-pointer']\"\n @input=\"onInput($event)\"\n @keydown.down.prevent=\"focusPane('rail')\"\n @keydown.enter.prevent=\"selectTopResult()\"\n @keydown.esc=\"close()\"\n />\n <span\n v-if=\"showClear\"\n data-id=\"clear\"\n :class=\"[activatorUi.clear(), focused && '!visible', { 'mr-2': !dense }]\"\n @click.stop.prevent=\"clearSelection()\"\n >\n <RuiIcon\n color=\"error\"\n name=\"lu-x\"\n size=\"18\"\n />\n </span>\n <span\n data-id=\"chevron\"\n :class=\"activatorUi.iconWrapper()\"\n >\n <RuiIcon\n :class=\"activatorUi.icon()\"\n :size=\"dense ? 16 : 24\"\n name=\"lu-chevron-down\"\n />\n </span>\n <RuiProgress\n v-if=\"loading\"\n :class=\"activatorUi.progress()\"\n color=\"primary\"\n thickness=\"3\"\n variant=\"indeterminate\"\n />\n <fieldset\n v-if=\"outlined\"\n :class=\"activatorUi.fieldset()\"\n >\n <legend :class=\"activatorUi.legend()\">\n {{ legendText }}\n </legend>\n </fieldset>\n </div>\n <RuiFormTextDetail\n v-if=\"!hideDetails\"\n class=\"px-3 pt-1\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n />\n </div>\n </slot>\n </template>\n\n <div\n :id=\"`${baseId}-panel`\"\n :class=\"ui.root({ class: cn(classNames?.root) })\"\n data-id=\"panel\"\n >\n <!-- On desktop the field itself is the search input, so the panel only\n needs its own search box in the mobile sheet. -->\n <div\n v-if=\"isMobile || $slots.header\"\n :class=\"ui.header()\"\n >\n <slot name=\"header\">\n <div :class=\"ui.title()\">\n {{ label }}\n </div>\n </slot>\n <RuiTextField\n v-if=\"searchable && isMobile\"\n v-model=\"searchInput\"\n :class=\"cn(classNames?.search)\"\n :placeholder=\"placeholder\"\n :dense=\"dense\"\n variant=\"outlined\"\n prepend-icon=\"lu-search\"\n hide-details\n clearable\n data-id=\"search\"\n />\n </div>\n\n <div :class=\"ui.body()\">\n <!-- Category rail (master) -->\n <div\n v-show=\"showRail\"\n ref=\"railRef\"\n :class=\"ui.rail({ class: cn(classNames?.rail) })\"\n role=\"tablist\"\n aria-orientation=\"vertical\"\n :aria-label=\"label\"\n :aria-activedescendant=\"focusedPane === 'rail' && activeRailIndex >= 0 ? `${baseId}-cat-${activeRailIndex}` : undefined\"\n tabindex=\"0\"\n data-id=\"rail\"\n @focus=\"focusedPane = 'rail'\"\n @keydown.down.prevent=\"moveRail(1)\"\n @keydown.up.prevent=\"moveRail(-1)\"\n @keydown.right.prevent=\"enterDetail()\"\n @keydown.enter.prevent=\"enterDetail()\"\n @keydown.home.prevent=\"setActiveCategory(railEntries[0]?.category ?? null)\"\n @keydown.end.prevent=\"setActiveCategory(railEntries[railEntries.length - 1]?.category ?? null)\"\n >\n <RuiButton\n v-for=\"(entry, index) in railEntries\"\n :id=\"`${baseId}-cat-${index}`\"\n :key=\"entry.category ?? '__all__'\"\n role=\"tab\"\n variant=\"list\"\n :size=\"dense ? 'sm' : undefined\"\n :active=\"entry.category === activeCategory\"\n :aria-selected=\"entry.category === activeCategory\"\n tabindex=\"-1\"\n :data-highlighted=\"focusedPane === 'rail' && index === activeRailIndex\"\n :data-category=\"entry.category ?? '__all__'\"\n @click=\"onCategoryClick(entry.category)\"\n >\n <slot\n name=\"category\"\n v-bind=\"{ active: entry.category === activeCategory, category: entry.category, count: entry.count, label: entry.label }\"\n >\n <span class=\"truncate\">{{ entry.label }}</span>\n <span :class=\"ui.railCount()\">{{ entry.count }}</span>\n </slot>\n </RuiButton>\n </div>\n\n <!-- Detail pane -->\n <div\n v-show=\"showDetail\"\n ref=\"detailRef\"\n :class=\"ui.detail({ class: cn(classNames?.detail) })\"\n role=\"listbox\"\n :aria-label=\"activeCategory ?? allLabel\"\n :aria-activedescendant=\"focusedPane === 'detail' && !isEmpty ? `${baseId}-opt-${highlightedItemIndex}` : undefined\"\n tabindex=\"0\"\n data-id=\"detail\"\n @focus=\"focusedPane = 'detail'\"\n @keydown.down.prevent=\"moveItem(1)\"\n @keydown.up.prevent=\"moveItem(-1)\"\n @keydown.left.prevent=\"backToRail()\"\n @keydown.home.prevent=\"highlightedItemIndex = 0\"\n @keydown.end.prevent=\"highlightedItemIndex = detailItems.length - 1\"\n @keydown.enter.prevent=\"selectHighlighted()\"\n @keydown.space.prevent=\"selectHighlighted()\"\n >\n <template v-if=\"mode === 'drill' && showDetail\">\n <button\n type=\"button\"\n :class=\"ui.backButton()\"\n data-id=\"back\"\n @click=\"backToRail()\"\n >\n <RuiIcon\n name=\"lu-chevron-left\"\n size=\"18\"\n />\n <span>{{ activeCategory ?? allLabel }}</span>\n </button>\n </template>\n\n <slot\n v-if=\"isEmpty\"\n name=\"empty\"\n >\n <div :class=\"ui.empty()\">\n {{ emptyText }}\n </div>\n </slot>\n\n <template\n v-for=\"row in detailRows\"\n v-else\n :key=\"row.type === 'header' ? `h-${row.category}` : `i-${row.index}`\"\n >\n <div\n v-if=\"row.type === 'header'\"\n :class=\"ui.subheader()\"\n role=\"presentation\"\n >\n {{ row.category }}\n </div>\n <RuiButton\n v-else\n :id=\"`${baseId}-opt-${row.index}`\"\n role=\"option\"\n variant=\"list\"\n :size=\"dense ? 'sm' : undefined\"\n :active=\"isSelected(row.item)\"\n :aria-selected=\"isSelected(row.item)\"\n tabindex=\"-1\"\n :data-highlighted=\"focusedPane === 'detail' && row.index === highlightedItemIndex\"\n :class=\"{ [ui.highlighted()]: focusedPane === 'detail' && row.index === highlightedItemIndex && !isSelected(row.item) }\"\n @click=\"selectItem(row.item)\"\n >\n <template\n v-if=\"$slots['item.prepend']\"\n #prepend\n >\n <slot\n name=\"item.prepend\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n >\n {{ getText(row.item) }}\n </slot>\n <template\n v-if=\"$slots['item.append']\"\n #append\n >\n <slot\n name=\"item.append\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n />\n </template>\n </RuiButton>\n </template>\n </div>\n </div>\n\n <div\n v-if=\"$slots.footer\"\n :class=\"ui.footer()\"\n >\n <slot\n name=\"footer\"\n v-bind=\"{ close }\"\n />\n </div>\n </div>\n </component>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"RuiCategoryPicker.js","names":[],"sources":["../../../../src/components/forms/category-picker/RuiCategoryPicker.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue, TItem\">\nimport type { KeyOfType } from '@/composables/dropdown-menu';\nimport type { VueClassValue } from '@/types/class-value';\nimport { breakpointsTailwind, useBreakpoints } from '@vueuse/core';\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport { categoryPickerActivatorStyles, categoryPickerStyles, type CategoryPickerVariant } from '@/components/forms/category-picker/category-picker-styles';\nimport RuiTextField from '@/components/forms/text-field/RuiTextField.vue';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiDialog, { type DialogProps } from '@/components/overlays/dialog/RuiDialog.vue';\nimport RuiMenu from '@/components/overlays/menu/RuiMenu.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { Placement } from '@/composables/floating';\nimport { useCategoryPicker } from '@/composables/forms/category-picker/use-category-picker';\nimport { useFormTextDetail } from '@/utils/form-text-detail';\nimport { cn } from '@/utils/tv';\n\nexport interface RuiCategoryPickerClassNames {\n root?: VueClassValue;\n rail?: VueClassValue;\n detail?: VueClassValue;\n search?: VueClassValue;\n}\n\nexport type CategoryPickerBreakpoint = 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\nexport interface RuiCategoryPickerProps<TValue, TItem> {\n /** The full catalogue of selectable items. */\n items: TItem[];\n /** Resolve an item's category label. Takes precedence over `categoryAttr`. */\n categoryOf?: (item: TItem) => string;\n /** Resolve an item's category from one of its own properties. */\n categoryAttr?: keyof TItem;\n /** Property whose value becomes the model value. Omit to use the item itself. */\n keyAttr?: KeyOfType<TItem, TValue extends (infer U)[] ? U : TValue>;\n /** Property that produces an item's display text. */\n textAttr?: keyof TItem;\n /** Field label, and the title of the mobile sheet. */\n label?: string;\n /** Placeholder shown in the field while it is empty or being searched. */\n placeholder?: string;\n /** Allow filtering: type in the field (desktop) or the sheet's search box (mobile). */\n searchable?: boolean;\n /**\n * Custom predicate. Receives the resolved category label as the third\n * argument, matching the `RuiAutoComplete` filter signature.\n */\n filter?: (item: TItem, query: string, category: string) => boolean;\n /** Show the synthetic \"All\" pseudo-category as the first rail entry. */\n showAll?: boolean;\n /** Label for the \"All\" pseudo-category. */\n allLabel?: string;\n /** Text shown when no item matches the current search. */\n emptyText?: string;\n disabled?: boolean;\n readOnly?: boolean;\n dense?: boolean;\n clearable?: boolean;\n loading?: boolean;\n /** Trigger field style: underline (`default`), `outlined`, or `filled`. */\n variant?: CategoryPickerVariant;\n /** Marks the trigger field as required (renders the asterisk). */\n required?: boolean;\n /** Validation errors rendered under the trigger field. */\n errorMessages?: string | string[];\n /** Success messages rendered under the trigger field. */\n successMessages?: string | string[];\n /** Hint rendered under the trigger field. */\n hint?: string;\n /** Hide the details row (errors/success/hint) under the trigger field. */\n hideDetails?: boolean;\n /** Below this breakpoint the picker collapses to a single-pane drill-in. */\n mobileBreakpoint?: CategoryPickerBreakpoint;\n /** Forwarded to the mobile bottom-sheet (`RuiDialog`). */\n dialogOptions?: DialogProps;\n classNames?: RuiCategoryPickerClassNames;\n}\n\ndefineOptions({\n name: 'RuiCategoryPicker',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<TValue>();\nconst searchInput = defineModel<string>('search', { default: '' });\n\nconst {\n items,\n categoryOf = undefined,\n categoryAttr = undefined,\n keyAttr = undefined,\n textAttr = undefined,\n label = 'Select',\n placeholder = 'Search',\n searchable = true,\n filter = undefined,\n showAll = true,\n allLabel = 'All',\n emptyText = 'No results',\n disabled = false,\n readOnly = false,\n dense = false,\n clearable = false,\n loading = false,\n variant = 'outlined',\n required = false,\n errorMessages = [],\n successMessages = [],\n hint = '',\n hideDetails = false,\n mobileBreakpoint = 'md',\n dialogOptions = undefined,\n classNames = undefined,\n} = defineProps<RuiCategoryPickerProps<TValue, TItem>>();\n\nconst emit = defineEmits<{\n select: [item: TItem];\n}>();\n\ndefineSlots<{\n 'activator'?: (props: { attrs: { onClick?: () => void; onMouseover?: () => void; onMouseleave?: () => void }; isOpen: boolean; open: () => void; item: TItem | undefined; text: string | undefined }) => any;\n 'activator.label'?: (props: { item: TItem | undefined }) => any;\n 'selection'?: (props: { item: TItem }) => any;\n 'selection.prepend'?: (props: { item: TItem }) => any;\n 'category'?: (props: { category: string | null; label: string; active: boolean; count: number }) => any;\n 'item'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'item.prepend'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'item.append'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'empty'?: () => any;\n 'header'?: () => any;\n 'footer'?: (props: { close: () => void }) => any;\n}>();\n\nconst isOpen = ref<boolean>(false);\nconst focusedPane = ref<'rail' | 'detail'>('rail');\nconst highlightedItemIndex = ref<number>(0);\nconst drillStep = ref<'category' | 'detail'>('category');\n\nconst railRef = useTemplateRef<HTMLDivElement>('railRef');\nconst detailRef = useTemplateRef<HTMLDivElement>('detailRef');\nconst activatorRef = useTemplateRef<HTMLInputElement>('activatorRef');\nconst fieldRef = useTemplateRef<HTMLElement>('fieldRef');\nconst isHovered = ref<boolean>(false);\n\nconst { focused } = useFocus(activatorRef);\nconst { hasError, hasSuccess } = useFormTextDetail(() => errorMessages, () => successMessages);\n\nconst baseId = useId();\nconst breakpoints = useBreakpoints(breakpointsTailwind);\n\nfunction resolveCategory(item: TItem): string {\n if (categoryOf)\n return categoryOf(item);\n if (categoryAttr !== undefined)\n return String(item[categoryAttr] ?? '');\n return '';\n}\n\nconst {\n activeCategory,\n detailItems,\n detailRows,\n getIdentifier,\n getText,\n isEmpty,\n isSelected,\n railEntries,\n setActiveCategory,\n syncActiveToSelection,\n} = useCategoryPicker<TValue, TItem>({\n allLabel: () => allLabel,\n categoryOf: resolveCategory,\n filter: () => filter,\n items: () => items,\n keyAttr,\n modelValue,\n search: searchInput,\n searchable: () => searchable,\n showAll: () => showAll,\n textAttr,\n});\n\nconst isMobile = computed<boolean>(() => get(breakpoints.smaller(mobileBreakpoint)));\n\nconst mode = computed<'twoPane' | 'drill'>(() => (get(isMobile) ? 'drill' : 'twoPane'));\n\n/**\n * Desktop anchors an outside-click popover under the field (RuiMenu); narrow\n * width shows a full-width bottom sheet (RuiDialog).\n */\nconst shell = computed<typeof RuiMenu | typeof RuiDialog>(() => (get(isMobile) ? RuiDialog : RuiMenu));\n\n/**\n * The bottom sheet's props. Each shell is given only its own props, so the\n * other component's never leak through as attributes.\n */\nfunction dialogShellProps(): Record<string, unknown> {\n return {\n ariaLabel: dialogOptions?.ariaLabel ?? label,\n bottomSheet: dialogOptions?.bottomSheet ?? true,\n maxWidth: dialogOptions?.maxWidth,\n persistent: dialogOptions?.persistent,\n width: dialogOptions?.width ?? '100%',\n };\n}\n\n/**\n * The popover's props. It anchors to the field box rather than the wrapper, so\n * it sits directly under the input instead of below the reserved details row,\n * and drops the menu's default vertical padding so the popover box equals the\n * panel: the size middleware caps the panel to the available height, and any\n * extra chrome padding would push the popover past the viewport edge.\n */\nfunction menuShellProps(): Record<string, unknown> {\n return {\n anchorEl: get(fieldRef) ?? undefined,\n classNames: { content: 'py-0', menu: 'z-[9999]' },\n closeOnContentClick: false,\n disableAutoFocus: true,\n fullWidth: true,\n options: { placement: Placement.bottomStart, size: true },\n persistent: dialogOptions?.persistent,\n persistOnActivatorClick: true,\n };\n}\n\nconst shellProps = computed<Record<string, unknown>>(() =>\n get(isMobile) ? dialogShellProps() : menuShellProps());\n\nconst ui = computed<ReturnType<typeof categoryPickerStyles>>(() => categoryPickerStyles({\n dense,\n mode: get(mode),\n}));\n\nconst selectedItem = computed<TItem | undefined>(() => {\n const value = get(modelValue);\n if (value === undefined)\n return undefined;\n return items.find(item => getIdentifier(item) === value);\n});\n\nconst selectedText = computed<string | undefined>(() => {\n const selected = get(selectedItem);\n return selected ? getText(selected) : undefined;\n});\n\nconst outlined = computed<boolean>(() => variant === 'outlined');\n\n/**\n * Desktop lets the user type in the field itself (no separate search box);\n * narrow width keeps a read-only trigger that opens the sheet's own search.\n */\nconst canType = computed<boolean>(() => searchable && !get(isMobile) && !readOnly && !disabled);\n\nconst isTyping = computed<boolean>(() => get(isOpen) && get(canType));\n\nconst float = computed<boolean>(() => (get(isOpen) || get(selectedItem) !== undefined || get(focused)) && get(outlined));\n\nconst legendText = computed<string>(() => {\n if (!get(float) || !label)\n return '';\n return required ? `${label} ﹡` : label;\n});\n\n/**\n * Drives both the clear button itself and the room the selection overlay has\n * to leave for it, so the two can never disagree.\n */\nconst showClear = computed<boolean>(() =>\n clearable && get(selectedItem) !== undefined && !disabled && !readOnly);\n\nconst activatorUi = computed<ReturnType<typeof categoryPickerActivatorStyles>>(() => categoryPickerActivatorStyles({\n dense,\n disabled,\n filled: variant === 'filled',\n float: get(float),\n hasError: get(hasError),\n hasSuccess: get(hasSuccess) && !get(hasError),\n hovered: get(isHovered),\n opened: get(isOpen),\n outlined: get(outlined),\n readonly: readOnly,\n withClear: get(showClear),\n}));\n\nconst activeRailIndex = computed<number>(() =>\n get(railEntries).findIndex(entry => entry.category === get(activeCategory)));\n\nconst showRail = computed<boolean>(() => get(mode) === 'twoPane' || get(drillStep) === 'category');\nconst showDetail = computed<boolean>(() => get(mode) === 'twoPane' || get(drillStep) === 'detail');\n\nfunction clamp(value: number, max: number): number {\n if (value < 0)\n return 0;\n if (value > max)\n return max;\n return value;\n}\n\nfunction open(): void {\n if (disabled)\n return;\n set(isOpen, true);\n}\n\nfunction close(): void {\n set(isOpen, false);\n}\n\nfunction onInput(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement))\n return;\n set(searchInput, target.value);\n if (!get(isOpen))\n open();\n}\n\nfunction selectItem(item: TItem): void {\n if (readOnly)\n return;\n set(modelValue, getIdentifier(item));\n emit('select', item);\n close();\n}\n\nfunction clearSelection(): void {\n set(modelValue, undefined);\n}\n\nfunction onCategoryClick(category: string | null): void {\n setActiveCategory(category);\n if (get(mode) === 'drill')\n set(drillStep, 'detail');\n else\n focusPane('detail');\n}\n\n/**\n * Moves focus to one of the panes. The focus call prevents scrolling because\n * the panes sit inside a teleported popover, and without it the browser\n * scrolls the page behind to reveal the pane.\n */\nfunction focusPane(pane: 'rail' | 'detail'): void {\n set(focusedPane, pane);\n nextTick(() => {\n (pane === 'rail' ? get(railRef) : get(detailRef))?.focus({ preventScroll: true });\n });\n}\n\nfunction moveRail(direction: 1 | -1): void {\n const entries = get(railEntries);\n if (entries.length === 0)\n return;\n const next = clamp(get(activeRailIndex) + direction, entries.length - 1);\n setActiveCategory(entries[next]?.category ?? null);\n}\n\nfunction moveItem(direction: 1 | -1): void {\n const total = get(detailItems).length;\n if (total === 0)\n return;\n set(highlightedItemIndex, clamp(get(highlightedItemIndex) + direction, total - 1));\n}\n\nfunction enterDetail(): void {\n if (get(detailItems).length === 0)\n return;\n resetHighlightToSelection();\n if (get(mode) === 'drill')\n set(drillStep, 'detail');\n focusPane('detail');\n}\n\nfunction selectHighlighted(): void {\n const item = get(detailItems)[get(highlightedItemIndex)];\n if (item !== undefined)\n selectItem(item);\n}\n\n/**\n * Enter from the field commits the best match for the current query, so a\n * type-to-pick flow (type a few letters, press Enter) works without leaving\n * the input. Does nothing when the field is empty.\n */\nfunction selectTopResult(): void {\n if (!get(searchInput).trim())\n return;\n const item = get(detailItems)[0];\n if (item !== undefined)\n selectItem(item);\n}\n\nfunction resetHighlightToSelection(): void {\n const index = get(detailItems).findIndex(item => isSelected(item));\n set(highlightedItemIndex, index >= 0 ? index : 0);\n}\n\nfunction backToRail(): void {\n if (get(mode) === 'drill')\n set(drillStep, 'category');\n else\n focusPane('rail');\n}\n\nfunction scrollActiveDescendantIntoView(container: HTMLElement | null, id: string): void {\n if (!container)\n return;\n nextTick(() => {\n container.querySelector(`#${id}`)?.scrollIntoView({ block: 'nearest' });\n });\n}\n\n// Reset the item highlight whenever the active category changes.\nwatch(activeCategory, () => {\n resetHighlightToSelection();\n});\n\nwatch(highlightedItemIndex, (index) => {\n scrollActiveDescendantIntoView(get(detailRef), `${baseId}-opt-${index}`);\n});\n\nwatch(activeRailIndex, (index) => {\n if (index >= 0)\n scrollActiveDescendantIntoView(get(railRef), `${baseId}-cat-${index}`);\n});\n\n/**\n * Opening takes focus where the user types next: desktop is a combobox, so\n * focus stays in the field and ArrowDown moves into the panes, while the\n * mobile sheet has no typing field and focuses the rail instead. Closing\n * resets the query, so the field shows the selection again next time, and\n * returns focus to the trigger, which the APG dialog contract asks for.\n */\nfunction onOpenChanged(value: boolean): void {\n if (value) {\n syncActiveToSelection();\n set(focusedPane, 'rail');\n set(drillStep, 'category');\n resetHighlightToSelection();\n nextTick(() => (get(canType) ? get(activatorRef) : get(railRef))?.focus({ preventScroll: true }));\n }\n else {\n set(searchInput, '');\n nextTick(() => get(activatorRef)?.focus({ preventScroll: true }));\n }\n}\n\nwatch(isOpen, onOpenChanged);\n</script>\n\n<template>\n <component\n :is=\"shell\"\n v-model=\"isOpen\"\n v-bind=\"{ ...$attrs, ...shellProps }\"\n >\n <template #activator=\"{ attrs }\">\n <slot\n name=\"activator\"\n v-bind=\"{ attrs, isOpen, item: selectedItem, open, text: selectedText }\"\n >\n <div :class=\"activatorUi.wrapper()\">\n <div\n ref=\"fieldRef\"\n role=\"combobox\"\n data-id=\"activator\"\n :class=\"activatorUi.activator()\"\n aria-haspopup=\"dialog\"\n :aria-controls=\"isOpen ? `${baseId}-panel` : undefined\"\n :aria-expanded=\"isOpen\"\n :aria-disabled=\"disabled || undefined\"\n :aria-readonly=\"(readOnly || !canType) || undefined\"\n :aria-required=\"required || undefined\"\n :aria-invalid=\"hasError || undefined\"\n :aria-busy=\"loading || undefined\"\n v-bind=\"readOnly ? {} : attrs\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n >\n <span\n v-if=\"outlined || (!selectedItem && !isTyping)\"\n :class=\"activatorUi.label()\"\n >\n <slot\n name=\"activator.label\"\n v-bind=\"{ item: selectedItem }\"\n >\n {{ label }}\n </slot>\n <span\n v-if=\"required\"\n :class=\"activatorUi.required()\"\n >\n ﹡\n </span>\n </span>\n <span\n v-if=\"selectedItem && !isTyping && $slots.selection\"\n data-id=\"selection\"\n :class=\"activatorUi.selection()\"\n >\n <slot\n name=\"selection.prepend\"\n v-bind=\"{ item: selectedItem }\"\n />\n <slot\n name=\"selection\"\n v-bind=\"{ item: selectedItem }\"\n />\n </span>\n <input\n ref=\"activatorRef\"\n data-id=\"search-input\"\n :value=\"isTyping ? searchInput : (($slots.selection && selectedItem) ? '' : (selectedText ?? ''))\"\n :placeholder=\"isTyping ? placeholder : ''\"\n :readonly=\"!canType\"\n :disabled=\"disabled\"\n :aria-invalid=\"hasError || undefined\"\n class=\"bg-transparent outline-none\"\n :class=\"[activatorUi.value(), !canType && 'cursor-pointer']\"\n @input=\"onInput($event)\"\n @keydown.down.prevent=\"focusPane('rail')\"\n @keydown.enter.prevent=\"selectTopResult()\"\n @keydown.esc=\"close()\"\n />\n <span\n v-if=\"showClear\"\n data-id=\"clear\"\n :class=\"[activatorUi.clear(), focused && '!visible', { 'mr-2': !dense }]\"\n @click.stop.prevent=\"clearSelection()\"\n >\n <RuiIcon\n color=\"error\"\n name=\"lu-x\"\n size=\"18\"\n />\n </span>\n <span\n data-id=\"chevron\"\n :class=\"activatorUi.iconWrapper()\"\n >\n <RuiIcon\n :class=\"activatorUi.icon()\"\n :size=\"dense ? 16 : 24\"\n name=\"lu-chevron-down\"\n />\n </span>\n <RuiProgress\n v-if=\"loading\"\n :class=\"activatorUi.progress()\"\n color=\"primary\"\n thickness=\"3\"\n variant=\"indeterminate\"\n />\n <fieldset\n v-if=\"outlined\"\n :class=\"activatorUi.fieldset()\"\n >\n <legend :class=\"activatorUi.legend()\">\n {{ legendText }}\n </legend>\n </fieldset>\n </div>\n <RuiFormTextDetail\n v-if=\"!hideDetails\"\n class=\"px-3 pt-1\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n />\n </div>\n </slot>\n </template>\n\n <div\n :id=\"`${baseId}-panel`\"\n :class=\"ui.root({ class: cn(classNames?.root) })\"\n data-id=\"panel\"\n >\n <!-- On desktop the field itself is the search input, so the panel only\n needs its own search box in the mobile sheet. -->\n <div\n v-if=\"isMobile || $slots.header\"\n :class=\"ui.header()\"\n >\n <slot name=\"header\">\n <div :class=\"ui.title()\">\n {{ label }}\n </div>\n </slot>\n <RuiTextField\n v-if=\"searchable && isMobile\"\n v-model=\"searchInput\"\n :class=\"cn(classNames?.search)\"\n :placeholder=\"placeholder\"\n :dense=\"dense\"\n variant=\"outlined\"\n prepend-icon=\"lu-search\"\n hide-details\n clearable\n data-id=\"search\"\n />\n </div>\n\n <div :class=\"ui.body()\">\n <!-- Category rail (master) -->\n <div\n v-show=\"showRail\"\n ref=\"railRef\"\n :class=\"ui.rail({ class: cn(classNames?.rail) })\"\n role=\"tablist\"\n aria-orientation=\"vertical\"\n :aria-label=\"label\"\n :aria-activedescendant=\"focusedPane === 'rail' && activeRailIndex >= 0 ? `${baseId}-cat-${activeRailIndex}` : undefined\"\n tabindex=\"0\"\n data-id=\"rail\"\n @focus=\"focusedPane = 'rail'\"\n @keydown.down.prevent=\"moveRail(1)\"\n @keydown.up.prevent=\"moveRail(-1)\"\n @keydown.right.prevent=\"enterDetail()\"\n @keydown.enter.prevent=\"enterDetail()\"\n @keydown.home.prevent=\"setActiveCategory(railEntries[0]?.category ?? null)\"\n @keydown.end.prevent=\"setActiveCategory(railEntries[railEntries.length - 1]?.category ?? null)\"\n >\n <RuiButton\n v-for=\"(entry, index) in railEntries\"\n :id=\"`${baseId}-cat-${index}`\"\n :key=\"entry.category ?? '__all__'\"\n role=\"tab\"\n variant=\"list\"\n :size=\"dense ? 'sm' : undefined\"\n :active=\"entry.category === activeCategory\"\n :aria-selected=\"entry.category === activeCategory\"\n tabindex=\"-1\"\n :data-highlighted=\"focusedPane === 'rail' && index === activeRailIndex\"\n :data-category=\"entry.category ?? '__all__'\"\n @click=\"onCategoryClick(entry.category)\"\n >\n <slot\n name=\"category\"\n v-bind=\"{ active: entry.category === activeCategory, category: entry.category, count: entry.count, label: entry.label }\"\n >\n <span class=\"truncate\">{{ entry.label }}</span>\n <span :class=\"ui.railCount()\">{{ entry.count }}</span>\n </slot>\n </RuiButton>\n </div>\n\n <!-- Detail pane -->\n <div\n v-show=\"showDetail\"\n ref=\"detailRef\"\n :class=\"ui.detail({ class: cn(classNames?.detail) })\"\n role=\"listbox\"\n :aria-label=\"activeCategory ?? allLabel\"\n :aria-activedescendant=\"focusedPane === 'detail' && !isEmpty ? `${baseId}-opt-${highlightedItemIndex}` : undefined\"\n tabindex=\"0\"\n data-id=\"detail\"\n @focus=\"focusedPane = 'detail'\"\n @keydown.down.prevent=\"moveItem(1)\"\n @keydown.up.prevent=\"moveItem(-1)\"\n @keydown.left.prevent=\"backToRail()\"\n @keydown.home.prevent=\"highlightedItemIndex = 0\"\n @keydown.end.prevent=\"highlightedItemIndex = detailItems.length - 1\"\n @keydown.enter.prevent=\"selectHighlighted()\"\n @keydown.space.prevent=\"selectHighlighted()\"\n >\n <template v-if=\"mode === 'drill' && showDetail\">\n <button\n type=\"button\"\n :class=\"ui.backButton()\"\n data-id=\"back\"\n @click=\"backToRail()\"\n >\n <RuiIcon\n name=\"lu-chevron-left\"\n size=\"18\"\n />\n <span>{{ activeCategory ?? allLabel }}</span>\n </button>\n </template>\n\n <slot\n v-if=\"isEmpty\"\n name=\"empty\"\n >\n <div :class=\"ui.empty()\">\n {{ emptyText }}\n </div>\n </slot>\n\n <template\n v-for=\"row in detailRows\"\n v-else\n :key=\"row.type === 'header' ? `h-${row.category}` : `i-${row.index}`\"\n >\n <div\n v-if=\"row.type === 'header'\"\n :class=\"ui.subheader()\"\n role=\"presentation\"\n >\n {{ row.category }}\n </div>\n <RuiButton\n v-else\n :id=\"`${baseId}-opt-${row.index}`\"\n role=\"option\"\n variant=\"list\"\n :size=\"dense ? 'sm' : undefined\"\n :active=\"isSelected(row.item)\"\n :aria-selected=\"isSelected(row.item)\"\n tabindex=\"-1\"\n :data-highlighted=\"focusedPane === 'detail' && row.index === highlightedItemIndex\"\n :class=\"{ [ui.highlighted()]: focusedPane === 'detail' && row.index === highlightedItemIndex && !isSelected(row.item) }\"\n @click=\"selectItem(row.item)\"\n >\n <template\n v-if=\"$slots['item.prepend']\"\n #prepend\n >\n <slot\n name=\"item.prepend\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n >\n {{ getText(row.item) }}\n </slot>\n <template\n v-if=\"$slots['item.append']\"\n #append\n >\n <slot\n name=\"item.append\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n />\n </template>\n </RuiButton>\n </template>\n </div>\n </div>\n\n <div\n v-if=\"$slots.footer\"\n :class=\"ui.footer()\"\n >\n <slot\n name=\"footer\"\n v-bind=\"{ close }\"\n />\n </div>\n </div>\n </component>\n</template>\n"],"mappings":""}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { KeyOfType } from '../../../composables/dropdown-menu.js';
|
|
2
2
|
import type { VueClassValue } from '../../../types/class-value';
|
|
3
|
-
import { type CategoryPickerVariant } from '
|
|
4
|
-
import { type DialogProps } from '
|
|
3
|
+
import { type CategoryPickerVariant } from './category-picker-styles.js';
|
|
4
|
+
import { type DialogProps } from '../../overlays/dialog/RuiDialog.vue';
|
|
5
5
|
export interface RuiCategoryPickerClassNames {
|
|
6
6
|
root?: VueClassValue;
|
|
7
7
|
rail?: VueClassValue;
|
|
@@ -141,15 +141,32 @@ var RuiCategoryPicker_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
|
|
|
141
141
|
});
|
|
142
142
|
const isMobile = computed(() => get$1(breakpoints.smaller(__props.mobileBreakpoint)));
|
|
143
143
|
const mode = computed(() => get$1(isMobile) ? "drill" : "twoPane");
|
|
144
|
+
/**
|
|
145
|
+
* Desktop anchors an outside-click popover under the field (RuiMenu); narrow
|
|
146
|
+
* width shows a full-width bottom sheet (RuiDialog).
|
|
147
|
+
*/
|
|
144
148
|
const shell = computed(() => get$1(isMobile) ? RuiDialog_default : RuiMenu_default);
|
|
145
|
-
|
|
146
|
-
|
|
149
|
+
/**
|
|
150
|
+
* The bottom sheet's props. Each shell is given only its own props, so the
|
|
151
|
+
* other component's never leak through as attributes.
|
|
152
|
+
*/
|
|
153
|
+
function dialogShellProps() {
|
|
154
|
+
return {
|
|
147
155
|
ariaLabel: __props.dialogOptions?.ariaLabel ?? __props.label,
|
|
148
156
|
bottomSheet: __props.dialogOptions?.bottomSheet ?? true,
|
|
149
157
|
maxWidth: __props.dialogOptions?.maxWidth,
|
|
150
158
|
persistent: __props.dialogOptions?.persistent,
|
|
151
159
|
width: __props.dialogOptions?.width ?? "100%"
|
|
152
160
|
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The popover's props. It anchors to the field box rather than the wrapper, so
|
|
164
|
+
* it sits directly under the input instead of below the reserved details row,
|
|
165
|
+
* and drops the menu's default vertical padding so the popover box equals the
|
|
166
|
+
* panel: the size middleware caps the panel to the available height, and any
|
|
167
|
+
* extra chrome padding would push the popover past the viewport edge.
|
|
168
|
+
*/
|
|
169
|
+
function menuShellProps() {
|
|
153
170
|
return {
|
|
154
171
|
anchorEl: get$1(fieldRef) ?? void 0,
|
|
155
172
|
classNames: {
|
|
@@ -166,7 +183,8 @@ var RuiCategoryPicker_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
|
|
|
166
183
|
persistent: __props.dialogOptions?.persistent,
|
|
167
184
|
persistOnActivatorClick: true
|
|
168
185
|
};
|
|
169
|
-
}
|
|
186
|
+
}
|
|
187
|
+
const shellProps = computed(() => get$1(isMobile) ? dialogShellProps() : menuShellProps());
|
|
170
188
|
const ui = computed(() => categoryPickerStyles({
|
|
171
189
|
dense: __props.dense,
|
|
172
190
|
mode: get$1(mode)
|
|
@@ -181,6 +199,10 @@ var RuiCategoryPicker_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
|
|
|
181
199
|
return selected ? getText(selected) : void 0;
|
|
182
200
|
});
|
|
183
201
|
const outlined = computed(() => __props.variant === "outlined");
|
|
202
|
+
/**
|
|
203
|
+
* Desktop lets the user type in the field itself (no separate search box);
|
|
204
|
+
* narrow width keeps a read-only trigger that opens the sheet's own search.
|
|
205
|
+
*/
|
|
184
206
|
const canType = computed(() => __props.searchable && !get$1(isMobile) && !__props.readOnly && !__props.disabled);
|
|
185
207
|
const isTyping = computed(() => get$1(isOpen) && get$1(canType));
|
|
186
208
|
const float = computed(() => (get$1(isOpen) || get$1(selectedItem) !== void 0 || get$1(focused)) && get$1(outlined));
|
|
@@ -188,6 +210,10 @@ var RuiCategoryPicker_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
|
|
|
188
210
|
if (!get$1(float) || !__props.label) return "";
|
|
189
211
|
return __props.required ? `${__props.label} ﹡` : __props.label;
|
|
190
212
|
});
|
|
213
|
+
/**
|
|
214
|
+
* Drives both the clear button itself and the room the selection overlay has
|
|
215
|
+
* to leave for it, so the two can never disagree.
|
|
216
|
+
*/
|
|
191
217
|
const showClear = computed(() => __props.clearable && get$1(selectedItem) !== void 0 && !__props.disabled && !__props.readOnly);
|
|
192
218
|
const activatorUi = computed(() => categoryPickerActivatorStyles({
|
|
193
219
|
dense: __props.dense,
|
|
@@ -237,6 +263,11 @@ var RuiCategoryPicker_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
|
|
|
237
263
|
if (get$1(mode) === "drill") set$1(drillStep, "detail");
|
|
238
264
|
else focusPane("detail");
|
|
239
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* Moves focus to one of the panes. The focus call prevents scrolling because
|
|
268
|
+
* the panes sit inside a teleported popover, and without it the browser
|
|
269
|
+
* scrolls the page behind to reveal the pane.
|
|
270
|
+
*/
|
|
240
271
|
function focusPane(pane) {
|
|
241
272
|
set$1(focusedPane, pane);
|
|
242
273
|
nextTick(() => {
|
|
@@ -264,6 +295,11 @@ var RuiCategoryPicker_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
|
|
|
264
295
|
const item = get$1(detailItems)[get$1(highlightedItemIndex)];
|
|
265
296
|
if (item !== void 0) selectItem(item);
|
|
266
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Enter from the field commits the best match for the current query, so a
|
|
300
|
+
* type-to-pick flow (type a few letters, press Enter) works without leaving
|
|
301
|
+
* the input. Does nothing when the field is empty.
|
|
302
|
+
*/
|
|
267
303
|
function selectTopResult() {
|
|
268
304
|
if (!get$1(searchInput).trim()) return;
|
|
269
305
|
const item = get$1(detailItems)[0];
|
|
@@ -292,7 +328,14 @@ var RuiCategoryPicker_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
|
|
|
292
328
|
watch(activeRailIndex, (index) => {
|
|
293
329
|
if (index >= 0) scrollActiveDescendantIntoView(get$1(railRef), `${baseId}-cat-${index}`);
|
|
294
330
|
});
|
|
295
|
-
|
|
331
|
+
/**
|
|
332
|
+
* Opening takes focus where the user types next: desktop is a combobox, so
|
|
333
|
+
* focus stays in the field and ArrowDown moves into the panes, while the
|
|
334
|
+
* mobile sheet has no typing field and focuses the rail instead. Closing
|
|
335
|
+
* resets the query, so the field shows the selection again next time, and
|
|
336
|
+
* returns focus to the trigger, which the APG dialog contract asks for.
|
|
337
|
+
*/
|
|
338
|
+
function onOpenChanged(value) {
|
|
296
339
|
if (value) {
|
|
297
340
|
syncActiveToSelection();
|
|
298
341
|
set$1(focusedPane, "rail");
|
|
@@ -303,7 +346,8 @@ var RuiCategoryPicker_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
|
|
|
303
346
|
set$1(searchInput, "");
|
|
304
347
|
nextTick(() => get$1(activatorRef)?.focus({ preventScroll: true }));
|
|
305
348
|
}
|
|
306
|
-
}
|
|
349
|
+
}
|
|
350
|
+
watch(isOpen, onOpenChanged);
|
|
307
351
|
return (_ctx, _cache) => {
|
|
308
352
|
return openBlock(), createBlock(resolveDynamicComponent(unref(shell)), mergeProps({
|
|
309
353
|
modelValue: unref(isOpen),
|