@fastkit/vui 0.8.8 → 0.8.11

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.
@@ -410,19 +410,24 @@ function defineFormSelectorComponent(opts) {
410
410
  "indeterminate": true,
411
411
  "class": "mr-1",
412
412
  "size": CONTROL_LOADING_SPINNER_SIZES[this.size || 'md']
413
- }, null), this.loadingMessageRef]), this.propItems.map(attrs => {
414
- const selected = selectorControl.isSelected(attrs.value);
415
- return itemRenderer({
416
- selected,
417
- control: selectorControl,
418
- attrs: { ...attrs,
419
- modelValue: selected,
420
- key: attrs.value
421
- },
422
- slots: {
423
- default: () => attrs.label(this.selectorControl)
424
- }
425
- });
413
+ }, null), this.loadingMessageRef]), this.propGroups.map(group => {
414
+ const {
415
+ items
416
+ } = group;
417
+ return vue.createVNode(vue.Fragment, null, [items.map(attrs => {
418
+ const selected = selectorControl.isSelected(attrs.value);
419
+ return itemRenderer({
420
+ selected,
421
+ control: selectorControl,
422
+ attrs: { ...attrs,
423
+ modelValue: selected,
424
+ key: attrs.value
425
+ },
426
+ slots: {
427
+ default: () => attrs.label(this.selectorControl)
428
+ }
429
+ });
430
+ })]);
426
431
  }), vueKit.renderSlotOrEmpty(this.$slots, 'default')])
427
432
  });
428
433
  }
@@ -447,7 +452,7 @@ function useElevation(props, opts = {}) {
447
452
  };
448
453
  }
449
454
 
450
- function _isSlot$e(s) {
455
+ function _isSlot$f(s) {
451
456
  return typeof s === 'function' || Object.prototype.toString.call(s) === '[object Object]' && !vue.isVNode(s);
452
457
  }
453
458
 
@@ -468,7 +473,7 @@ const VSkeltonLoaderBone = vue.defineComponent({
468
473
  const TagName = props.tag;
469
474
  return vue.createVNode(TagName, vue.mergeProps(ctx.attrs, {
470
475
  "class": "v-skelton-loader-bone"
471
- }), _isSlot$e(_slot = vueUtils.renderSlotOrEmpty(ctx.slots)) ? _slot : {
476
+ }), _isSlot$f(_slot = vueUtils.renderSlotOrEmpty(ctx.slots)) ? _slot : {
472
477
  default: () => [_slot]
473
478
  });
474
479
  };
@@ -1291,7 +1296,7 @@ const VButtonGroup = vue.defineComponent({
1291
1296
 
1292
1297
  });
1293
1298
 
1294
- function _isSlot$d(s) {
1299
+ function _isSlot$e(s) {
1295
1300
  return typeof s === 'function' || Object.prototype.toString.call(s) === '[object Object]' && !vue.isVNode(s);
1296
1301
  }
1297
1302
 
@@ -1540,7 +1545,7 @@ const VPagination = vue.defineComponent({
1540
1545
  "class": classes,
1541
1546
  "to": to,
1542
1547
  "key": key
1543
- }, _isSlot$d(children) ? children : {
1548
+ }, _isSlot$e(children) ? children : {
1544
1549
  default: () => [children]
1545
1550
  });
1546
1551
  } else {
@@ -1696,7 +1701,7 @@ const VDrawerLayout = vue.defineComponent({
1696
1701
 
1697
1702
  });
1698
1703
 
1699
- function _isSlot$c(s) {
1704
+ function _isSlot$d(s) {
1700
1705
  return typeof s === 'function' || Object.prototype.toString.call(s) === '[object Object]' && !vue.isVNode(s);
1701
1706
  }
1702
1707
 
@@ -1733,7 +1738,7 @@ const VHero = vue.defineComponent({
1733
1738
  }, {
1734
1739
  default: () => [vue.createVNode(HTagName, {
1735
1740
  "class": "v-hero__title"
1736
- }, _isSlot$c(_slot = vueUtils.renderSlotOrEmpty(ctx.slots, 'default')) ? _slot : {
1741
+ }, _isSlot$d(_slot = vueUtils.renderSlotOrEmpty(ctx.slots, 'default')) ? _slot : {
1737
1742
  default: () => [_slot]
1738
1743
  }), adornment && vue.createVNode("div", {
1739
1744
  "class": "v-hero__adornment"
@@ -1745,7 +1750,7 @@ const VHero = vue.defineComponent({
1745
1750
 
1746
1751
  });
1747
1752
 
1748
- function _isSlot$b(s) {
1753
+ function _isSlot$c(s) {
1749
1754
  return typeof s === 'function' || Object.prototype.toString.call(s) === '[object Object]' && !vue.isVNode(s);
1750
1755
  }
1751
1756
 
@@ -1786,7 +1791,7 @@ function renderNavigationItemInput(input, extraProps) {
1786
1791
  } = resolveNavigationItemInput(input);
1787
1792
  return vue.createVNode(VNavigationItem, { ...props,
1788
1793
  ...extraProps
1789
- }, _isSlot$b(label) ? label : {
1794
+ }, _isSlot$c(label) ? label : {
1790
1795
  default: () => [label]
1791
1796
  });
1792
1797
  }
@@ -1941,7 +1946,7 @@ const VNavigationItem = vue.defineComponent({
1941
1946
  "class": ['v-navigation-item', classes.value],
1942
1947
  "onClick": onClick,
1943
1948
  "onChangeActive": onChangeActive
1944
- }), _isSlot$b(_slot = vueUtils.renderSlotOrEmpty(ctx.slots, 'default')) ? _slot : {
1949
+ }), _isSlot$c(_slot = vueUtils.renderSlotOrEmpty(ctx.slots, 'default')) ? _slot : {
1945
1950
  default: () => [_slot]
1946
1951
  }), _children && vue.createVNode(vueUtils.VExpandTransition, null, {
1947
1952
  default: () => [vue.withDirectives(vue.createVNode("div", {
@@ -2125,7 +2130,7 @@ const VCheckbox = vue.defineComponent({
2125
2130
 
2126
2131
  });
2127
2132
 
2128
- function _isSlot$a(s) {
2133
+ function _isSlot$b(s) {
2129
2134
  return typeof s === 'function' || Object.prototype.toString.call(s) === '[object Object]' && !vue.isVNode(s);
2130
2135
  }
2131
2136
 
@@ -2137,7 +2142,7 @@ const VCheckboxGroup = defineFormSelectorComponent({
2137
2142
  itemRenderer: ({
2138
2143
  attrs,
2139
2144
  slots
2140
- }) => vue.createVNode(VCheckbox, attrs, _isSlot$a(slots) ? slots : {
2145
+ }) => vue.createVNode(VCheckbox, attrs, _isSlot$b(slots) ? slots : {
2141
2146
  default: () => [slots]
2142
2147
  })
2143
2148
  });
@@ -2189,7 +2194,7 @@ const VRadio = vue.defineComponent({
2189
2194
 
2190
2195
  });
2191
2196
 
2192
- function _isSlot$9(s) {
2197
+ function _isSlot$a(s) {
2193
2198
  return typeof s === 'function' || Object.prototype.toString.call(s) === '[object Object]' && !vue.isVNode(s);
2194
2199
  }
2195
2200
 
@@ -2200,7 +2205,7 @@ const VRadioGroup = defineFormSelectorComponent({
2200
2205
  itemRenderer: ({
2201
2206
  attrs,
2202
2207
  slots
2203
- }) => vue.createVNode(VRadio, attrs, _isSlot$9(slots) ? slots : {
2208
+ }) => vue.createVNode(VRadio, attrs, _isSlot$a(slots) ? slots : {
2204
2209
  default: () => [slots]
2205
2210
  })
2206
2211
  });
@@ -2262,7 +2267,7 @@ const VSwitch = vue.defineComponent({
2262
2267
 
2263
2268
  });
2264
2269
 
2265
- function _isSlot$8(s) {
2270
+ function _isSlot$9(s) {
2266
2271
  return typeof s === 'function' || Object.prototype.toString.call(s) === '[object Object]' && !vue.isVNode(s);
2267
2272
  }
2268
2273
 
@@ -2274,7 +2279,7 @@ const VSwitchGroup = defineFormSelectorComponent({
2274
2279
  itemRenderer: ({
2275
2280
  attrs,
2276
2281
  slots
2277
- }) => vue.createVNode(VSwitch, attrs, _isSlot$8(slots) ? slots : {
2282
+ }) => vue.createVNode(VSwitch, attrs, _isSlot$9(slots) ? slots : {
2278
2283
  default: () => [slots]
2279
2284
  })
2280
2285
  });
@@ -2313,10 +2318,14 @@ const VOption = vue.defineComponent({
2313
2318
  selectorItemControl,
2314
2319
  classes
2315
2320
  } = this;
2316
- return vue.createVNode("label", {
2321
+ return vue.createVNode("label", vue.mergeProps({
2317
2322
  "class": classes,
2318
- "onClick": selectorItemControl.handleClickElement
2319
- }, [vue.createVNode("span", {
2323
+ "onClick": selectorItemControl.handleClickElement,
2324
+ "tabindex": selectorItemControl.tabindex,
2325
+ "data-value": selectorItemControl.propValue
2326
+ }, {
2327
+ 'aria-disabled': selectorItemControl.isDisabled
2328
+ }), [vue.createVNode("span", {
2320
2329
  "class": "v-option__label"
2321
2330
  }, [vueKit.renderSlotOrEmpty(this.$slots, 'default')])]);
2322
2331
  }
@@ -2470,6 +2479,13 @@ const VControlField = vue.defineComponent({
2470
2479
 
2471
2480
  });
2472
2481
 
2482
+ function _isSlot$8(s) {
2483
+ return typeof s === 'function' || Object.prototype.toString.call(s) === '[object Object]' && !vue.isVNode(s);
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]);
2473
2489
  const {
2474
2490
  props: props$3,
2475
2491
  emits: emits$3
@@ -2491,6 +2507,7 @@ const VSelect = vue.defineComponent({
2491
2507
  emits: emits$3,
2492
2508
 
2493
2509
  setup(props, ctx) {
2510
+ const menuRef = vue.ref(null);
2494
2511
  const menuOpened = vue.ref(false);
2495
2512
 
2496
2513
  const showMenu = () => {
@@ -2553,6 +2570,107 @@ const VSelect = vue.defineComponent({
2553
2570
  return children;
2554
2571
  };
2555
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
+ });
2556
2674
  return { ...inputControl.expose(),
2557
2675
  ...control,
2558
2676
  iconName,
@@ -2561,7 +2679,9 @@ const VSelect = vue.defineComponent({
2561
2679
  focus,
2562
2680
  showMenu,
2563
2681
  closeMenu,
2564
- renderSelections
2682
+ renderSelections,
2683
+ menuRef: () => menuRef,
2684
+ clearKeyFocused
2565
2685
  };
2566
2686
  },
2567
2687
 
@@ -2573,18 +2693,29 @@ const VSelect = vue.defineComponent({
2573
2693
  selectedItems
2574
2694
  } = this;
2575
2695
  const children = this.$slots.default && this.$slots.default() || [];
2576
- const propOptions = this.propItems.map(item => {
2577
- return vue.createVNode(VOption, {
2696
+ const propGroups = this.propGroups.map(group => {
2697
+ let _slot;
2698
+
2699
+ return vue.createVNode(VOptionGroup, {
2700
+ "key": group.id,
2701
+ "groupId": group.id,
2702
+ "label": group.label(selectorControl),
2703
+ "disabled": group.disabled
2704
+ }, _isSlot$8(_slot = group.items.map(item => vue.createVNode(VOption, {
2578
2705
  "disabled": item.disabled,
2579
2706
  "value": item.value,
2580
2707
  "key": item.value
2581
2708
  }, {
2582
2709
  default: () => item.label(selectorControl)
2710
+ }))) ? _slot : {
2711
+ default: () => [_slot]
2583
2712
  });
2584
2713
  });
2585
2714
  return vue.createVNode(VFormControl, {
2586
2715
  "nodeControl": nodeControl,
2587
- "class": ['v-select', this.classes],
2716
+ "class": ['v-select', this.classes, {
2717
+ 'v-select--multiple': this.multiple
2718
+ }],
2588
2719
  "label": this.label,
2589
2720
  "hint": this.hint,
2590
2721
  "hinttip": this.hinttip,
@@ -2600,11 +2731,13 @@ const VSelect = vue.defineComponent({
2600
2731
  "distance": 0,
2601
2732
  "alwaysRender": true,
2602
2733
  "modelValue": this.menuOpened,
2603
- "onUpdate:modelValue": $event => this.menuOpened = $event
2734
+ "onUpdate:modelValue": $event => this.menuOpened = $event,
2735
+ "ref": this.menuRef(),
2736
+ "onClose": this.clearKeyFocused
2604
2737
  }, {
2605
2738
  default: () => [vue.createVNode("div", {
2606
2739
  "class": ['v-select__body', this.classes]
2607
- }, [children, propOptions])],
2740
+ }, [children, propGroups])],
2608
2741
  activator: ({
2609
2742
  attrs,
2610
2743
  control
@@ -2617,6 +2750,7 @@ const VSelect = vue.defineComponent({
2617
2750
  "endAdornment": this.endAdornment,
2618
2751
  "size": this.size,
2619
2752
  "focused": this.menuOpened,
2753
+ "autoHeight": this.multiple,
2620
2754
  "onClick": ev => {
2621
2755
  if (this.canOperation && !control.isActive) {
2622
2756
  let t = ev.target;
@@ -5560,15 +5694,7 @@ function mergeVuiPluginOptions(base, override) {
5560
5694
  return merged;
5561
5695
  }
5562
5696
  class VuiPlugin {
5563
- static installedApps = new Set();
5564
-
5565
5697
  static install(app, opts) {
5566
- const {
5567
- installedApps
5568
- } = this;
5569
- if (installedApps.has(app)) return;
5570
- const unmountApp = app.unmount;
5571
- installedApps.add(app);
5572
5698
  const {
5573
5699
  colorScheme,
5574
5700
  stack,
@@ -5603,12 +5729,9 @@ class VuiPlugin {
5603
5729
  const $vui = new VuiService(opts, app.config.globalProperties.$vstack);
5604
5730
  app.provide(VuiInjectionKey, $vui);
5605
5731
  app.config.globalProperties.$vui = $vui;
5606
-
5607
- app.unmount = function () {
5608
- installedApps.delete(app);
5732
+ vueKit.onAppUnmount(app, () => {
5609
5733
  delete app.config.globalProperties.$vui;
5610
- unmountApp();
5611
- };
5734
+ });
5612
5735
  }
5613
5736
 
5614
5737
  }
@@ -5621,11 +5744,14 @@ exports.VMenu = vueKit.VMenu;
5621
5744
  exports.VSnackbar = vueKit.VSnackbar;
5622
5745
  exports.VTooltip = vueKit.VTooltip;
5623
5746
  exports.ICON_NAMES = iconFont.ICON_NAMES;
5747
+ exports.ARROW_KEY_TYPES = ARROW_KEY_TYPES;
5624
5748
  exports.AVATAR_SIZES = AVATAR_SIZES;
5625
5749
  exports.CHIP_SIZES = CHIP_SIZES;
5750
+ exports.CHOICE_KEY_TYPES = CHOICE_KEY_TYPES;
5626
5751
  exports.CONTROL_FIELD_VARIANTS = CONTROL_FIELD_VARIANTS;
5627
5752
  exports.CONTROL_LOADING_SPINNER_SIZES = CONTROL_LOADING_SPINNER_SIZES;
5628
5753
  exports.CONTROL_SIZES = CONTROL_SIZES;
5754
+ exports.KEYBORD_EVENT_TYPES = KEYBORD_EVENT_TYPES;
5629
5755
  exports.PAGINATION_ALIGNS = PAGINATION_ALIGNS;
5630
5756
  exports.VApp = VApp;
5631
5757
  exports.VAvatar = VAvatar;
package/dist/vui.css CHANGED
@@ -1833,12 +1833,25 @@ thead th {
1833
1833
  opacity: 0.1;
1834
1834
  }
1835
1835
  .v-option {
1836
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
1837
+ padding: 0;
1838
+ touch-action: manipulation;
1839
+ cursor: pointer;
1840
+ user-select: none;
1841
+ background-color: transparent;
1842
+ border: 0;
1843
+ outline: 0;
1844
+ appearance: none;
1836
1845
  position: relative;
1837
1846
  display: flex;
1838
1847
  width: 100%;
1839
- padding: 0.375em 1em;
1840
- cursor: pointer;
1841
- user-select: none;
1848
+ padding: 0.75em 1em;
1849
+ }
1850
+ .v-option::-moz-focus-inner {
1851
+ border: 0;
1852
+ }
1853
+ .v-option, .v-option:hover, .v-option:focus {
1854
+ text-decoration: none;
1842
1855
  }
1843
1856
  .v-option input {
1844
1857
  position: absolute;
@@ -1867,7 +1880,7 @@ thead th {
1867
1880
  .v-option::after {
1868
1881
  background: var(--palette-primary);
1869
1882
  }
1870
- .v-option:hover::before, .v-option:focus-within::before {
1883
+ .v-option:hover::before, .v-option:focus-within::before, .v-option--key-focused::before {
1871
1884
  opacity: 0.12;
1872
1885
  }
1873
1886
  .v-option__label {
@@ -1884,9 +1897,27 @@ thead th {
1884
1897
  .v-option--has-not-value .v-option__label {
1885
1898
  font-style: italic;
1886
1899
  }
1900
+ .v-option[disabled], .v-option[tabindex="-1"] {
1901
+ color: var(--palette-muted);
1902
+ pointer-events: none;
1903
+ opacity: 0.4;
1904
+ }
1905
+ .v-option[disabled]::before, .v-option[tabindex="-1"]::before {
1906
+ opacity: 0 !important;
1907
+ }
1887
1908
  .v-option-group {
1888
1909
  display: block;
1889
1910
  }
1911
+ .v-option-group__label {
1912
+ padding: 1em;
1913
+ font-size: 0.875em;
1914
+ color: var(--palette-caption);
1915
+ opacity: 0.6;
1916
+ }
1917
+ .v-option-group--disabled .v-option-group__label {
1918
+ color: var(--palette-muted);
1919
+ opacity: 0.4;
1920
+ }
1890
1921
  .v-select__input {
1891
1922
  cursor: pointer;
1892
1923
  }
@@ -1914,6 +1945,17 @@ thead th {
1914
1945
  white-space: nowrap;
1915
1946
  vertical-align: middle;
1916
1947
  }
1948
+ .v-select--multiple .v-select__selections {
1949
+ display: block;
1950
+ }
1951
+ .v-select--multiple .v-select__selections__inner {
1952
+ display: block;
1953
+ overflow: auto;
1954
+ white-space: unset;
1955
+ }
1956
+ .v-select--multiple .v-control-field__body {
1957
+ padding: 0.2em 0;
1958
+ }
1917
1959
  .v-select__placeholder {
1918
1960
  font-style: italic;
1919
1961
  color: var(--control-disabled-color);