@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 CHANGED
@@ -2321,7 +2321,8 @@ const VOption = vue.defineComponent({
2321
2321
  return vue.createVNode("label", vue.mergeProps({
2322
2322
  "class": classes,
2323
2323
  "onClick": selectorItemControl.handleClickElement,
2324
- "tabindex": selectorItemControl.tabindex
2324
+ "tabindex": selectorItemControl.tabindex,
2325
+ "data-value": selectorItemControl.propValue
2325
2326
  }, {
2326
2327
  'aria-disabled': selectorItemControl.isDisabled
2327
2328
  }), [vue.createVNode("span", {
@@ -2482,6 +2483,9 @@ function _isSlot$8(s) {
2482
2483
  return typeof s === 'function' || Object.prototype.toString.call(s) === '[object Object]' && !vue.isVNode(s);
2483
2484
  }
2484
2485
 
2486
+ const ARROW_KEY_TYPES = vueKit.useKeybord.Key(['ArrowUp', 'ArrowDown']);
2487
+ const CHOICE_KEY_TYPES = vueKit.useKeybord.Key(['Enter', ' ']);
2488
+ const KEYBORD_EVENT_TYPES = vueKit.useKeybord.Key([...ARROW_KEY_TYPES, ...CHOICE_KEY_TYPES]);
2485
2489
  const {
2486
2490
  props: props$3,
2487
2491
  emits: emits$3
@@ -2503,6 +2507,7 @@ const VSelect = vue.defineComponent({
2503
2507
  emits: emits$3,
2504
2508
 
2505
2509
  setup(props, ctx) {
2510
+ const menuRef = vue.ref(null);
2506
2511
  const menuOpened = vue.ref(false);
2507
2512
 
2508
2513
  const showMenu = () => {
@@ -2565,6 +2570,107 @@ const VSelect = vue.defineComponent({
2565
2570
  return children;
2566
2571
  };
2567
2572
 
2573
+ const clearKeyFocused = () => {
2574
+ const menu = menuRef.value;
2575
+ if (!menu) return;
2576
+ const bodyEl = menu.stackMenuControl.bodyRef.value;
2577
+ if (!bodyEl) return;
2578
+ const els = Array.from(bodyEl.querySelectorAll('.v-option'));
2579
+ els.forEach(el => el.classList.remove('v-option--key-focused'));
2580
+ };
2581
+
2582
+ const getItemElements = () => {
2583
+ const menu = menuRef.value;
2584
+ if (!menu) return;
2585
+ const bodyEl = menu.stackMenuControl.bodyRef.value;
2586
+ if (!bodyEl) return;
2587
+ const els = Array.from(bodyEl.querySelectorAll('.v-option')).filter(el => {
2588
+ if (el.tabIndex === -1) return false;
2589
+ const disabled = el.getAttribute('disabled');
2590
+ const ariaDisabled = el.getAttribute('aria-disabled');
2591
+ if (ariaDisabled === 'true') return false;
2592
+ return disabled == null || disabled === '';
2593
+ });
2594
+ if (!els.length) return;
2595
+ return els;
2596
+ };
2597
+
2598
+ const arrowKeyHandler = ev => {
2599
+ const {
2600
+ key
2601
+ } = ev;
2602
+ if (!menuOpened.value || !ARROW_KEY_TYPES.includes(key)) return;
2603
+ const els = getItemElements();
2604
+ if (!els) return;
2605
+ const currentEl = els.find(el => el.classList.contains('v-option--key-focused'));
2606
+ const currentIndex = currentEl && els.indexOf(currentEl);
2607
+ let nextIndex;
2608
+ const {
2609
+ length
2610
+ } = els;
2611
+ const isUp = key === 'ArrowUp';
2612
+
2613
+ if (currentIndex == null) {
2614
+ nextIndex = isUp ? length - 1 : 0;
2615
+ } else {
2616
+ const shiftAmount = key === 'ArrowUp' ? -1 : 1;
2617
+ nextIndex = currentIndex + shiftAmount;
2618
+
2619
+ if (nextIndex < 0) {
2620
+ nextIndex = length - 1;
2621
+ } else if (nextIndex >= length) {
2622
+ nextIndex = 0;
2623
+ }
2624
+ }
2625
+
2626
+ const nextEl = els[nextIndex];
2627
+
2628
+ if (nextEl) {
2629
+ clearKeyFocused();
2630
+ nextEl.classList.add('v-option--key-focused');
2631
+ nextEl.scrollIntoView({
2632
+ block: 'nearest',
2633
+ inline: 'nearest',
2634
+ behavior: 'smooth'
2635
+ });
2636
+ ev.preventDefault();
2637
+ }
2638
+ };
2639
+
2640
+ const choiceKeyHandler = ev => {
2641
+ const {
2642
+ key
2643
+ } = ev;
2644
+ if (!menuOpened.value || !CHOICE_KEY_TYPES.includes(key)) return;
2645
+ const els = getItemElements();
2646
+ if (!els) return;
2647
+ const currentEl = els.find(el => el.classList.contains('v-option--key-focused'));
2648
+
2649
+ if (currentEl) {
2650
+ const ev = new MouseEvent('click', {
2651
+ view: window,
2652
+ bubbles: true,
2653
+ cancelable: true
2654
+ });
2655
+ currentEl.dispatchEvent(ev);
2656
+ ev.preventDefault();
2657
+ vue.nextTick(() => {
2658
+ currentEl.classList.add('v-option--key-focused');
2659
+ });
2660
+ }
2661
+ };
2662
+
2663
+ const keybordEventHandler = ev => {
2664
+ if (ARROW_KEY_TYPES.includes(ev.key)) return arrowKeyHandler(ev);
2665
+ if (CHOICE_KEY_TYPES.includes(ev.key)) return choiceKeyHandler(ev);
2666
+ };
2667
+
2668
+ vueKit.useKeybord([{
2669
+ key: KEYBORD_EVENT_TYPES,
2670
+ handler: keybordEventHandler
2671
+ }], {
2672
+ autorun: true
2673
+ });
2568
2674
  return { ...inputControl.expose(),
2569
2675
  ...control,
2570
2676
  iconName,
@@ -2573,7 +2679,9 @@ const VSelect = vue.defineComponent({
2573
2679
  focus,
2574
2680
  showMenu,
2575
2681
  closeMenu,
2576
- renderSelections
2682
+ renderSelections,
2683
+ menuRef: () => menuRef,
2684
+ clearKeyFocused
2577
2685
  };
2578
2686
  },
2579
2687
 
@@ -2624,15 +2732,8 @@ const VSelect = vue.defineComponent({
2624
2732
  "alwaysRender": true,
2625
2733
  "modelValue": this.menuOpened,
2626
2734
  "onUpdate:modelValue": $event => this.menuOpened = $event,
2627
- "itemElements": body => body.querySelectorAll('.v-option'),
2628
- "onChoiceItemElement": item => {
2629
- const ev = new MouseEvent('click', {
2630
- view: window,
2631
- bubbles: true,
2632
- cancelable: true
2633
- });
2634
- item.dispatchEvent(ev);
2635
- }
2735
+ "ref": this.menuRef(),
2736
+ "onClose": this.clearKeyFocused
2636
2737
  }, {
2637
2738
  default: () => [vue.createVNode("div", {
2638
2739
  "class": ['v-select__body', this.classes]
@@ -5654,11 +5755,14 @@ exports.VMenu = vueKit.VMenu;
5654
5755
  exports.VSnackbar = vueKit.VSnackbar;
5655
5756
  exports.VTooltip = vueKit.VTooltip;
5656
5757
  exports.ICON_NAMES = iconFont.ICON_NAMES;
5758
+ exports.ARROW_KEY_TYPES = ARROW_KEY_TYPES;
5657
5759
  exports.AVATAR_SIZES = AVATAR_SIZES;
5658
5760
  exports.CHIP_SIZES = CHIP_SIZES;
5761
+ exports.CHOICE_KEY_TYPES = CHOICE_KEY_TYPES;
5659
5762
  exports.CONTROL_FIELD_VARIANTS = CONTROL_FIELD_VARIANTS;
5660
5763
  exports.CONTROL_LOADING_SPINNER_SIZES = CONTROL_LOADING_SPINNER_SIZES;
5661
5764
  exports.CONTROL_SIZES = CONTROL_SIZES;
5765
+ exports.KEYBORD_EVENT_TYPES = KEYBORD_EVENT_TYPES;
5662
5766
  exports.PAGINATION_ALIGNS = PAGINATION_ALIGNS;
5663
5767
  exports.VApp = VApp;
5664
5768
  exports.VAvatar = VAvatar;
@@ -2321,7 +2321,8 @@ const VOption = vue.defineComponent({
2321
2321
  return vue.createVNode("label", vue.mergeProps({
2322
2322
  "class": classes,
2323
2323
  "onClick": selectorItemControl.handleClickElement,
2324
- "tabindex": selectorItemControl.tabindex
2324
+ "tabindex": selectorItemControl.tabindex,
2325
+ "data-value": selectorItemControl.propValue
2325
2326
  }, {
2326
2327
  'aria-disabled': selectorItemControl.isDisabled
2327
2328
  }), [vue.createVNode("span", {
@@ -2482,6 +2483,9 @@ function _isSlot$8(s) {
2482
2483
  return typeof s === 'function' || Object.prototype.toString.call(s) === '[object Object]' && !vue.isVNode(s);
2483
2484
  }
2484
2485
 
2486
+ const ARROW_KEY_TYPES = vueKit.useKeybord.Key(['ArrowUp', 'ArrowDown']);
2487
+ const CHOICE_KEY_TYPES = vueKit.useKeybord.Key(['Enter', ' ']);
2488
+ const KEYBORD_EVENT_TYPES = vueKit.useKeybord.Key([...ARROW_KEY_TYPES, ...CHOICE_KEY_TYPES]);
2485
2489
  const {
2486
2490
  props: props$3,
2487
2491
  emits: emits$3
@@ -2503,6 +2507,7 @@ const VSelect = vue.defineComponent({
2503
2507
  emits: emits$3,
2504
2508
 
2505
2509
  setup(props, ctx) {
2510
+ const menuRef = vue.ref(null);
2506
2511
  const menuOpened = vue.ref(false);
2507
2512
 
2508
2513
  const showMenu = () => {
@@ -2565,6 +2570,107 @@ const VSelect = vue.defineComponent({
2565
2570
  return children;
2566
2571
  };
2567
2572
 
2573
+ const clearKeyFocused = () => {
2574
+ const menu = menuRef.value;
2575
+ if (!menu) return;
2576
+ const bodyEl = menu.stackMenuControl.bodyRef.value;
2577
+ if (!bodyEl) return;
2578
+ const els = Array.from(bodyEl.querySelectorAll('.v-option'));
2579
+ els.forEach(el => el.classList.remove('v-option--key-focused'));
2580
+ };
2581
+
2582
+ const getItemElements = () => {
2583
+ const menu = menuRef.value;
2584
+ if (!menu) return;
2585
+ const bodyEl = menu.stackMenuControl.bodyRef.value;
2586
+ if (!bodyEl) return;
2587
+ const els = Array.from(bodyEl.querySelectorAll('.v-option')).filter(el => {
2588
+ if (el.tabIndex === -1) return false;
2589
+ const disabled = el.getAttribute('disabled');
2590
+ const ariaDisabled = el.getAttribute('aria-disabled');
2591
+ if (ariaDisabled === 'true') return false;
2592
+ return disabled == null || disabled === '';
2593
+ });
2594
+ if (!els.length) return;
2595
+ return els;
2596
+ };
2597
+
2598
+ const arrowKeyHandler = ev => {
2599
+ const {
2600
+ key
2601
+ } = ev;
2602
+ if (!menuOpened.value || !ARROW_KEY_TYPES.includes(key)) return;
2603
+ const els = getItemElements();
2604
+ if (!els) return;
2605
+ const currentEl = els.find(el => el.classList.contains('v-option--key-focused'));
2606
+ const currentIndex = currentEl && els.indexOf(currentEl);
2607
+ let nextIndex;
2608
+ const {
2609
+ length
2610
+ } = els;
2611
+ const isUp = key === 'ArrowUp';
2612
+
2613
+ if (currentIndex == null) {
2614
+ nextIndex = isUp ? length - 1 : 0;
2615
+ } else {
2616
+ const shiftAmount = key === 'ArrowUp' ? -1 : 1;
2617
+ nextIndex = currentIndex + shiftAmount;
2618
+
2619
+ if (nextIndex < 0) {
2620
+ nextIndex = length - 1;
2621
+ } else if (nextIndex >= length) {
2622
+ nextIndex = 0;
2623
+ }
2624
+ }
2625
+
2626
+ const nextEl = els[nextIndex];
2627
+
2628
+ if (nextEl) {
2629
+ clearKeyFocused();
2630
+ nextEl.classList.add('v-option--key-focused');
2631
+ nextEl.scrollIntoView({
2632
+ block: 'nearest',
2633
+ inline: 'nearest',
2634
+ behavior: 'smooth'
2635
+ });
2636
+ ev.preventDefault();
2637
+ }
2638
+ };
2639
+
2640
+ const choiceKeyHandler = ev => {
2641
+ const {
2642
+ key
2643
+ } = ev;
2644
+ if (!menuOpened.value || !CHOICE_KEY_TYPES.includes(key)) return;
2645
+ const els = getItemElements();
2646
+ if (!els) return;
2647
+ const currentEl = els.find(el => el.classList.contains('v-option--key-focused'));
2648
+
2649
+ if (currentEl) {
2650
+ const ev = new MouseEvent('click', {
2651
+ view: window,
2652
+ bubbles: true,
2653
+ cancelable: true
2654
+ });
2655
+ currentEl.dispatchEvent(ev);
2656
+ ev.preventDefault();
2657
+ vue.nextTick(() => {
2658
+ currentEl.classList.add('v-option--key-focused');
2659
+ });
2660
+ }
2661
+ };
2662
+
2663
+ const keybordEventHandler = ev => {
2664
+ if (ARROW_KEY_TYPES.includes(ev.key)) return arrowKeyHandler(ev);
2665
+ if (CHOICE_KEY_TYPES.includes(ev.key)) return choiceKeyHandler(ev);
2666
+ };
2667
+
2668
+ vueKit.useKeybord([{
2669
+ key: KEYBORD_EVENT_TYPES,
2670
+ handler: keybordEventHandler
2671
+ }], {
2672
+ autorun: true
2673
+ });
2568
2674
  return { ...inputControl.expose(),
2569
2675
  ...control,
2570
2676
  iconName,
@@ -2573,7 +2679,9 @@ const VSelect = vue.defineComponent({
2573
2679
  focus,
2574
2680
  showMenu,
2575
2681
  closeMenu,
2576
- renderSelections
2682
+ renderSelections,
2683
+ menuRef: () => menuRef,
2684
+ clearKeyFocused
2577
2685
  };
2578
2686
  },
2579
2687
 
@@ -2624,15 +2732,8 @@ const VSelect = vue.defineComponent({
2624
2732
  "alwaysRender": true,
2625
2733
  "modelValue": this.menuOpened,
2626
2734
  "onUpdate:modelValue": $event => this.menuOpened = $event,
2627
- "itemElements": body => body.querySelectorAll('.v-option'),
2628
- "onChoiceItemElement": item => {
2629
- const ev = new MouseEvent('click', {
2630
- view: window,
2631
- bubbles: true,
2632
- cancelable: true
2633
- });
2634
- item.dispatchEvent(ev);
2635
- }
2735
+ "ref": this.menuRef(),
2736
+ "onClose": this.clearKeyFocused
2636
2737
  }, {
2637
2738
  default: () => [vue.createVNode("div", {
2638
2739
  "class": ['v-select__body', this.classes]
@@ -5654,11 +5755,14 @@ exports.VMenu = vueKit.VMenu;
5654
5755
  exports.VSnackbar = vueKit.VSnackbar;
5655
5756
  exports.VTooltip = vueKit.VTooltip;
5656
5757
  exports.ICON_NAMES = iconFont.ICON_NAMES;
5758
+ exports.ARROW_KEY_TYPES = ARROW_KEY_TYPES;
5657
5759
  exports.AVATAR_SIZES = AVATAR_SIZES;
5658
5760
  exports.CHIP_SIZES = CHIP_SIZES;
5761
+ exports.CHOICE_KEY_TYPES = CHOICE_KEY_TYPES;
5659
5762
  exports.CONTROL_FIELD_VARIANTS = CONTROL_FIELD_VARIANTS;
5660
5763
  exports.CONTROL_LOADING_SPINNER_SIZES = CONTROL_LOADING_SPINNER_SIZES;
5661
5764
  exports.CONTROL_SIZES = CONTROL_SIZES;
5765
+ exports.KEYBORD_EVENT_TYPES = KEYBORD_EVENT_TYPES;
5662
5766
  exports.PAGINATION_ALIGNS = PAGINATION_ALIGNS;
5663
5767
  exports.VApp = VApp;
5664
5768
  exports.VAvatar = VAvatar;
package/dist/vui.css CHANGED
@@ -1880,7 +1880,7 @@ thead th {
1880
1880
  .v-option::after {
1881
1881
  background: var(--palette-primary);
1882
1882
  }
1883
- .v-option:hover::before, .v-option:focus-within::before {
1883
+ .v-option:hover::before, .v-option:focus-within::before, .v-option--key-focused::before {
1884
1884
  opacity: 0.12;
1885
1885
  }
1886
1886
  .v-option__label {
package/dist/vui.d.ts CHANGED
@@ -92,6 +92,7 @@ import { ValidationResult } from '@fastkit/vue-kit';
92
92
  import { VDialog } from '@fastkit/vue-kit';
93
93
  import { VDialogProps } from '@fastkit/vue-kit';
94
94
  import { VMenu } from '@fastkit/vue-kit';
95
+ import { VMenuControl } from '@fastkit/vue-stack';
95
96
  import { VNode } from 'vue';
96
97
  import { VNodeChild } from 'vue';
97
98
  import { VNodeChildOrSlot } from '@fastkit/vue-kit';
@@ -109,6 +110,8 @@ import { WritableComputedRef } from 'vue';
109
110
 
110
111
  declare type ARGS = Record<string, unknown> & VNodeProps;
111
112
 
113
+ export declare const ARROW_KEY_TYPES: ("ArrowDown" | "ArrowUp")[];
114
+
112
115
  export declare const AVATAR_SIZES: readonly ["sm", "md", "lg"];
113
116
 
114
117
  export declare type AvatarSize = typeof AVATAR_SIZES[number];
@@ -139,6 +142,8 @@ export declare const CHIP_SIZES: readonly ["xs", "sm", "md", "lg", "xl"];
139
142
 
140
143
  export declare type ChipSize = typeof CHIP_SIZES[number];
141
144
 
145
+ export declare const CHOICE_KEY_TYPES: ("Enter" | " ")[];
146
+
142
147
  export declare function configureDataTableDefaults(defaults: Partial<DataTableDefaults>): void;
143
148
 
144
149
  export declare interface ContentSwitcherSeeTopOptions {
@@ -1638,6 +1643,8 @@ declare interface InputBoxSlots {
1638
1643
 
1639
1644
  export declare function installVuiPlugin(app: App, opts: VuiPluginOptions): App<any>;
1640
1645
 
1646
+ export declare const KEYBORD_EVENT_TYPES: ("Enter" | " " | "ArrowDown" | "ArrowUp")[];
1647
+
1641
1648
  export declare const listTileEmits: {
1642
1649
  changeActive: (isActive: boolean) => boolean;
1643
1650
  };
@@ -12931,6 +12938,10 @@ export declare const VSelect: DefineComponent<{
12931
12938
  showMenu: () => void;
12932
12939
  closeMenu: () => void;
12933
12940
  renderSelections: (selectedItems: FormSelectorItemControl[]) => VNodeChild[];
12941
+ menuRef: () => Ref<{
12942
+ stackMenuControl: VMenuControl;
12943
+ } | null>;
12944
+ clearKeyFocused: () => void;
12934
12945
  parentControl: ControlProvider | null;
12935
12946
  size: ComputedRef<"sm" | "md" | "lg">;
12936
12947
  classes: ComputedRef<any>;
@@ -13153,6 +13164,10 @@ export declare const VSelect: DefineComponent<{
13153
13164
  showMenu: () => void;
13154
13165
  closeMenu: () => void;
13155
13166
  renderSelections: (selectedItems: FormSelectorItemControl[]) => VNodeChild[];
13167
+ menuRef: () => Ref<{
13168
+ stackMenuControl: VMenuControl;
13169
+ } | null>;
13170
+ clearKeyFocused: () => void;
13156
13171
  parentControl: ControlProvider | null;
13157
13172
  size: ComputedRef<"sm" | "md" | "lg">;
13158
13173
  classes: ComputedRef<any>;