@veritree/ui 0.21.1-1 → 0.21.1-10

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 (29) hide show
  1. package/mixins/floating-ui-content.js +102 -0
  2. package/mixins/floating-ui-item.js +216 -0
  3. package/mixins/floating-ui.js +14 -6
  4. package/mixins/form-control.js +72 -0
  5. package/package.json +2 -2
  6. package/src/components/Button/VTButton.vue +1 -1
  7. package/src/components/DropdownMenu/VTDropdownMenu.vue +5 -16
  8. package/src/components/DropdownMenu/VTDropdownMenuContent.vue +17 -65
  9. package/src/components/DropdownMenu/VTDropdownMenuDivider.vue +8 -5
  10. package/src/components/DropdownMenu/VTDropdownMenuItem.vue +9 -143
  11. package/src/components/DropdownMenu/VTDropdownMenuLabel.vue +3 -3
  12. package/src/components/DropdownMenu/VTDropdownMenuTrigger.vue +67 -103
  13. package/src/components/Form/VTInput.vue +28 -40
  14. package/src/components/Form/VTInputQty.vue +165 -0
  15. package/src/components/Form/VTTextarea.vue +22 -0
  16. package/src/components/Listbox/VTListbox.vue +6 -11
  17. package/src/components/Listbox/VTListboxContent.vue +11 -76
  18. package/src/components/Listbox/VTListboxItem.vue +9 -181
  19. package/src/components/Listbox/VTListboxLabel.vue +0 -10
  20. package/src/components/Listbox/VTListboxList.vue +24 -33
  21. package/src/components/Listbox/VTListboxSearch.vue +21 -18
  22. package/src/components/Listbox/VTListboxTrigger.vue +58 -97
  23. package/src/components/Popover/VTPopover.vue +1 -14
  24. package/src/components/Popover/VTPopoverContent.vue +14 -65
  25. package/src/components/Popover/VTPopoverDivider.vue +4 -11
  26. package/src/components/Popover/VTPopoverItem.vue +16 -13
  27. package/src/components/Popover/VTPopoverTrigger.vue +118 -21
  28. package/src/components/Utils/FloatingUi.vue +6 -1
  29. package/src/utils/components.js +0 -18
@@ -0,0 +1,22 @@
1
+ <template>
2
+ <textarea
3
+ :class="classComputed"
4
+ :value="value"
5
+ :disabled="disabled"
6
+ v-on="listeners"
7
+ />
8
+ </template>
9
+
10
+ <script>
11
+ import { formControlMixin } from '../../../mixins/form-control';
12
+
13
+ export default {
14
+ mixins: [formControlMixin],
15
+
16
+ data() {
17
+ return {
18
+ name: 'textarea',
19
+ };
20
+ },
21
+ };
22
+ </script>
@@ -15,9 +15,7 @@ export default {
15
15
 
16
16
  provide() {
17
17
  return {
18
- api: () => {
19
- const { dark: isDark } = this;
20
-
18
+ apiListbox: () => {
21
19
  const registerTrigger = (trigger) => {
22
20
  if (!trigger) return;
23
21
  this.componentTrigger = trigger;
@@ -33,10 +31,10 @@ export default {
33
31
  this.search = search;
34
32
  };
35
33
 
36
- const registerList = (list) => {
37
- if (!list) return;
38
- this.list = list;
39
- };
34
+ // const registerList = (list) => {
35
+ // if (!list) return;
36
+ // this.list = list;
37
+ // };
40
38
 
41
39
  const registerItem = (item) => {
42
40
  if (!item) return;
@@ -55,17 +53,15 @@ export default {
55
53
 
56
54
  return {
57
55
  id: this.componentId,
58
- isDark,
59
56
  component: this.component,
60
57
  componentTrigger: this.componentTrigger,
61
58
  componentContent: this.componentContent,
62
- list: this.list,
63
59
  items: this.items,
64
60
  search: this.search,
65
61
  registerTrigger,
66
62
  registerContent,
67
63
  registerSearch,
68
- registerList,
64
+ // registerList,
69
65
  registerItem,
70
66
  unregisterItem,
71
67
  emit,
@@ -97,7 +93,6 @@ export default {
97
93
  return {
98
94
  componentId: genId(),
99
95
  search: null,
100
- list: null,
101
96
  items: [],
102
97
  };
103
98
  },
@@ -5,6 +5,7 @@
5
5
  :aria-activedescendant="activeDescedant"
6
6
  :headless="headless"
7
7
  :class="{ 'listbox-content': headless }"
8
+ :floating-ui-class="floatingUiClass"
8
9
  role="listbox"
9
10
  >
10
11
  <slot></slot>
@@ -12,54 +13,32 @@
12
13
  </template>
13
14
 
14
15
  <script>
15
- import FloatingUi from '../Utils/FloatingUi.vue';
16
+ import { floatingUiContentMixin } from '../../../mixins/floating-ui-content';
16
17
 
17
18
  export default {
18
19
  name: 'VTListboxContent',
19
20
 
20
- components: {
21
- FloatingUi,
22
- },
23
-
24
- inject: ['api'],
21
+ mixins: [floatingUiContentMixin],
25
22
 
26
- props: {
27
- headless: {
28
- type: Boolean,
29
- default: false,
30
- },
31
- bottom: {
32
- type: Boolean,
33
- default: false,
34
- },
35
- top: {
36
- type: Boolean,
37
- default: true,
38
- },
39
- },
23
+ inject: ['apiListbox'],
40
24
 
41
25
  data() {
42
26
  return {
43
27
  activeDescedant: null,
44
- visible: false,
45
28
  };
46
29
  },
47
30
 
48
31
  computed: {
49
32
  id() {
50
- return `listbox-content-${this.api().id}`;
33
+ return `listbox-content-${this.apiListbox().id}`;
51
34
  },
52
35
 
53
36
  component() {
54
- return this.api().component;
37
+ return this.apiListbox().component;
55
38
  },
56
39
 
57
40
  componentTrigger() {
58
- return this.api().componentTrigger;
59
- },
60
-
61
- search() {
62
- return this.api().search;
41
+ return this.apiListbox().componentTrigger;
63
42
  },
64
43
  },
65
44
 
@@ -68,57 +47,13 @@ export default {
68
47
  id: this.id,
69
48
  show: this.show,
70
49
  hide: this.hide,
50
+ getMousemove: this.getMousemove,
51
+ setMousemove: this.setMousemove,
52
+ unsetMousemove: this.unsetMousemove,
71
53
  setActiveDescedant: this.setActiveDescedant,
72
54
  };
73
55
 
74
- this.api().registerContent(content);
75
-
76
- // T-107 Create a directive or mixin for this
77
- document.addEventListener('click', (e) => {
78
- if (!e) {
79
- return;
80
- }
81
-
82
- e.stopPropagation();
83
-
84
- if (this.visible && !this.$el.contains(e.target)) {
85
- this.componentTrigger.onClick();
86
- }
87
- });
88
- },
89
-
90
- destroyed() {
91
- // T-162 Create a directive or mixin for this
92
- document.removeEventListener('click', this.componentTrigger.onClick);
93
- },
94
-
95
- methods: {
96
- show() {
97
- if (this.visible) return;
98
-
99
- this.visible = true;
100
-
101
- this.$nextTick(() => {
102
- this.component.setActive();
103
- if (this.search) this.search.el.focus();
104
- });
105
- },
106
-
107
- hide() {
108
- if (!this.visible) return;
109
-
110
- this.visible = false;
111
-
112
- this.$nextTick(() => {
113
- this.componentTrigger.focus();
114
- this.componentTrigger.toggleExpanded();
115
- this.component.clearActive();
116
- });
117
- },
118
-
119
- setActiveDescedant(id) {
120
- this.activeDescedant = id;
121
- },
56
+ this.apiListbox().registerContent(content);
122
57
  },
123
58
  };
124
59
  </script>
@@ -1,14 +1,7 @@
1
1
  <template>
2
2
  <li
3
3
  :id="id"
4
- :class="{
5
- ListboxItem: headless,
6
- 'relative z-10 flex items-center gap-2 px-3 py-2 no-underline': !headless,
7
- 'hover:bg-secondary-200/10': !dark && !headless,
8
- 'text-white': dark && !headless,
9
- 'pointer-events-none opacity-75': disabled && !headless,
10
- 'bg-secondary-200/10': selected && !headless,
11
- }"
4
+ :class="classComputed"
12
5
  :aria-disabled="disabled"
13
6
  :aria-selected="String(selected)"
14
7
  :tabindex="tabIndex"
@@ -24,28 +17,21 @@
24
17
  @mouseout="onMouseleave"
25
18
  @keydown.tab.prevent
26
19
  >
27
- <span class="truncate"><slot></slot></span>
20
+ <slot></slot>
28
21
  </li>
29
22
  </template>
30
23
 
31
24
  <script>
32
- import { scrollElementIntoView } from '../../utils/components';
33
- import { genId } from '../../utils/ids';
25
+ import { floatingUiItemMixin } from '../../../mixins/floating-ui-item';
34
26
 
35
27
  export default {
36
28
  name: 'VTListboxItem',
37
29
 
38
- inject: ['api'],
30
+ mixins: [floatingUiItemMixin],
31
+
32
+ inject: ['apiListbox'],
39
33
 
40
34
  props: {
41
- headless: {
42
- type: Boolean,
43
- default: false,
44
- },
45
- disabled: {
46
- type: Boolean,
47
- default: false,
48
- },
49
35
  value: {
50
36
  type: [String, Number, Object, Array],
51
37
  required: true,
@@ -54,172 +40,14 @@ export default {
54
40
 
55
41
  data() {
56
42
  return {
57
- id: `listboxitem-${genId()}`,
58
- index: null,
59
- selected: false,
60
- tabIndex: 0,
43
+ apiInjected: this.apiListbox,
44
+ componentName: 'listbox-item',
61
45
  };
62
46
  },
63
47
 
64
48
  computed: {
65
- dark() {
66
- return this.api().isDark;
67
- },
68
-
69
- items() {
70
- return this.api().items;
71
- },
72
-
73
- el() {
74
- return this.$el;
75
- },
76
-
77
- componentTrigger() {
78
- return this.api().componentTrigger;
79
- },
80
-
81
- componentContent() {
82
- return this.api().componentContent;
83
- },
84
-
85
- list() {
86
- return this.api().list;
87
- },
88
-
89
49
  search() {
90
- return this.api().search;
91
- },
92
- },
93
-
94
- watch: {
95
- selected(newValue) {
96
- if (!newValue || !this.list) return;
97
-
98
- if (this.componentContent) {
99
- this.componentContent.setActiveDescedant(this.id);
100
- }
101
-
102
- const isMousemove = this.list.getMousemove();
103
-
104
- if (!isMousemove) {
105
- scrollElementIntoView(this.el, this.list.el);
106
- }
107
- },
108
- },
109
-
110
- mounted() {
111
- const item = {
112
- id: this.id,
113
- focus: this.focus,
114
- select: this.select,
115
- unselect: this.unselect,
116
- onClick: this.onClick,
117
- };
118
-
119
- this.api().registerItem(item);
120
-
121
- this.index = this.items.length - 1;
122
- },
123
-
124
- beforeDestroy() {
125
- this.api().unregisterItem(this.id);
126
- },
127
-
128
- methods: {
129
- select() {
130
- this.selected = true;
131
- },
132
-
133
- unselect() {
134
- this.selected = false;
135
- },
136
-
137
- focus() {
138
- if (!this.el) return;
139
-
140
- this.tabIndex = -1;
141
- this.selected = true;
142
- this.el.focus();
143
- },
144
-
145
- focusFirstItem() {
146
- this.setFocusToItem(0);
147
- },
148
-
149
- focusLastItem() {
150
- this.setFocusToItem(this.items.length - 1);
151
- },
152
-
153
- /**
154
- * Focus the previous item in the menu.
155
- * If is the first item, jump to the last item.
156
- */
157
- focusPreviousItem() {
158
- const isLast = this.index === this.items.length - 1;
159
- const goToIndex = isLast ? 0 : this.index + 1;
160
-
161
- this.setFocusToItem(goToIndex);
162
- },
163
-
164
- /**
165
- * Focus the next item in the menu.
166
- * If is the last item, jump to the first item.
167
- */
168
- focusNextItem() {
169
- const isFirst = this.index === 0;
170
- const goToIndex = isFirst ? this.items.length - 1 : this.index - 1;
171
-
172
- this.setFocusToItem(goToIndex);
173
- },
174
-
175
- /**
176
- * Focus item by remove its tabindex and calling
177
- * focus to the element.
178
- *
179
- * @param {Number, String} goToIndex
180
- */
181
- setFocusToItem(goToIndex) {
182
- this.tabIndex = 0;
183
- this.selected = false;
184
-
185
- const isMousemove = this.list.getMousemove();
186
-
187
- if (isMousemove) {
188
- this.list.unsetMousemove();
189
- this.items.forEach((item) => item.unselect());
190
- }
191
-
192
- this.items[goToIndex].focus();
193
- },
194
-
195
- /**
196
- * Hides componentContent/menu and focus on componentTrigger
197
- */
198
- leaveMenu() {
199
- if (this.componentContent) this.componentContent.hide();
200
- },
201
-
202
- onKeyEsc() {
203
- this.leaveMenu();
204
- },
205
-
206
- onClick() {
207
- if (this.disabled) return;
208
-
209
- this.api().emit(this.value);
210
- this.$nextTick(() => this.leaveMenu());
211
- },
212
-
213
- onMousemove() {
214
- this.items.forEach((item) => item.unselect());
215
-
216
- this.select();
217
- this.list.setMousemove();
218
- },
219
-
220
- onMouseleave() {
221
- this.unselect();
222
- this.list.unsetMousemove();
50
+ return this.apiInjected().search;
223
51
  },
224
52
  },
225
53
  };
@@ -4,8 +4,6 @@
4
4
  :class="{
5
5
  ListboxLabel: headless,
6
6
  'mb-2 block text-xs font-normal uppercase': !headless,
7
- 'text-inherit': !dark && !headless,
8
- 'text-white': dark && !headless,
9
7
  }"
10
8
  >
11
9
  <slot></slot>
@@ -16,8 +14,6 @@
16
14
  export default {
17
15
  name: 'VTListboxLabel',
18
16
 
19
- inject: ['api'],
20
-
21
17
  props: {
22
18
  as: {
23
19
  type: String,
@@ -28,11 +24,5 @@ export default {
28
24
  default: false,
29
25
  },
30
26
  },
31
-
32
- computed: {
33
- dark() {
34
- return this.api().isDark;
35
- },
36
- },
37
27
  };
38
28
  </script>
@@ -1,22 +1,19 @@
1
1
  <template>
2
2
  <ul
3
3
  :id="id"
4
- :class="{
5
- ListboxList: headless,
6
- '-mx-3 max-h-[160px] w-auto overflow-y-auto': !headless,
7
- }"
4
+ :class="[
5
+ headless ? 'listbox-list' : 'max-h-[160px] w-auto overflow-y-auto -mx-3',
6
+ ]"
8
7
  >
9
8
  <slot></slot>
10
9
  </ul>
11
10
  </template>
12
11
 
13
12
  <script>
14
- import { genId } from "../../utils/ids";
15
-
16
13
  export default {
17
- name: "VTListboxList",
14
+ name: 'VTListboxList',
18
15
 
19
- inject: ["api"],
16
+ inject: ['apiListbox'],
20
17
 
21
18
  props: {
22
19
  headless: {
@@ -25,39 +22,33 @@ export default {
25
22
  },
26
23
  },
27
24
 
28
- data() {
29
- return {
30
- id: `listboxlist-${genId()}`,
31
- isMousemove: false,
32
- };
25
+ computed: {
26
+ id() {
27
+ return `listbox-list-${this.apiListbox().id}`;
28
+ },
33
29
  },
34
30
 
35
- mounted() {
36
- const list = {
37
- el: this.$el,
38
- getMousemove: this.getMousemove,
39
- setMousemove: this.setMousemove,
40
- unsetMousemove: this.unsetMousemove,
41
- };
31
+ // mounted() {
32
+ // const list = {
33
+ // el: this.$el,
34
+ // };
42
35
 
43
- this.api().registerList(list);
44
- },
36
+ // this.apiListbox().registerList(list);
37
+ // },
45
38
 
46
39
  methods: {
47
40
  // Mousemove instead of mouseover to support keyboard navigation.
48
41
  // The problem with mouseover is that when scrolling (scrollIntoView),
49
42
  // mouseover event gets triggered.
50
- setMousemove() {
51
- this.isMousemove = true;
52
- },
53
-
54
- unsetMousemove() {
55
- this.isMousemove = false;
56
- },
57
-
58
- getMousemove() {
59
- return this.isMousemove;
60
- },
43
+ // setMousemove() {
44
+ // this.isMousemove = true;
45
+ // },
46
+ // unsetMousemove() {
47
+ // this.isMousemove = false;
48
+ // },
49
+ // getMousemove() {
50
+ // return this.isMousemove;
51
+ // },
61
52
  },
62
53
  };
63
54
  </script>
@@ -1,9 +1,10 @@
1
1
  <template>
2
2
  <input
3
3
  v-model="search"
4
- :class="{ 'listbox-search': headless, 'form-control mb-1': !headless }"
5
4
  type="text"
5
+ :class="classComputed"
6
6
  @input="onChange"
7
+ @click.stop
7
8
  @keydown.down.prevent="focusNextItem"
8
9
  @keydown.up.prevent="focusPreviousItem"
9
10
  @keydown.home.prevent="focusFirstItem"
@@ -15,36 +16,34 @@
15
16
  </template>
16
17
 
17
18
  <script>
19
+ import { formControlMixin } from '../../../mixins/form-control';
20
+
18
21
  export default {
19
22
  name: 'VTListboxSearch',
20
23
 
21
- inject: ['api'],
24
+ mixins: [formControlMixin],
22
25
 
23
- props: {
24
- headless: {
25
- type: Boolean,
26
- default: false,
27
- },
28
- },
26
+ inject: ['apiListbox'],
29
27
 
30
28
  data() {
31
29
  return {
32
- search: '',
30
+ name: 'listbox-search',
33
31
  index: -1,
32
+ search: '',
34
33
  };
35
34
  },
36
35
 
37
36
  computed: {
38
- componentContent() {
39
- return this.api().componentContent;
37
+ componentTrigger() {
38
+ return this.apiListbox().componentTrigger;
40
39
  },
41
40
 
42
- list() {
43
- return this.api().list;
41
+ componentContent() {
42
+ return this.apiListbox().componentContent;
44
43
  },
45
44
 
46
45
  items() {
47
- return this.api().items;
46
+ return this.apiListbox().items;
48
47
  },
49
48
 
50
49
  item() {
@@ -57,7 +56,8 @@ export default {
57
56
  el: this.$el,
58
57
  };
59
58
 
60
- this.api().registerSearch(search);
59
+ this.apiListbox().registerSearch(search);
60
+ this.$nextTick(() => setTimeout(() => this.$el.focus(), 150));
61
61
  },
62
62
 
63
63
  beforeDestroy() {
@@ -105,10 +105,10 @@ export default {
105
105
  },
106
106
 
107
107
  unselectItem() {
108
- const isMousemove = this.list.getMousemove();
108
+ const isMousemove = this.componentContent.getMousemove();
109
109
 
110
110
  if (isMousemove) {
111
- this.list.unsetMousemove();
111
+ this.componentContent.unsetMousemove();
112
112
  this.items.forEach((item) => item.unselect());
113
113
  }
114
114
 
@@ -127,7 +127,10 @@ export default {
127
127
  },
128
128
 
129
129
  hide() {
130
- if (this.componentContent) this.componentContent.hide();
130
+ if (this.componentTrigger) {
131
+ this.componentTrigger.cancel();
132
+ this.componentTrigger.focus();
133
+ }
131
134
  },
132
135
  },
133
136
  };