@veritree/ui 0.23.0 → 0.24.0-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.
Files changed (73) hide show
  1. package/index.js +64 -72
  2. package/mixins/floating-ui-content.js +1 -1
  3. package/mixins/floating-ui-item.js +11 -7
  4. package/mixins/floating-ui.js +8 -1
  5. package/mixins/form-control-icon.js +3 -3
  6. package/mixins/form-control.js +9 -14
  7. package/nuxt.js +30 -24
  8. package/package.json +14 -6
  9. package/src/components/Alert/VTAlert.vue +55 -14
  10. package/src/components/Button/VTButton.vue +19 -11
  11. package/src/components/Checkbox/VTCheckbox.vue +134 -0
  12. package/src/components/Checkbox/VTCheckboxLabel.vue +3 -0
  13. package/src/components/Checkbox/VTCheckboxText.vue +20 -0
  14. package/src/components/Dialog/VTDialog.vue +22 -23
  15. package/src/components/Dialog/VTDialogClose.vue +1 -1
  16. package/src/components/Dialog/VTDialogContent.vue +13 -5
  17. package/src/components/Dialog/VTDialogFooter.vue +8 -2
  18. package/src/components/Dialog/VTDialogHeader.vue +2 -1
  19. package/src/components/Dialog/VTDialogMain.vue +1 -1
  20. package/src/components/Dialog/VTDialogOverlay.vue +5 -1
  21. package/src/components/Disclosure/VTDisclosureContent.vue +1 -1
  22. package/src/components/Disclosure/VTDisclosureDetails.vue +1 -1
  23. package/src/components/Disclosure/VTDisclosureHeader.vue +2 -2
  24. package/src/components/Disclosure/VTDisclosureIcon.vue +1 -1
  25. package/src/components/Drawer/VTDrawer.vue +6 -15
  26. package/src/components/Drawer/VTDrawerClose.vue +5 -5
  27. package/src/components/Drawer/VTDrawerContent.vue +10 -10
  28. package/src/components/Drawer/VTDrawerFooter.vue +4 -4
  29. package/src/components/Drawer/VTDrawerHeader.vue +4 -4
  30. package/src/components/Drawer/VTDrawerMain.vue +5 -5
  31. package/src/components/Drawer/VTDrawerOverlay.vue +6 -6
  32. package/src/components/Drawer/VTDrawerTitle.vue +5 -5
  33. package/src/components/DropdownMenu/VTDropdownMenu.vue +0 -6
  34. package/src/components/DropdownMenu/VTDropdownMenuContent.vue +10 -1
  35. package/src/components/DropdownMenu/VTDropdownMenuDivider.vue +7 -16
  36. package/src/components/DropdownMenu/VTDropdownMenuItem.vue +5 -1
  37. package/src/components/DropdownMenu/VTDropdownMenuLabel.vue +1 -10
  38. package/src/components/DropdownMenu/VTDropdownMenuTrigger.vue +2 -4
  39. package/src/components/Form/VTFormFeedback.vue +7 -1
  40. package/src/components/Form/VTFormGroup.vue +5 -7
  41. package/src/components/Form/VTFormLabel.vue +22 -0
  42. package/src/components/Form/VTFormRow.vue +5 -0
  43. package/src/components/Form/VTInput.vue +2 -5
  44. package/src/components/Form/VTInputIcon.vue +1 -2
  45. package/src/components/Form/VTInputPassword.vue +14 -5
  46. package/src/components/Form/VTTextarea.vue +3 -6
  47. package/src/components/Image/VTImage.vue +39 -8
  48. package/src/components/Listbox/VTListbox.vue +72 -5
  49. package/src/components/Listbox/VTListboxContent.vue +0 -1
  50. package/src/components/Listbox/VTListboxItem.vue +11 -1
  51. package/src/components/Listbox/VTListboxLabel.vue +3 -4
  52. package/src/components/Listbox/VTListboxList.vue +3 -1
  53. package/src/components/Listbox/VTListboxSearch.vue +10 -7
  54. package/src/components/Listbox/VTListboxTrigger.vue +2 -0
  55. package/src/components/Popover/VTPopoverContent.vue +3 -3
  56. package/src/components/Popover/VTPopoverItem.vue +6 -2
  57. package/src/components/ProgressBar/VTProgressBar.vue +21 -3
  58. package/src/components/Skeleton/VTSkeleton.vue +11 -0
  59. package/src/components/Skeleton/VTSkeletonItem.vue +9 -0
  60. package/src/components/Tabs/VTTab.vue +6 -5
  61. package/src/components/Tabs/VTTabGroup.vue +9 -7
  62. package/src/components/Tabs/VTTabPanel.vue +4 -5
  63. package/src/components/Tooltip/VTTooltipTrigger.vue +3 -5
  64. package/src/components/Transitions/FadeInOut.vue +2 -2
  65. package/src/components/Utils/FloatingUi.vue +31 -13
  66. package/src/utils/components.js +18 -0
  67. package/src/utils/images.js +18 -25
  68. package/src/components/Input/VTInput.vue +0 -82
  69. package/src/components/Input/VTInputDate.vue +0 -36
  70. package/src/components/Input/VTInputFile.vue +0 -60
  71. package/src/components/Input/VTInputUpload.vue +0 -54
  72. package/src/components/Modal/VTModal.vue +0 -69
  73. package/src/utils/genId.js +0 -13
@@ -12,7 +12,7 @@
12
12
  export default {
13
13
  name: 'VTDrawerMain',
14
14
 
15
- inject: ['api'],
15
+ inject: ['apiDrawer'],
16
16
 
17
17
  props: {
18
18
  as: {
@@ -23,15 +23,15 @@ export default {
23
23
 
24
24
  computed: {
25
25
  dark() {
26
- return this.api().isDark;
26
+ return this.apiDrawer().isDark;
27
27
  },
28
28
 
29
29
  headless() {
30
- return this.api().isHeadless;
30
+ return this.apiDrawer().isHeadless;
31
31
  },
32
32
 
33
33
  id() {
34
- return `${this.api().id}-desc`;
34
+ return `${this.apiDrawer().id}-desc`;
35
35
  },
36
36
  },
37
37
 
@@ -42,7 +42,7 @@ export default {
42
42
  methods: {
43
43
  // In here because if there is no body, the dialog will not be described by
44
44
  setDialogDescribedby() {
45
- const dialog = document.getElementById(this.api().id);
45
+ const dialog = document.getElementById(this.apiDrawer().id);
46
46
 
47
47
  if (dialog) {
48
48
  dialog.setAttribute('aria-describedby', this.id);
@@ -12,14 +12,14 @@
12
12
  </template>
13
13
 
14
14
  <script>
15
- import FadeInOut from "../Transitions/FadeInOut.vue";
15
+ import FadeInOut from '../Transitions/FadeInOut.vue';
16
16
 
17
17
  export default {
18
18
  components: {
19
19
  FadeInOut,
20
20
  },
21
21
 
22
- inject: ["api"],
22
+ inject: ['apiDrawer'],
23
23
 
24
24
  data() {
25
25
  return {
@@ -29,21 +29,21 @@ export default {
29
29
 
30
30
  computed: {
31
31
  dark() {
32
- return this.api().isDark;
32
+ return this.apiDrawer().isDark;
33
33
  },
34
34
 
35
35
  headless() {
36
- return this.api().isHeadless;
36
+ return this.apiDrawer().isHeadless;
37
37
  },
38
38
 
39
39
  id() {
40
- return `${this.api().id}-overlay`;
40
+ return `${this.apiDrawer().id}-overlay`;
41
41
  },
42
42
  },
43
43
 
44
44
  mounted() {
45
45
  this.visible = true;
46
- this.api().registerOverlay(this);
46
+ this.apiDrawer().registerOverlay(this);
47
47
  },
48
48
 
49
49
  methods: {
@@ -15,7 +15,7 @@
15
15
  export default {
16
16
  name: 'VTDrawerTitle',
17
17
 
18
- inject: ['api'],
18
+ inject: ['apiDrawer'],
19
19
 
20
20
  props: {
21
21
  as: {
@@ -26,15 +26,15 @@ export default {
26
26
 
27
27
  computed: {
28
28
  dark() {
29
- return this.api().isDark;
29
+ return this.apiDrawer().isDark;
30
30
  },
31
31
 
32
32
  headless() {
33
- return this.api().isHeadless;
33
+ return this.apiDrawer().isHeadless;
34
34
  },
35
35
 
36
36
  id() {
37
- return `${this.api().id}-title`;
37
+ return `${this.apiDrawer().id}-title`;
38
38
  },
39
39
  },
40
40
 
@@ -45,7 +45,7 @@ export default {
45
45
  methods: {
46
46
  // In here because if there is no header, the dialog will not be labelled by
47
47
  setDialogLabelledby() {
48
- const dialog = document.getElementById(this.api().id);
48
+ const dialog = document.getElementById(this.apiDrawer().id);
49
49
 
50
50
  if (dialog) {
51
51
  dialog.setAttribute('aria-labelledby', this.id);
@@ -83,11 +83,5 @@ export default {
83
83
  floatingUiMinWidth: 200,
84
84
  };
85
85
  },
86
-
87
- computed: {
88
- id() {
89
- return `dropdown-menu-${this.componentId}`;
90
- },
91
- },
92
86
  };
93
87
  </script>
@@ -3,7 +3,6 @@
3
3
  :id="id"
4
4
  :visible="visible"
5
5
  :headless="headless"
6
- :portal-class="$vnode.data.staticClass"
7
6
  component="dropdown"
8
7
  >
9
8
  <slot></slot>
@@ -46,5 +45,15 @@ export default {
46
45
 
47
46
  this.apiDropdownMenu().registerContent(content);
48
47
  },
48
+
49
+ methods: {
50
+ hidden() {
51
+ this.$emit('hidden');
52
+ },
53
+
54
+ shown() {
55
+ this.$emit('shown');
56
+ },
57
+ },
49
58
  };
50
59
  </script>
@@ -1,9 +1,8 @@
1
1
  <template>
2
2
  <div
3
- :class="{
4
- PopoverDivider: headless,
5
- '-mx-3 my-2 h-[1px]': !headless,
6
- }"
3
+ :class="[
4
+ headless ? 'dropdown-menu-divider' : '-mx-3 my-2 h-[1px] bg-gray-200',
5
+ ]"
7
6
  ></div>
8
7
  </template>
9
8
 
@@ -13,18 +12,10 @@ export default {
13
12
 
14
13
  inject: ['apiDropdownMenu'],
15
14
 
16
- headless: {
17
- type: Boolean,
18
- default: false,
19
- },
20
-
21
- computed: {
22
- dark() {
23
- return this.apiDropdownMenu().isDark;
24
- },
25
-
26
- headless() {
27
- return this.apiDropdownMenu().isHeadless;
15
+ props: {
16
+ headless: {
17
+ type: Boolean,
18
+ default: false,
28
19
  },
29
20
  },
30
21
  };
@@ -51,7 +51,11 @@ export default {
51
51
 
52
52
  computed: {
53
53
  as() {
54
- return this.href ? 'a' : this.to ? 'NuxtLink' : 'button';
54
+ return this.href
55
+ ? 'a'
56
+ : this.to
57
+ ? resolveComponent('NuxtLink')
58
+ : 'button';
55
59
  },
56
60
  },
57
61
  };
@@ -1,11 +1,6 @@
1
1
  <template>
2
2
  <span
3
- :class="{
4
- MenuLabel: headless,
5
- 'mb-2 block text-xs uppercase': !headless,
6
- 'text-inherit': !dark,
7
- 'text-white': dark,
8
- }"
3
+ :class="[headless ? 'dropdown-menu-label' : 'mb-2 block text-xs uppercase']"
9
4
  >
10
5
  <slot></slot>
11
6
  </span>
@@ -18,10 +13,6 @@ export default {
18
13
  inject: ['apiDropdownMenu'],
19
14
 
20
15
  computed: {
21
- dark() {
22
- return this.apiDropdownMenu().isDark;
23
- },
24
-
25
16
  headless() {
26
17
  return this.apiDropdownMenu().isHeadless;
27
18
  },
@@ -57,7 +57,7 @@ export default {
57
57
 
58
58
  mounted() {
59
59
  const trigger = {
60
- id: this.id,
60
+ id: this.$el.getAttribute('id'), // avoids issues with hydration
61
61
  el: this.$el,
62
62
  cancel: this.cancel,
63
63
  focus: this.focus,
@@ -89,9 +89,7 @@ export default {
89
89
  * errors related to adding the event listener.
90
90
  */
91
91
  addTriggerEvents() {
92
- this.trigger.addEventListener('click', (e) => {
93
- this.onClick(e);
94
- });
92
+ this.trigger.addEventListener('click', this.onClick);
95
93
  },
96
94
 
97
95
  init(e) {
@@ -19,7 +19,13 @@
19
19
  : null,
20
20
  ]"
21
21
  />
22
- <span :class="[headless ? 'form-feedback--text' : 'text-sm text-gray-500']">
22
+ <span
23
+ :class="[
24
+ headless
25
+ ? 'form-feedback--text'
26
+ : 'text-sm lowercase text-gray-500 first-letter:uppercase',
27
+ ]"
28
+ >
23
29
  <slot />
24
30
  </span>
25
31
  </div>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <component :is="as" :class="classes" class="form-group">
2
+ <component :is="as" :class="[headless ? 'form-control' : 'mt-3']">
3
3
  <slot></slot>
4
4
  </component>
5
5
  </template>
@@ -11,12 +11,10 @@ export default {
11
11
  type: String,
12
12
  default: 'div',
13
13
  },
14
- },
15
-
16
- data() {
17
- return {
18
- classes: {},
19
- };
14
+ headless: {
15
+ type: Boolean,
16
+ default: false,
17
+ },
20
18
  },
21
19
  };
22
20
  </script>
@@ -0,0 +1,22 @@
1
+ <template>
2
+ <label
3
+ :class="[
4
+ headless
5
+ ? 'form-label'
6
+ : 'mb-1 flex justify-between gap-3 text-sm font-semibold',
7
+ ]"
8
+ >
9
+ <slot />
10
+ </label>
11
+ </template>
12
+
13
+ <script>
14
+ export default {
15
+ props: {
16
+ headless: {
17
+ type: Boolean,
18
+ default: false,
19
+ },
20
+ },
21
+ };
22
+ </script>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <div class="grid grid-cols-1 md:grid-cols-2 md:gap-4">
3
+ <slot />
4
+ </div>
5
+ </template>
@@ -1,9 +1,9 @@
1
1
  <template>
2
2
  <input
3
3
  :class="classComputed"
4
- :value="value"
4
+ :value="modelValue"
5
5
  :disabled="disabled"
6
- v-on="listeners"
6
+ v-bind="attrsComputed"
7
7
  />
8
8
  </template>
9
9
 
@@ -12,10 +12,8 @@ import {
12
12
  formControlMixin,
13
13
  formControlStyleMixin,
14
14
  } from '../../../mixins/form-control';
15
-
16
15
  export default {
17
16
  mixins: [formControlMixin, formControlStyleMixin],
18
-
19
17
  data() {
20
18
  return {
21
19
  name: 'input',
@@ -30,7 +28,6 @@ input[type='date']::-webkit-calendar-picker-indicator {
30
28
  position: absolute;
31
29
  opacity: 0;
32
30
  } */
33
-
34
31
  input[type='number'] {
35
32
  appearance: textfield;
36
33
  }
@@ -4,8 +4,7 @@
4
4
  :class="classComputed"
5
5
  :value="value"
6
6
  :disabled="disabled"
7
- v-bind="$attrs"
8
- v-on="listeners"
7
+ v-bind="attrsComputed"
9
8
  />
10
9
  <div :class="classComputedWrapperIcon">
11
10
  <component :is="icon" class="h-5 w-5" />
@@ -2,21 +2,25 @@
2
2
  <div :class="classComputedWrapper">
3
3
  <input
4
4
  :class="classComputed"
5
- :value="value"
5
+ :value="modelValue"
6
6
  :disabled="disabled"
7
7
  :type="reveal ? 'text' : 'password'"
8
- v-bind="$attrs"
9
- v-on="listeners"
8
+ v-bind="attrsComputed"
10
9
  />
11
10
  <div :class="classComputedWrapperIcon">
12
- <VTButton v-show="value.length" variant="icon" @click="reveal = !reveal">
13
- <component :is="iconVisibility" class="h-5 w-5" />
11
+ <VTButton
12
+ v-show="modelValue.length"
13
+ variant="icon"
14
+ @click="reveal = !reveal"
15
+ >
16
+ <component :is="iconVisibility" class="h-5 w-5 text-gray-500" />
14
17
  </VTButton>
15
18
  </div>
16
19
  </div>
17
20
  </template>
18
21
 
19
22
  <script>
23
+ import { IconVisibilityOn, IconVisibilityOff } from '@veritree/icons';
20
24
  import { formControlIconMixin } from '../../../mixins/form-control-icon';
21
25
 
22
26
  export default {
@@ -24,6 +28,11 @@ export default {
24
28
 
25
29
  mixins: [formControlIconMixin],
26
30
 
31
+ components: {
32
+ IconVisibilityOn,
33
+ IconVisibilityOff,
34
+ },
35
+
27
36
  props: {
28
37
  iconPlacement: {
29
38
  type: String,
@@ -3,18 +3,15 @@
3
3
  :class="classComputed"
4
4
  :value="value"
5
5
  :disabled="disabled"
6
- v-on="listeners"
6
+ v-bind="attrsComputed"
7
7
  />
8
8
  </template>
9
9
 
10
10
  <script>
11
- import {
12
- formControlMixin,
13
- formControlStyleMixin,
14
- } from '../../../mixins/form-control';
11
+ import { formControlMixin } from '../../../mixins/form-control';
15
12
 
16
13
  export default {
17
- mixins: [formControlMixin, formControlStyleMixin],
14
+ mixins: [formControlMixin],
18
15
 
19
16
  data() {
20
17
  return {
@@ -1,7 +1,14 @@
1
1
  <template>
2
2
  <img
3
3
  :src="srcComputed"
4
- :class="{ 'animate-pulse': !isLoaded }"
4
+ :class="[
5
+ headless ? null : isLoaded ? null : 'animate-pulse',
6
+ headless
7
+ ? null
8
+ : hasObjectFit
9
+ ? `h-full w-full ${objectFitComputed}`
10
+ : null,
11
+ ]"
5
12
  v-bind="$attrs"
6
13
  @load="onLoad"
7
14
  @error="onError"
@@ -15,18 +22,26 @@ export default {
15
22
  name: 'VTImage',
16
23
 
17
24
  props: {
18
- src: {
19
- type: String,
20
- default: '',
25
+ headless: {
26
+ type: Boolean,
27
+ default: false,
21
28
  },
22
29
  cdnSrc: {
23
30
  type: [String, Object],
24
31
  default: null,
25
32
  },
33
+ objectFit: {
34
+ type: String,
35
+ default: null,
36
+ },
26
37
  placeholder: {
27
38
  type: String,
28
39
  default: `data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22600%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20%25%7Bw%7D%20%25%7Bh%7D%22%20preserveAspectRatio%3D%22none%22%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20style%3D%22fill%3A%23bbb%3B%22%3E%3C%2Frect%3E%3C%2Fsvg%3E`,
29
40
  },
41
+ src: {
42
+ type: String,
43
+ default: '',
44
+ },
30
45
  },
31
46
 
32
47
  data() {
@@ -45,17 +60,33 @@ export default {
45
60
  return this.placeholder;
46
61
  }
47
62
 
48
- if (this.cdnSrc) {
49
- return this.handleImageResizing(this.cdnSrc, this.$attrs.width);
63
+ if (this.src) {
64
+ return this.src.includes('cloudfront.net/')
65
+ ? handleImageResizing(this.src, this.$attrs.width)
66
+ : this.src;
50
67
  }
51
68
 
52
- if (this.src) {
53
- return this.src;
69
+ if (this.cdnSrc) {
70
+ return handleImageResizing(this.cdnSrc, this.$attrs.width);
54
71
  }
55
72
  }
56
73
 
57
74
  return null;
58
75
  },
76
+
77
+ hasObjectFit() {
78
+ return this.objectFit;
79
+ },
80
+
81
+ objectFitComputed() {
82
+ return this.hasObjectFit
83
+ ? this.objectFit === 'cover'
84
+ ? 'object-cover'
85
+ : this.objectFit === 'contain'
86
+ ? 'object-contain'
87
+ : null
88
+ : null;
89
+ },
59
90
  },
60
91
 
61
92
  methods: {
@@ -16,16 +16,39 @@ export default {
16
16
  provide() {
17
17
  return {
18
18
  apiListbox: () => {
19
+ const $mutable = {};
20
+
21
+ // Used to get and update the value computed
22
+ // that will then emit the value up to the
23
+ // parent component
24
+ Object.defineProperty($mutable, 'valueComputed', {
25
+ enumerable: true,
26
+ get: () => this.valueComputed,
27
+ set: (value) => (this.valueComputed = value),
28
+ });
29
+
30
+ /**
31
+ * This function registers a trigger by setting its value to the componentTrigger property of the current object.
32
+ * @param {VueComponent} trigger - The trigger to be registered.
33
+ */
19
34
  const registerTrigger = (trigger) => {
20
35
  if (!trigger) return;
21
36
  this.componentTrigger = trigger;
22
37
  };
23
38
 
39
+ /**
40
+ * Registers content to be used in the children components by setting its value to the componentContent property of the current object.
41
+ * @param {any} content - The content to be registered.
42
+ */
24
43
  const registerContent = (content) => {
25
44
  if (!content) return;
26
45
  this.componentContent = content;
27
46
  };
28
47
 
48
+ /**
49
+ * Registers search to be used in the children components by setting its value to the componentSearch property of the current object.
50
+ * @param {any} search - The search to be registered.
51
+ */
29
52
  const registerSearch = (search) => {
30
53
  if (!search) return;
31
54
  this.componentSearch = search;
@@ -52,8 +75,7 @@ export default {
52
75
  };
53
76
 
54
77
  const emit = (value) => {
55
- this.$emit('input', value);
56
- this.$emit('change', value);
78
+ this.valueComputed = value;
57
79
  };
58
80
 
59
81
  return {
@@ -63,8 +85,9 @@ export default {
63
85
  componentContent: this.componentContent,
64
86
  componentSearch: this.componentSearch,
65
87
  items: this.items,
88
+ multiple: this.multiple,
66
89
  search: this.search,
67
- selectedValue: this.value,
90
+ $mutable,
68
91
  registerTrigger,
69
92
  registerContent,
70
93
  registerSearch,
@@ -79,22 +102,55 @@ export default {
79
102
  },
80
103
 
81
104
  props: {
82
- value: {
83
- type: [String, Number, Object],
105
+ /**
106
+ * The value of the component. Can be a string, number, object, or array.
107
+ * @type {string|number|object|array}
108
+ * @default null
109
+ */
110
+ modelValue: {
111
+ type: [String, Number, Object, Array],
84
112
  default: null,
85
113
  },
114
+ /**
115
+ * Determines whether the button will use its default atomic style (tailwind) or its default class
116
+ * @type {boolean}
117
+ * @values
118
+ * - true: The button will have no default style and can be fully customized with a custom class
119
+ * - false: The button will use its default atomic style (tailwind) and can be further customized with additional classes
120
+ * @default null
121
+ */
86
122
  headless: {
87
123
  type: Boolean,
88
124
  default: false,
89
125
  },
126
+ /**
127
+ * The placement of the component relative to its trigger element.
128
+ * @type {string}
129
+ * @values 'top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end', 'right', 'right-start', 'right-end'
130
+ * @default 'bottom-start'
131
+ */
90
132
  placement: {
91
133
  type: String,
92
134
  default: 'bottom-start',
93
135
  },
136
+ /**
137
+ * Determines whether the component should be aligned to the right of its trigger element.
138
+ * @type {boolean}
139
+ * @default false
140
+ */
94
141
  right: {
95
142
  type: Boolean,
96
143
  default: false,
97
144
  },
145
+ /**
146
+ * Determines whether the component should allow multiple selections.
147
+ * @type {boolean}
148
+ * @default false
149
+ */
150
+ multiple: {
151
+ type: Boolean,
152
+ default: false,
153
+ },
98
154
  },
99
155
 
100
156
  data() {
@@ -103,6 +159,7 @@ export default {
103
159
  componentSearch: null,
104
160
  search: '',
105
161
  items: [],
162
+ itemsChecked: [],
106
163
  /**
107
164
  * Explaining the need for the floatingUiMinWidth data
108
165
  *
@@ -125,6 +182,16 @@ export default {
125
182
  id() {
126
183
  return `listbox-${this.componentId}`;
127
184
  },
185
+
186
+ valueComputed: {
187
+ get() {
188
+ return this.modelValue;
189
+ },
190
+ set(newValue) {
191
+ this.$emit('update:modelValue', newValue);
192
+ this.$emit('change', newValue);
193
+ },
194
+ },
128
195
  },
129
196
  };
130
197
  </script>
@@ -4,7 +4,6 @@
4
4
  :visible="visible"
5
5
  :headless="headless"
6
6
  :aria-activedescendant="activeDescedant"
7
- :portal-class="$vnode.data.staticClass"
8
7
  component="listbox"
9
8
  role="listbox"
10
9
  >
@@ -16,6 +16,16 @@
16
16
  @keydown.enter.prevent="onClick"
17
17
  @keydown.tab.prevent
18
18
  >
19
+ <span v-if="apiListbox().multiple">
20
+ <input
21
+ v-model="apiListbox().$mutable.valueComputed"
22
+ :id="`${id}-checkbox`"
23
+ :value="value"
24
+ type="checkbox"
25
+ @click.stop
26
+ @change="onChange"
27
+ />
28
+ </span>
19
29
  <slot></slot>
20
30
  <span v-if="wasSelected" class="ml-auto">
21
31
  <IconCheckOutline class="text-secondary-200 h-5 w-5" />
@@ -75,7 +85,7 @@ export default {
75
85
  wasSelected() {
76
86
  return (
77
87
  JSON.stringify(this.value) ===
78
- JSON.stringify(this.apiListbox().selectedValue)
88
+ JSON.stringify(this.apiListbox().$mutable.valueComputed)
79
89
  );
80
90
  },
81
91
  },