@fastkit/vui 0.8.9 → 0.8.12

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.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, onAppUnmount } 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
- "itemElements": body => body.querySelectorAll('.v-option'),
2622
- "onChoiceItemElement": item => {
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]
@@ -5587,15 +5688,7 @@ function mergeVuiPluginOptions(base, override) {
5587
5688
  return merged;
5588
5689
  }
5589
5690
  class VuiPlugin {
5590
- static installedApps = new Set();
5591
-
5592
5691
  static install(app, opts) {
5593
- const {
5594
- installedApps
5595
- } = this;
5596
- if (installedApps.has(app)) return;
5597
- const unmountApp = app.unmount;
5598
- installedApps.add(app);
5599
5692
  const {
5600
5693
  colorScheme,
5601
5694
  stack,
@@ -5630,12 +5723,9 @@ class VuiPlugin {
5630
5723
  const $vui = new VuiService(opts, app.config.globalProperties.$vstack);
5631
5724
  app.provide(VuiInjectionKey, $vui);
5632
5725
  app.config.globalProperties.$vui = $vui;
5633
-
5634
- app.unmount = function () {
5635
- installedApps.delete(app);
5726
+ onAppUnmount(app, () => {
5636
5727
  delete app.config.globalProperties.$vui;
5637
- unmountApp();
5638
- };
5728
+ });
5639
5729
  }
5640
5730
 
5641
5731
  }
@@ -5643,4 +5733,4 @@ function installVuiPlugin(app, opts) {
5643
5733
  return app.use(VuiPlugin, opts);
5644
5734
  }
5645
5735
 
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 };
5736
+ 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.9",
3
+ "version": "0.8.12",
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.9",
36
- "@fastkit/icon-font": "0.8.9",
37
- "@fastkit/tiny-logger": "0.8.9",
38
- "@fastkit/vite-kit": "0.8.9",
39
- "@fastkit/vue-kit": "0.8.9",
35
+ "@fastkit/color-scheme": "0.8.12",
36
+ "@fastkit/icon-font": "0.8.12",
37
+ "@fastkit/tiny-logger": "0.8.12",
38
+ "@fastkit/vite-kit": "0.8.12",
39
+ "@fastkit/vue-kit": "0.8.12",
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",
@@ -36,7 +36,8 @@
36
36
  }
37
37
 
38
38
  &:hover,
39
- &:focus-within {
39
+ &:focus-within,
40
+ &--key-focused {
40
41
  &::before {
41
42
  opacity: 0.12;
42
43
  }
@@ -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 { ref, VNodeChild, defineComponent, PropType, computed } from 'vue';
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
- itemElements={(body) => body.querySelectorAll('.v-option')}
183
- onChoiceItemElement={(item) => {
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
package/src/plugin.tsx CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  installVueStackPlugin,
11
11
  VueStackServiceOptions,
12
12
  VueColorSchemePlugin,
13
+ onAppUnmount,
13
14
  } from '@fastkit/vue-kit';
14
15
  import { VButton } from './components/VButton';
15
16
 
@@ -46,14 +47,7 @@ declare module '@vue/runtime-core' {
46
47
  }
47
48
 
48
49
  export class VuiPlugin {
49
- static readonly installedApps = new Set<App>();
50
-
51
50
  static install(app: App, opts: VuiPluginOptions) {
52
- const { installedApps } = this;
53
- if (installedApps.has(app)) return;
54
- const unmountApp = app.unmount;
55
- installedApps.add(app);
56
-
57
51
  const { colorScheme, stack, uiSettings } = opts;
58
52
 
59
53
  // ColorScheme
@@ -88,11 +82,9 @@ export class VuiPlugin {
88
82
  app.provide(VuiInjectionKey, $vui);
89
83
  app.config.globalProperties.$vui = $vui;
90
84
 
91
- app.unmount = function () {
92
- installedApps.delete(app);
85
+ onAppUnmount(app, () => {
93
86
  delete app.config.globalProperties.$vui;
94
- unmountApp();
95
- };
87
+ });
96
88
  }
97
89
  }
98
90