@veritree/ui 0.19.2 → 0.20.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 (78) hide show
  1. package/index.js +64 -68
  2. package/mixins/floating-ui-content.js +81 -0
  3. package/mixins/floating-ui-item.js +266 -0
  4. package/mixins/floating-ui.js +67 -0
  5. package/mixins/form-control-icon.js +53 -0
  6. package/mixins/form-control.js +71 -0
  7. package/nuxt.js +30 -23
  8. package/package.json +9 -4
  9. package/src/components/Alert/VTAlert.vue +55 -14
  10. package/src/components/Avatar/VTAvatar.vue +32 -29
  11. package/src/components/Button/VTButton.vue +9 -6
  12. package/src/components/Checkbox/VTCheckbox.vue +134 -0
  13. package/src/components/Checkbox/VTCheckboxLabel.vue +3 -0
  14. package/src/components/Checkbox/VTCheckboxText.vue +20 -0
  15. package/src/components/Dialog/VTDialog.vue +22 -32
  16. package/src/components/Dialog/VTDialogClose.vue +19 -25
  17. package/src/components/Dialog/VTDialogContent.vue +24 -19
  18. package/src/components/Dialog/VTDialogFooter.vue +11 -16
  19. package/src/components/Dialog/VTDialogHeader.vue +16 -18
  20. package/src/components/Dialog/VTDialogMain.vue +11 -18
  21. package/src/components/Dialog/VTDialogOverlay.vue +14 -18
  22. package/src/components/Dialog/VTDialogTitle.vue +10 -7
  23. package/src/components/Disclosure/VTDisclosureContent.vue +1 -1
  24. package/src/components/Disclosure/VTDisclosureDetails.vue +1 -1
  25. package/src/components/Disclosure/VTDisclosureHeader.vue +2 -2
  26. package/src/components/Disclosure/VTDisclosureIcon.vue +1 -1
  27. package/src/components/Drawer/VTDrawer.vue +14 -16
  28. package/src/components/Drawer/VTDrawerClose.vue +9 -9
  29. package/src/components/Drawer/VTDrawerContent.vue +8 -8
  30. package/src/components/Drawer/VTDrawerFooter.vue +3 -3
  31. package/src/components/Drawer/VTDrawerHeader.vue +4 -4
  32. package/src/components/Drawer/VTDrawerMain.vue +5 -5
  33. package/src/components/Drawer/VTDrawerOverlay.vue +6 -6
  34. package/src/components/Drawer/VTDrawerTitle.vue +5 -5
  35. package/src/components/DropdownMenu/VTDropdownMenu.vue +45 -28
  36. package/src/components/DropdownMenu/VTDropdownMenuContent.vue +29 -64
  37. package/src/components/DropdownMenu/VTDropdownMenuDivider.vue +8 -14
  38. package/src/components/DropdownMenu/VTDropdownMenuItem.vue +11 -124
  39. package/src/components/DropdownMenu/VTDropdownMenuLabel.vue +3 -12
  40. package/src/components/DropdownMenu/VTDropdownMenuTrigger.vue +91 -121
  41. package/src/components/Form/VTFormFeedback.vue +39 -22
  42. package/src/components/Form/VTFormGroup.vue +5 -7
  43. package/src/components/Form/VTFormLabel.vue +22 -0
  44. package/src/components/Form/VTFormRow.vue +5 -0
  45. package/src/components/Form/VTInput.vue +40 -0
  46. package/src/components/Form/VTInputIcon.vue +35 -0
  47. package/src/components/Form/VTInputPassword.vue +55 -0
  48. package/src/components/Form/VTTextarea.vue +22 -0
  49. package/src/components/Listbox/VTListbox.vue +122 -50
  50. package/src/components/Listbox/VTListboxContent.vue +20 -116
  51. package/src/components/Listbox/VTListboxItem.vue +115 -166
  52. package/src/components/Listbox/VTListboxLabel.vue +3 -14
  53. package/src/components/Listbox/VTListboxList.vue +10 -40
  54. package/src/components/Listbox/VTListboxSearch.vue +76 -68
  55. package/src/components/Listbox/VTListboxTrigger.vue +75 -86
  56. package/src/components/Popover/VTPopover.vue +42 -29
  57. package/src/components/Popover/VTPopoverContent.vue +24 -59
  58. package/src/components/Popover/VTPopoverDivider.vue +4 -11
  59. package/src/components/Popover/VTPopoverItem.vue +21 -14
  60. package/src/components/Popover/VTPopoverTrigger.vue +126 -21
  61. package/src/components/ProgressBar/VTProgressBar.vue +21 -3
  62. package/src/components/Skeleton/VTSkeleton.vue +11 -0
  63. package/src/components/Skeleton/VTSkeletonItem.vue +9 -0
  64. package/src/components/Tabs/VTTab.vue +4 -3
  65. package/src/components/Tabs/VTTabGroup.vue +9 -7
  66. package/src/components/Tabs/VTTabPanel.vue +4 -5
  67. package/src/components/Tooltip/VTTooltip.vue +65 -0
  68. package/src/components/Tooltip/VTTooltipContent.vue +59 -0
  69. package/src/components/Tooltip/VTTooltipTrigger.vue +98 -0
  70. package/src/components/Transitions/FadeInOut.vue +2 -2
  71. package/src/components/Utils/FloatingUi.vue +93 -0
  72. package/package-lock.json +0 -13
  73. package/src/components/Input/VTInput.vue +0 -82
  74. package/src/components/Input/VTInputDate.vue +0 -36
  75. package/src/components/Input/VTInputFile.vue +0 -60
  76. package/src/components/Input/VTInputUpload.vue +0 -54
  77. package/src/components/Modal/VTModal.vue +0 -69
  78. package/src/utils/genId.js +0 -13
@@ -1,39 +1,57 @@
1
1
  <template>
2
- <div :class="{ Listbox: headless, relative: !headless, 'z-20': active }">
2
+ <div :class="{ listbox: headless }">
3
3
  <slot></slot>
4
4
  </div>
5
5
  </template>
6
6
 
7
7
  <script>
8
- import { genId } from "../../utils/ids";
8
+ import { floatingUiMixin } from '../../../mixins/floating-ui';
9
+ import { genId } from '../../utils/ids';
9
10
 
10
11
  export default {
11
- name: "VTListbox",
12
+ name: 'VTListbox',
13
+
14
+ mixins: [floatingUiMixin],
12
15
 
13
16
  provide() {
14
17
  return {
15
- api: () => {
16
- const { dark: isDark, right: isRight } = this;
17
- const { id, listbox, trigger, content, search, list, items } = this;
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
- this.trigger = trigger;
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
- this.content = content;
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;
32
- };
33
-
34
- const registerList = (list) => {
35
- if (!list) return;
36
- this.list = list;
54
+ this.componentSearch = search;
37
55
  };
38
56
 
39
57
  const registerItem = (item) => {
@@ -46,27 +64,37 @@ 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("input", value);
51
- this.$emit("change", value);
78
+ this.valueComputed = value;
52
79
  };
53
80
 
54
81
  return {
55
- id,
56
- isDark,
57
- isRight,
58
- listbox,
59
- trigger,
60
- search,
61
- content,
62
- list,
63
- items,
82
+ id: this.componentId,
83
+ component: this.component,
84
+ componentTrigger: this.componentTrigger,
85
+ componentContent: this.componentContent,
86
+ componentSearch: this.componentSearch,
87
+ items: this.items,
88
+ multiple: this.multiple,
89
+ search: this.search,
90
+ $mutable,
64
91
  registerTrigger,
65
92
  registerContent,
66
93
  registerSearch,
67
- registerList,
68
94
  registerItem,
69
95
  unregisterItem,
96
+ pushSearch,
97
+ clearSearch,
70
98
  emit,
71
99
  };
72
100
  },
@@ -74,19 +102,52 @@ export default {
74
102
  },
75
103
 
76
104
  props: {
77
- value: {
78
- 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],
79
112
  default: null,
80
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
+ */
81
122
  headless: {
82
123
  type: Boolean,
83
124
  default: false,
84
125
  },
85
- 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: {
86
142
  type: Boolean,
87
143
  default: false,
88
144
  },
89
- right: {
145
+ /**
146
+ * Determines whether the component should allow multiple selections.
147
+ * @type {boolean}
148
+ * @default false
149
+ */
150
+ multiple: {
90
151
  type: Boolean,
91
152
  default: false,
92
153
  },
@@ -94,31 +155,42 @@ export default {
94
155
 
95
156
  data() {
96
157
  return {
97
- id: `listbox-${genId()}`,
98
- listbox: null,
99
- trigger: null,
100
- content: null,
101
- search: null,
102
- list: null,
158
+ componentId: genId(),
159
+ componentSearch: null,
160
+ search: '',
103
161
  items: [],
104
- active: false,
105
- };
106
- },
107
-
108
- mounted() {
109
- this.listbox = {
110
- setActive: this.setActive,
111
- clearActive: this.clearActive,
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,
112
178
  };
113
179
  },
114
180
 
115
- methods: {
116
- setActive() {
117
- this.active = true;
181
+ computed: {
182
+ id() {
183
+ return `listbox-${this.componentId}`;
118
184
  },
119
185
 
120
- clearActive() {
121
- this.active = null;
186
+ valueComputed: {
187
+ get() {
188
+ return this.modelValue;
189
+ },
190
+ set(newValue) {
191
+ this.$emit('update:modelValue', newValue);
192
+ this.$emit('change', newValue);
193
+ },
122
194
  },
123
195
  },
124
196
  };
@@ -1,95 +1,43 @@
1
1
  <template>
2
- <transition
3
- enter-active-class="duration-200 ease-out"
4
- enter-class="translate-y-[15px] opacity-0"
5
- enter-to-class="translate-y-0 opacity-100"
6
- leave-active-class="duration-200 ease-in"
7
- leave-class="translate-y-0 opacity-100"
8
- leave-to-class="translate-y-[15px] opacity-0"
9
- @after-leave="hide"
2
+ <FloatingUi
3
+ :id="id"
4
+ :visible="visible"
5
+ :headless="headless"
6
+ :aria-activedescendant="activeDescedant"
7
+ component="listbox"
8
+ role="listbox"
10
9
  >
11
- <div
12
- v-if="visible"
13
- :id="id"
14
- :aria-activedescendant="activeDescedant"
15
- :class="{
16
- MenuList: headless,
17
- 'absolute z-10 grid w-full min-w-[222px] overflow-hidden rounded-md py-2 px-3':
18
- !headless,
19
- 'border-gray-100 bg-white shadow-300': !dark && !headless,
20
- 'bg-forest-default border border-solid border-gray-700 shadow-gray-700':
21
- dark && !headless,
22
- 'left-0': !right && !headless,
23
- 'right-0': right && !headless,
24
- 'top-full mt-3': isTop && !headless,
25
- 'bottom-full mb-3': isBottom && !headless,
26
- }"
27
- role="listbox"
28
- >
29
- <slot></slot>
30
- </div>
31
- </transition>
10
+ <slot></slot>
11
+ </FloatingUi>
32
12
  </template>
33
13
 
34
14
  <script>
35
- import { genId } from "../../utils/ids";
15
+ import { floatingUiContentMixin } from '../../../mixins/floating-ui-content';
36
16
 
37
17
  export default {
38
- name: "VTListboxContent",
18
+ name: 'VTListboxContent',
39
19
 
40
- inject: ["api"],
20
+ mixins: [floatingUiContentMixin],
41
21
 
42
- props: {
43
- headless: {
44
- type: Boolean,
45
- default: false,
46
- },
47
- bottom: {
48
- type: Boolean,
49
- default: false,
50
- },
51
- top: {
52
- type: Boolean,
53
- default: true,
54
- },
55
- },
22
+ inject: ['apiListbox'],
56
23
 
57
24
  data() {
58
25
  return {
59
- id: `listboxcontent-${genId()}`,
60
26
  activeDescedant: null,
61
- visible: false,
62
27
  };
63
28
  },
64
29
 
65
30
  computed: {
66
- dark() {
67
- return this.api().isDark;
68
- },
69
-
70
- right() {
71
- return this.api().isRight;
72
- },
73
-
74
- listbox() {
75
- return this.api().listbox;
76
- },
77
-
78
- trigger() {
79
- return this.api().trigger;
31
+ id() {
32
+ return `listbox-content-${this.apiListbox().id}`;
80
33
  },
81
34
 
82
- search() {
83
- return this.api().search;
35
+ component() {
36
+ return this.apiListbox().component;
84
37
  },
85
38
 
86
- // directions
87
- isTop() {
88
- return this.top && !this.bottom;
89
- },
90
-
91
- isBottom() {
92
- return this.bottom;
39
+ componentTrigger() {
40
+ return this.apiListbox().componentTrigger;
93
41
  },
94
42
  },
95
43
 
@@ -101,51 +49,7 @@ export default {
101
49
  setActiveDescedant: this.setActiveDescedant,
102
50
  };
103
51
 
104
- this.api().registerContent(content);
105
-
106
- // TODO: Create a directive or mixin for this
107
- document.addEventListener("click", (e) => {
108
- e.stopPropagation();
109
- if (this.visible && !this.$el.contains(e.target)) this.trigger.onClick();
110
- });
111
- },
112
-
113
- destroyed() {
114
- // TODO: Create a directive or mixin for this
115
- document.removeEventListener("click", this.trigger.onClick());
116
- },
117
-
118
- methods: {
119
- show() {
120
- if (this.visible) return;
121
-
122
- this.visible = true;
123
-
124
- this.$nextTick(() => {
125
- this.listbox.setActive();
126
-
127
- if (this.search) this.search.el.focus();
128
- });
129
- },
130
-
131
- hide() {
132
- if (!this.visible) return;
133
-
134
- this.visible = false;
135
-
136
- this.$nextTick(() => {
137
- this.trigger.focus();
138
-
139
- setTimeout(() => {
140
- this.listbox.clearActive();
141
- this.trigger.contract();
142
- }, 100);
143
- });
144
- },
145
-
146
- setActiveDescedant(id) {
147
- this.activeDescedant = id;
148
- },
52
+ this.apiListbox().registerContent(content);
149
53
  },
150
54
  };
151
55
  </script>