@fastkit/vui 0.8.9 → 0.8.10
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/vui.cjs.js +115 -11
- package/dist/vui.cjs.prod.js +115 -11
- package/dist/vui.css +1 -1
- package/dist/vui.d.ts +15 -0
- package/dist/vui.min.css +1 -1
- package/dist/vui.min.css.map +1 -1
- package/dist/vui.mjs +115 -14
- package/package.json +6 -6
- package/src/components/VOption/VOption.scss +2 -1
- package/src/components/VOption/VOption.tsx +1 -0
- package/src/components/VSelect/VSelect.tsx +144 -10
package/dist/vui.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createPropsOptions, createFormControlSettings, defineSlotsProps, useFormControl, VTooltip, renderSlotOrEmpty, createFormSelectorSettings, createFormControlProps, useFormSelectorControl, resolveVNodeChildOrSlots, createFormSelectorItemSettings, useFormSelectorItemControl, createFormSelectorItemGroupProps, useFormSelectorItemGroupControl, useParentFormNode, VMenu, createTextInputSettings, useTextInputControl, createTextareaSettings, useTextareaControl, createFormSettings, useForm, getDocumentScroller, useVScrollerRef, VScroller, useInjectTheme, VStackRoot, resolveVStackDynamicInput, VueColorSchemePlugin, installVueStackPlugin } from '@fastkit/vue-kit';
|
|
1
|
+
import { createPropsOptions, createFormControlSettings, defineSlotsProps, useFormControl, VTooltip, renderSlotOrEmpty, createFormSelectorSettings, createFormControlProps, useFormSelectorControl, resolveVNodeChildOrSlots, createFormSelectorItemSettings, useFormSelectorItemControl, createFormSelectorItemGroupProps, useFormSelectorItemGroupControl, useParentFormNode, useKeybord, VMenu, createTextInputSettings, useTextInputControl, createTextareaSettings, useTextareaControl, createFormSettings, useForm, getDocumentScroller, useVScrollerRef, VScroller, useInjectTheme, VStackRoot, resolveVStackDynamicInput, VueColorSchemePlugin, installVueStackPlugin } from '@fastkit/vue-kit';
|
|
2
2
|
export * from '@fastkit/vue-kit';
|
|
3
3
|
export { VDialog, VMenu, VSnackbar, VTooltip } from '@fastkit/vue-kit';
|
|
4
|
-
import { inject, computed, provide, createVNode, mergeProps, defineComponent, Fragment, isVNode, ref, watch, onMounted, onBeforeUnmount, cloneVNode, withDirectives, createTextVNode, vShow, onBeforeUpdate, vModelSelect, Transition, reactive } from 'vue';
|
|
4
|
+
import { inject, computed, provide, createVNode, mergeProps, defineComponent, Fragment, isVNode, ref, watch, onMounted, onBeforeUnmount, cloneVNode, withDirectives, createTextVNode, vShow, onBeforeUpdate, vModelSelect, nextTick, Transition, reactive } from 'vue';
|
|
5
5
|
import { useRouter, RouterLink, useLink, useRoute } from 'vue-router';
|
|
6
6
|
import { createPropsOptions as createPropsOptions$1, renderSlotOrEmpty as renderSlotOrEmpty$1, defineSlotsProps as defineSlotsProps$1, htmlAttributesPropOptions, navigationableInheritProps, useNavigationable, VLink, isFragment, rawNumberPropType, resolveNumberish, resizeDirectiveArgument, VExpandTransition, LocationService, setDefaultRouterLink } from '@fastkit/vue-utils';
|
|
7
7
|
import { getDocumentScroller as getDocumentScroller$1 } from '@fastkit/vue-scroller';
|
|
@@ -2315,7 +2315,8 @@ const VOption = defineComponent({
|
|
|
2315
2315
|
return createVNode("label", mergeProps({
|
|
2316
2316
|
"class": classes,
|
|
2317
2317
|
"onClick": selectorItemControl.handleClickElement,
|
|
2318
|
-
"tabindex": selectorItemControl.tabindex
|
|
2318
|
+
"tabindex": selectorItemControl.tabindex,
|
|
2319
|
+
"data-value": selectorItemControl.propValue
|
|
2319
2320
|
}, {
|
|
2320
2321
|
'aria-disabled': selectorItemControl.isDisabled
|
|
2321
2322
|
}), [createVNode("span", {
|
|
@@ -2476,6 +2477,9 @@ function _isSlot$8(s) {
|
|
|
2476
2477
|
return typeof s === 'function' || Object.prototype.toString.call(s) === '[object Object]' && !isVNode(s);
|
|
2477
2478
|
}
|
|
2478
2479
|
|
|
2480
|
+
const ARROW_KEY_TYPES = useKeybord.Key(['ArrowUp', 'ArrowDown']);
|
|
2481
|
+
const CHOICE_KEY_TYPES = useKeybord.Key(['Enter', ' ']);
|
|
2482
|
+
const KEYBORD_EVENT_TYPES = useKeybord.Key([...ARROW_KEY_TYPES, ...CHOICE_KEY_TYPES]);
|
|
2479
2483
|
const {
|
|
2480
2484
|
props: props$3,
|
|
2481
2485
|
emits: emits$3
|
|
@@ -2497,6 +2501,7 @@ const VSelect = defineComponent({
|
|
|
2497
2501
|
emits: emits$3,
|
|
2498
2502
|
|
|
2499
2503
|
setup(props, ctx) {
|
|
2504
|
+
const menuRef = ref(null);
|
|
2500
2505
|
const menuOpened = ref(false);
|
|
2501
2506
|
|
|
2502
2507
|
const showMenu = () => {
|
|
@@ -2559,6 +2564,107 @@ const VSelect = defineComponent({
|
|
|
2559
2564
|
return children;
|
|
2560
2565
|
};
|
|
2561
2566
|
|
|
2567
|
+
const clearKeyFocused = () => {
|
|
2568
|
+
const menu = menuRef.value;
|
|
2569
|
+
if (!menu) return;
|
|
2570
|
+
const bodyEl = menu.stackMenuControl.bodyRef.value;
|
|
2571
|
+
if (!bodyEl) return;
|
|
2572
|
+
const els = Array.from(bodyEl.querySelectorAll('.v-option'));
|
|
2573
|
+
els.forEach(el => el.classList.remove('v-option--key-focused'));
|
|
2574
|
+
};
|
|
2575
|
+
|
|
2576
|
+
const getItemElements = () => {
|
|
2577
|
+
const menu = menuRef.value;
|
|
2578
|
+
if (!menu) return;
|
|
2579
|
+
const bodyEl = menu.stackMenuControl.bodyRef.value;
|
|
2580
|
+
if (!bodyEl) return;
|
|
2581
|
+
const els = Array.from(bodyEl.querySelectorAll('.v-option')).filter(el => {
|
|
2582
|
+
if (el.tabIndex === -1) return false;
|
|
2583
|
+
const disabled = el.getAttribute('disabled');
|
|
2584
|
+
const ariaDisabled = el.getAttribute('aria-disabled');
|
|
2585
|
+
if (ariaDisabled === 'true') return false;
|
|
2586
|
+
return disabled == null || disabled === '';
|
|
2587
|
+
});
|
|
2588
|
+
if (!els.length) return;
|
|
2589
|
+
return els;
|
|
2590
|
+
};
|
|
2591
|
+
|
|
2592
|
+
const arrowKeyHandler = ev => {
|
|
2593
|
+
const {
|
|
2594
|
+
key
|
|
2595
|
+
} = ev;
|
|
2596
|
+
if (!menuOpened.value || !ARROW_KEY_TYPES.includes(key)) return;
|
|
2597
|
+
const els = getItemElements();
|
|
2598
|
+
if (!els) return;
|
|
2599
|
+
const currentEl = els.find(el => el.classList.contains('v-option--key-focused'));
|
|
2600
|
+
const currentIndex = currentEl && els.indexOf(currentEl);
|
|
2601
|
+
let nextIndex;
|
|
2602
|
+
const {
|
|
2603
|
+
length
|
|
2604
|
+
} = els;
|
|
2605
|
+
const isUp = key === 'ArrowUp';
|
|
2606
|
+
|
|
2607
|
+
if (currentIndex == null) {
|
|
2608
|
+
nextIndex = isUp ? length - 1 : 0;
|
|
2609
|
+
} else {
|
|
2610
|
+
const shiftAmount = key === 'ArrowUp' ? -1 : 1;
|
|
2611
|
+
nextIndex = currentIndex + shiftAmount;
|
|
2612
|
+
|
|
2613
|
+
if (nextIndex < 0) {
|
|
2614
|
+
nextIndex = length - 1;
|
|
2615
|
+
} else if (nextIndex >= length) {
|
|
2616
|
+
nextIndex = 0;
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
const nextEl = els[nextIndex];
|
|
2621
|
+
|
|
2622
|
+
if (nextEl) {
|
|
2623
|
+
clearKeyFocused();
|
|
2624
|
+
nextEl.classList.add('v-option--key-focused');
|
|
2625
|
+
nextEl.scrollIntoView({
|
|
2626
|
+
block: 'nearest',
|
|
2627
|
+
inline: 'nearest',
|
|
2628
|
+
behavior: 'smooth'
|
|
2629
|
+
});
|
|
2630
|
+
ev.preventDefault();
|
|
2631
|
+
}
|
|
2632
|
+
};
|
|
2633
|
+
|
|
2634
|
+
const choiceKeyHandler = ev => {
|
|
2635
|
+
const {
|
|
2636
|
+
key
|
|
2637
|
+
} = ev;
|
|
2638
|
+
if (!menuOpened.value || !CHOICE_KEY_TYPES.includes(key)) return;
|
|
2639
|
+
const els = getItemElements();
|
|
2640
|
+
if (!els) return;
|
|
2641
|
+
const currentEl = els.find(el => el.classList.contains('v-option--key-focused'));
|
|
2642
|
+
|
|
2643
|
+
if (currentEl) {
|
|
2644
|
+
const ev = new MouseEvent('click', {
|
|
2645
|
+
view: window,
|
|
2646
|
+
bubbles: true,
|
|
2647
|
+
cancelable: true
|
|
2648
|
+
});
|
|
2649
|
+
currentEl.dispatchEvent(ev);
|
|
2650
|
+
ev.preventDefault();
|
|
2651
|
+
nextTick(() => {
|
|
2652
|
+
currentEl.classList.add('v-option--key-focused');
|
|
2653
|
+
});
|
|
2654
|
+
}
|
|
2655
|
+
};
|
|
2656
|
+
|
|
2657
|
+
const keybordEventHandler = ev => {
|
|
2658
|
+
if (ARROW_KEY_TYPES.includes(ev.key)) return arrowKeyHandler(ev);
|
|
2659
|
+
if (CHOICE_KEY_TYPES.includes(ev.key)) return choiceKeyHandler(ev);
|
|
2660
|
+
};
|
|
2661
|
+
|
|
2662
|
+
useKeybord([{
|
|
2663
|
+
key: KEYBORD_EVENT_TYPES,
|
|
2664
|
+
handler: keybordEventHandler
|
|
2665
|
+
}], {
|
|
2666
|
+
autorun: true
|
|
2667
|
+
});
|
|
2562
2668
|
return { ...inputControl.expose(),
|
|
2563
2669
|
...control,
|
|
2564
2670
|
iconName,
|
|
@@ -2567,7 +2673,9 @@ const VSelect = defineComponent({
|
|
|
2567
2673
|
focus,
|
|
2568
2674
|
showMenu,
|
|
2569
2675
|
closeMenu,
|
|
2570
|
-
renderSelections
|
|
2676
|
+
renderSelections,
|
|
2677
|
+
menuRef: () => menuRef,
|
|
2678
|
+
clearKeyFocused
|
|
2571
2679
|
};
|
|
2572
2680
|
},
|
|
2573
2681
|
|
|
@@ -2618,15 +2726,8 @@ const VSelect = defineComponent({
|
|
|
2618
2726
|
"alwaysRender": true,
|
|
2619
2727
|
"modelValue": this.menuOpened,
|
|
2620
2728
|
"onUpdate:modelValue": $event => this.menuOpened = $event,
|
|
2621
|
-
"
|
|
2622
|
-
"
|
|
2623
|
-
const ev = new MouseEvent('click', {
|
|
2624
|
-
view: window,
|
|
2625
|
-
bubbles: true,
|
|
2626
|
-
cancelable: true
|
|
2627
|
-
});
|
|
2628
|
-
item.dispatchEvent(ev);
|
|
2629
|
-
}
|
|
2729
|
+
"ref": this.menuRef(),
|
|
2730
|
+
"onClose": this.clearKeyFocused
|
|
2630
2731
|
}, {
|
|
2631
2732
|
default: () => [createVNode("div", {
|
|
2632
2733
|
"class": ['v-select__body', this.classes]
|
|
@@ -5643,4 +5744,4 @@ function installVuiPlugin(app, opts) {
|
|
|
5643
5744
|
return app.use(VuiPlugin, opts);
|
|
5644
5745
|
}
|
|
5645
5746
|
|
|
5646
|
-
export { AVATAR_SIZES, CHIP_SIZES, CONTROL_FIELD_VARIANTS, CONTROL_LOADING_SPINNER_SIZES, CONTROL_SIZES, PAGINATION_ALIGNS, VApp, VAvatar, VBreadcrumbs, VBusyImage, VButton, VButtonGroup, VCard, VCardActions, VCardContent, VCheckbox, VCheckboxGroup, VChip, VContentSwitcher, VDataTable, VDrawerLayout, VForm, VFormControl, VGridContainer, VGridItem, VHero, VIcon, VListTile, VNavigation, VNavigationItem, VNumberField, VOption, VOptionGroup, VPagination, VPaper, VRadio, VRadioGroup, VSelect, VSkeltonLoaderBone, VSwitch, VSwitchGroup, VTabs, VTextField, VTextarea, VToolbar, VToolbarEdge, VToolbarMenu, VToolbarTitle, VUI_CHECKBOX_GROUP_SYMBOL, VUI_CHECKBOX_SYMBOL, VUI_FORM_SYMBOL, VUI_OPTION_SYMBOL, VUI_RADIO_GROUP_SYMBOL, VUI_RADIO_SYMBOL, VUI_SELECT_SYMBOL, VUI_SWITCH_GROUP_SYMBOL, VUI_SWITCH_SYMBOL, VUI_TEXTAREA_SYMBOL, VUI_TEXT_FIELD_SYMBOL, VUI_WYSIWYG_EDITOR_SYMBOL, VWysiwygEditor, VuiColorProviderInjectionKey, VuiControlFieldInjectionKey, VuiControlInjectionKey, VuiInjectionKey, VuiPlugin, VuiService, WysiwygBulletListTool, WysiwygColorExtension, WysiwygEditorInitializeContext, WysiwygFormatBoldTool, WysiwygFormatItalicTool, WysiwygFormatUnderlineTool, WysiwygHistoryTool, WysiwygLinkTool, WysiwygLinter, WysiwygLinterBadWords, WysiwygLinterHeadingLevel, WysiwygLinterPlugin, WysiwygLinterPunctuation, WysiwygOrderedListTool, configureDataTableDefaults, createAvatarProps, createCardProps, createChipProps, createControlFieldProviderProps, createControlProps, createElevationProps, createListTileProps, createNavigationItemProps, createPaperBaseProps, createPaperProps, createRequiredChipRenderer, createVFormProps, createWysiwygColorTool, createWysiwygExtension, defineFormSelectorComponent, iconProps, installVuiPlugin, listTileEmits, mergeVuiPluginOptions, mergeVuiServiceIconSettings, mergeVuiServiceOptions, mergeVuiServiceUISettings, paginationProps, rawIconProp, renderNavigationItemInput, resolveNavigationItemInput, resolveRawIconProp, resolveRawWysiwygEditorTools, resolveRawWysiwygExtensions, splitAttrs, useControl, useControlField, useElevation, useVui, useVuiColorProvider, vueButtonProps };
|
|
5747
|
+
export { ARROW_KEY_TYPES, AVATAR_SIZES, CHIP_SIZES, CHOICE_KEY_TYPES, CONTROL_FIELD_VARIANTS, CONTROL_LOADING_SPINNER_SIZES, CONTROL_SIZES, KEYBORD_EVENT_TYPES, PAGINATION_ALIGNS, VApp, VAvatar, VBreadcrumbs, VBusyImage, VButton, VButtonGroup, VCard, VCardActions, VCardContent, VCheckbox, VCheckboxGroup, VChip, VContentSwitcher, VDataTable, VDrawerLayout, VForm, VFormControl, VGridContainer, VGridItem, VHero, VIcon, VListTile, VNavigation, VNavigationItem, VNumberField, VOption, VOptionGroup, VPagination, VPaper, VRadio, VRadioGroup, VSelect, VSkeltonLoaderBone, VSwitch, VSwitchGroup, VTabs, VTextField, VTextarea, VToolbar, VToolbarEdge, VToolbarMenu, VToolbarTitle, VUI_CHECKBOX_GROUP_SYMBOL, VUI_CHECKBOX_SYMBOL, VUI_FORM_SYMBOL, VUI_OPTION_SYMBOL, VUI_RADIO_GROUP_SYMBOL, VUI_RADIO_SYMBOL, VUI_SELECT_SYMBOL, VUI_SWITCH_GROUP_SYMBOL, VUI_SWITCH_SYMBOL, VUI_TEXTAREA_SYMBOL, VUI_TEXT_FIELD_SYMBOL, VUI_WYSIWYG_EDITOR_SYMBOL, VWysiwygEditor, VuiColorProviderInjectionKey, VuiControlFieldInjectionKey, VuiControlInjectionKey, VuiInjectionKey, VuiPlugin, VuiService, WysiwygBulletListTool, WysiwygColorExtension, WysiwygEditorInitializeContext, WysiwygFormatBoldTool, WysiwygFormatItalicTool, WysiwygFormatUnderlineTool, WysiwygHistoryTool, WysiwygLinkTool, WysiwygLinter, WysiwygLinterBadWords, WysiwygLinterHeadingLevel, WysiwygLinterPlugin, WysiwygLinterPunctuation, WysiwygOrderedListTool, configureDataTableDefaults, createAvatarProps, createCardProps, createChipProps, createControlFieldProviderProps, createControlProps, createElevationProps, createListTileProps, createNavigationItemProps, createPaperBaseProps, createPaperProps, createRequiredChipRenderer, createVFormProps, createWysiwygColorTool, createWysiwygExtension, defineFormSelectorComponent, iconProps, installVuiPlugin, listTileEmits, mergeVuiPluginOptions, mergeVuiServiceIconSettings, mergeVuiServiceOptions, mergeVuiServiceUISettings, paginationProps, rawIconProp, renderNavigationItemInput, resolveNavigationItemInput, resolveRawIconProp, resolveRawWysiwygEditorTools, resolveRawWysiwygExtensions, splitAttrs, useControl, useControlField, useElevation, useVui, useVuiColorProvider, vueButtonProps };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fastkit/vui",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.10",
|
|
4
4
|
"description": "@fastkit/vui",
|
|
5
5
|
"buildOptions": {
|
|
6
6
|
"name": "Vui",
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"vue": "^3.2.26"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@fastkit/color-scheme": "0.8.
|
|
36
|
-
"@fastkit/icon-font": "0.8.
|
|
37
|
-
"@fastkit/tiny-logger": "0.8.
|
|
38
|
-
"@fastkit/vite-kit": "0.8.
|
|
39
|
-
"@fastkit/vue-kit": "0.8.
|
|
35
|
+
"@fastkit/color-scheme": "0.8.10",
|
|
36
|
+
"@fastkit/icon-font": "0.8.10",
|
|
37
|
+
"@fastkit/tiny-logger": "0.8.10",
|
|
38
|
+
"@fastkit/vite-kit": "0.8.10",
|
|
39
|
+
"@fastkit/vue-kit": "0.8.10",
|
|
40
40
|
"@tiptap/extension-link": "^2.0.0-beta.36",
|
|
41
41
|
"@tiptap/extension-underline": "^2.0.0-beta.23",
|
|
42
42
|
"@tiptap/extension-text-style": "^2.0.0-beta.23",
|
|
@@ -46,6 +46,7 @@ export const VOption = defineComponent({
|
|
|
46
46
|
class={classes}
|
|
47
47
|
onClick={selectorItemControl.handleClickElement}
|
|
48
48
|
tabindex={selectorItemControl.tabindex}
|
|
49
|
+
data-value={selectorItemControl.propValue}
|
|
49
50
|
{...({
|
|
50
51
|
'aria-disabled': selectorItemControl.isDisabled,
|
|
51
52
|
} as any)}>
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import './VSelect.scss';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ref,
|
|
4
|
+
Ref,
|
|
5
|
+
VNodeChild,
|
|
6
|
+
defineComponent,
|
|
7
|
+
PropType,
|
|
8
|
+
computed,
|
|
9
|
+
nextTick,
|
|
10
|
+
} from 'vue';
|
|
3
11
|
import {
|
|
4
12
|
createFormSelectorSettings,
|
|
5
13
|
useFormSelectorControl,
|
|
@@ -10,6 +18,7 @@ import {
|
|
|
10
18
|
createPropsOptions,
|
|
11
19
|
VNodeChildOrSlot,
|
|
12
20
|
resolveVNodeChildOrSlots,
|
|
21
|
+
useKeybord,
|
|
13
22
|
} from '@fastkit/vue-kit';
|
|
14
23
|
import { VFormControl } from '../VFormControl';
|
|
15
24
|
import {
|
|
@@ -29,6 +38,16 @@ import { VMenu } from '../kits';
|
|
|
29
38
|
import { VOptionGroup } from '../VOptionGroup';
|
|
30
39
|
import { VOption } from '../VOption';
|
|
31
40
|
import { VButton } from '..';
|
|
41
|
+
import { VMenuControl } from '@fastkit/vue-stack';
|
|
42
|
+
|
|
43
|
+
export const ARROW_KEY_TYPES = useKeybord.Key(['ArrowUp', 'ArrowDown']);
|
|
44
|
+
|
|
45
|
+
export const CHOICE_KEY_TYPES = useKeybord.Key(['Enter', ' ']);
|
|
46
|
+
|
|
47
|
+
export const KEYBORD_EVENT_TYPES = useKeybord.Key([
|
|
48
|
+
...ARROW_KEY_TYPES,
|
|
49
|
+
...CHOICE_KEY_TYPES,
|
|
50
|
+
]);
|
|
32
51
|
|
|
33
52
|
const { props, emits } = createFormSelectorSettings();
|
|
34
53
|
|
|
@@ -49,6 +68,7 @@ export const VSelect = defineComponent({
|
|
|
49
68
|
},
|
|
50
69
|
emits,
|
|
51
70
|
setup(props, ctx) {
|
|
71
|
+
const menuRef: Ref<{ stackMenuControl: VMenuControl } | null> = ref(null);
|
|
52
72
|
const menuOpened = ref(false);
|
|
53
73
|
const showMenu = () => {
|
|
54
74
|
menuOpened.value = true;
|
|
@@ -112,9 +132,128 @@ export const VSelect = defineComponent({
|
|
|
112
132
|
<span class="v-select__placeholder">{props.placeholder}</span>,
|
|
113
133
|
);
|
|
114
134
|
}
|
|
135
|
+
|
|
115
136
|
return children;
|
|
116
137
|
};
|
|
117
138
|
|
|
139
|
+
const clearKeyFocused = () => {
|
|
140
|
+
const menu = menuRef.value;
|
|
141
|
+
if (!menu) return;
|
|
142
|
+
const bodyEl = menu.stackMenuControl.bodyRef.value;
|
|
143
|
+
if (!bodyEl) return;
|
|
144
|
+
|
|
145
|
+
const els = Array.from(
|
|
146
|
+
bodyEl.querySelectorAll('.v-option'),
|
|
147
|
+
) as HTMLElement[];
|
|
148
|
+
|
|
149
|
+
els.forEach((el) => el.classList.remove('v-option--key-focused'));
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const getItemElements = (): HTMLElement[] | void => {
|
|
153
|
+
const menu = menuRef.value;
|
|
154
|
+
if (!menu) return;
|
|
155
|
+
|
|
156
|
+
const bodyEl = menu.stackMenuControl.bodyRef.value;
|
|
157
|
+
|
|
158
|
+
if (!bodyEl) return;
|
|
159
|
+
|
|
160
|
+
const els = (
|
|
161
|
+
Array.from(bodyEl.querySelectorAll('.v-option')) as HTMLElement[]
|
|
162
|
+
).filter((el) => {
|
|
163
|
+
if (el.tabIndex === -1) return false;
|
|
164
|
+
const disabled = el.getAttribute('disabled');
|
|
165
|
+
const ariaDisabled = el.getAttribute('aria-disabled');
|
|
166
|
+
if (ariaDisabled === 'true') return false;
|
|
167
|
+
return disabled == null || disabled === '';
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (!els.length) return;
|
|
171
|
+
|
|
172
|
+
return els;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const arrowKeyHandler = (ev: KeyboardEvent) => {
|
|
176
|
+
const { key } = ev;
|
|
177
|
+
if (!menuOpened.value || !ARROW_KEY_TYPES.includes(key as any)) return;
|
|
178
|
+
|
|
179
|
+
const els = getItemElements();
|
|
180
|
+
|
|
181
|
+
if (!els) return;
|
|
182
|
+
|
|
183
|
+
const currentEl = els.find((el) =>
|
|
184
|
+
el.classList.contains('v-option--key-focused'),
|
|
185
|
+
);
|
|
186
|
+
const currentIndex = currentEl && els.indexOf(currentEl);
|
|
187
|
+
let nextIndex: number;
|
|
188
|
+
const { length } = els;
|
|
189
|
+
const isUp = key === 'ArrowUp';
|
|
190
|
+
if (currentIndex == null) {
|
|
191
|
+
nextIndex = isUp ? length - 1 : 0;
|
|
192
|
+
} else {
|
|
193
|
+
const shiftAmount = key === 'ArrowUp' ? -1 : 1;
|
|
194
|
+
nextIndex = currentIndex + shiftAmount;
|
|
195
|
+
if (nextIndex < 0) {
|
|
196
|
+
nextIndex = length - 1;
|
|
197
|
+
} else if (nextIndex >= length) {
|
|
198
|
+
nextIndex = 0;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const nextEl = els[nextIndex];
|
|
203
|
+
|
|
204
|
+
if (nextEl) {
|
|
205
|
+
clearKeyFocused();
|
|
206
|
+
nextEl.classList.add('v-option--key-focused');
|
|
207
|
+
nextEl.scrollIntoView({
|
|
208
|
+
block: 'nearest',
|
|
209
|
+
inline: 'nearest',
|
|
210
|
+
behavior: 'smooth',
|
|
211
|
+
});
|
|
212
|
+
ev.preventDefault();
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const choiceKeyHandler = (ev: KeyboardEvent) => {
|
|
217
|
+
const { key } = ev;
|
|
218
|
+
if (!menuOpened.value || !CHOICE_KEY_TYPES.includes(key as any)) return;
|
|
219
|
+
|
|
220
|
+
const els = getItemElements();
|
|
221
|
+
|
|
222
|
+
if (!els) return;
|
|
223
|
+
|
|
224
|
+
const currentEl = els.find((el) =>
|
|
225
|
+
el.classList.contains('v-option--key-focused'),
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
if (currentEl) {
|
|
229
|
+
const ev = new MouseEvent('click', {
|
|
230
|
+
view: window,
|
|
231
|
+
bubbles: true,
|
|
232
|
+
cancelable: true,
|
|
233
|
+
});
|
|
234
|
+
currentEl.dispatchEvent(ev);
|
|
235
|
+
ev.preventDefault();
|
|
236
|
+
nextTick(() => {
|
|
237
|
+
currentEl.classList.add('v-option--key-focused');
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const keybordEventHandler = (ev: KeyboardEvent) => {
|
|
243
|
+
if (ARROW_KEY_TYPES.includes(ev.key as any)) return arrowKeyHandler(ev);
|
|
244
|
+
if (CHOICE_KEY_TYPES.includes(ev.key as any)) return choiceKeyHandler(ev);
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
useKeybord(
|
|
248
|
+
[
|
|
249
|
+
{
|
|
250
|
+
key: KEYBORD_EVENT_TYPES,
|
|
251
|
+
handler: keybordEventHandler,
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
{ autorun: true },
|
|
255
|
+
);
|
|
256
|
+
|
|
118
257
|
return {
|
|
119
258
|
...inputControl.expose(),
|
|
120
259
|
...control,
|
|
@@ -125,6 +264,8 @@ export const VSelect = defineComponent({
|
|
|
125
264
|
showMenu,
|
|
126
265
|
closeMenu,
|
|
127
266
|
renderSelections,
|
|
267
|
+
menuRef: () => menuRef,
|
|
268
|
+
clearKeyFocused,
|
|
128
269
|
};
|
|
129
270
|
},
|
|
130
271
|
render() {
|
|
@@ -179,15 +320,8 @@ export const VSelect = defineComponent({
|
|
|
179
320
|
distance={0}
|
|
180
321
|
alwaysRender
|
|
181
322
|
v-model={this.menuOpened}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const ev = new MouseEvent('click', {
|
|
185
|
-
view: window,
|
|
186
|
-
bubbles: true,
|
|
187
|
-
cancelable: true,
|
|
188
|
-
});
|
|
189
|
-
item.dispatchEvent(ev);
|
|
190
|
-
}}
|
|
323
|
+
ref={this.menuRef()}
|
|
324
|
+
onClose={this.clearKeyFocused}
|
|
191
325
|
v-slots={{
|
|
192
326
|
activator: ({ attrs, control }) => [
|
|
193
327
|
<VControlField
|