@veritree/ui 0.21.1-8 → 0.22.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 (74) hide show
  1. package/.prettierrc +2 -1
  2. package/index.js +64 -68
  3. package/mixins/floating-ui-content.js +6 -6
  4. package/mixins/floating-ui-item.js +266 -0
  5. package/mixins/floating-ui.js +11 -14
  6. package/mixins/form-control-icon.js +53 -0
  7. package/mixins/form-control.js +71 -0
  8. package/nuxt.js +30 -23
  9. package/package.json +8 -4
  10. package/src/components/Alert/VTAlert.vue +55 -14
  11. package/src/components/Button/VTButton.vue +20 -7
  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 +6 -15
  28. package/src/components/Drawer/VTDrawerClose.vue +5 -5
  29. package/src/components/Drawer/VTDrawerContent.vue +9 -9
  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 +23 -5
  36. package/src/components/DropdownMenu/VTDropdownMenuContent.vue +15 -3
  37. package/src/components/DropdownMenu/VTDropdownMenuDivider.vue +7 -16
  38. package/src/components/DropdownMenu/VTDropdownMenuItem.vue +10 -148
  39. package/src/components/DropdownMenu/VTDropdownMenuLabel.vue +1 -10
  40. package/src/components/DropdownMenu/VTDropdownMenuTrigger.vue +1 -3
  41. package/src/components/Form/VTFormFeedback.vue +11 -5
  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 +30 -42
  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 +105 -16
  50. package/src/components/Listbox/VTListboxContent.vue +3 -10
  51. package/src/components/Listbox/VTListboxItem.vue +111 -175
  52. package/src/components/Listbox/VTListboxLabel.vue +3 -4
  53. package/src/components/Listbox/VTListboxList.vue +3 -35
  54. package/src/components/Listbox/VTListboxSearch.vue +69 -62
  55. package/src/components/Listbox/VTListboxTrigger.vue +13 -40
  56. package/src/components/Popover/VTPopover.vue +19 -0
  57. package/src/components/Popover/VTPopoverItem.vue +6 -2
  58. package/src/components/ProgressBar/VTProgressBar.vue +21 -3
  59. package/src/components/Skeleton/VTSkeleton.vue +11 -0
  60. package/src/components/Skeleton/VTSkeletonItem.vue +9 -0
  61. package/src/components/Tabs/VTTab.vue +4 -3
  62. package/src/components/Tabs/VTTabGroup.vue +9 -7
  63. package/src/components/Tabs/VTTabPanel.vue +4 -5
  64. package/src/components/Tooltip/VTTooltip.vue +65 -0
  65. package/src/components/Tooltip/VTTooltipContent.vue +59 -0
  66. package/src/components/Tooltip/VTTooltipTrigger.vue +98 -0
  67. package/src/components/Transitions/FadeInOut.vue +2 -2
  68. package/src/components/Utils/FloatingUi.vue +56 -24
  69. package/src/components/Input/VTInput.vue +0 -82
  70. package/src/components/Input/VTInputDate.vue +0 -36
  71. package/src/components/Input/VTInputFile.vue +0 -60
  72. package/src/components/Input/VTInputUpload.vue +0 -54
  73. package/src/components/Modal/VTModal.vue +0 -69
  74. package/src/utils/genId.js +0 -13
@@ -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);
@@ -16,8 +16,6 @@ export default {
16
16
  provide() {
17
17
  return {
18
18
  apiDropdownMenu: () => {
19
- const { dark: isDark, headless: isHeadless } = this;
20
-
21
19
  const registerTrigger = (trigger) => {
22
20
  if (!trigger) return;
23
21
  this.componentTrigger = trigger;
@@ -33,8 +31,9 @@ export default {
33
31
  this.items.push(item);
34
32
  };
35
33
 
36
- const unregisterItems = () => {
37
- this.items = [];
34
+ const unregisterItem = (id) => {
35
+ const index = this.items.findIndex((item) => item.id === id);
36
+ this.items.splice(index, 1);
38
37
  };
39
38
 
40
39
  return {
@@ -46,7 +45,7 @@ export default {
46
45
  registerTrigger,
47
46
  registerContent,
48
47
  registerItem,
49
- unregisterItems,
48
+ unregisterItem,
50
49
  };
51
50
  },
52
51
  };
@@ -57,12 +56,31 @@ export default {
57
56
  type: Boolean,
58
57
  default: false,
59
58
  },
59
+ placement: {
60
+ type: String,
61
+ default: 'bottom-start',
62
+ },
60
63
  },
61
64
 
62
65
  data() {
63
66
  return {
64
67
  componentId: genId(),
65
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,
66
84
  };
67
85
  },
68
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,9 +40,20 @@ export default {
39
40
  id: this.id,
40
41
  hide: this.hide,
41
42
  show: this.show,
43
+ setActiveDescedant: this.setActiveDescedant,
42
44
  };
43
45
 
44
46
  this.apiDropdownMenu().registerContent(content);
45
47
  },
48
+
49
+ methods: {
50
+ hidden() {
51
+ this.$emit('hidden');
52
+ },
53
+
54
+ shown() {
55
+ this.$emit('shown');
56
+ },
57
+ },
46
58
  };
47
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
  };
@@ -3,34 +3,14 @@
3
3
  :is="as"
4
4
  :id="id"
5
5
  :to="to"
6
- :class="[
7
- // default styles
8
- headless
9
- ? 'dropdown-menu-item'
10
- : 'relative z-10 -mx-3 flex items-center gap-2 px-3 py-2 text-inherit no-underline hover:bg-secondary-200/10',
11
- // disabled state styles
12
- headless
13
- ? disabled
14
- ? 'listbox-item--disabled'
15
- : null
16
- : disabled
17
- ? 'pointer-events-none opacity-75'
18
- : null,
19
- // selected state styles
20
- headless
21
- ? selected
22
- ? 'lisbox-item--selected'
23
- : null
24
- : selected
25
- ? 'bg-secondary-200/10'
26
- : null,
27
- ]"
28
- :tabindex="tabIndex"
6
+ :class="classComputed"
29
7
  :aria-disabled="disabled"
8
+ :tabindex="tabIndex"
9
+ class="-mx-3"
30
10
  role="menuitem"
31
11
  @click.stop.prevent="onClick"
32
- @keydown.down.prevent="focusPreviousItem"
33
- @keydown.up.prevent="focusNextItem"
12
+ @keydown.down.prevent="focusNextItem"
13
+ @keydown.up.prevent="focusPreviousItem"
34
14
  @keydown.home.prevent="focusFirstItem"
35
15
  @keydown.end.prevent="focusLastItem"
36
16
  @keydown.esc.prevent="onKeyEsc"
@@ -42,11 +22,13 @@
42
22
  </template>
43
23
 
44
24
  <script>
45
- import { genId } from '../../utils/ids';
25
+ import { floatingUiItemMixin } from '../../../mixins/floating-ui-item';
46
26
 
47
27
  export default {
48
28
  name: 'VTDropdownMenuItem',
49
29
 
30
+ mixins: [floatingUiItemMixin],
31
+
50
32
  inject: ['apiDropdownMenu'],
51
33
 
52
34
  props: {
@@ -58,139 +40,19 @@ export default {
58
40
  type: String,
59
41
  default: null,
60
42
  },
61
- disabled: {
62
- type: Boolean,
63
- default: false,
64
- },
65
- headless: {
66
- type: Boolean,
67
- default: false,
68
- },
69
43
  },
70
44
 
71
45
  data() {
72
46
  return {
73
- index: null,
74
- selected: false,
75
- tabIndex: 0,
47
+ apiInjected: this.apiDropdownMenu,
48
+ componentName: 'dropdown-menu-item',
76
49
  };
77
50
  },
78
51
 
79
52
  computed: {
80
- id() {
81
- return `dropdown-menu-item-${this.apiDropdownMenu().id}-${genId()}`;
82
- },
83
-
84
53
  as() {
85
54
  return this.href ? 'a' : this.to ? 'NuxtLink' : 'button';
86
55
  },
87
-
88
- items() {
89
- return this.apiDropdownMenu().items;
90
- },
91
-
92
- el() {
93
- return this.$el;
94
- },
95
-
96
- componentTrigger() {
97
- return this.apiDropdownMenu().componentTrigger;
98
- },
99
- },
100
-
101
- mounted() {
102
- const item = {
103
- select: this.select,
104
- unselect: this.unselect,
105
- focus: this.focus,
106
- };
107
-
108
- this.apiDropdownMenu().registerItem(item);
109
-
110
- this.index = this.items.length - 1;
111
- },
112
-
113
- methods: {
114
- select() {
115
- this.selected = true;
116
- },
117
-
118
- unselect() {
119
- this.selected = false;
120
- },
121
-
122
- focus() {
123
- if (!this.el) return;
124
-
125
- this.tabIndex = -1;
126
- this.selected = true;
127
- this.el.focus();
128
- },
129
-
130
- focusFirstItem() {
131
- this.setFocusToItem(0);
132
- },
133
-
134
- focusLastItem() {
135
- this.setFocusToItem(this.items.length - 1);
136
- },
137
-
138
- /**
139
- * Focus the previous item in the menu.
140
- * If is the first item, jump to the last item.
141
- */
142
- focusPreviousItem() {
143
- const isLast = this.index === this.items.length - 1;
144
- const goToIndex = isLast ? 0 : this.index + 1;
145
-
146
- this.setFocusToItem(goToIndex);
147
- },
148
-
149
- /**
150
- * Focus the next item in the menu.
151
- * If is the last item, jump to the first item.
152
- */
153
- focusNextItem() {
154
- const isFirst = this.index === 0;
155
- const goToIndex = isFirst ? this.items.length - 1 : this.index - 1;
156
-
157
- this.setFocusToItem(goToIndex);
158
- },
159
-
160
- /**
161
- * Focus item by remove its tabindex and calling
162
- * focus to the element.
163
- *
164
- * @param {Number, String} goToIndex
165
- */
166
- setFocusToItem(goToIndex) {
167
- this.tabIndex = 0;
168
- this.selected = false;
169
-
170
- // set all selected to false
171
- this.items.forEach((item) => item.unselect());
172
-
173
- // focus item
174
- this.items[goToIndex].focus();
175
- },
176
-
177
- leaveMenu() {
178
- if (this.componentTrigger) {
179
- this.componentTrigger.cancel();
180
- this.componentTrigger.focus();
181
- }
182
- },
183
-
184
- onKeyEsc() {
185
- this.leaveMenu();
186
- },
187
-
188
- onClick() {
189
- if (this.disabled) return;
190
-
191
- this.$emit('click');
192
- this.$nextTick(() => this.leaveMenu());
193
- },
194
56
  },
195
57
  };
196
58
  </script>
@@ -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
  },
@@ -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) {
@@ -1,25 +1,31 @@
1
1
  <template>
2
2
  <div
3
3
  :class="[
4
- headless ? 'form-feedback' : 'flex gap-2 mt-1 items-baseline',
4
+ headless ? 'form-feedback' : 'mt-1 flex items-baseline gap-2',
5
5
  // variant styles
6
- headless ? `form-feedback--{$variant}` : null,
6
+ headless ? `form-feedback--${variant}` : null,
7
7
  ]"
8
8
  >
9
9
  <component
10
10
  v-if="showIcon"
11
11
  :is="icon"
12
12
  :class="[
13
- headless ? 'form-feedback__icon' : 'relative top-1 shrink-0 h-4 w-4',
13
+ headless ? 'form-feedback__icon' : 'relative top-1 h-4 w-4 shrink-0',
14
14
  // variant styles
15
15
  headless
16
- ? `form-feedback__icon--{$variant}`
16
+ ? `form-feedback__icon--${variant}`
17
17
  : isError
18
18
  ? 'text-error-500'
19
19
  : null,
20
20
  ]"
21
21
  />
22
- <span :class="[headless ? 'form-feedback--text' : 'text-gray-500 text-sm']">
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,52 +1,40 @@
1
1
  <template>
2
2
  <input
3
- v-bind="$attrs"
4
- :class="[
5
- headless
6
- ? 'form-control'
7
- : 'border border-solid py-2 px-3 rounded text-inherit max-w-full',
8
- headless
9
- ? `form-control--${variant}`
10
- : isError
11
- ? 'border-error-300'
12
- : 'border-gray-300',
13
- ]"
14
- :value="value"
15
- @input="$emit('input', $event.target.value)"
16
- @blur="$emit('blur')"
3
+ :class="classComputed"
4
+ :value="modelValue"
5
+ :disabled="disabled"
6
+ v-bind="attrsComputed"
17
7
  />
18
8
  </template>
19
9
 
20
10
  <script>
11
+ import {
12
+ formControlMixin,
13
+ formControlStyleMixin,
14
+ } from '../../../mixins/form-control';
21
15
  export default {
22
- model: {
23
- prop: 'value',
24
- event: 'input',
25
- },
26
-
27
- props: {
28
- disabled: {
29
- type: Boolean,
30
- default: false,
31
- },
32
- value: {
33
- type: [String, Number, Object, Array],
34
- default: null,
35
- },
36
- headless: {
37
- type: Boolean,
38
- default: false,
39
- },
40
- variant: {
41
- type: [String, Object, Function],
42
- default: '',
43
- },
44
- },
45
-
46
- computed: {
47
- isError() {
48
- return this.variant === 'error';
49
- },
16
+ mixins: [formControlMixin, formControlStyleMixin],
17
+ data() {
18
+ return {
19
+ name: 'input',
20
+ };
50
21
  },
51
22
  };
52
23
  </script>
24
+
25
+ <style scoped>
26
+ /* input[type='date']::-webkit-inner-spin-button,
27
+ input[type='date']::-webkit-calendar-picker-indicator {
28
+ position: absolute;
29
+ opacity: 0;
30
+ } */
31
+ input[type='number'] {
32
+ appearance: textfield;
33
+ }
34
+
35
+ input[type='number']::-webkit-inner-spin-button,
36
+ input[type='number']::-webkit-outer-spin-button {
37
+ appearance: none;
38
+ -webkit-appearance: none;
39
+ }
40
+ </style>
@@ -0,0 +1,35 @@
1
+ <template>
2
+ <div :class="classComputedWrapper">
3
+ <input
4
+ :class="classComputed"
5
+ :value="value"
6
+ :disabled="disabled"
7
+ v-bind="attrsComputed"
8
+ />
9
+ <div :class="classComputedWrapperIcon">
10
+ <component :is="icon" class="h-5 w-5" />
11
+ </div>
12
+ </div>
13
+ </template>
14
+
15
+ <script>
16
+ import { formControlIconMixin } from '../../../mixins/form-control-icon';
17
+
18
+ export default {
19
+ name: 'VTInputIcon',
20
+
21
+ mixins: [formControlIconMixin],
22
+
23
+ props: {
24
+ icon: {
25
+ type: String,
26
+ default: null,
27
+ required: true,
28
+ },
29
+ iconPlacement: {
30
+ type: String,
31
+ default: 'left',
32
+ },
33
+ },
34
+ };
35
+ </script>