@veritree/ui 0.21.1-8 → 0.21.1-9
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.
- package/mixins/floating-ui-content.js +22 -1
- package/mixins/floating-ui-item.js +222 -0
- package/mixins/floating-ui.js +14 -8
- package/mixins/form-control.js +72 -0
- package/package.json +1 -1
- package/src/components/DropdownMenu/VTDropdownMenu.vue +4 -5
- package/src/components/DropdownMenu/VTDropdownMenuContent.vue +4 -0
- package/src/components/DropdownMenu/VTDropdownMenuItem.vue +8 -146
- package/src/components/Form/VTInput.vue +28 -40
- package/src/components/Form/VTInputQty.vue +165 -0
- package/src/components/Form/VTTextarea.vue +22 -0
- package/src/components/Listbox/VTListbox.vue +5 -7
- package/src/components/Listbox/VTListboxContent.vue +3 -6
- package/src/components/Listbox/VTListboxItem.vue +8 -193
- package/src/components/Listbox/VTListboxList.vue +15 -26
- package/src/components/Listbox/VTListboxSearch.vue +11 -13
- package/src/components/Listbox/VTListboxTrigger.vue +7 -40
- package/src/components/Utils/FloatingUi.vue +1 -1
- package/src/utils/components.js +0 -18
|
@@ -18,8 +18,9 @@ export const floatingUiContentMixin = {
|
|
|
18
18
|
|
|
19
19
|
data() {
|
|
20
20
|
return {
|
|
21
|
-
visible: false,
|
|
22
21
|
el: null,
|
|
22
|
+
isMousemove: false,
|
|
23
|
+
visible: false,
|
|
23
24
|
};
|
|
24
25
|
},
|
|
25
26
|
|
|
@@ -77,5 +78,25 @@ export const floatingUiContentMixin = {
|
|
|
77
78
|
this.componentTrigger.cancel();
|
|
78
79
|
}
|
|
79
80
|
},
|
|
81
|
+
|
|
82
|
+
// Mousemove instead of mouseover to support keyboard navigation.
|
|
83
|
+
// The problem with mouseover is that when scrolling (scrollIntoView),
|
|
84
|
+
// mouseover event gets triggered.
|
|
85
|
+
setMousemove() {
|
|
86
|
+
this.isMousemove = true;
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
unsetMousemove() {
|
|
90
|
+
this.isMousemove = false;
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
getMousemove() {
|
|
94
|
+
return this.isMousemove;
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
//
|
|
98
|
+
setActiveDescedant(id) {
|
|
99
|
+
this.activeDescedant = id;
|
|
100
|
+
},
|
|
80
101
|
},
|
|
81
102
|
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import FloatingUiItem from '../src/components/Utils/FloatingUiItem.vue';
|
|
2
|
+
|
|
3
|
+
export const floatingUiItemMixin = {
|
|
4
|
+
components: {
|
|
5
|
+
FloatingUiItem,
|
|
6
|
+
},
|
|
7
|
+
|
|
8
|
+
props: {
|
|
9
|
+
headless: {
|
|
10
|
+
type: Boolean,
|
|
11
|
+
default: false,
|
|
12
|
+
},
|
|
13
|
+
disabled: {
|
|
14
|
+
type: Boolean,
|
|
15
|
+
default: false,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
computed: {
|
|
20
|
+
id() {
|
|
21
|
+
return `${this.componentName}-${this.apiInjected().id}-${this.index}`;
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
items() {
|
|
25
|
+
return this.apiInjected().items;
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
el() {
|
|
29
|
+
return this.$el;
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
componentContent() {
|
|
33
|
+
return this.apiInjected().componentContent;
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
componentTrigger() {
|
|
37
|
+
return this.apiInjected().componentTrigger;
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
classComputed() {
|
|
41
|
+
return [
|
|
42
|
+
// default styles
|
|
43
|
+
this.headless
|
|
44
|
+
? `${this.componentName}`
|
|
45
|
+
: 'relative z-10 flex items-center gap-2 px-3 py-2 text-inherit no-underline',
|
|
46
|
+
// disabled state styles
|
|
47
|
+
this.headless
|
|
48
|
+
? this.disabled
|
|
49
|
+
? `${this.componentName}--disabled`
|
|
50
|
+
: null
|
|
51
|
+
: this.disabled
|
|
52
|
+
? 'pointer-events-none opacity-75'
|
|
53
|
+
: null,
|
|
54
|
+
// selected state styles
|
|
55
|
+
this.headless
|
|
56
|
+
? this.selected
|
|
57
|
+
? `${this.componentName}--selected`
|
|
58
|
+
: null
|
|
59
|
+
: this.selected
|
|
60
|
+
? 'bg-secondary-200/10'
|
|
61
|
+
: null,
|
|
62
|
+
];
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
data() {
|
|
67
|
+
return {
|
|
68
|
+
index: null,
|
|
69
|
+
selected: false,
|
|
70
|
+
tabIndex: 0,
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
watch: {
|
|
75
|
+
selected(newValue) {
|
|
76
|
+
if (!newValue || !this.componentContent) return;
|
|
77
|
+
|
|
78
|
+
this.componentContent.setActiveDescedant(this.id);
|
|
79
|
+
|
|
80
|
+
const isMousemove = this.componentContent.getMousemove();
|
|
81
|
+
|
|
82
|
+
if (!isMousemove) {
|
|
83
|
+
this.el.scrollIntoView({ block: 'nearest' });
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
mounted() {
|
|
89
|
+
this.init();
|
|
90
|
+
this.addMouseEventListeners();
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
beforeDestroy() {
|
|
94
|
+
this.apiInjected().unregisterItem(this.id);
|
|
95
|
+
this.removeMouseEventListeners();
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
methods: {
|
|
99
|
+
init() {
|
|
100
|
+
const item = {
|
|
101
|
+
id: this.id,
|
|
102
|
+
select: this.select,
|
|
103
|
+
unselect: this.unselect,
|
|
104
|
+
focus: this.focus,
|
|
105
|
+
onClick: this.onClick,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
this.apiInjected().registerItem(item);
|
|
109
|
+
|
|
110
|
+
this.index = this.items.length - 1;
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
addMouseEventListeners() {
|
|
114
|
+
this.el.addEventListener('mousemove', this.onMousemove);
|
|
115
|
+
this.el.addEventListener('mouseleave', this.onMouseleave);
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
removeMouseEventListeners() {
|
|
119
|
+
this.el.removeEventListener('mousemove', this.onMousemove);
|
|
120
|
+
this.el.removeEventListener('mouseleave', this.onMouseleave);
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
select() {
|
|
124
|
+
this.selected = true;
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
unselect() {
|
|
128
|
+
this.selected = false;
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
focus() {
|
|
132
|
+
if (!this.el) return;
|
|
133
|
+
|
|
134
|
+
this.tabIndex = -1;
|
|
135
|
+
this.selected = true;
|
|
136
|
+
this.el.focus();
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
focusFirstItem() {
|
|
140
|
+
this.setFocusToItem(0);
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
focusLastItem() {
|
|
144
|
+
this.setFocusToItem(this.items.length - 1);
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Focus the previous item in the menu.
|
|
149
|
+
* If is the first item, jump to the last item.
|
|
150
|
+
*/
|
|
151
|
+
focusPreviousItem() {
|
|
152
|
+
const isLast = this.index === this.items.length - 1;
|
|
153
|
+
const goToIndex = isLast ? 0 : this.index + 1;
|
|
154
|
+
|
|
155
|
+
this.setFocusToItem(goToIndex);
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Focus the next item in the menu.
|
|
160
|
+
* If is the last item, jump to the first item.
|
|
161
|
+
*/
|
|
162
|
+
focusNextItem() {
|
|
163
|
+
const isFirst = this.index === 0;
|
|
164
|
+
const goToIndex = isFirst ? this.items.length - 1 : this.index - 1;
|
|
165
|
+
|
|
166
|
+
this.setFocusToItem(goToIndex);
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Focus item by remove its tabindex and calling
|
|
171
|
+
* focus to the element.
|
|
172
|
+
*
|
|
173
|
+
* @param {Number, String} goToIndex
|
|
174
|
+
*/
|
|
175
|
+
setFocusToItem(goToIndex) {
|
|
176
|
+
this.tabIndex = 0;
|
|
177
|
+
this.selected = false;
|
|
178
|
+
|
|
179
|
+
// set all selected to false
|
|
180
|
+
this.items.forEach((item) => item.unselect());
|
|
181
|
+
|
|
182
|
+
// focus item
|
|
183
|
+
this.items[goToIndex].focus();
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
leaveMenu() {
|
|
187
|
+
if (this.componentTrigger) {
|
|
188
|
+
this.componentTrigger.cancel();
|
|
189
|
+
this.componentTrigger.focus();
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
onClick() {
|
|
194
|
+
if (this.disabled) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
this.value ? this.apiInjected().emit(this.value) : this.$emit('click');
|
|
199
|
+
this.$nextTick(() => this.leaveMenu());
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
onKeyEsc() {
|
|
203
|
+
this.leaveMenu();
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
onMousemove() {
|
|
207
|
+
if (this.selected) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
this.items.forEach((item) => item.unselect());
|
|
212
|
+
|
|
213
|
+
this.select();
|
|
214
|
+
this.componentContent.setMousemove();
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
onMouseleave() {
|
|
218
|
+
this.unselect();
|
|
219
|
+
this.componentContent.unsetMousemove();
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
};
|
package/mixins/floating-ui.js
CHANGED
|
@@ -4,8 +4,8 @@ export const floatingUiMixin = {
|
|
|
4
4
|
props: {
|
|
5
5
|
placement: {
|
|
6
6
|
type: String,
|
|
7
|
-
default: 'bottom-start'
|
|
8
|
-
}
|
|
7
|
+
default: 'bottom-start',
|
|
8
|
+
},
|
|
9
9
|
},
|
|
10
10
|
|
|
11
11
|
data() {
|
|
@@ -14,7 +14,7 @@ export const floatingUiMixin = {
|
|
|
14
14
|
componentTrigger: null,
|
|
15
15
|
componentContent: null,
|
|
16
16
|
active: false,
|
|
17
|
-
}
|
|
17
|
+
};
|
|
18
18
|
},
|
|
19
19
|
|
|
20
20
|
watch: {
|
|
@@ -40,11 +40,11 @@ export const floatingUiMixin = {
|
|
|
40
40
|
},
|
|
41
41
|
|
|
42
42
|
positionContentToTrigger() {
|
|
43
|
+
// console.log(window.innerWidth)
|
|
44
|
+
|
|
43
45
|
const trigger = document.getElementById(this.componentTrigger.id);
|
|
44
46
|
const content = document.getElementById(this.componentContent.id);
|
|
45
47
|
|
|
46
|
-
// console.log(this.placement);
|
|
47
|
-
|
|
48
48
|
computePosition(trigger, content, {
|
|
49
49
|
placement: this.placement,
|
|
50
50
|
middleware: [
|
|
@@ -53,8 +53,14 @@ export const floatingUiMixin = {
|
|
|
53
53
|
shift({ padding: 5 }),
|
|
54
54
|
size({
|
|
55
55
|
apply({ rects }) {
|
|
56
|
+
// the min width to floating uis should be 200
|
|
57
|
+
// since less than that can look not as nice
|
|
58
|
+
const minWidthLimit = 200;
|
|
59
|
+
const width = rects.reference.width;
|
|
60
|
+
const minWidth = width < minWidthLimit ? minWidthLimit : width;
|
|
61
|
+
|
|
56
62
|
Object.assign(content.style, {
|
|
57
|
-
minWidth: `${
|
|
63
|
+
minWidth: `${minWidth}px`,
|
|
58
64
|
});
|
|
59
65
|
},
|
|
60
66
|
}),
|
|
@@ -66,5 +72,5 @@ export const floatingUiMixin = {
|
|
|
66
72
|
});
|
|
67
73
|
});
|
|
68
74
|
},
|
|
69
|
-
}
|
|
70
|
-
}
|
|
75
|
+
},
|
|
76
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export const formControlMixin = {
|
|
2
|
+
model: {
|
|
3
|
+
prop: 'value',
|
|
4
|
+
event: 'input',
|
|
5
|
+
},
|
|
6
|
+
|
|
7
|
+
props: {
|
|
8
|
+
disabled: {
|
|
9
|
+
type: Boolean,
|
|
10
|
+
default: false,
|
|
11
|
+
},
|
|
12
|
+
headless: {
|
|
13
|
+
type: Boolean,
|
|
14
|
+
default: false,
|
|
15
|
+
},
|
|
16
|
+
value: {
|
|
17
|
+
type: [String, Number, Object, Array],
|
|
18
|
+
default: null,
|
|
19
|
+
},
|
|
20
|
+
variant: {
|
|
21
|
+
type: [String, Object, Function],
|
|
22
|
+
default: '',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
computed: {
|
|
27
|
+
listeners() {
|
|
28
|
+
// `Object.assign` merges objects together to form a new object
|
|
29
|
+
return Object.assign(
|
|
30
|
+
{},
|
|
31
|
+
// We add all the listeners from the parent
|
|
32
|
+
this.$listeners,
|
|
33
|
+
// Then we can add custom listeners or override the
|
|
34
|
+
// behavior of some listeners.
|
|
35
|
+
{
|
|
36
|
+
// This ensures that the component works with v-model
|
|
37
|
+
input: (event) => {
|
|
38
|
+
// if (this.lazy) return;
|
|
39
|
+
this.$emit('input', event.target.value);
|
|
40
|
+
},
|
|
41
|
+
blur: (event) => {
|
|
42
|
+
this.$emit('blur', event);
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
classComputed() {
|
|
49
|
+
return [
|
|
50
|
+
this.headless
|
|
51
|
+
? `${this.name}`
|
|
52
|
+
: 'leading-0 flex w-full max-w-full appearance-none items-center justify-between gap-3 rounded border border-solid px-3 py-2 font-inherit text-base text-inherit file:hidden focus:border-secondary-200',
|
|
53
|
+
// variant styles
|
|
54
|
+
this.headless
|
|
55
|
+
? `${this.name}--${this.variant}`
|
|
56
|
+
: this.isError
|
|
57
|
+
? 'border-error-300'
|
|
58
|
+
: 'border-gray-300',
|
|
59
|
+
// height styles
|
|
60
|
+
this.headless
|
|
61
|
+
? null
|
|
62
|
+
: this.name === 'textarea'
|
|
63
|
+
? 'min-h-10' // limit it because input type number height can be different from other input types
|
|
64
|
+
: 'h-10',
|
|
65
|
+
];
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
isError() {
|
|
69
|
+
return this.variant === 'error';
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
};
|
package/package.json
CHANGED
|
@@ -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
|
|
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
|
-
|
|
48
|
+
unregisterItem,
|
|
50
49
|
};
|
|
51
50
|
},
|
|
52
51
|
};
|
|
@@ -39,6 +39,10 @@ export default {
|
|
|
39
39
|
id: this.id,
|
|
40
40
|
hide: this.hide,
|
|
41
41
|
show: this.show,
|
|
42
|
+
getMousemove: this.getMousemove,
|
|
43
|
+
setMousemove: this.setMousemove,
|
|
44
|
+
unsetMousemove: this.unsetMousemove,
|
|
45
|
+
setActiveDescedant: this.setActiveDescedant,
|
|
42
46
|
};
|
|
43
47
|
|
|
44
48
|
this.apiDropdownMenu().registerContent(content);
|
|
@@ -3,30 +3,10 @@
|
|
|
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
12
|
@keydown.down.prevent="focusPreviousItem"
|
|
@@ -42,11 +22,13 @@
|
|
|
42
22
|
</template>
|
|
43
23
|
|
|
44
24
|
<script>
|
|
45
|
-
import {
|
|
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
|
-
|
|
74
|
-
|
|
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,52 +1,40 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<input
|
|
3
|
-
|
|
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
|
-
]"
|
|
3
|
+
:class="classComputed"
|
|
14
4
|
:value="value"
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
:disabled="disabled"
|
|
6
|
+
v-on="listeners"
|
|
17
7
|
/>
|
|
18
8
|
</template>
|
|
19
9
|
|
|
20
10
|
<script>
|
|
21
|
-
|
|
22
|
-
model: {
|
|
23
|
-
prop: 'value',
|
|
24
|
-
event: 'input',
|
|
25
|
-
},
|
|
11
|
+
import { formControlMixin } from '../../../mixins/form-control';
|
|
26
12
|
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
},
|
|
13
|
+
export default {
|
|
14
|
+
mixins: [formControlMixin],
|
|
45
15
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
16
|
+
data() {
|
|
17
|
+
return {
|
|
18
|
+
name: 'input',
|
|
19
|
+
};
|
|
50
20
|
},
|
|
51
21
|
};
|
|
52
22
|
</script>
|
|
23
|
+
|
|
24
|
+
<style scoped>
|
|
25
|
+
input[type='date']::-webkit-inner-spin-button,
|
|
26
|
+
input[type='date']::-webkit-calendar-picker-indicator {
|
|
27
|
+
position: absolute;
|
|
28
|
+
opacity: 0;
|
|
29
|
+
}
|
|
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>
|