@rotki/ui-library 2.12.1 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/accordions/accordion/RuiAccordion.vue.d.ts +6 -14
- package/dist/components/accordions/accordion/RuiAccordion.vue.js +3 -4
- package/dist/components/accordions/accordion/RuiAccordion.vue.js.map +1 -1
- package/dist/components/accordions/accordions/RuiAccordions.vue.d.ts +10 -9
- package/dist/components/accordions/accordions/RuiAccordions.vue.js +27 -26
- package/dist/components/accordions/accordions/RuiAccordions.vue.js.map +1 -1
- package/dist/components/alerts/RuiAlert.vue.d.ts +7 -17
- package/dist/components/alerts/RuiAlert.vue2.js +8 -16
- package/dist/components/alerts/RuiAlert.vue2.js.map +1 -1
- package/dist/components/buttons/button/RuiButton.vue.d.ts +5 -7
- package/dist/components/buttons/button/RuiButton.vue2.js +19 -20
- package/dist/components/buttons/button/RuiButton.vue2.js.map +1 -1
- package/dist/components/buttons/button-group/RuiButtonGroup.vue.d.ts +6 -5
- package/dist/components/buttons/button-group/RuiButtonGroup.vue2.js +42 -41
- package/dist/components/buttons/button-group/RuiButtonGroup.vue2.js.map +1 -1
- package/dist/components/calendar/RuiCalendar.vue.d.ts +1 -6
- package/dist/components/calendar/RuiCalendar.vue.js +7 -8
- package/dist/components/calendar/RuiCalendar.vue.js.map +1 -1
- package/dist/components/calendar/RuiCalendarGrid.vue.js +1 -1
- package/dist/components/calendar/RuiCalendarGrid.vue2.js +20 -18
- package/dist/components/calendar/RuiCalendarGrid.vue2.js.map +1 -1
- package/dist/components/calendar/RuiCalendarHeader.vue.js +1 -1
- package/dist/components/calendar/RuiCalendarHeader.vue2.js +3 -4
- package/dist/components/calendar/RuiCalendarHeader.vue2.js.map +1 -1
- package/dist/components/calendar/RuiCalendarMenu.vue.js +1 -1
- package/dist/components/calendar/RuiCalendarMenu.vue2.js +48 -40
- package/dist/components/calendar/RuiCalendarMenu.vue2.js.map +1 -1
- package/dist/components/cards/RuiCard.vue.d.ts +9 -24
- package/dist/components/cards/RuiCard.vue2.js +7 -7
- package/dist/components/cards/RuiCard.vue2.js.map +1 -1
- package/dist/components/cards/RuiCardHeader.vue.d.ts +5 -10
- package/dist/components/cards/RuiCardHeader.vue2.js +1 -1
- package/dist/components/cards/RuiCardHeader.vue2.js.map +1 -1
- package/dist/components/chips/RuiChip.vue.d.ts +7 -21
- package/dist/components/chips/RuiChip.vue2.js +16 -21
- package/dist/components/chips/RuiChip.vue2.js.map +1 -1
- package/dist/components/color-picker/RuiColorBoard.vue.d.ts +1 -1
- package/dist/components/color-picker/RuiColorBoard.vue2.js +19 -19
- package/dist/components/color-picker/RuiColorBoard.vue2.js.map +1 -1
- package/dist/components/color-picker/RuiColorDisplay.vue.js +3 -5
- package/dist/components/color-picker/RuiColorDisplay.vue.js.map +1 -1
- package/dist/components/color-picker/RuiColorHue.vue2.js +17 -14
- package/dist/components/color-picker/RuiColorHue.vue2.js.map +1 -1
- package/dist/components/color-picker/RuiColorInput.vue.js +9 -7
- package/dist/components/color-picker/RuiColorInput.vue.js.map +1 -1
- package/dist/components/color-picker/RuiColorPicker.vue.d.ts +7 -9
- package/dist/components/color-picker/RuiColorPicker.vue.js +23 -14
- package/dist/components/color-picker/RuiColorPicker.vue.js.map +1 -1
- package/dist/components/color-picker/utils.js +22 -5
- package/dist/components/color-picker/utils.js.map +1 -1
- package/dist/components/date-time-picker/RuiDateTimePicker.vue.d.ts +2 -44
- package/dist/components/date-time-picker/RuiDateTimePicker.vue2.js +75 -79
- package/dist/components/date-time-picker/RuiDateTimePicker.vue2.js.map +1 -1
- package/dist/components/date-time-picker/RuiDateTimePickerMenu.vue.js +1 -1
- package/dist/components/date-time-picker/dayjs-setup.d.ts +1 -0
- package/dist/components/date-time-picker/dayjs-setup.js +8 -0
- package/dist/components/date-time-picker/dayjs-setup.js.map +1 -0
- package/dist/components/date-time-picker/segment-config.d.ts +14 -0
- package/dist/components/date-time-picker/segment-config.js +23 -0
- package/dist/components/date-time-picker/segment-config.js.map +1 -0
- package/dist/components/date-time-picker/segment-utils.d.ts +5 -0
- package/dist/components/date-time-picker/segment-utils.js +36 -0
- package/dist/components/date-time-picker/segment-utils.js.map +1 -0
- package/dist/components/date-time-picker/use-date-time-selection.d.ts +1 -0
- package/dist/components/date-time-picker/use-date-time-selection.js +1 -0
- package/dist/components/date-time-picker/use-date-time-selection.js.map +1 -1
- package/dist/components/date-time-picker/use-keyboard-handler.d.ts +4 -8
- package/dist/components/date-time-picker/use-keyboard-handler.js +9 -54
- package/dist/components/date-time-picker/use-keyboard-handler.js.map +1 -1
- package/dist/components/date-time-picker/utils.d.ts +1 -0
- package/dist/components/date-time-picker/utils.js +1 -0
- package/dist/components/date-time-picker/utils.js.map +1 -1
- package/dist/components/divider/RuiDivider.vue.d.ts +1 -3
- package/dist/components/divider/RuiDivider.vue.js +6 -3
- package/dist/components/divider/RuiDivider.vue.js.map +1 -1
- package/dist/components/forms/auto-complete/RuiAutoComplete.vue.d.ts +10 -1138
- package/dist/components/forms/auto-complete/RuiAutoComplete.vue2.js +153 -160
- package/dist/components/forms/auto-complete/RuiAutoComplete.vue2.js.map +1 -1
- package/dist/components/forms/checkbox/RuiCheckbox.vue.d.ts +15 -24
- package/dist/components/forms/checkbox/RuiCheckbox.vue2.js +32 -32
- package/dist/components/forms/checkbox/RuiCheckbox.vue2.js.map +1 -1
- package/dist/components/forms/radio-button/radio/RuiRadio.vue.d.ts +2 -2
- package/dist/components/forms/radio-button/radio/RuiRadio.vue2.js +19 -22
- package/dist/components/forms/radio-button/radio/RuiRadio.vue2.js.map +1 -1
- package/dist/components/forms/radio-button/radio-group/RuiRadioGroup.vue.d.ts +4 -2
- package/dist/components/forms/radio-button/radio-group/RuiRadioGroup.vue2.js +13 -16
- package/dist/components/forms/radio-button/radio-group/RuiRadioGroup.vue2.js.map +1 -1
- package/dist/components/forms/revealable-text-field/RuiRevealableTextField.vue.d.ts +7 -13
- package/dist/components/forms/revealable-text-field/RuiRevealableTextField.vue.js +10 -9
- package/dist/components/forms/revealable-text-field/RuiRevealableTextField.vue.js.map +1 -1
- package/dist/components/forms/select/RuiMenuSelect.vue.d.ts +10 -785
- package/dist/components/forms/select/RuiMenuSelect.vue2.js +60 -56
- package/dist/components/forms/select/RuiMenuSelect.vue2.js.map +1 -1
- package/dist/components/forms/select/RuiSimpleSelect.vue.d.ts +11 -10
- package/dist/components/forms/select/RuiSimpleSelect.vue2.js +21 -26
- package/dist/components/forms/select/RuiSimpleSelect.vue2.js.map +1 -1
- package/dist/components/forms/slider/RuiSlider.vue.d.ts +10 -26
- package/dist/components/forms/slider/RuiSlider.vue2.js +45 -48
- package/dist/components/forms/slider/RuiSlider.vue2.js.map +1 -1
- package/dist/components/forms/switch/RuiSwitch.vue.d.ts +12 -20
- package/dist/components/forms/switch/RuiSwitch.vue2.js +22 -21
- package/dist/components/forms/switch/RuiSwitch.vue2.js.map +1 -1
- package/dist/components/forms/text-area/RuiTextArea.vue.d.ts +5 -30
- package/dist/components/forms/text-area/RuiTextArea.vue2.js +47 -83
- package/dist/components/forms/text-area/RuiTextArea.vue2.js.map +1 -1
- package/dist/components/forms/text-field/RuiTextField.vue.d.ts +10 -30
- package/dist/components/forms/text-field/RuiTextField.vue2.js +36 -67
- package/dist/components/forms/text-field/RuiTextField.vue2.js.map +1 -1
- package/dist/components/helpers/RuiFormTextDetail.vue.d.ts +1 -5
- package/dist/components/helpers/RuiFormTextDetail.vue.js +5 -9
- package/dist/components/helpers/RuiFormTextDetail.vue.js.map +1 -1
- package/dist/components/icons/LazyIcon.vue.d.ts +1 -3
- package/dist/components/icons/RuiIcon.vue.d.ts +1 -4
- package/dist/components/icons/RuiIcon.vue2.js +11 -14
- package/dist/components/icons/RuiIcon.vue2.js.map +1 -1
- package/dist/components/index.d.ts +4 -4
- package/dist/components/loaders/RuiSkeletonBase.vue.d.ts +1 -3
- package/dist/components/loaders/RuiSkeletonBase.vue2.js +2 -2
- package/dist/components/loaders/RuiSkeletonBase.vue2.js.map +1 -1
- package/dist/components/loaders/RuiSkeletonLoader.vue.d.ts +1 -4
- package/dist/components/loaders/RuiSkeletonLoader.vue2.js +2 -2
- package/dist/components/loaders/RuiSkeletonLoader.vue2.js.map +1 -1
- package/dist/components/logos/RuiLogo.vue.d.ts +1 -7
- package/dist/components/logos/RuiLogo.vue.js +13 -13
- package/dist/components/logos/RuiLogo.vue.js.map +1 -1
- package/dist/components/overlays/badge/RuiBadge.vue.d.ts +5 -20
- package/dist/components/overlays/badge/RuiBadge.vue2.js +11 -13
- package/dist/components/overlays/badge/RuiBadge.vue2.js.map +1 -1
- package/dist/components/overlays/bottom-sheet/RuiBottomSheet.vue.d.ts +10 -5
- package/dist/components/overlays/bottom-sheet/RuiBottomSheet.vue.js +18 -7
- package/dist/components/overlays/bottom-sheet/RuiBottomSheet.vue.js.map +1 -1
- package/dist/components/overlays/dialog/RuiDialog.vue.d.ts +22 -27
- package/dist/components/overlays/dialog/RuiDialog.vue2.js +90 -67
- package/dist/components/overlays/dialog/RuiDialog.vue2.js.map +1 -1
- package/dist/components/overlays/menu/RuiMenu.vue.d.ts +24 -35
- package/dist/components/overlays/menu/RuiMenu.vue2.js +71 -78
- package/dist/components/overlays/menu/RuiMenu.vue2.js.map +1 -1
- package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.vue.d.ts +23 -30
- package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.vue2.js +78 -49
- package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.vue2.js.map +1 -1
- package/dist/components/overlays/notification/RuiNotification.vue.d.ts +12 -12
- package/dist/components/overlays/notification/RuiNotification.vue.js +27 -22
- package/dist/components/overlays/notification/RuiNotification.vue.js.map +1 -1
- package/dist/components/overlays/tooltip/RuiTooltip.vue.d.ts +7 -18
- package/dist/components/overlays/tooltip/RuiTooltip.vue2.js +16 -9
- package/dist/components/overlays/tooltip/RuiTooltip.vue2.js.map +1 -1
- package/dist/components/progress/RuiProgress.vue.d.ts +8 -10
- package/dist/components/progress/RuiProgress.vue2.js +18 -24
- package/dist/components/progress/RuiProgress.vue2.js.map +1 -1
- package/dist/components/steppers/RuiFooterStepper.vue.d.ts +10 -11
- package/dist/components/steppers/RuiFooterStepper.vue2.js +33 -26
- package/dist/components/steppers/RuiFooterStepper.vue2.js.map +1 -1
- package/dist/components/steppers/RuiStepper.vue.d.ts +6 -15
- package/dist/components/steppers/RuiStepper.vue2.js +39 -36
- package/dist/components/steppers/RuiStepper.vue2.js.map +1 -1
- package/dist/components/steppers/RuiStepperCustomIcon.vue.d.ts +1 -3
- package/dist/components/steppers/RuiStepperCustomIcon.vue2.js +2 -2
- package/dist/components/steppers/RuiStepperCustomIcon.vue2.js.map +1 -1
- package/dist/components/steppers/RuiStepperIcon.vue.d.ts +1 -3
- package/dist/components/steppers/RuiStepperIcon.vue2.js +2 -2
- package/dist/components/steppers/RuiStepperIcon.vue2.js.map +1 -1
- package/dist/components/tables/RuiDataTable.vue.d.ts +29 -81
- package/dist/components/tables/RuiDataTable.vue2.js +196 -601
- package/dist/components/tables/RuiDataTable.vue2.js.map +1 -1
- package/dist/components/tables/RuiDataTable.vue3.js +24 -27
- package/dist/components/tables/RuiDataTable.vue3.js.map +1 -1
- package/dist/components/tables/RuiExpandButton.vue.d.ts +3 -6
- package/dist/components/tables/RuiExpandButton.vue2.js +4 -2
- package/dist/components/tables/RuiExpandButton.vue2.js.map +1 -1
- package/dist/components/tables/RuiTableHead.vue.d.ts +4 -7
- package/dist/components/tables/RuiTableHead.vue2.js +20 -20
- package/dist/components/tables/RuiTableHead.vue2.js.map +1 -1
- package/dist/components/tables/RuiTablePagination.vue.d.ts +10 -10
- package/dist/components/tables/RuiTablePagination.vue2.js +27 -19
- package/dist/components/tables/RuiTablePagination.vue2.js.map +1 -1
- package/dist/components/tabs/tab/RuiTab.vue.d.ts +3 -18
- package/dist/components/tabs/tab/RuiTab.vue2.js +84 -52
- package/dist/components/tabs/tab/RuiTab.vue2.js.map +1 -1
- package/dist/components/tabs/tab-item/RuiTabItem.vue.d.ts +3 -11
- package/dist/components/tabs/tab-item/RuiTabItem.vue2.js +3 -2
- package/dist/components/tabs/tab-item/RuiTabItem.vue2.js.map +1 -1
- package/dist/components/tabs/tab-items/RuiTabItems.vue.d.ts +2 -5
- package/dist/components/tabs/tab-items/RuiTabItems.vue2.js +18 -18
- package/dist/components/tabs/tab-items/RuiTabItems.vue2.js.map +1 -1
- package/dist/components/tabs/tabs/RuiTabs.vue.d.ts +10 -14
- package/dist/components/tabs/tabs/RuiTabs.vue2.js +101 -94
- package/dist/components/tabs/tabs/RuiTabs.vue2.js.map +1 -1
- package/dist/components/time-picker/RuiTimePicker.vue.d.ts +1 -4
- package/dist/components/time-picker/RuiTimePicker.vue2.js +88 -38
- package/dist/components/time-picker/RuiTimePicker.vue2.js.map +1 -1
- package/dist/composables/breakpoint.js +8 -13
- package/dist/composables/breakpoint.js.map +1 -1
- package/dist/composables/dropdown-menu.d.ts +7 -4
- package/dist/composables/dropdown-menu.js +73 -50
- package/dist/composables/dropdown-menu.js.map +1 -1
- package/dist/composables/forms/auto-complete/focus.d.ts +7 -5
- package/dist/composables/forms/auto-complete/focus.js +28 -17
- package/dist/composables/forms/auto-complete/focus.js.map +1 -1
- package/dist/composables/forms/auto-complete/index.d.ts +0 -9
- package/dist/composables/forms/auto-complete/keyboard-navigation.d.ts +8 -3
- package/dist/composables/forms/auto-complete/keyboard-navigation.js +11 -5
- package/dist/composables/forms/auto-complete/keyboard-navigation.js.map +1 -1
- package/dist/composables/forms/auto-complete/search.d.ts +7 -0
- package/dist/composables/forms/auto-complete/search.js +10 -8
- package/dist/composables/forms/auto-complete/search.js.map +1 -1
- package/dist/composables/forms/auto-complete/value.d.ts +4 -0
- package/dist/composables/forms/auto-complete/value.js +24 -42
- package/dist/composables/forms/auto-complete/value.js.map +1 -1
- package/dist/composables/forms/use-label-with-quote.d.ts +2 -0
- package/dist/composables/forms/use-label-with-quote.js +14 -0
- package/dist/composables/forms/use-label-with-quote.js.map +1 -0
- package/dist/composables/forms/use-prepend-append-width.d.ts +6 -0
- package/dist/composables/forms/use-prepend-append-width.js +25 -0
- package/dist/composables/forms/use-prepend-append-width.js.map +1 -0
- package/dist/composables/icons.js +1 -1
- package/dist/composables/icons.js.map +1 -1
- package/dist/composables/popper.js +63 -49
- package/dist/composables/popper.js.map +1 -1
- package/dist/composables/sticky-header.d.ts +7 -5
- package/dist/composables/sticky-header.js +51 -41
- package/dist/composables/sticky-header.js.map +1 -1
- package/dist/composables/tables/data-table/columns.d.ts +25 -0
- package/dist/composables/tables/data-table/columns.js +54 -0
- package/dist/composables/tables/data-table/columns.js.map +1 -0
- package/dist/composables/tables/data-table/expansion.d.ts +17 -0
- package/dist/composables/tables/data-table/expansion.js +38 -0
- package/dist/composables/tables/data-table/expansion.js.map +1 -0
- package/dist/composables/tables/data-table/grouping.d.ts +29 -0
- package/dist/composables/tables/data-table/grouping.js +136 -0
- package/dist/composables/tables/data-table/grouping.js.map +1 -0
- package/dist/composables/tables/data-table/pagination.d.ts +33 -0
- package/dist/composables/tables/data-table/pagination.js +119 -0
- package/dist/composables/tables/data-table/pagination.js.map +1 -0
- package/dist/composables/tables/data-table/selection.d.ts +29 -0
- package/dist/composables/tables/data-table/selection.js +164 -0
- package/dist/composables/tables/data-table/selection.js.map +1 -0
- package/dist/composables/tables/data-table/sort.d.ts +32 -0
- package/dist/composables/tables/data-table/sort.js +136 -0
- package/dist/composables/tables/data-table/sort.js.map +1 -0
- package/dist/composables/tables/data-table/types.d.ts +14 -0
- package/dist/composables/tables/data-table/types.js +11 -0
- package/dist/composables/tables/data-table/types.js.map +1 -0
- package/dist/composables/theme.js +5 -21
- package/dist/composables/theme.js.map +1 -1
- package/dist/composables/timeout-manager.d.ts +4 -2
- package/dist/composables/timeout-manager.js +2 -1
- package/dist/composables/timeout-manager.js.map +1 -1
- package/dist/icons/icons_1.d.ts +15 -15
- package/dist/icons/icons_1.js +123 -123
- package/dist/icons/icons_1.js.map +1 -1
- package/dist/icons/icons_2.d.ts +46 -46
- package/dist/icons/icons_2.js +301 -301
- package/dist/icons/icons_2.js.map +1 -1
- package/dist/icons/icons_3.d.ts +43 -43
- package/dist/icons/icons_3.js +252 -252
- package/dist/icons/icons_3.js.map +1 -1
- package/dist/icons/icons_4.d.ts +72 -2
- package/dist/icons/icons_4.js +368 -18
- package/dist/icons/icons_4.js.map +1 -1
- package/dist/icons/index.d.ts +1 -1
- package/dist/icons/index.js +1 -1
- package/dist/icons/index.js.map +1 -1
- package/dist/index.js +105 -42
- package/dist/index.js.map +1 -1
- package/dist/style.css +189 -162
- package/dist/theme/index.js +15 -1
- package/dist/theme/index.js.map +1 -1
- package/dist/utils/assert.d.ts +1 -1
- package/dist/utils/assert.js.map +1 -1
- package/dist/utils/form-text-detail.d.ts +9 -8
- package/dist/utils/form-text-detail.js +1 -2
- package/dist/utils/form-text-detail.js.map +1 -1
- package/dist/utils/helpers.d.ts +4 -32
- package/dist/utils/helpers.js +8 -9
- package/dist/utils/helpers.js.map +1 -1
- package/dist/utils/is-equal.d.ts +1 -1
- package/dist/utils/is-equal.js +5 -3
- package/dist/utils/is-equal.js.map +1 -1
- package/dist/vite-plugin/index.js +4 -4
- package/dist/vite-plugin/index.js.map +1 -1
- package/dist/vite-plugin/scanner.js +3 -3
- package/dist/vite-plugin/scanner.js.map +1 -1
- package/dist/web-types.json +390 -371
- package/package.json +47 -44
- package/dist/utils/generate-id.d.ts +0 -5
- package/dist/utils/generate-id.js +0 -21
- package/dist/utils/generate-id.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { computed, ref, watch, nextTick } from "vue";
|
|
1
|
+
import { computed, shallowRef, ref, watch, nextTick } from "vue";
|
|
2
2
|
import { get, set } from "@vueuse/shared";
|
|
3
3
|
import { useVirtualList, useArrayMap, watchOnce } from "@vueuse/core";
|
|
4
4
|
function useDropdownOptionProperty({
|
|
@@ -9,8 +9,7 @@ function useDropdownOptionProperty({
|
|
|
9
9
|
if (textAttr) {
|
|
10
10
|
if (typeof textAttr === "function")
|
|
11
11
|
return textAttr(item);
|
|
12
|
-
else
|
|
13
|
-
return item[textAttr]?.toString();
|
|
12
|
+
else return item[textAttr]?.toString();
|
|
14
13
|
}
|
|
15
14
|
return item?.toString();
|
|
16
15
|
}
|
|
@@ -24,13 +23,23 @@ function useDropdownOptionProperty({
|
|
|
24
23
|
getText
|
|
25
24
|
};
|
|
26
25
|
}
|
|
26
|
+
function toSelectedArray(val) {
|
|
27
|
+
if (Array.isArray(val))
|
|
28
|
+
return val;
|
|
29
|
+
if (val !== null && val !== void 0)
|
|
30
|
+
return [val];
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
27
33
|
function useDropdownMenu({
|
|
28
34
|
appendWidth,
|
|
29
35
|
autoFocus,
|
|
30
36
|
autoSelectFirst,
|
|
31
37
|
dense,
|
|
32
|
-
disabled =
|
|
38
|
+
disabled = shallowRef(false),
|
|
39
|
+
getIdentifier: externalGetIdentifier,
|
|
40
|
+
getText: externalGetText,
|
|
33
41
|
hideSelected,
|
|
42
|
+
isOpen: externalIsOpen,
|
|
34
43
|
itemHeight = 48,
|
|
35
44
|
keyAttr,
|
|
36
45
|
menuRef,
|
|
@@ -41,25 +50,36 @@ function useDropdownMenu({
|
|
|
41
50
|
textAttr,
|
|
42
51
|
value
|
|
43
52
|
}) {
|
|
53
|
+
const { getIdentifier: defaultGetIdentifier, getText: defaultGetText } = useDropdownOptionProperty({
|
|
54
|
+
keyAttr,
|
|
55
|
+
textAttr
|
|
56
|
+
});
|
|
57
|
+
const getIdentifier = externalGetIdentifier ?? defaultGetIdentifier;
|
|
58
|
+
const getText = externalGetText ?? defaultGetText;
|
|
59
|
+
const activeIdentifiers = computed(() => {
|
|
60
|
+
const val = get(value);
|
|
61
|
+
const selected = toSelectedArray(val);
|
|
62
|
+
const identifiers = /* @__PURE__ */ new Set();
|
|
63
|
+
for (const item of selected) {
|
|
64
|
+
identifiers.add(getIdentifier(item));
|
|
65
|
+
}
|
|
66
|
+
return identifiers;
|
|
67
|
+
});
|
|
68
|
+
function isActiveItem(item) {
|
|
69
|
+
return get(activeIdentifiers).has(getIdentifier(item));
|
|
70
|
+
}
|
|
44
71
|
const options = computed(() => {
|
|
45
72
|
const options2 = get(allOptions);
|
|
46
73
|
if (!hideSelected)
|
|
47
74
|
return options2;
|
|
48
75
|
return options2.filter((item) => !isActiveItem(item));
|
|
49
76
|
});
|
|
50
|
-
const {
|
|
51
|
-
|
|
52
|
-
|
|
77
|
+
const { containerProps, list, scrollTo, wrapperProps } = useVirtualList(options, {
|
|
78
|
+
itemHeight,
|
|
79
|
+
overscan
|
|
53
80
|
});
|
|
54
|
-
const { containerProps, list, scrollTo, wrapperProps } = useVirtualList(
|
|
55
|
-
options,
|
|
56
|
-
{
|
|
57
|
-
itemHeight,
|
|
58
|
-
overscan
|
|
59
|
-
}
|
|
60
|
-
);
|
|
61
81
|
const renderedData = useArrayMap(list, ({ data, index: _index }) => ({ _index, item: data }));
|
|
62
|
-
const isOpen =
|
|
82
|
+
const isOpen = externalIsOpen ?? shallowRef(false);
|
|
63
83
|
const valueKey = computed(() => {
|
|
64
84
|
const selected = get(value);
|
|
65
85
|
if (!keyAttr || !selected)
|
|
@@ -67,63 +87,67 @@ function useDropdownMenu({
|
|
|
67
87
|
return Array.isArray(selected) ? selected.map((item) => item[keyAttr]) : selected[keyAttr];
|
|
68
88
|
});
|
|
69
89
|
const highlightedIndex = ref(get(autoSelectFirst) ? 0 : -1);
|
|
90
|
+
const optionWidthBounds = computed(() => {
|
|
91
|
+
let min = 0;
|
|
92
|
+
let max = 0;
|
|
93
|
+
for (const option of get(options)) {
|
|
94
|
+
const length = getText(option)?.toString()?.length ?? 0;
|
|
95
|
+
if (min === 0 && max === 0) {
|
|
96
|
+
min = length;
|
|
97
|
+
max = length;
|
|
98
|
+
} else if (length < min) {
|
|
99
|
+
min = length;
|
|
100
|
+
} else if (length > max) {
|
|
101
|
+
max = length;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return { min, max };
|
|
105
|
+
});
|
|
70
106
|
const menuWidth = computed(() => {
|
|
71
|
-
const
|
|
107
|
+
const { min, max } = get(optionWidthBounds);
|
|
72
108
|
const maxWidth = 30;
|
|
73
109
|
const paddingX = 1.5;
|
|
74
110
|
const fontMultiplier = get(dense) ? 12 : 13;
|
|
75
|
-
|
|
76
|
-
const length = getText(option)?.toString()?.length ?? 0;
|
|
77
|
-
if (widths.min === 0 && widths.max === 0) {
|
|
78
|
-
widths.min = length;
|
|
79
|
-
widths.max = length;
|
|
80
|
-
} else if (length < widths.min) {
|
|
81
|
-
widths.min = length;
|
|
82
|
-
} else if (length > widths.max) {
|
|
83
|
-
widths.max = length;
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
const difference = widths.max - widths.min;
|
|
111
|
+
const difference = max - min;
|
|
87
112
|
function computeValue(width) {
|
|
88
113
|
const additionalWidths = (prependWidth ? prependWidth + 0.5 : 0) + (appendWidth ? appendWidth + 0.5 : 0);
|
|
89
114
|
return `${Math.min(width * fontMultiplier / 16 + paddingX + additionalWidths, maxWidth)}rem`;
|
|
90
115
|
}
|
|
91
116
|
if (difference <= 5)
|
|
92
|
-
return computeValue(
|
|
93
|
-
return computeValue(
|
|
117
|
+
return computeValue(max);
|
|
118
|
+
return computeValue(min + difference / 2);
|
|
94
119
|
});
|
|
95
120
|
function toggle(state = false) {
|
|
96
121
|
set(isOpen, state);
|
|
97
122
|
}
|
|
98
123
|
function itemIndexInValue(item) {
|
|
99
124
|
const val = get(value);
|
|
100
|
-
const selected =
|
|
125
|
+
const selected = toSelectedArray(val);
|
|
101
126
|
if (selected.length === 0)
|
|
102
127
|
return -1;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
return selectedItem[keyAttr] === item[keyAttr];
|
|
106
|
-
return selectedItem === item;
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
function isActiveItem(item) {
|
|
110
|
-
return itemIndexInValue(item) !== -1;
|
|
128
|
+
const itemId = getIdentifier(item);
|
|
129
|
+
return selected.findIndex((selectedItem) => getIdentifier(selectedItem) === itemId);
|
|
111
130
|
}
|
|
112
131
|
async function adjustScrollByHighlightedIndex(smooth = false) {
|
|
113
132
|
const index = get(highlightedIndex);
|
|
114
133
|
if (index > -1) {
|
|
115
134
|
await nextTick(() => {
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
135
|
+
const menuEl = get(menuRef);
|
|
136
|
+
const container = menuEl?.parentElement;
|
|
137
|
+
if (container && menuEl) {
|
|
138
|
+
const highlightedElem = menuEl.getElementsByClassName("highlighted")[0];
|
|
119
139
|
if (highlightedElem) {
|
|
120
|
-
highlightedElem.scrollIntoView?.({
|
|
121
|
-
|
|
140
|
+
highlightedElem.scrollIntoView?.({
|
|
141
|
+
behavior: smooth ? "smooth" : "auto",
|
|
142
|
+
block: "nearest"
|
|
143
|
+
});
|
|
144
|
+
if (get(autoFocus) && "focus" in highlightedElem && typeof highlightedElem.focus === "function") {
|
|
122
145
|
highlightedElem?.focus();
|
|
146
|
+
}
|
|
123
147
|
} else {
|
|
124
148
|
scrollTo(index);
|
|
125
149
|
if (get(autoFocus)) {
|
|
126
|
-
const elem =
|
|
150
|
+
const elem = menuEl.getElementsByClassName("highlighted")[0];
|
|
127
151
|
if (elem && "focus" in elem && typeof elem.focus === "function")
|
|
128
152
|
elem.focus();
|
|
129
153
|
}
|
|
@@ -136,7 +160,7 @@ function useDropdownMenu({
|
|
|
136
160
|
await nextTick(() => {
|
|
137
161
|
if (open) {
|
|
138
162
|
const val = get(value);
|
|
139
|
-
if (Array.isArray(val) && val.length > 0 ||
|
|
163
|
+
if (Array.isArray(val) && val.length > 0 || val !== null && val !== void 0) {
|
|
140
164
|
const index = get(options).findIndex(isActiveItem);
|
|
141
165
|
if (index > -1)
|
|
142
166
|
set(highlightedIndex, index);
|
|
@@ -180,8 +204,7 @@ function useDropdownMenu({
|
|
|
180
204
|
set(highlightedIndex, 0);
|
|
181
205
|
else if (position < 0)
|
|
182
206
|
set(highlightedIndex, total - 1);
|
|
183
|
-
else
|
|
184
|
-
set(highlightedIndex, position);
|
|
207
|
+
else set(highlightedIndex, position);
|
|
185
208
|
};
|
|
186
209
|
const applyHighlighted = () => {
|
|
187
210
|
if (!setValue || !get(isOpen))
|
|
@@ -189,8 +212,8 @@ function useDropdownMenu({
|
|
|
189
212
|
const highlightedIndexVal = get(highlightedIndex);
|
|
190
213
|
if (highlightedIndexVal === -1)
|
|
191
214
|
return;
|
|
192
|
-
const entry = get(options)
|
|
193
|
-
if (entry)
|
|
215
|
+
const entry = get(options)[highlightedIndexVal];
|
|
216
|
+
if (entry !== void 0)
|
|
194
217
|
setValue(entry);
|
|
195
218
|
};
|
|
196
219
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown-menu.js","sources":["../../src/composables/dropdown-menu.ts"],"sourcesContent":["import type { Ref } from 'vue';\n\nexport type KeyOfType<T, V> = T extends string | number | boolean | symbol | null | undefined\n ? never\n : V extends object\n ? keyof T\n : keyof {\n [P in keyof T as T[P] extends V\n ? P\n : V extends T[P]\n ? P\n : never]: any\n };\n\nexport interface DropdownItemAttr<TValue, TItem> {\n keyAttr?: KeyOfType<TItem, TValue extends Array<infer U> ? U : TValue>;\n textAttr?: keyof TItem | ((item: TItem) => string);\n}\n\nexport interface DropdownOptions<TValue, TItem> {\n options: Ref<TItem[]>;\n dense?: Ref<boolean>;\n disabled?: Ref<boolean>;\n value: Ref<TItem | TItem[] | undefined>;\n menuRef: Ref<HTMLElement>;\n keyAttr?: KeyOfType<TItem, TValue extends Array<infer U> ? U : TValue>;\n textAttr?: keyof TItem | ((item: TItem) => string);\n appendWidth?: number;\n prependWidth?: number;\n itemHeight?: number;\n overscan?: number;\n autoSelectFirst?: boolean;\n autoFocus?: boolean;\n setValue?: (val: TItem) => void;\n hideSelected?: boolean;\n}\n\nexport function useDropdownOptionProperty<TValue, TItem>({\n keyAttr,\n textAttr,\n}: DropdownItemAttr<TValue, TItem>) {\n function getText(item: TItem): string | undefined {\n if (textAttr) {\n if (typeof textAttr === 'function')\n return textAttr(item);\n\n else\n return item[textAttr]?.toString();\n }\n\n return item?.toString();\n }\n\n function getIdentifier(item: TItem): TItem | TItem[KeyOfType<TItem, TValue extends Array<infer U> ? U : TValue>] {\n if (keyAttr)\n return item[keyAttr];\n\n return item;\n }\n\n return {\n getIdentifier,\n getText,\n };\n}\n\nexport function useDropdownMenu<TValue, TItem>({\n appendWidth,\n autoFocus,\n autoSelectFirst,\n dense,\n disabled = ref(false),\n hideSelected,\n itemHeight = 48,\n keyAttr,\n menuRef,\n options: allOptions,\n overscan = 5,\n prependWidth,\n setValue,\n textAttr,\n value,\n}: DropdownOptions<TValue, TItem>) {\n const options = computed(() => {\n const options = get(allOptions);\n if (!hideSelected)\n return options;\n\n return options.filter(item => !isActiveItem(item));\n });\n\n const { getIdentifier, getText } = useDropdownOptionProperty({\n keyAttr,\n textAttr,\n });\n\n const { containerProps, list, scrollTo, wrapperProps } = useVirtualList<TItem>(\n options,\n {\n itemHeight,\n overscan,\n },\n );\n\n const renderedData = useArrayMap(list, ({ data, index: _index }) => ({ _index, item: data }));\n\n const isOpen = ref(false);\n\n const valueKey = computed(() => {\n const selected = get(value);\n if (!keyAttr || !selected)\n return selected;\n return Array.isArray(selected) ? selected.map(item => item[keyAttr]) : selected[keyAttr];\n });\n\n const highlightedIndex: Ref<number> = ref(get(autoSelectFirst) ? 0 : -1);\n\n const menuWidth = computed(() => {\n const widths = { max: 0, min: 0 };\n const maxWidth = 30;\n const paddingX = 1.5;\n const fontMultiplier = get(dense) ? 12 : 13;\n\n get(options).forEach((option) => {\n const length = getText(option)?.toString()?.length ?? 0;\n if (widths.min === 0 && widths.max === 0) {\n widths.min = length;\n widths.max = length;\n }\n else if (length < widths.min) {\n widths.min = length;\n }\n else if (length > widths.max) {\n widths.max = length;\n }\n });\n\n const difference = widths.max - widths.min;\n\n function computeValue(width: number) {\n const additionalWidths = (prependWidth ? prependWidth + 0.5 : 0) + (appendWidth ? appendWidth + 0.5 : 0);\n return `${Math.min((width * fontMultiplier) / 16 + paddingX + additionalWidths, maxWidth)}rem`;\n }\n\n if (difference <= 5)\n return computeValue(widths.max);\n\n return computeValue(widths.min + difference / 2);\n });\n\n function toggle(state: boolean = false) {\n set(isOpen, state);\n }\n\n function itemIndexInValue(item: TItem): number {\n const val = get(value);\n const selected: TItem[] = Array.isArray(val) ? val : (val ? [val] : []);\n\n if (selected.length === 0)\n return -1;\n\n return selected.findIndex((selectedItem) => {\n if (keyAttr)\n return selectedItem[keyAttr] === item[keyAttr];\n return selectedItem === item;\n });\n }\n\n function isActiveItem(item: TItem): boolean {\n return itemIndexInValue(item) !== -1;\n }\n\n async function adjustScrollByHighlightedIndex(smooth: boolean = false) {\n const index = get(highlightedIndex);\n if (index > -1) {\n await nextTick(() => {\n const container = get(menuRef)?.parentElement;\n if (container) {\n const highlightedElem = get(menuRef).getElementsByClassName('highlighted')[0];\n\n if (highlightedElem) {\n highlightedElem.scrollIntoView?.({ behavior: smooth ? 'smooth' : 'auto', block: 'nearest' });\n if (get(autoFocus) && 'focus' in highlightedElem && typeof highlightedElem.focus === 'function')\n highlightedElem?.focus();\n }\n else {\n scrollTo(index);\n if (get(autoFocus)) {\n const elem = get(menuRef).getElementsByClassName('highlighted')[0];\n if (elem && 'focus' in elem && typeof elem.focus === 'function')\n elem.focus();\n }\n }\n }\n });\n }\n }\n\n async function updateOpen(open: boolean) {\n await nextTick(() => {\n if (open) {\n const val = get(value);\n\n // set highlighted index to active item\n if ((Array.isArray(val) && val.length > 0) || !!val) {\n const index = get(options).findIndex(isActiveItem);\n if (index > -1)\n set(highlightedIndex, index);\n }\n\n watchOnce(list, async () => {\n await adjustScrollByHighlightedIndex();\n });\n }\n });\n }\n\n watch([isOpen, disabled], async ([open, _]) => {\n await updateOpen(open);\n });\n\n watch(highlightedIndex, async (curr, prev) => {\n if (curr !== prev)\n await adjustScrollByHighlightedIndex(true);\n });\n\n watch(options, async () => {\n if (get(highlightedIndex) !== -1) {\n if (get(autoSelectFirst)) {\n set(highlightedIndex, 0);\n await adjustScrollByHighlightedIndex();\n }\n else {\n set(highlightedIndex, -1);\n scrollTo(0);\n }\n }\n else {\n scrollTo(0);\n }\n });\n\n const moveHighlight = (up: boolean) => {\n if (!get(isOpen)) {\n toggle(true);\n return;\n }\n\n let position = get(highlightedIndex);\n const move = up ? -1 : 1;\n\n position += move;\n\n const total = get(options).length;\n\n if (position >= total)\n set(highlightedIndex, 0);\n else if (position < 0)\n set(highlightedIndex, total - 1);\n else\n set(highlightedIndex, position);\n };\n\n const applyHighlighted = () => {\n if (!setValue || !get(isOpen))\n return;\n\n const highlightedIndexVal = get(highlightedIndex);\n if (highlightedIndexVal === -1)\n return;\n\n const entry = get(options).find((_data, index) => highlightedIndexVal === index);\n if (entry)\n setValue(entry);\n };\n\n return {\n applyHighlighted,\n containerProps,\n getIdentifier,\n getText,\n highlightedIndex,\n isActiveItem,\n isOpen,\n itemIndexInValue,\n menuWidth,\n moveHighlight,\n optionsWithSelectedHidden: options,\n renderedData,\n toggle,\n valueKey,\n wrapperProps,\n };\n}\n"],"names":["options"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dropdown-menu.js","sources":["../../src/composables/dropdown-menu.ts"],"sourcesContent":["import type { Ref, TemplateRef } from 'vue';\n\nexport type KeyOfType<T, V> = T extends string | number | boolean | symbol | null | undefined\n ? never\n : V extends object\n ? keyof T\n : keyof {\n [P in keyof T as T[P] extends V ? P : V extends T[P] ? P : never]: any;\n };\n\nexport interface DropdownItemAttr<TValue, TItem> {\n keyAttr?: KeyOfType<TItem, TValue extends Array<infer U> ? U : TValue>;\n textAttr?: keyof TItem | ((item: TItem) => string);\n}\n\nexport interface DropdownOptions<TValue, TItem> {\n options: Ref<TItem[]>;\n dense?: Ref<boolean>;\n disabled?: Ref<boolean>;\n value: Ref<TItem | TItem[] | undefined>;\n menuRef: Ref<HTMLElement> | TemplateRef<HTMLElement>;\n keyAttr?: KeyOfType<TItem, TValue extends Array<infer U> ? U : TValue>;\n textAttr?: keyof TItem | ((item: TItem) => string);\n appendWidth?: number;\n prependWidth?: number;\n itemHeight?: number;\n overscan?: number;\n autoSelectFirst?: boolean;\n autoFocus?: boolean;\n setValue?: (val: TItem) => void;\n hideSelected?: boolean;\n isOpen?: Ref<boolean>;\n getText?: (item: TItem) => string | undefined;\n getIdentifier?: (item: TItem) => any;\n}\n\nexport function useDropdownOptionProperty<TValue, TItem>({\n keyAttr,\n textAttr,\n}: DropdownItemAttr<TValue, TItem>) {\n function getText(item: TItem): string | undefined {\n if (textAttr) {\n if (typeof textAttr === 'function')\n return textAttr(item);\n else return item[textAttr]?.toString();\n }\n\n return item?.toString();\n }\n\n function getIdentifier(\n item: TItem,\n ): TItem | TItem[KeyOfType<TItem, TValue extends Array<infer U> ? U : TValue>] {\n if (keyAttr)\n return item[keyAttr];\n\n return item;\n }\n\n return {\n getIdentifier,\n getText,\n };\n}\n\nfunction toSelectedArray<TItem>(val: TItem | TItem[] | undefined): TItem[] {\n if (Array.isArray(val))\n return val;\n if (val !== null && val !== undefined)\n return [val];\n return [];\n}\n\nexport function useDropdownMenu<TValue, TItem>({\n appendWidth,\n autoFocus,\n autoSelectFirst,\n dense,\n disabled = shallowRef(false),\n getIdentifier: externalGetIdentifier,\n getText: externalGetText,\n hideSelected,\n isOpen: externalIsOpen,\n itemHeight = 48,\n keyAttr,\n menuRef,\n options: allOptions,\n overscan = 5,\n prependWidth,\n setValue,\n textAttr,\n value,\n}: DropdownOptions<TValue, TItem>) {\n const { getIdentifier: defaultGetIdentifier, getText: defaultGetText } = useDropdownOptionProperty({\n keyAttr,\n textAttr,\n });\n\n const getIdentifier = externalGetIdentifier ?? defaultGetIdentifier;\n const getText = externalGetText ?? defaultGetText;\n\n const activeIdentifiers = computed<Set<any>>(() => {\n const val = get(value);\n const selected: TItem[] = toSelectedArray(val);\n const identifiers = new Set<any>();\n for (const item of selected) {\n identifiers.add(getIdentifier(item));\n }\n return identifiers;\n });\n\n function isActiveItem(item: TItem): boolean {\n return get(activeIdentifiers).has(getIdentifier(item));\n }\n\n const options = computed<TItem[]>(() => {\n const options = get(allOptions);\n if (!hideSelected)\n return options;\n\n return options.filter(item => !isActiveItem(item));\n });\n\n const { containerProps, list, scrollTo, wrapperProps } = useVirtualList<TItem>(options, {\n itemHeight,\n overscan,\n });\n\n const renderedData = useArrayMap(list, ({ data, index: _index }) => ({ _index, item: data }));\n\n const isOpen = externalIsOpen ?? shallowRef<boolean>(false);\n\n const valueKey = computed(() => {\n const selected = get(value);\n if (!keyAttr || !selected)\n return selected;\n return Array.isArray(selected) ? selected.map(item => item[keyAttr]) : selected[keyAttr];\n });\n\n const highlightedIndex: Ref<number> = ref(get(autoSelectFirst) ? 0 : -1);\n\n const optionWidthBounds = computed<{ min: number; max: number }>(() => {\n let min = 0;\n let max = 0;\n for (const option of get(options)) {\n const length = getText(option)?.toString()?.length ?? 0;\n if (min === 0 && max === 0) {\n min = length;\n max = length;\n }\n else if (length < min) {\n min = length;\n }\n else if (length > max) {\n max = length;\n }\n }\n return { min, max };\n });\n\n const menuWidth = computed<string>(() => {\n const { min, max } = get(optionWidthBounds);\n const maxWidth = 30;\n const paddingX = 1.5;\n const fontMultiplier = get(dense) ? 12 : 13;\n const difference = max - min;\n\n function computeValue(width: number): string {\n const additionalWidths =\n (prependWidth ? prependWidth + 0.5 : 0) + (appendWidth ? appendWidth + 0.5 : 0);\n return `${Math.min((width * fontMultiplier) / 16 + paddingX + additionalWidths, maxWidth)}rem`;\n }\n\n if (difference <= 5)\n return computeValue(max);\n\n return computeValue(min + difference / 2);\n });\n\n function toggle(state: boolean = false): void {\n set(isOpen, state);\n }\n\n function itemIndexInValue(item: TItem): number {\n const val = get(value);\n const selected: TItem[] = toSelectedArray(val);\n\n if (selected.length === 0)\n return -1;\n\n const itemId = getIdentifier(item);\n return selected.findIndex(selectedItem => getIdentifier(selectedItem) === itemId);\n }\n\n async function adjustScrollByHighlightedIndex(smooth: boolean = false): Promise<void> {\n const index = get(highlightedIndex);\n if (index > -1) {\n await nextTick(() => {\n const menuEl = get(menuRef);\n const container = menuEl?.parentElement;\n if (container && menuEl) {\n const highlightedElem = menuEl.getElementsByClassName('highlighted')[0];\n\n if (highlightedElem) {\n highlightedElem.scrollIntoView?.({\n behavior: smooth ? 'smooth' : 'auto',\n block: 'nearest',\n });\n if (\n get(autoFocus) &&\n 'focus' in highlightedElem &&\n typeof highlightedElem.focus === 'function'\n ) {\n highlightedElem?.focus();\n }\n }\n else {\n scrollTo(index);\n if (get(autoFocus)) {\n const elem = menuEl.getElementsByClassName('highlighted')[0];\n if (elem && 'focus' in elem && typeof elem.focus === 'function')\n elem.focus();\n }\n }\n }\n });\n }\n }\n\n async function updateOpen(open: boolean): Promise<void> {\n await nextTick(() => {\n if (open) {\n const val = get(value);\n\n // set highlighted index to active item\n if ((Array.isArray(val) && val.length > 0) || (val !== null && val !== undefined)) {\n const index = get(options).findIndex(isActiveItem);\n if (index > -1)\n set(highlightedIndex, index);\n }\n\n watchOnce(list, async () => {\n await adjustScrollByHighlightedIndex();\n });\n }\n });\n }\n\n watch([isOpen, disabled], async ([open, _]) => {\n await updateOpen(open);\n });\n\n watch(highlightedIndex, async (curr, prev) => {\n if (curr !== prev)\n await adjustScrollByHighlightedIndex(true);\n });\n\n watch(options, async () => {\n if (get(highlightedIndex) !== -1) {\n if (get(autoSelectFirst)) {\n set(highlightedIndex, 0);\n await adjustScrollByHighlightedIndex();\n }\n else {\n set(highlightedIndex, -1);\n scrollTo(0);\n }\n }\n else {\n scrollTo(0);\n }\n });\n\n const moveHighlight = (up: boolean): void => {\n if (!get(isOpen)) {\n toggle(true);\n return;\n }\n\n let position = get(highlightedIndex);\n const move = up ? -1 : 1;\n\n position += move;\n\n const total = get(options).length;\n\n if (position >= total)\n set(highlightedIndex, 0);\n else if (position < 0)\n set(highlightedIndex, total - 1);\n else set(highlightedIndex, position);\n };\n\n const applyHighlighted = (): void => {\n if (!setValue || !get(isOpen))\n return;\n\n const highlightedIndexVal = get(highlightedIndex);\n if (highlightedIndexVal === -1)\n return;\n\n const entry = get(options)[highlightedIndexVal];\n if (entry !== undefined)\n setValue(entry);\n };\n\n return {\n applyHighlighted,\n containerProps,\n getIdentifier,\n getText,\n highlightedIndex,\n isActiveItem,\n isOpen,\n itemIndexInValue,\n menuWidth,\n moveHighlight,\n optionsWithSelectedHidden: options,\n renderedData,\n toggle,\n valueKey,\n wrapperProps,\n };\n}\n"],"names":["options"],"mappings":";;;AAoCO,SAAS,0BAAyC;AAAA,EACvD;AAAA,EACA;AACF,GAAoC;AAClC,WAAS,QAAQ,MAAiC;AAChD,QAAI,UAAU;AACZ,UAAI,OAAO,aAAa;AACtB,eAAO,SAAS,IAAI;AAAA,UACjB,QAAO,KAAK,QAAQ,GAAG,SAAA;AAAA,IAC9B;AAEA,WAAO,MAAM,SAAA;AAAA,EACf;AAEA,WAAS,cACP,MAC6E;AAC7E,QAAI;AACF,aAAO,KAAK,OAAO;AAErB,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EAAA;AAEJ;AAEA,SAAS,gBAAuB,KAA2C;AACzE,MAAI,MAAM,QAAQ,GAAG;AACnB,WAAO;AACT,MAAI,QAAQ,QAAQ,QAAQ;AAC1B,WAAO,CAAC,GAAG;AACb,SAAO,CAAA;AACT;AAEO,SAAS,gBAA+B;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW,WAAW,KAAK;AAAA,EAC3B,eAAe;AAAA,EACf,SAAS;AAAA,EACT;AAAA,EACA,QAAQ;AAAA,EACR,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAmC;AACjC,QAAM,EAAE,eAAe,sBAAsB,SAAS,eAAA,IAAmB,0BAA0B;AAAA,IACjG;AAAA,IACA;AAAA,EAAA,CACD;AAED,QAAM,gBAAgB,yBAAyB;AAC/C,QAAM,UAAU,mBAAmB;AAEnC,QAAM,oBAAoB,SAAmB,MAAM;AACjD,UAAM,MAAM,IAAI,KAAK;AACrB,UAAM,WAAoB,gBAAgB,GAAG;AAC7C,UAAM,kCAAkB,IAAA;AACxB,eAAW,QAAQ,UAAU;AAC3B,kBAAY,IAAI,cAAc,IAAI,CAAC;AAAA,IACrC;AACA,WAAO;AAAA,EACT,CAAC;AAED,WAAS,aAAa,MAAsB;AAC1C,WAAO,IAAI,iBAAiB,EAAE,IAAI,cAAc,IAAI,CAAC;AAAA,EACvD;AAEA,QAAM,UAAU,SAAkB,MAAM;AACtC,UAAMA,WAAU,IAAI,UAAU;AAC9B,QAAI,CAAC;AACH,aAAOA;AAET,WAAOA,SAAQ,OAAO,CAAA,SAAQ,CAAC,aAAa,IAAI,CAAC;AAAA,EACnD,CAAC;AAED,QAAM,EAAE,gBAAgB,MAAM,UAAU,aAAA,IAAiB,eAAsB,SAAS;AAAA,IACtF;AAAA,IACA;AAAA,EAAA,CACD;AAED,QAAM,eAAe,YAAY,MAAM,CAAC,EAAE,MAAM,OAAO,OAAA,OAAc,EAAE,QAAQ,MAAM,OAAO;AAE5F,QAAM,SAAS,kBAAkB,WAAoB,KAAK;AAE1D,QAAM,WAAW,SAAS,MAAM;AAC9B,UAAM,WAAW,IAAI,KAAK;AAC1B,QAAI,CAAC,WAAW,CAAC;AACf,aAAO;AACT,WAAO,MAAM,QAAQ,QAAQ,IAAI,SAAS,IAAI,CAAA,SAAQ,KAAK,OAAO,CAAC,IAAI,SAAS,OAAO;AAAA,EACzF,CAAC;AAED,QAAM,mBAAgC,IAAI,IAAI,eAAe,IAAI,IAAI,EAAE;AAEvE,QAAM,oBAAoB,SAAuC,MAAM;AACrE,QAAI,MAAM;AACV,QAAI,MAAM;AACV,eAAW,UAAU,IAAI,OAAO,GAAG;AACjC,YAAM,SAAS,QAAQ,MAAM,GAAG,SAAA,GAAY,UAAU;AACtD,UAAI,QAAQ,KAAK,QAAQ,GAAG;AAC1B,cAAM;AACN,cAAM;AAAA,MACR,WACS,SAAS,KAAK;AACrB,cAAM;AAAA,MACR,WACS,SAAS,KAAK;AACrB,cAAM;AAAA,MACR;AAAA,IACF;AACA,WAAO,EAAE,KAAK,IAAA;AAAA,EAChB,CAAC;AAED,QAAM,YAAY,SAAiB,MAAM;AACvC,UAAM,EAAE,KAAK,QAAQ,IAAI,iBAAiB;AAC1C,UAAM,WAAW;AACjB,UAAM,WAAW;AACjB,UAAM,iBAAiB,IAAI,KAAK,IAAI,KAAK;AACzC,UAAM,aAAa,MAAM;AAEzB,aAAS,aAAa,OAAuB;AAC3C,YAAM,oBACH,eAAe,eAAe,MAAM,MAAM,cAAc,cAAc,MAAM;AAC/E,aAAO,GAAG,KAAK,IAAK,QAAQ,iBAAkB,KAAK,WAAW,kBAAkB,QAAQ,CAAC;AAAA,IAC3F;AAEA,QAAI,cAAc;AAChB,aAAO,aAAa,GAAG;AAEzB,WAAO,aAAa,MAAM,aAAa,CAAC;AAAA,EAC1C,CAAC;AAED,WAAS,OAAO,QAAiB,OAAa;AAC5C,QAAI,QAAQ,KAAK;AAAA,EACnB;AAEA,WAAS,iBAAiB,MAAqB;AAC7C,UAAM,MAAM,IAAI,KAAK;AACrB,UAAM,WAAoB,gBAAgB,GAAG;AAE7C,QAAI,SAAS,WAAW;AACtB,aAAO;AAET,UAAM,SAAS,cAAc,IAAI;AACjC,WAAO,SAAS,UAAU,CAAA,iBAAgB,cAAc,YAAY,MAAM,MAAM;AAAA,EAClF;AAEA,iBAAe,+BAA+B,SAAkB,OAAsB;AACpF,UAAM,QAAQ,IAAI,gBAAgB;AAClC,QAAI,QAAQ,IAAI;AACd,YAAM,SAAS,MAAM;AACnB,cAAM,SAAS,IAAI,OAAO;AAC1B,cAAM,YAAY,QAAQ;AAC1B,YAAI,aAAa,QAAQ;AACvB,gBAAM,kBAAkB,OAAO,uBAAuB,aAAa,EAAE,CAAC;AAEtE,cAAI,iBAAiB;AACnB,4BAAgB,iBAAiB;AAAA,cAC/B,UAAU,SAAS,WAAW;AAAA,cAC9B,OAAO;AAAA,YAAA,CACR;AACD,gBACE,IAAI,SAAS,KACb,WAAW,mBACX,OAAO,gBAAgB,UAAU,YACjC;AACA,+BAAiB,MAAA;AAAA,YACnB;AAAA,UACF,OACK;AACH,qBAAS,KAAK;AACd,gBAAI,IAAI,SAAS,GAAG;AAClB,oBAAM,OAAO,OAAO,uBAAuB,aAAa,EAAE,CAAC;AAC3D,kBAAI,QAAQ,WAAW,QAAQ,OAAO,KAAK,UAAU;AACnD,qBAAK,MAAA;AAAA,YACT;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,iBAAe,WAAW,MAA8B;AACtD,UAAM,SAAS,MAAM;AACnB,UAAI,MAAM;AACR,cAAM,MAAM,IAAI,KAAK;AAGrB,YAAK,MAAM,QAAQ,GAAG,KAAK,IAAI,SAAS,KAAO,QAAQ,QAAQ,QAAQ,QAAY;AACjF,gBAAM,QAAQ,IAAI,OAAO,EAAE,UAAU,YAAY;AACjD,cAAI,QAAQ;AACV,gBAAI,kBAAkB,KAAK;AAAA,QAC/B;AAEA,kBAAU,MAAM,YAAY;AAC1B,gBAAM,+BAAA;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,CAAC,QAAQ,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM;AAC7C,UAAM,WAAW,IAAI;AAAA,EACvB,CAAC;AAED,QAAM,kBAAkB,OAAO,MAAM,SAAS;AAC5C,QAAI,SAAS;AACX,YAAM,+BAA+B,IAAI;AAAA,EAC7C,CAAC;AAED,QAAM,SAAS,YAAY;AACzB,QAAI,IAAI,gBAAgB,MAAM,IAAI;AAChC,UAAI,IAAI,eAAe,GAAG;AACxB,YAAI,kBAAkB,CAAC;AACvB,cAAM,+BAAA;AAAA,MACR,OACK;AACH,YAAI,kBAAkB,EAAE;AACxB,iBAAS,CAAC;AAAA,MACZ;AAAA,IACF,OACK;AACH,eAAS,CAAC;AAAA,IACZ;AAAA,EACF,CAAC;AAED,QAAM,gBAAgB,CAAC,OAAsB;AAC3C,QAAI,CAAC,IAAI,MAAM,GAAG;AAChB,aAAO,IAAI;AACX;AAAA,IACF;AAEA,QAAI,WAAW,IAAI,gBAAgB;AACnC,UAAM,OAAO,KAAK,KAAK;AAEvB,gBAAY;AAEZ,UAAM,QAAQ,IAAI,OAAO,EAAE;AAE3B,QAAI,YAAY;AACd,UAAI,kBAAkB,CAAC;AAAA,aAChB,WAAW;AAClB,UAAI,kBAAkB,QAAQ,CAAC;AAAA,QAC5B,KAAI,kBAAkB,QAAQ;AAAA,EACrC;AAEA,QAAM,mBAAmB,MAAY;AACnC,QAAI,CAAC,YAAY,CAAC,IAAI,MAAM;AAC1B;AAEF,UAAM,sBAAsB,IAAI,gBAAgB;AAChD,QAAI,wBAAwB;AAC1B;AAEF,UAAM,QAAQ,IAAI,OAAO,EAAE,mBAAmB;AAC9C,QAAI,UAAU;AACZ,eAAS,KAAK;AAAA,EAClB;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,2BAA2B;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;"}
|
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
import type { ComputedRef, MaybeRef, Ref } from 'vue';
|
|
1
|
+
import type { ComputedRef, MaybeRef, Ref, TemplateRef } from 'vue';
|
|
2
2
|
export interface UseAutoCompleteFocusOptions {
|
|
3
|
+
/** Whether custom (free-text) values are allowed. */
|
|
3
4
|
customValue: MaybeRef<boolean>;
|
|
5
|
+
/** Whether to apply the current value as the search text when focused. */
|
|
4
6
|
shouldApplyValueAsSearch: MaybeRef<boolean>;
|
|
7
|
+
/** Whether the autocomplete is disabled. */
|
|
5
8
|
disabled: MaybeRef<boolean>;
|
|
6
9
|
}
|
|
7
|
-
export interface UseAutoCompleteFocusDeps
|
|
10
|
+
export interface UseAutoCompleteFocusDeps {
|
|
8
11
|
activatorFocused: Ref<boolean>;
|
|
9
12
|
activatorFocusedWithin: Ref<boolean>;
|
|
10
13
|
focusedValueIndex: Ref<number>;
|
|
11
|
-
filteredOptions: Ref<TItem[]>;
|
|
12
14
|
internalSearch: Ref<string>;
|
|
13
15
|
isOpen: Ref<boolean>;
|
|
14
16
|
justOpened: Ref<boolean>;
|
|
15
17
|
menuWrapperFocusedWithin: Ref<boolean>;
|
|
16
18
|
searchInputFocused: Ref<boolean>;
|
|
17
|
-
textInput: Ref<HTMLInputElement | undefined>;
|
|
19
|
+
textInput: Ref<HTMLInputElement | undefined> | TemplateRef<HTMLInputElement>;
|
|
18
20
|
setSearchAsValue: () => void;
|
|
19
21
|
updateInternalSearch: (value?: string) => void;
|
|
20
22
|
}
|
|
@@ -25,4 +27,4 @@ export interface UseAutoCompleteFocusReturn {
|
|
|
25
27
|
onInputFocused: () => void;
|
|
26
28
|
setInputFocus: () => Promise<void>;
|
|
27
29
|
}
|
|
28
|
-
export declare function useAutoCompleteFocus
|
|
30
|
+
export declare function useAutoCompleteFocus(options: UseAutoCompleteFocusOptions, deps: UseAutoCompleteFocusDeps): UseAutoCompleteFocusReturn;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { computed, nextTick } from "vue";
|
|
1
|
+
import { computed, watch, nextTick } from "vue";
|
|
2
2
|
import { get, set } from "@vueuse/shared";
|
|
3
3
|
import { watchDebounced } from "@vueuse/core";
|
|
4
4
|
function useAutoCompleteFocus(options, deps) {
|
|
@@ -39,24 +39,35 @@ function useAutoCompleteFocus(options, deps) {
|
|
|
39
39
|
set(deps.justOpened, true);
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const filteredOptions = get(deps.filteredOptions);
|
|
47
|
-
const internalSearch = get(deps.internalSearch);
|
|
48
|
-
if (customValue && filteredOptions.length === 0 && internalSearch) {
|
|
49
|
-
deps.setSearchAsValue();
|
|
50
|
-
}
|
|
51
|
-
const shouldApply = get(options.shouldApplyValueAsSearch);
|
|
52
|
-
if (!shouldApply) {
|
|
53
|
-
deps.updateInternalSearch();
|
|
54
|
-
}
|
|
42
|
+
let pendingCustomSearch = "";
|
|
43
|
+
watch(anyFocused, (focused) => {
|
|
44
|
+
if (!focused && get(options.customValue)) {
|
|
45
|
+
pendingCustomSearch = get(deps.internalSearch);
|
|
55
46
|
}
|
|
56
|
-
}, {
|
|
57
|
-
debounce: 200,
|
|
58
|
-
maxWait: 400
|
|
59
47
|
});
|
|
48
|
+
watchDebounced(
|
|
49
|
+
anyFocused,
|
|
50
|
+
(focused) => {
|
|
51
|
+
if (!focused) {
|
|
52
|
+
set(deps.isOpen, false);
|
|
53
|
+
const customValue = get(options.customValue);
|
|
54
|
+
const searchToUse = pendingCustomSearch;
|
|
55
|
+
pendingCustomSearch = "";
|
|
56
|
+
if (customValue && searchToUse) {
|
|
57
|
+
deps.updateInternalSearch(searchToUse);
|
|
58
|
+
deps.setSearchAsValue();
|
|
59
|
+
}
|
|
60
|
+
const shouldApply = get(options.shouldApplyValueAsSearch);
|
|
61
|
+
if (!shouldApply) {
|
|
62
|
+
deps.updateInternalSearch();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
debounce: 200,
|
|
68
|
+
maxWait: 400
|
|
69
|
+
}
|
|
70
|
+
);
|
|
60
71
|
return {
|
|
61
72
|
anyFocused,
|
|
62
73
|
inputClass,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"focus.js","sources":["../../../../src/composables/forms/auto-complete/focus.ts"],"sourcesContent":["import type { ComputedRef, MaybeRef, Ref } from 'vue';\n\nexport interface UseAutoCompleteFocusOptions {\n customValue: MaybeRef<boolean>;\n shouldApplyValueAsSearch: MaybeRef<boolean>;\n disabled: MaybeRef<boolean>;\n}\n\nexport interface UseAutoCompleteFocusDeps
|
|
1
|
+
{"version":3,"file":"focus.js","sources":["../../../../src/composables/forms/auto-complete/focus.ts"],"sourcesContent":["import type { ComputedRef, MaybeRef, Ref, TemplateRef } from 'vue';\n\nexport interface UseAutoCompleteFocusOptions {\n /** Whether custom (free-text) values are allowed. */\n customValue: MaybeRef<boolean>;\n /** Whether to apply the current value as the search text when focused. */\n shouldApplyValueAsSearch: MaybeRef<boolean>;\n /** Whether the autocomplete is disabled. */\n disabled: MaybeRef<boolean>;\n}\n\nexport interface UseAutoCompleteFocusDeps {\n activatorFocused: Ref<boolean>;\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 inputClass: ComputedRef<string>;\n onActivatorFocused: () => Promise<void>;\n onInputFocused: () => void;\n setInputFocus: () => Promise<void>;\n}\n\nexport function useAutoCompleteFocus(\n options: UseAutoCompleteFocusOptions,\n deps: UseAutoCompleteFocusDeps,\n): UseAutoCompleteFocusReturn {\n const anyFocused = computed<boolean>(\n () => get(deps.activatorFocusedWithin) || get(deps.menuWrapperFocusedWithin),\n );\n\n const inputClass = computed<string>(() => {\n const isFocused = get(anyFocused);\n const isDisabled = get(options.disabled);\n const shouldApply = get(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 async function setInputFocus(): Promise<void> {\n await nextTick(() => {\n set(deps.searchInputFocused, true);\n });\n }\n\n async function onActivatorFocused(): Promise<void> {\n await nextTick(() => {\n if (!get(deps.activatorFocused)) {\n set(deps.searchInputFocused, true);\n }\n });\n }\n\n function onInputFocused(): void {\n set(deps.focusedValueIndex, -1);\n\n const shouldApply = get(options.shouldApplyValueAsSearch);\n if (shouldApply) {\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 // Capture the search value immediately when focus is lost, before the debounced\n // handler fires. Other watchers (value.ts) may clear internalSearch when isOpen\n // changes, so we need to preserve it for the custom value blur flow.\n let pendingCustomSearch = '';\n\n watch(anyFocused, (focused) => {\n if (!focused && get(options.customValue)) {\n pendingCustomSearch = get(deps.internalSearch);\n }\n });\n\n // Close menu if the activator is not focused anymore\n // Using debounced to avoid the menu closing momentarily while focus switches.\n watchDebounced(\n anyFocused,\n (focused) => {\n if (!focused) {\n set(deps.isOpen, false);\n\n const customValue = get(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 = get(options.shouldApplyValueAsSearch);\n if (!shouldApply) {\n deps.updateInternalSearch();\n }\n }\n },\n {\n debounce: 200,\n maxWait: 400,\n },\n );\n\n return {\n anyFocused,\n inputClass,\n onActivatorFocused,\n onInputFocused,\n setInputFocus,\n };\n}\n"],"names":[],"mappings":";;;AAiCO,SAAS,qBACd,SACA,MAC4B;AAC5B,QAAM,aAAa;AAAA,IACjB,MAAM,IAAI,KAAK,sBAAsB,KAAK,IAAI,KAAK,wBAAwB;AAAA,EAAA;AAG7E,QAAM,aAAa,SAAiB,MAAM;AACxC,UAAM,YAAY,IAAI,UAAU;AAChC,UAAM,aAAa,IAAI,QAAQ,QAAQ;AACvC,UAAM,cAAc,IAAI,QAAQ,wBAAwB;AACxD,UAAM,YAAY,IAAI,KAAK,cAAc;AAEzC,SAAK,CAAC,aAAa,eAAe,CAAC;AACjC,aAAO;AACT,QAAI;AACF,aAAO;AACT,WAAO;AAAA,EACT,CAAC;AAED,iBAAe,gBAA+B;AAC5C,UAAM,SAAS,MAAM;AACnB,UAAI,KAAK,oBAAoB,IAAI;AAAA,IACnC,CAAC;AAAA,EACH;AAEA,iBAAe,qBAAoC;AACjD,UAAM,SAAS,MAAM;AACnB,UAAI,CAAC,IAAI,KAAK,gBAAgB,GAAG;AAC/B,YAAI,KAAK,oBAAoB,IAAI;AAAA,MACnC;AAAA,IACF,CAAC;AAAA,EACH;AAEA,WAAS,iBAAuB;AAC9B,QAAI,KAAK,mBAAmB,EAAE;AAE9B,UAAM,cAAc,IAAI,QAAQ,wBAAwB;AACxD,QAAI,aAAa;AACf,YAAM,YAAY,IAAI,KAAK,SAAS;AACpC,iBAAW,OAAA;AAAA,IACb;AAEA,QAAI,CAAC,IAAI,KAAK,MAAM,GAAG;AACrB,UAAI,KAAK,YAAY,IAAI;AAAA,IAC3B;AAAA,EACF;AAKA,MAAI,sBAAsB;AAE1B,QAAM,YAAY,CAAC,YAAY;AAC7B,QAAI,CAAC,WAAW,IAAI,QAAQ,WAAW,GAAG;AACxC,4BAAsB,IAAI,KAAK,cAAc;AAAA,IAC/C;AAAA,EACF,CAAC;AAID;AAAA,IACE;AAAA,IACA,CAAC,YAAY;AACX,UAAI,CAAC,SAAS;AACZ,YAAI,KAAK,QAAQ,KAAK;AAEtB,cAAM,cAAc,IAAI,QAAQ,WAAW;AAC3C,cAAM,cAAc;AACpB,8BAAsB;AAEtB,YAAI,eAAe,aAAa;AAC9B,eAAK,qBAAqB,WAAW;AACrC,eAAK,iBAAA;AAAA,QACP;AAEA,cAAM,cAAc,IAAI,QAAQ,wBAAwB;AACxD,YAAI,CAAC,aAAa;AAChB,eAAK,qBAAA;AAAA,QACP;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE,UAAU;AAAA,MACV,SAAS;AAAA,IAAA;AAAA,EACX;AAGF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;"}
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
export declare function useAutoCompleteProps<T extends object>(props: {
|
|
2
|
-
keyProp?: Extract<keyof T, string>;
|
|
3
|
-
textProp?: Extract<keyof T, string>;
|
|
4
|
-
itemDisabledProp?: Extract<keyof T, string>;
|
|
5
|
-
}): {
|
|
6
|
-
getItemKey: (item: T) => string | undefined;
|
|
7
|
-
getTextValue: (item: T) => string | undefined;
|
|
8
|
-
isItemDisabled: (item: T) => boolean;
|
|
9
|
-
};
|
|
10
1
|
export * from './focus.js';
|
|
11
2
|
export * from './keyboard-navigation.js';
|
|
12
3
|
export * from './search.js';
|
|
@@ -1,20 +1,25 @@
|
|
|
1
|
-
import type { MaybeRef, Ref } from 'vue';
|
|
1
|
+
import type { MaybeRef, Ref, TemplateRef } from 'vue';
|
|
2
2
|
export interface UseAutoCompleteKeyboardNavigationOptions {
|
|
3
|
+
/** Whether multiple items can be selected. */
|
|
3
4
|
multiple: MaybeRef<boolean>;
|
|
5
|
+
/** Whether custom (free-text) values are allowed. */
|
|
4
6
|
customValue: MaybeRef<boolean>;
|
|
7
|
+
/** Whether selected values are displayed as chips. */
|
|
8
|
+
chips: MaybeRef<boolean>;
|
|
5
9
|
}
|
|
6
10
|
export interface UseAutoCompleteKeyboardNavigationDeps<TItem> {
|
|
7
11
|
internalSearch: Ref<string>;
|
|
8
12
|
value: Ref<TItem[]>;
|
|
9
13
|
filteredOptions: Ref<TItem[]>;
|
|
10
|
-
options: Ref<TItem[]>;
|
|
11
14
|
isOpen: Ref<boolean>;
|
|
12
15
|
highlightedIndex: Ref<number>;
|
|
13
16
|
searchInputFocused: Ref<boolean>;
|
|
14
17
|
applyHighlighted: () => void;
|
|
15
18
|
clear: () => void;
|
|
19
|
+
removeValue: (item: TItem) => void;
|
|
16
20
|
setSearchAsValue: () => void;
|
|
17
|
-
|
|
21
|
+
getText: (item: TItem) => string | undefined;
|
|
22
|
+
activator: Ref<HTMLElement | undefined> | TemplateRef<HTMLElement>;
|
|
18
23
|
}
|
|
19
24
|
export interface UseAutoCompleteKeyboardNavigationReturn {
|
|
20
25
|
focusedValueIndex: Ref<number>;
|
|
@@ -42,8 +42,7 @@ function useAutoCompleteKeyboardNavigation(options, deps) {
|
|
|
42
42
|
if (typeof highlightedOption === "string") {
|
|
43
43
|
hasExactMatch = highlightedOption === internalSearch;
|
|
44
44
|
} else if (highlightedOption && typeof highlightedOption === "object") {
|
|
45
|
-
|
|
46
|
-
hasExactMatch = label === internalSearch;
|
|
45
|
+
hasExactMatch = (deps.getText(highlightedOption) ?? "") === internalSearch;
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
48
|
if (!hasExactMatch) {
|
|
@@ -89,10 +88,17 @@ function useAutoCompleteKeyboardNavigation(options, deps) {
|
|
|
89
88
|
const total = value.length;
|
|
90
89
|
const multiple = get(options.multiple);
|
|
91
90
|
if (!internalSearch && total > 0) {
|
|
92
|
-
if (multiple)
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
if (multiple) {
|
|
92
|
+
if (get(options.chips)) {
|
|
93
|
+
set(focusedValueIndex, total - 1);
|
|
94
|
+
} else {
|
|
95
|
+
const lastItem = value[total - 1];
|
|
96
|
+
if (lastItem !== void 0)
|
|
97
|
+
deps.removeValue(lastItem);
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
95
100
|
deps.clear();
|
|
101
|
+
}
|
|
96
102
|
}
|
|
97
103
|
}
|
|
98
104
|
watch(deps.value, () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyboard-navigation.js","sources":["../../../../src/composables/forms/auto-complete/keyboard-navigation.ts"],"sourcesContent":["import type { MaybeRef, Ref } from 'vue';\nimport { get, set } from '@vueuse/shared';\n\nexport interface UseAutoCompleteKeyboardNavigationOptions {\n multiple: MaybeRef<boolean>;\n customValue: MaybeRef<boolean>;\n}\n\nexport interface UseAutoCompleteKeyboardNavigationDeps<TItem> {\n internalSearch: Ref<string>;\n value: Ref<TItem[]>;\n filteredOptions: Ref<TItem[]>;\n
|
|
1
|
+
{"version":3,"file":"keyboard-navigation.js","sources":["../../../../src/composables/forms/auto-complete/keyboard-navigation.ts"],"sourcesContent":["import type { MaybeRef, Ref, TemplateRef } from 'vue';\nimport { get, set } from '@vueuse/shared';\n\nexport interface UseAutoCompleteKeyboardNavigationOptions {\n /** Whether multiple items can be selected. */\n multiple: MaybeRef<boolean>;\n /** Whether custom (free-text) values are allowed. */\n customValue: MaybeRef<boolean>;\n /** Whether selected values are displayed as chips. */\n chips: MaybeRef<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 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 const chipElementsCache = ref<Map<number, HTMLElement>>(new Map());\n\n function setValueFocus(index: number): void {\n set(focusedValueIndex, index);\n }\n\n function moveSelectedValueHighlight(event: KeyboardEvent, next: boolean): void {\n const multiple = get(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 onEnter(event: KeyboardEvent): void {\n const filteredOptions = get(deps.filteredOptions);\n const highlightedIndex = get(deps.highlightedIndex);\n const isOpen = get(deps.isOpen);\n const customValue = get(options.customValue);\n const multiple = get(options.multiple);\n const value = get(deps.value);\n const internalSearch = get(deps.internalSearch);\n\n // Check if we should use custom value\n if (customValue && internalSearch && isOpen) {\n // Check if any option exactly matches the search term\n let hasExactMatch = false;\n\n if (highlightedIndex > -1 && highlightedIndex < filteredOptions.length) {\n const highlightedOption = filteredOptions[highlightedIndex];\n if (typeof highlightedOption === 'string') {\n hasExactMatch = highlightedOption === internalSearch;\n }\n else if (highlightedOption && typeof highlightedOption === 'object') {\n hasExactMatch = (deps.getText(highlightedOption) ?? '') === internalSearch;\n }\n }\n\n // If no exact match with highlighted option, use custom value\n if (!hasExactMatch) {\n deps.setSearchAsValue();\n if (!multiple)\n set(deps.isOpen, false);\n event.preventDefault();\n return;\n }\n }\n\n if (filteredOptions.length > 0 && highlightedIndex > -1 && isOpen) {\n deps.applyHighlighted();\n event.preventDefault();\n }\n else if (filteredOptions.length === 0 && customValue && internalSearch) {\n deps.setSearchAsValue();\n if (!multiple)\n set(deps.isOpen, false);\n event.preventDefault();\n }\n else if (!isOpen && value.length === 0) {\n set(deps.isOpen, true);\n event.preventDefault();\n }\n else {\n const activator = get(deps.activator);\n const form = activator?.closest('form');\n if (form) {\n form.dispatchEvent(new Event('submit'));\n }\n }\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 = get(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 = get(options.multiple);\n\n if (!internalSearch && total > 0) {\n if (multiple) {\n if (get(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 and clear cache\n watch(deps.value, () => {\n setValueFocus(-1);\n get(chipElementsCache).clear();\n });\n\n // Watch focused value index to handle chip focus\n watch(focusedValueIndex, async (index) => {\n const multiple = get(options.multiple);\n if (index === -1 || !multiple)\n return;\n\n await nextTick(() => {\n const cache = get(chipElementsCache);\n let activeChip = cache.get(index);\n\n if (!activeChip) {\n const activator = get(deps.activator);\n const element = activator?.querySelector(`[data-index=\"${index}\"]`) as HTMLElement | null;\n if (element) {\n activeChip = element;\n cache.set(index, element);\n }\n }\n\n if (activeChip && 'focus' in activeChip && typeof activeChip.focus === 'function')\n activeChip.focus();\n });\n });\n\n return {\n focusedValueIndex,\n moveSelectedValueHighlight,\n onEnter,\n onInputDeletePressed,\n onTab,\n setValueFocus,\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import type { MaybeRef, Ref } from 'vue';
|
|
2
2
|
export interface UseAutoCompleteSearchOptions<TItem> {
|
|
3
|
+
/** The property used as the unique identifier for each item. */
|
|
3
4
|
keyAttr?: MaybeRef<keyof TItem | undefined>;
|
|
5
|
+
/** The property used to display text for each item. */
|
|
4
6
|
textAttr?: MaybeRef<keyof TItem | undefined>;
|
|
7
|
+
/** Whether to disable client-side filtering of options. */
|
|
5
8
|
noFilter?: MaybeRef<boolean>;
|
|
9
|
+
/** Custom filter function to match items against the search query. */
|
|
6
10
|
filter?: MaybeRef<((item: TItem, queryText: string) => boolean) | undefined>;
|
|
11
|
+
/** Whether custom (free-text) values are allowed. */
|
|
7
12
|
customValue?: MaybeRef<boolean>;
|
|
13
|
+
/** Whether to hide the custom value option from the dropdown. */
|
|
8
14
|
hideCustomValue?: MaybeRef<boolean>;
|
|
15
|
+
/** Whether to return the full item object instead of just the key. */
|
|
9
16
|
returnObject?: MaybeRef<boolean>;
|
|
10
17
|
}
|
|
11
18
|
export interface UseAutoCompleteSearchReturn<TItem> {
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { get, set } from "@vueuse/shared";
|
|
2
2
|
import { getTextToken } from "../../../utils/helpers.js";
|
|
3
|
-
import {
|
|
3
|
+
import { shallowRef, computed, watch, onUnmounted } from "vue";
|
|
4
4
|
import { refDebounced } from "@vueuse/core";
|
|
5
5
|
function useAutoCompleteSearch(options, searchModel, opts) {
|
|
6
|
-
const internalSearch =
|
|
6
|
+
const internalSearch = shallowRef("");
|
|
7
7
|
const debouncedInternalSearch = refDebounced(internalSearch, 50);
|
|
8
|
-
const justOpened =
|
|
8
|
+
const justOpened = shallowRef(false);
|
|
9
|
+
const MAX_CACHE_SIZE = 500;
|
|
9
10
|
const tokenCache = /* @__PURE__ */ new Map();
|
|
10
11
|
const getCachedTextToken = (text) => {
|
|
11
|
-
if (tokenCache.has(text))
|
|
12
|
+
if (tokenCache.has(text))
|
|
12
13
|
return tokenCache.get(text);
|
|
13
|
-
|
|
14
|
+
if (tokenCache.size >= MAX_CACHE_SIZE)
|
|
15
|
+
tokenCache.clear();
|
|
14
16
|
const token = getTextToken(text);
|
|
15
17
|
tokenCache.set(text, token);
|
|
16
18
|
return token;
|
|
@@ -28,10 +30,10 @@ function useAutoCompleteSearch(options, searchModel, opts) {
|
|
|
28
30
|
const usedFilter = filter || ((item) => {
|
|
29
31
|
if (!item)
|
|
30
32
|
return false;
|
|
31
|
-
const keywords = [keyAttr ?
|
|
33
|
+
const keywords = [getCachedTextToken(keyAttr ? String(item[keyAttr]) : item.toString())];
|
|
32
34
|
if (textAttr && typeof item === "object")
|
|
33
|
-
keywords.push(getCachedTextToken(item[textAttr]));
|
|
34
|
-
return keywords.some((keyword) =>
|
|
35
|
+
keywords.push(getCachedTextToken(String(item[textAttr])));
|
|
36
|
+
return keywords.some((keyword) => keyword.includes(searchToken));
|
|
35
37
|
});
|
|
36
38
|
const filtered = optionsValue.filter((item) => usedFilter(item, search));
|
|
37
39
|
const customValue = get(opts.customValue);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.js","sources":["../../../../src/composables/forms/auto-complete/search.ts"],"sourcesContent":["import type { MaybeRef, Ref } from 'vue';\nimport { get, set } from '@vueuse/shared';\nimport { getTextToken } from '@/utils/helpers';\n\nexport interface UseAutoCompleteSearchOptions<TItem> {\n keyAttr?: MaybeRef<keyof TItem | undefined>;\n textAttr?: MaybeRef<keyof TItem | undefined>;\n noFilter?: MaybeRef<boolean>;\n filter?: MaybeRef<((item: TItem, queryText: string) => boolean) | undefined>;\n customValue?: MaybeRef<boolean>;\n hideCustomValue?: MaybeRef<boolean>;\n returnObject?: MaybeRef<boolean>;\n}\n\nexport interface UseAutoCompleteSearchReturn<TItem> {\n internalSearch: Ref<string>;\n debouncedInternalSearch: Readonly<Ref<string>>;\n filteredOptions: Readonly<Ref<TItem[]>>;\n justOpened: Ref<boolean>;\n updateInternalSearch: (value?: string) => void;\n textValueToProperValue: (val: any, returnObject?: boolean) => TItem;\n}\n\nexport function useAutoCompleteSearch<TItem>(\n options: MaybeRef<TItem[]>,\n searchModel: Ref<string>,\n opts: UseAutoCompleteSearchOptions<TItem>,\n): UseAutoCompleteSearchReturn<TItem> {\n const internalSearch =
|
|
1
|
+
{"version":3,"file":"search.js","sources":["../../../../src/composables/forms/auto-complete/search.ts"],"sourcesContent":["import type { MaybeRef, Ref } from 'vue';\nimport { get, set } from '@vueuse/shared';\nimport { getTextToken } from '@/utils/helpers';\n\nexport interface UseAutoCompleteSearchOptions<TItem> {\n /** The property used as the unique identifier for each item. */\n keyAttr?: MaybeRef<keyof TItem | undefined>;\n /** The property used to display text for each item. */\n textAttr?: MaybeRef<keyof TItem | undefined>;\n /** Whether to disable client-side filtering of options. */\n noFilter?: MaybeRef<boolean>;\n /** Custom filter function to match items against the search query. */\n filter?: MaybeRef<((item: TItem, queryText: string) => boolean) | undefined>;\n /** Whether custom (free-text) values are allowed. */\n customValue?: MaybeRef<boolean>;\n /** Whether to hide the custom value option from the dropdown. */\n hideCustomValue?: MaybeRef<boolean>;\n /** Whether to return the full item object instead of just the key. */\n returnObject?: MaybeRef<boolean>;\n}\n\nexport interface UseAutoCompleteSearchReturn<TItem> {\n internalSearch: Ref<string>;\n debouncedInternalSearch: Readonly<Ref<string>>;\n filteredOptions: Readonly<Ref<TItem[]>>;\n justOpened: Ref<boolean>;\n updateInternalSearch: (value?: string) => void;\n textValueToProperValue: (val: any, returnObject?: boolean) => TItem;\n}\n\nexport function useAutoCompleteSearch<TItem>(\n options: MaybeRef<TItem[]>,\n searchModel: Ref<string>,\n opts: UseAutoCompleteSearchOptions<TItem>,\n): UseAutoCompleteSearchReturn<TItem> {\n const internalSearch = shallowRef<string>('');\n const debouncedInternalSearch = refDebounced(internalSearch, 50);\n const justOpened = shallowRef<boolean>(false);\n\n // Memoize token computation to avoid repeated string processing\n const MAX_CACHE_SIZE = 500;\n const tokenCache = new Map<string, string>();\n const getCachedTextToken = (text: string): string => {\n if (tokenCache.has(text))\n return tokenCache.get(text)!;\n\n if (tokenCache.size >= MAX_CACHE_SIZE)\n tokenCache.clear();\n\n const token = getTextToken(text);\n tokenCache.set(text, token);\n return token;\n };\n\n const filteredOptions = computed<TItem[]>(() => {\n const search = get(debouncedInternalSearch);\n const optionsValue = get(options);\n\n const noFilter = get(opts.noFilter);\n if (noFilter || !search || get(justOpened))\n return optionsValue;\n\n const keyAttr = get(opts.keyAttr);\n const textAttr = get(opts.textAttr);\n const filter = get(opts.filter);\n\n // Cache the search token once\n const searchToken = getCachedTextToken(search);\n\n const usedFilter = filter || ((item) => {\n if (!item)\n return false;\n\n const keywords: string[] = [getCachedTextToken(keyAttr ? String((item as any)[keyAttr]) : item.toString())];\n\n if (textAttr && typeof item === 'object')\n keywords.push(getCachedTextToken(String((item as any)[textAttr])));\n\n return keywords.some(keyword => keyword.includes(searchToken));\n });\n\n const filtered = optionsValue.filter(item => usedFilter(item, search));\n\n const customValue = get(opts.customValue);\n const hideCustomValue = get(opts.hideCustomValue);\n if (!customValue || hideCustomValue || !search) {\n return filtered;\n }\n\n // Use a more efficient check for custom value inclusion\n const isCustomValueIncluded = filtered.some((item) => {\n if (!item) {\n return false;\n }\n\n const val = textAttr ? (item as any)[textAttr] : item.toString();\n return val === search;\n });\n\n if (isCustomValueIncluded) {\n return filtered;\n }\n\n return [textValueToProperValue(search, {\n keyAttr,\n returnObject: get(opts.returnObject),\n textAttr,\n }), ...filtered];\n });\n\n function textValueToProperValue(val: any, options: { keyAttr?: keyof TItem; textAttr?: keyof TItem; returnObject?: boolean }): TItem {\n const { keyAttr, returnObject, textAttr } = options;\n if (!keyAttr || returnObject)\n return val;\n\n return {\n [keyAttr]: val,\n ...(\n textAttr\n ? { [textAttr]: val }\n : {}\n ),\n } as TItem;\n }\n\n function updateInternalSearch(value: string = ''): void {\n set(searchModel, value);\n set(internalSearch, value);\n }\n\n watch(searchModel, (search) => {\n set(internalSearch, search);\n }, { immediate: true });\n\n // Clear token cache when component unmounts to prevent memory leaks\n onUnmounted(() => {\n tokenCache.clear();\n });\n\n return {\n debouncedInternalSearch,\n filteredOptions,\n internalSearch,\n justOpened,\n textValueToProperValue: (val: any, returnObjectOverride: boolean = false) => textValueToProperValue(val, {\n keyAttr: get(opts.keyAttr),\n returnObject: returnObjectOverride || get(opts.returnObject),\n textAttr: get(opts.textAttr),\n }),\n updateInternalSearch,\n };\n}\n"],"names":["options"],"mappings":"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"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { ComputedRef, MaybeRef, Ref } from 'vue';
|
|
2
2
|
export interface UseAutoCompleteValueOptions<TItem> {
|
|
3
|
+
/** The property used as the unique identifier for each item. */
|
|
3
4
|
keyAttr?: MaybeRef<keyof TItem | undefined>;
|
|
5
|
+
/** Whether to return the full item object instead of just the key. */
|
|
4
6
|
returnObject?: MaybeRef<boolean>;
|
|
7
|
+
/** Whether custom (free-text) values are allowed. */
|
|
5
8
|
customValue?: MaybeRef<boolean>;
|
|
6
9
|
}
|
|
7
10
|
export interface UseAutoCompleteValueReturn<TItem> {
|
|
@@ -14,6 +17,7 @@ export interface UseAutoCompleteValueDeps<TItem> {
|
|
|
14
17
|
textValueToProperValue: (val: any, returnObject?: boolean) => TItem;
|
|
15
18
|
shouldApplyValueAsSearch: MaybeRef<boolean>;
|
|
16
19
|
isOpen: MaybeRef<boolean>;
|
|
20
|
+
multiple: MaybeRef<boolean>;
|
|
17
21
|
updateInternalSearch: (value?: string) => void;
|
|
18
22
|
}
|
|
19
23
|
export declare function useAutoCompleteValue<TValue, TItem>(modelValue: Ref<TValue | undefined>, options: MaybeRef<TItem[]>, opts: UseAutoCompleteValueOptions<TItem>, deps: UseAutoCompleteValueDeps<TItem>): UseAutoCompleteValueReturn<TItem>;
|