@veritree/ui 0.19.2-20 → 0.19.2-22

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 (40) hide show
  1. package/mixins/floating-ui-content.js +1 -22
  2. package/mixins/floating-ui-item.js +93 -46
  3. package/mixins/floating-ui.js +5 -12
  4. package/package.json +1 -1
  5. package/src/components/Alert/VTAlert.vue +55 -14
  6. package/src/components/Dialog/VTDialog.vue +13 -11
  7. package/src/components/Dialog/VTDialogClose.vue +13 -19
  8. package/src/components/Dialog/VTDialogContent.vue +19 -14
  9. package/src/components/Dialog/VTDialogFooter.vue +9 -14
  10. package/src/components/Dialog/VTDialogHeader.vue +11 -13
  11. package/src/components/Dialog/VTDialogMain.vue +6 -13
  12. package/src/components/Dialog/VTDialogOverlay.vue +9 -13
  13. package/src/components/Dialog/VTDialogTitle.vue +8 -5
  14. package/src/components/Disclosure/VTDisclosureHeader.vue +1 -1
  15. package/src/components/DropdownMenu/VTDropdownMenu.vue +19 -0
  16. package/src/components/DropdownMenu/VTDropdownMenuContent.vue +14 -6
  17. package/src/components/DropdownMenu/VTDropdownMenuDivider.vue +7 -16
  18. package/src/components/DropdownMenu/VTDropdownMenuItem.vue +3 -7
  19. package/src/components/DropdownMenu/VTDropdownMenuLabel.vue +1 -10
  20. package/src/components/DropdownMenu/VTDropdownMenuTrigger.vue +4 -9
  21. package/src/components/Listbox/VTListbox.vue +105 -14
  22. package/src/components/Listbox/VTListboxContent.vue +3 -7
  23. package/src/components/Listbox/VTListboxItem.vue +127 -6
  24. package/src/components/Listbox/VTListboxLabel.vue +3 -4
  25. package/src/components/Listbox/VTListboxList.vue +3 -24
  26. package/src/components/Listbox/VTListboxSearch.vue +60 -53
  27. package/src/components/Listbox/VTListboxTrigger.vue +12 -6
  28. package/src/components/Popover/VTPopover.vue +19 -0
  29. package/src/components/ProgressBar/VTProgressBar.vue +21 -3
  30. package/src/components/Skeleton/VTSkeleton.vue +11 -0
  31. package/src/components/Skeleton/VTSkeletonItem.vue +9 -0
  32. package/src/components/Tabs/VTTab.vue +13 -11
  33. package/src/components/Tooltip/VTTooltip.vue +65 -0
  34. package/src/components/Tooltip/VTTooltipContent.vue +59 -0
  35. package/src/components/Tooltip/VTTooltipTrigger.vue +98 -0
  36. package/src/components/Utils/FloatingUi.vue +52 -17
  37. package/src/components/Input/VTInput.vue +0 -82
  38. package/src/components/Input/VTInputDate.vue +0 -36
  39. package/src/components/Input/VTInputFile.vue +0 -60
  40. package/src/components/Input/VTInputUpload.vue +0 -54
@@ -2,10 +2,12 @@
2
2
  <component
3
3
  :is="as"
4
4
  :id="id"
5
- :class="{
6
- 'Dialog-header': headless,
7
- 'mb-8 flex justify-between gap-x-3 items-center w-full': !headless,
8
- }"
5
+ :class="[
6
+ headless
7
+ ? 'dialog-header'
8
+ : '-mx-4 -mt-4 flex items-center justify-between gap-x-3 p-4 md:-mx-6 md:-mt-6 md:p-6',
9
+ ]"
10
+ @click.stop
9
11
  >
10
12
  <slot></slot>
11
13
  </component>
@@ -18,6 +20,10 @@ export default {
18
20
  inject: ['apiDialog'],
19
21
 
20
22
  props: {
23
+ headless: {
24
+ type: Boolean,
25
+ default: false,
26
+ },
21
27
  as: {
22
28
  type: String,
23
29
  default: 'header',
@@ -25,16 +31,8 @@ export default {
25
31
  },
26
32
 
27
33
  computed: {
28
- dark() {
29
- return this.apiDialog().isDark;
30
- },
31
-
32
- headless() {
33
- return this.apiDialog().isHeadless;
34
- },
35
-
36
34
  id() {
37
- return `${this.apiDialog().id}-header`;
35
+ return `dialog-header-${this.apiDialog().componentId}`;
38
36
  },
39
37
  },
40
38
 
@@ -2,10 +2,7 @@
2
2
  <component
3
3
  :is="as"
4
4
  :id="id"
5
- :class="{
6
- 'Dialog-body': headless,
7
- 'flex-1 w-full h-full overflow-y-auto': !headless,
8
- }"
5
+ :class="[headless ? 'dialog-body' : 'h-full flex-1 overflow-y-auto']"
9
6
  >
10
7
  <slot></slot>
11
8
  </component>
@@ -18,6 +15,10 @@ export default {
18
15
  inject: ['apiDialog'],
19
16
 
20
17
  props: {
18
+ headless: {
19
+ type: Boolean,
20
+ default: false,
21
+ },
21
22
  as: {
22
23
  type: String,
23
24
  default: 'main',
@@ -25,16 +26,8 @@ export default {
25
26
  },
26
27
 
27
28
  computed: {
28
- dark() {
29
- return this.apiDialog().isDark;
30
- },
31
-
32
- headless() {
33
- return this.apiDialog().isHeadless;
34
- },
35
-
36
29
  id() {
37
- return `${this.apiDialog().id}-desc`;
30
+ return `dialog-main-${this.apiDialog().componentId}`;
38
31
  },
39
32
  },
40
33
 
@@ -3,11 +3,8 @@
3
3
  <div
4
4
  v-if="visible"
5
5
  :id="id"
6
- :class="{
7
- 'Dialog-overlay': headless,
8
- 'bg-primary-200/80 fixed inset-0': !headless,
9
- }"
10
- ></div>
6
+ :class="[headless ? 'dialog-overlay' : 'bg-primary-200/80 fixed inset-0']"
7
+ />
11
8
  </FadeInOut>
12
9
  </template>
13
10
 
@@ -23,6 +20,13 @@ export default {
23
20
 
24
21
  inject: ['apiDialog'],
25
22
 
23
+ props: {
24
+ headless: {
25
+ type: Boolean,
26
+ default: false,
27
+ },
28
+ },
29
+
26
30
  data() {
27
31
  return {
28
32
  visible: false,
@@ -33,14 +37,6 @@ export default {
33
37
  id() {
34
38
  return `dialog-overlay-${this.apiDialog().componentId}`;
35
39
  },
36
-
37
- dark() {
38
- return this.apiDialog().isDark;
39
- },
40
-
41
- headless() {
42
- return this.apiDialog().isHeadless;
43
- },
44
40
  },
45
41
 
46
42
  mounted() {
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <span
3
3
  :id="id"
4
- :class="{ 'Dialog-header': headless, 'text-2xl font-semibold': !headless }"
4
+ :class="[headless ? 'dialog-header' : 'text-2xl font-semibold']"
5
5
  >
6
6
  <slot></slot
7
7
  ></span>
@@ -13,13 +13,16 @@ export default {
13
13
 
14
14
  inject: ['apiDialog'],
15
15
 
16
- computed: {
17
- headless() {
18
- return this.apiDialog().isHeadless;
16
+ props: {
17
+ headless: {
18
+ type: Boolean,
19
+ default: false,
19
20
  },
21
+ },
20
22
 
23
+ computed: {
21
24
  id() {
22
- return `${this.apiDialog().id}-title`;
25
+ return `dialog-overlay-${this.apiDialog().componentId}`;
23
26
  },
24
27
  },
25
28
  };
@@ -4,7 +4,7 @@
4
4
  :class="[
5
5
  headless
6
6
  ? 'details-header'
7
- : 'flex cursor-pointer justify-between gap-3 text-body font-semibold',
7
+ : 'text-body flex cursor-pointer justify-between gap-3 font-semibold',
8
8
  ]"
9
9
  :aria-controls="ariaControls"
10
10
  :aria-expanded="String(ariaExpanded)"
@@ -56,12 +56,31 @@ export default {
56
56
  type: Boolean,
57
57
  default: false,
58
58
  },
59
+ placement: {
60
+ type: String,
61
+ default: 'bottom-start',
62
+ },
59
63
  },
60
64
 
61
65
  data() {
62
66
  return {
63
67
  componentId: genId(),
64
68
  items: [],
69
+ /**
70
+ * Explaining the need for the floatingUiMinWidth data
71
+ *
72
+ * The floating ui is a result of two items:
73
+ *
74
+ * 1. Trigger: the action button
75
+ * 2. Content: the popper/wrapper that appears after triggering the action button
76
+ *
77
+ * By default, the content will match the triggers width.
78
+ * The problem with this is that the trigger width
79
+ * might be too small causing the content to not fit
80
+ * what is inside it properly. So, to avoid this,
81
+ * a min width is needed.
82
+ */
83
+ floatingUiMinWidth: 200,
65
84
  };
66
85
  },
67
86
 
@@ -1,10 +1,9 @@
1
1
  <template>
2
2
  <FloatingUi
3
- :visible="visible"
4
3
  :id="id"
4
+ :visible="visible"
5
5
  :headless="headless"
6
- :class="{ 'dropdown-menu-content': headless }"
7
- :floating-ui-class="floatingUiClass"
6
+ component="dropdown"
8
7
  >
9
8
  <slot></slot>
10
9
  </FloatingUi>
@@ -16,6 +15,8 @@ import { floatingUiContentMixin } from '../../../mixins/floating-ui-content';
16
15
  export default {
17
16
  name: 'VTDropdownMenuContent',
18
17
 
18
+ inheritAttrs: false,
19
+
19
20
  mixins: [floatingUiContentMixin],
20
21
 
21
22
  inject: ['apiDropdownMenu'],
@@ -39,13 +40,20 @@ export default {
39
40
  id: this.id,
40
41
  hide: this.hide,
41
42
  show: this.show,
42
- getMousemove: this.getMousemove,
43
- setMousemove: this.setMousemove,
44
- unsetMousemove: this.unsetMousemove,
45
43
  setActiveDescedant: this.setActiveDescedant,
46
44
  };
47
45
 
48
46
  this.apiDropdownMenu().registerContent(content);
49
47
  },
48
+
49
+ methods: {
50
+ hidden() {
51
+ this.$emit('hidden');
52
+ },
53
+
54
+ shown() {
55
+ this.$emit('shown');
56
+ },
57
+ },
50
58
  };
51
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
  };
@@ -9,8 +9,8 @@
9
9
  class="-mx-3"
10
10
  role="menuitem"
11
11
  @click.stop.prevent="onClick"
12
- @keydown.down.prevent="focusPreviousItem"
13
- @keydown.up.prevent="focusNextItem"
12
+ @keydown.down.prevent="focusNextItem"
13
+ @keydown.up.prevent="focusPreviousItem"
14
14
  @keydown.home.prevent="focusFirstItem"
15
15
  @keydown.end.prevent="focusLastItem"
16
16
  @keydown.esc.prevent="onKeyEsc"
@@ -51,11 +51,7 @@ export default {
51
51
 
52
52
  computed: {
53
53
  as() {
54
- return this.href
55
- ? 'a'
56
- : this.to
57
- ? resolveComponent('NuxtLink')
58
- : 'button';
54
+ return this.href ? 'a' : this.to ? 'NuxtLink' : 'button';
59
55
  },
60
56
  },
61
57
  };
@@ -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
  },
@@ -24,14 +24,13 @@ export default {
24
24
  hasPopup: false,
25
25
  controls: null,
26
26
  trigger: null,
27
- id: null,
28
27
  };
29
28
  },
30
29
 
31
30
  computed: {
32
- // id() {
33
- // return `dropdown-menu-trigger-${this.apiDropdownMenu().id}`;
34
- // },
31
+ id() {
32
+ return `dropdown-menu-trigger-${this.apiDropdownMenu().id}`;
33
+ },
35
34
 
36
35
  componentContent() {
37
36
  return this.apiDropdownMenu().componentContent;
@@ -57,8 +56,6 @@ export default {
57
56
  },
58
57
 
59
58
  mounted() {
60
- this.id = `dropdown-menu-trigger-${this.apiDropdownMenu().id}`;
61
-
62
59
  const trigger = {
63
60
  id: this.id,
64
61
  el: this.$el,
@@ -92,9 +89,7 @@ export default {
92
89
  * errors related to adding the event listener.
93
90
  */
94
91
  addTriggerEvents() {
95
- this.trigger.addEventListener('click', (e) => {
96
- this.onClick(e);
97
- });
92
+ this.trigger.addEventListener('click', this.onClick);
98
93
  },
99
94
 
100
95
  init(e) {
@@ -16,26 +16,44 @@ 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
- this.search = search;
54
+ this.componentSearch = search;
32
55
  };
33
56
 
34
- // const registerList = (list) => {
35
- // if (!list) return;
36
- // this.list = list;
37
- // };
38
-
39
57
  const registerItem = (item) => {
40
58
  if (!item) return;
41
59
  this.items.push(item);
@@ -46,9 +64,18 @@ export default {
46
64
  this.items.splice(index, 1);
47
65
  };
48
66
 
67
+ const pushSearch = (key) => {
68
+ key === 'Backspace' && this.search.length
69
+ ? (this.search = this.search.slice(0, -1))
70
+ : (this.search += key);
71
+ };
72
+
73
+ const clearSearch = () => {
74
+ this.search = '';
75
+ };
76
+
49
77
  const emit = (value) => {
50
- this.$emit('update:modelValue', value);
51
- this.$emit('change', value);
78
+ this.valueComputed = value;
52
79
  };
53
80
 
54
81
  return {
@@ -56,14 +83,18 @@ export default {
56
83
  component: this.component,
57
84
  componentTrigger: this.componentTrigger,
58
85
  componentContent: this.componentContent,
86
+ componentSearch: this.componentSearch,
59
87
  items: this.items,
88
+ multiple: this.multiple,
60
89
  search: this.search,
90
+ $mutable,
61
91
  registerTrigger,
62
92
  registerContent,
63
93
  registerSearch,
64
- // registerList,
65
94
  registerItem,
66
95
  unregisterItem,
96
+ pushSearch,
97
+ clearSearch,
67
98
  emit,
68
99
  };
69
100
  },
@@ -71,19 +102,52 @@ export default {
71
102
  },
72
103
 
73
104
  props: {
74
- value: {
75
- 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],
76
112
  default: null,
77
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
+ */
78
122
  headless: {
79
123
  type: Boolean,
80
124
  default: false,
81
125
  },
82
- dark: {
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
+ */
132
+ placement: {
133
+ type: String,
134
+ default: 'bottom-start',
135
+ },
136
+ /**
137
+ * Determines whether the component should be aligned to the right of its trigger element.
138
+ * @type {boolean}
139
+ * @default false
140
+ */
141
+ right: {
83
142
  type: Boolean,
84
143
  default: false,
85
144
  },
86
- right: {
145
+ /**
146
+ * Determines whether the component should allow multiple selections.
147
+ * @type {boolean}
148
+ * @default false
149
+ */
150
+ multiple: {
87
151
  type: Boolean,
88
152
  default: false,
89
153
  },
@@ -92,8 +156,25 @@ export default {
92
156
  data() {
93
157
  return {
94
158
  componentId: genId(),
95
- search: null,
159
+ componentSearch: null,
160
+ search: '',
96
161
  items: [],
162
+ itemsChecked: [],
163
+ /**
164
+ * Explaining the need for the floatingUiMinWidth data
165
+ *
166
+ * The floating ui is a result of two items:
167
+ *
168
+ * 1. Trigger: the action button
169
+ * 2. Content: the popper/wrapper that appears after triggering the action button
170
+ *
171
+ * By default, the content will match the triggers width.
172
+ * The problem with this is that the trigger width
173
+ * might be too small causing the content to not fit
174
+ * what is inside it properly. So, to avoid this,
175
+ * a min width is needed.
176
+ */
177
+ floatingUiMinWidth: 200,
97
178
  };
98
179
  },
99
180
 
@@ -101,6 +182,16 @@ export default {
101
182
  id() {
102
183
  return `listbox-${this.componentId}`;
103
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
+ },
104
195
  },
105
196
  };
106
197
  </script>
@@ -1,11 +1,10 @@
1
1
  <template>
2
2
  <FloatingUi
3
- :visible="visible"
4
3
  :id="id"
5
- :aria-activedescendant="activeDescedant"
4
+ :visible="visible"
6
5
  :headless="headless"
7
- :class="{ 'listbox-content': headless }"
8
- :floating-ui-class="floatingUiClass"
6
+ :aria-activedescendant="activeDescedant"
7
+ component="listbox"
9
8
  role="listbox"
10
9
  >
11
10
  <slot></slot>
@@ -47,9 +46,6 @@ export default {
47
46
  id: this.id,
48
47
  show: this.show,
49
48
  hide: this.hide,
50
- getMousemove: this.getMousemove,
51
- setMousemove: this.setMousemove,
52
- unsetMousemove: this.unsetMousemove,
53
49
  setActiveDescedant: this.setActiveDescedant,
54
50
  };
55
51