@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.
- package/mixins/floating-ui-content.js +102 -0
- package/mixins/floating-ui-item.js +216 -0
- package/mixins/floating-ui.js +14 -6
- package/mixins/form-control.js +72 -0
- package/package.json +2 -2
- package/src/components/Button/VTButton.vue +1 -1
- package/src/components/DropdownMenu/VTDropdownMenu.vue +5 -16
- package/src/components/DropdownMenu/VTDropdownMenuContent.vue +17 -65
- package/src/components/DropdownMenu/VTDropdownMenuDivider.vue +8 -5
- package/src/components/DropdownMenu/VTDropdownMenuItem.vue +9 -143
- package/src/components/DropdownMenu/VTDropdownMenuLabel.vue +3 -3
- package/src/components/DropdownMenu/VTDropdownMenuTrigger.vue +67 -103
- 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 +6 -11
- package/src/components/Listbox/VTListboxContent.vue +11 -76
- package/src/components/Listbox/VTListboxItem.vue +9 -181
- package/src/components/Listbox/VTListboxLabel.vue +0 -10
- package/src/components/Listbox/VTListboxList.vue +24 -33
- package/src/components/Listbox/VTListboxSearch.vue +21 -18
- package/src/components/Listbox/VTListboxTrigger.vue +58 -97
- package/src/components/Popover/VTPopover.vue +1 -14
- package/src/components/Popover/VTPopoverContent.vue +14 -65
- package/src/components/Popover/VTPopoverDivider.vue +4 -11
- package/src/components/Popover/VTPopoverItem.vue +16 -13
- package/src/components/Popover/VTPopoverTrigger.vue +118 -21
- package/src/components/Utils/FloatingUi.vue +6 -1
- package/src/utils/components.js +0 -18
|
@@ -1,35 +1,19 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<button
|
|
3
3
|
:id="id"
|
|
4
|
+
:class="classComputed"
|
|
4
5
|
:aria-expanded="expanded"
|
|
5
6
|
:aria-haspopup="hasPopup"
|
|
6
|
-
:class="{
|
|
7
|
-
'listbox-button': headless,
|
|
8
|
-
'flex w-full justify-between rounded-md border border-solid py-2 px-3':
|
|
9
|
-
!headless,
|
|
10
|
-
'border-gray-300 text-gray-500': !dark && !headless,
|
|
11
|
-
'border-white/70 text-white focus-visible:ring-2 focus-visible:ring-white':
|
|
12
|
-
dark && !headless,
|
|
13
|
-
}"
|
|
14
7
|
type="button"
|
|
15
8
|
@click.prevent="onClick"
|
|
16
|
-
@keydown.down.prevent="
|
|
17
|
-
@keydown.up.prevent="
|
|
9
|
+
@keydown.down.prevent="onKeyDownOrUp"
|
|
10
|
+
@keydown.up.prevent="onKeyDownOrUp"
|
|
18
11
|
@keydown.esc.stop="onKeyEsc"
|
|
19
12
|
>
|
|
20
|
-
<span
|
|
21
|
-
:class="{
|
|
22
|
-
'listbox-button__text': headless,
|
|
23
|
-
'text-left': !headless,
|
|
24
|
-
}"
|
|
25
|
-
>
|
|
13
|
+
<span :class="[headless ? 'listbox-button__text' : 'text-left truncate']">
|
|
26
14
|
<slot></slot>
|
|
27
15
|
</span>
|
|
28
|
-
<span
|
|
29
|
-
:class="{
|
|
30
|
-
'listbox-button__icon': headless,
|
|
31
|
-
}"
|
|
32
|
-
>
|
|
16
|
+
<span :class="[headless ? 'listbox-button__icon' : 'shrink-0']">
|
|
33
17
|
<IconChevronDown
|
|
34
18
|
class="transition-transform"
|
|
35
19
|
:class="{ 'rotate-180': expanded }"
|
|
@@ -39,6 +23,7 @@
|
|
|
39
23
|
</template>
|
|
40
24
|
|
|
41
25
|
<script>
|
|
26
|
+
import { formControlMixin } from '../../../mixins/form-control';
|
|
42
27
|
import { IconChevronDown } from '@veritree/icons';
|
|
43
28
|
|
|
44
29
|
export default {
|
|
@@ -46,17 +31,13 @@ export default {
|
|
|
46
31
|
|
|
47
32
|
components: { IconChevronDown },
|
|
48
33
|
|
|
49
|
-
|
|
34
|
+
mixins: [formControlMixin],
|
|
50
35
|
|
|
51
|
-
|
|
52
|
-
headless: {
|
|
53
|
-
type: Boolean,
|
|
54
|
-
default: false,
|
|
55
|
-
},
|
|
56
|
-
},
|
|
36
|
+
inject: ['apiListbox'],
|
|
57
37
|
|
|
58
38
|
data() {
|
|
59
39
|
return {
|
|
40
|
+
name: 'listbox-button',
|
|
60
41
|
expanded: false,
|
|
61
42
|
hasPopup: false,
|
|
62
43
|
};
|
|
@@ -64,19 +45,15 @@ export default {
|
|
|
64
45
|
|
|
65
46
|
computed: {
|
|
66
47
|
id() {
|
|
67
|
-
return `listbox-trigger-${this.
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
dark() {
|
|
71
|
-
return this.api().isDark;
|
|
48
|
+
return `listbox-trigger-${this.apiListbox().id}`;
|
|
72
49
|
},
|
|
73
50
|
|
|
74
51
|
componentContent() {
|
|
75
|
-
return this.
|
|
52
|
+
return this.apiListbox().componentContent;
|
|
76
53
|
},
|
|
77
54
|
|
|
78
55
|
items() {
|
|
79
|
-
return this.
|
|
56
|
+
return this.apiListbox().items;
|
|
80
57
|
},
|
|
81
58
|
|
|
82
59
|
firstMenuItem() {
|
|
@@ -90,101 +67,85 @@ export default {
|
|
|
90
67
|
|
|
91
68
|
mounted() {
|
|
92
69
|
const trigger = {
|
|
93
|
-
toggleExpanded: this.toggleExpanded,
|
|
94
70
|
el: this.$el,
|
|
71
|
+
cancel: this.cancel,
|
|
95
72
|
focus: this.focus,
|
|
96
73
|
id: this.id,
|
|
97
|
-
onClick: this.onClick,
|
|
98
74
|
};
|
|
99
75
|
|
|
100
|
-
this.
|
|
76
|
+
this.apiListbox().registerTrigger(trigger);
|
|
101
77
|
},
|
|
102
78
|
|
|
103
79
|
methods: {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
this.expanded = true;
|
|
109
|
-
this.componentContent.show();
|
|
110
|
-
},
|
|
111
|
-
|
|
112
|
-
focus() {
|
|
113
|
-
this.$el.focus();
|
|
114
|
-
},
|
|
115
|
-
|
|
116
|
-
toggleExpanded() {
|
|
117
|
-
if (!this.expanded) return;
|
|
118
|
-
this.expanded = false;
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* On click, do the following:
|
|
123
|
-
*
|
|
124
|
-
* 1. Toggle aria expanded attribute/state
|
|
125
|
-
* 2. Open the menu if it's closed
|
|
126
|
-
* 3. Close the menu if it's open
|
|
127
|
-
*/
|
|
128
|
-
onClick(e) {
|
|
129
|
-
if (!this.componentContent) return;
|
|
80
|
+
init(e) {
|
|
81
|
+
if (!this.componentContent) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
130
84
|
|
|
131
85
|
if (this.expanded) {
|
|
132
|
-
this.
|
|
86
|
+
this.cancel();
|
|
133
87
|
return;
|
|
134
88
|
}
|
|
135
89
|
|
|
90
|
+
this.expanded = true;
|
|
91
|
+
|
|
136
92
|
// delay stop propagation to close other visible
|
|
137
93
|
// dropdowns and delay click event to control
|
|
138
94
|
// this dropdown visibility
|
|
139
95
|
setTimeout(() => e.stopImmediatePropagation(), 50);
|
|
140
|
-
setTimeout(() => this.
|
|
96
|
+
setTimeout(() => this.showComponentContent(), 100);
|
|
141
97
|
},
|
|
142
98
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
*/
|
|
149
|
-
onKeyArrowDown() {
|
|
150
|
-
if (!this.componentContent) return;
|
|
99
|
+
cancel() {
|
|
100
|
+
if (!this.componentContent) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
this.expanded = false;
|
|
151
104
|
|
|
152
|
-
this.
|
|
105
|
+
this.hideComponentContent();
|
|
106
|
+
},
|
|
153
107
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
// be available when the content is fully visible.
|
|
157
|
-
this.$nextTick(() => {
|
|
158
|
-
setTimeout(() => this.firstMenuItem.focus(), 150);
|
|
159
|
-
});
|
|
108
|
+
focus() {
|
|
109
|
+
this.$el.focus();
|
|
160
110
|
},
|
|
161
111
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
* 1. if the menu is not expanded, expand it and focus the last menu item
|
|
166
|
-
* 2. if the menu is expanded, focus the last menu item
|
|
167
|
-
*/
|
|
168
|
-
onKeyArrowUp() {
|
|
169
|
-
if (!this.componentContent) return;
|
|
112
|
+
showComponentContent() {
|
|
113
|
+
this.componentContent.show();
|
|
114
|
+
},
|
|
170
115
|
|
|
171
|
-
|
|
116
|
+
hideComponentContent() {
|
|
117
|
+
this.componentContent.hide();
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
onClick(e) {
|
|
121
|
+
this.init(e);
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
onKeyDownOrUp(e) {
|
|
125
|
+
if (!this.expanded) {
|
|
126
|
+
this.$el.click(e);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const keyCode = e.code;
|
|
130
|
+
const listItemPosition =
|
|
131
|
+
keyCode === 'ArrowDown'
|
|
132
|
+
? 'firstMenuItem'
|
|
133
|
+
: keyCode === 'ArrowUp'
|
|
134
|
+
? 'lastMenuItem'
|
|
135
|
+
: null;
|
|
172
136
|
|
|
173
137
|
// settimeout here is delaying the focusing the element
|
|
174
138
|
// since it is not rendered yet. All items will only
|
|
175
139
|
// be available when the content is fully visible.
|
|
176
140
|
this.$nextTick(() => {
|
|
177
|
-
setTimeout(() => this.
|
|
141
|
+
setTimeout(() => this[listItemPosition].focus(), 100);
|
|
178
142
|
});
|
|
179
143
|
},
|
|
180
144
|
|
|
145
|
+
// change it to a better name or move the methods inside to another function
|
|
181
146
|
onKeyEsc() {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (this.expanded) {
|
|
185
|
-
this.toggleExpanded();
|
|
186
|
-
this.componentContent.hide();
|
|
187
|
-
}
|
|
147
|
+
this.cancel();
|
|
148
|
+
this.focus();
|
|
188
149
|
},
|
|
189
150
|
},
|
|
190
151
|
};
|
|
@@ -20,9 +20,7 @@ export default {
|
|
|
20
20
|
|
|
21
21
|
provide() {
|
|
22
22
|
return {
|
|
23
|
-
|
|
24
|
-
const { dark: isDark, headless: isHeadless, right: isRight } = this;
|
|
25
|
-
|
|
23
|
+
apiPopover: () => {
|
|
26
24
|
const registerTrigger = (trigger) => {
|
|
27
25
|
if (!trigger) return;
|
|
28
26
|
this.componentTrigger = trigger;
|
|
@@ -35,9 +33,6 @@ export default {
|
|
|
35
33
|
|
|
36
34
|
return {
|
|
37
35
|
id: this.componentId,
|
|
38
|
-
isDark,
|
|
39
|
-
isHeadless,
|
|
40
|
-
isRight,
|
|
41
36
|
component: this.component,
|
|
42
37
|
componentTrigger: this.componentTrigger,
|
|
43
38
|
componentContent: this.componentContent,
|
|
@@ -53,14 +48,6 @@ export default {
|
|
|
53
48
|
type: Boolean,
|
|
54
49
|
default: false,
|
|
55
50
|
},
|
|
56
|
-
dark: {
|
|
57
|
-
type: Boolean,
|
|
58
|
-
default: false,
|
|
59
|
-
},
|
|
60
|
-
right: {
|
|
61
|
-
type: Boolean,
|
|
62
|
-
default: false,
|
|
63
|
-
},
|
|
64
51
|
},
|
|
65
52
|
|
|
66
53
|
data() {
|
|
@@ -3,96 +3,45 @@
|
|
|
3
3
|
:visible="visible"
|
|
4
4
|
:id="id"
|
|
5
5
|
:headless="headless"
|
|
6
|
-
:class="{
|
|
7
|
-
|
|
8
|
-
}"
|
|
6
|
+
:class="{ 'popover-content': headless }"
|
|
7
|
+
:floating-ui-class="floatingUiClass"
|
|
9
8
|
>
|
|
10
9
|
<slot></slot>
|
|
11
10
|
</FloatingUi>
|
|
12
11
|
</template>
|
|
13
12
|
|
|
14
13
|
<script>
|
|
15
|
-
import
|
|
14
|
+
import { floatingUiContentMixin } from '../../../mixins/floating-ui-content';
|
|
16
15
|
|
|
17
16
|
export default {
|
|
18
17
|
name: 'VTPopoverContent',
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
FloatingUi,
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
inject: ['api'],
|
|
19
|
+
mixins: [floatingUiContentMixin],
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
visible: false,
|
|
29
|
-
};
|
|
30
|
-
},
|
|
21
|
+
inject: ['apiPopover'],
|
|
31
22
|
|
|
32
23
|
computed: {
|
|
33
24
|
id() {
|
|
34
|
-
return `popover-content-${this.
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
headless() {
|
|
38
|
-
return this.api().isHeadless;
|
|
25
|
+
return `popover-content-${this.apiPopover().id}`;
|
|
39
26
|
},
|
|
40
27
|
|
|
41
28
|
component() {
|
|
42
|
-
return this.
|
|
29
|
+
return this.apiPopover().component;
|
|
43
30
|
},
|
|
44
31
|
|
|
45
32
|
componentTrigger() {
|
|
46
|
-
return this.
|
|
33
|
+
return this.apiPopover().componentTrigger;
|
|
47
34
|
},
|
|
48
35
|
},
|
|
49
36
|
|
|
50
37
|
mounted() {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
e.stopPropagation();
|
|
60
|
-
|
|
61
|
-
if (this.visible && !this.$el.contains(e.target)) {
|
|
62
|
-
this.hide();
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
},
|
|
66
|
-
|
|
67
|
-
destroyed() {
|
|
68
|
-
// T-325 Create a directive or mixin for this
|
|
69
|
-
document.removeEventListener('click', this.componentTrigger.onClick);
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
methods: {
|
|
73
|
-
show() {
|
|
74
|
-
if (this.visible) return;
|
|
75
|
-
|
|
76
|
-
this.visible = true;
|
|
77
|
-
|
|
78
|
-
this.$nextTick(() => {
|
|
79
|
-
this.component.setActive();
|
|
80
|
-
});
|
|
81
|
-
},
|
|
82
|
-
|
|
83
|
-
hide() {
|
|
84
|
-
if (!this.visible) return;
|
|
85
|
-
|
|
86
|
-
this.visible = false;
|
|
87
|
-
|
|
88
|
-
this.$nextTick(() => {
|
|
89
|
-
this.componentTrigger.focus();
|
|
38
|
+
const content = {
|
|
39
|
+
id: this.id,
|
|
40
|
+
show: this.show,
|
|
41
|
+
hide: this.hide,
|
|
42
|
+
};
|
|
90
43
|
|
|
91
|
-
|
|
92
|
-
this.component.clearActive();
|
|
93
|
-
}, 100);
|
|
94
|
-
});
|
|
95
|
-
},
|
|
44
|
+
this.apiPopover().registerContent(content);
|
|
96
45
|
},
|
|
97
46
|
};
|
|
98
47
|
</script>
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
:class="{
|
|
4
4
|
PopoverDivider: headless,
|
|
5
5
|
'h-[1px]': !headless,
|
|
6
|
-
'bg-white': !dark,
|
|
7
|
-
'bg-fd-500': dark,
|
|
8
6
|
}"
|
|
9
7
|
></div>
|
|
10
8
|
</template>
|
|
@@ -13,15 +11,10 @@
|
|
|
13
11
|
export default {
|
|
14
12
|
name: 'VTPopoverDivider',
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return this.api().isDark;
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
headless() {
|
|
24
|
-
return this.api().isHeadless;
|
|
14
|
+
props: {
|
|
15
|
+
headless: {
|
|
16
|
+
type: Boolean,
|
|
17
|
+
default: false,
|
|
25
18
|
},
|
|
26
19
|
},
|
|
27
20
|
};
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<component
|
|
3
3
|
:is="as"
|
|
4
|
-
:class="
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}"
|
|
4
|
+
:class="[
|
|
5
|
+
// default styles
|
|
6
|
+
headless
|
|
7
|
+
? 'popover-item'
|
|
8
|
+
: 'relative z-10 -mx-3 flex items-center gap-2 px-3 py-2 text-inherit no-underline hover:bg-secondary-200/10',
|
|
9
|
+
]"
|
|
11
10
|
@click="onClick"
|
|
12
11
|
>
|
|
13
12
|
<slot></slot>
|
|
@@ -18,26 +17,30 @@
|
|
|
18
17
|
export default {
|
|
19
18
|
name: 'VTPopoverItem',
|
|
20
19
|
|
|
21
|
-
inject: ['
|
|
20
|
+
inject: ['apiPopover'],
|
|
22
21
|
|
|
23
22
|
props: {
|
|
24
|
-
|
|
25
|
-
type:
|
|
26
|
-
default:
|
|
23
|
+
headless: {
|
|
24
|
+
type: Boolean,
|
|
25
|
+
default: false,
|
|
27
26
|
},
|
|
28
27
|
href: {
|
|
29
28
|
type: String,
|
|
30
29
|
default: null,
|
|
31
30
|
},
|
|
31
|
+
to: {
|
|
32
|
+
type: [String, Object],
|
|
33
|
+
default: null,
|
|
34
|
+
},
|
|
32
35
|
},
|
|
33
36
|
|
|
34
37
|
computed: {
|
|
35
38
|
dark() {
|
|
36
|
-
return this.
|
|
39
|
+
return this.apiPopover().isDark;
|
|
37
40
|
},
|
|
38
41
|
|
|
39
42
|
headless() {
|
|
40
|
-
return this.
|
|
43
|
+
return this.apiPopover().isHeadless;
|
|
41
44
|
},
|
|
42
45
|
|
|
43
46
|
as() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div :id="id" @keydown.esc.prevent="
|
|
2
|
+
<div :id="id" @keydown.esc.prevent="onKeyEsc">
|
|
3
3
|
<slot></slot>
|
|
4
4
|
</div>
|
|
5
5
|
</template>
|
|
@@ -8,52 +8,149 @@
|
|
|
8
8
|
export default {
|
|
9
9
|
name: 'VTPopoverTrigger',
|
|
10
10
|
|
|
11
|
-
inject: ['
|
|
11
|
+
inject: ['apiPopover'],
|
|
12
|
+
|
|
13
|
+
data() {
|
|
14
|
+
return {
|
|
15
|
+
expanded: false,
|
|
16
|
+
hasPopup: false,
|
|
17
|
+
controls: null,
|
|
18
|
+
trigger: null,
|
|
19
|
+
};
|
|
20
|
+
},
|
|
12
21
|
|
|
13
22
|
computed: {
|
|
14
23
|
id() {
|
|
15
|
-
return `popover-trigger-${this.
|
|
24
|
+
return `popover-trigger-${this.apiPopover().id}`;
|
|
16
25
|
},
|
|
17
26
|
|
|
18
27
|
componentContent() {
|
|
19
|
-
return this.
|
|
28
|
+
return this.apiPopover().componentContent;
|
|
20
29
|
},
|
|
21
30
|
},
|
|
22
31
|
|
|
23
32
|
mounted() {
|
|
24
|
-
|
|
25
|
-
|
|
33
|
+
const trigger = {
|
|
34
|
+
id: this.id,
|
|
35
|
+
el: this.$el,
|
|
36
|
+
cancel: this.cancel,
|
|
37
|
+
focus: this.focus,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
this.apiPopover().registerTrigger(trigger);
|
|
41
|
+
|
|
42
|
+
this.setTrigger();
|
|
43
|
+
this.addTriggerEvents();
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
destroyed() {
|
|
47
|
+
this.trigger.removeEventListener('click', this.onClick);
|
|
26
48
|
},
|
|
27
49
|
|
|
28
50
|
methods: {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
51
|
+
setTrigger() {
|
|
52
|
+
this.trigger = this.$el.querySelector(':first-child');
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Add event listener to slot element
|
|
57
|
+
*
|
|
58
|
+
* The click event has to be added to the slot child element
|
|
59
|
+
* since we are not setting the onclick on the component
|
|
60
|
+
* itself.
|
|
61
|
+
*
|
|
62
|
+
* Slot must have only one child element. It avoids
|
|
63
|
+
* errors related to adding the event listener.
|
|
64
|
+
*/
|
|
65
|
+
addTriggerEvents() {
|
|
66
|
+
this.trigger.addEventListener('click', (e) => {
|
|
67
|
+
this.onClick(e);
|
|
68
|
+
});
|
|
69
|
+
},
|
|
32
70
|
|
|
33
|
-
|
|
34
|
-
|
|
71
|
+
init(e) {
|
|
72
|
+
if (!this.componentContent) {
|
|
35
73
|
return;
|
|
36
74
|
}
|
|
37
75
|
|
|
38
|
-
|
|
39
|
-
this.
|
|
40
|
-
|
|
41
|
-
}
|
|
76
|
+
if (this.expanded) {
|
|
77
|
+
this.cancel();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.expanded = true;
|
|
82
|
+
|
|
83
|
+
// delay stop propagation to close other visible
|
|
84
|
+
// dropdowns and delay click event to control
|
|
85
|
+
// this dropdown visibility
|
|
86
|
+
setTimeout(() => e.stopImmediatePropagation(), 50);
|
|
87
|
+
setTimeout(() => this.showComponentContent(), 100);
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
cancel() {
|
|
91
|
+
if (!this.componentContent) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.expanded = false;
|
|
96
|
+
|
|
97
|
+
this.hideComponentContent();
|
|
42
98
|
},
|
|
43
99
|
|
|
44
100
|
focus() {
|
|
45
|
-
this
|
|
101
|
+
if (this.trigger) this.trigger.focus();
|
|
46
102
|
},
|
|
47
103
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
else this.componentContent.show();
|
|
104
|
+
showComponentContent() {
|
|
105
|
+
this.componentContent.show();
|
|
51
106
|
},
|
|
52
107
|
|
|
53
|
-
|
|
54
|
-
if (!this.componentContent) return;
|
|
108
|
+
hideComponentContent() {
|
|
55
109
|
this.componentContent.hide();
|
|
56
110
|
},
|
|
111
|
+
|
|
112
|
+
toggleAriaHasPopup() {
|
|
113
|
+
if (this.expanded) {
|
|
114
|
+
this.hasPopup = this.componentContent !== null;
|
|
115
|
+
this.controls = this.hasPopup ? this.componentContent.id : null;
|
|
116
|
+
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
this.hasPopup = null;
|
|
121
|
+
this.controls = null;
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
onClick(e) {
|
|
125
|
+
this.init(e);
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
onKeyDownOrUp(e) {
|
|
129
|
+
if (!this.expanded) {
|
|
130
|
+
this.$el.click(e);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const keyCode = e.code;
|
|
134
|
+
const listItemPosition =
|
|
135
|
+
keyCode === 'ArrowDown'
|
|
136
|
+
? 'firstMenuItem'
|
|
137
|
+
: keyCode === 'ArrowUp'
|
|
138
|
+
? 'lastMenuItem'
|
|
139
|
+
: null;
|
|
140
|
+
|
|
141
|
+
// settimeout here is delaying the focusing the element
|
|
142
|
+
// since it is not rendered yet. All items will only
|
|
143
|
+
// be available when the content is fully visible.
|
|
144
|
+
this.$nextTick(() => {
|
|
145
|
+
setTimeout(() => this[listItemPosition].focus(), 100);
|
|
146
|
+
});
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
// change it to a better name or move the methods inside to another function
|
|
150
|
+
onKeyEsc() {
|
|
151
|
+
this.cancel();
|
|
152
|
+
this.focus();
|
|
153
|
+
},
|
|
57
154
|
},
|
|
58
155
|
};
|
|
59
156
|
</script>
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
:class="[
|
|
16
16
|
headless
|
|
17
17
|
? null
|
|
18
|
-
: 'absolute z-50 grid
|
|
18
|
+
: 'absolute z-50 grid overflow-x-hidden rounded-md py-2 px-3 border-gray-100 bg-white shadow-300',
|
|
19
|
+
floatingUiClass ? floatingUiClass : null,
|
|
19
20
|
]"
|
|
20
21
|
>
|
|
21
22
|
<slot></slot>
|
|
@@ -45,6 +46,10 @@ export default {
|
|
|
45
46
|
type: Boolean,
|
|
46
47
|
default: false,
|
|
47
48
|
},
|
|
49
|
+
floatingUiClass: {
|
|
50
|
+
type: [String, Function],
|
|
51
|
+
default: null,
|
|
52
|
+
},
|
|
48
53
|
},
|
|
49
54
|
|
|
50
55
|
methods: {
|
package/src/utils/components.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
* @param {HTMLElement} el
|
|
4
|
-
* @param {HTMLElement} parent
|
|
5
|
-
*/
|
|
6
|
-
export const scrollElementIntoView = (el, parent) => {
|
|
7
|
-
// this works better than scrollIntoView
|
|
8
|
-
if (parent.scrollHeight <= parent.clientHeight) return;
|
|
9
|
-
|
|
10
|
-
const scrollBottom = parent.clientHeight + parent.scrollTop;
|
|
11
|
-
const elBottom = el.offsetTop + el.offsetHeight;
|
|
12
|
-
|
|
13
|
-
if (elBottom > scrollBottom) {
|
|
14
|
-
parent.scrollTop = elBottom - parent.clientHeight;
|
|
15
|
-
} else if (el.offsetTop < parent.scrollTop) {
|
|
16
|
-
parent.scrollTop = el.offsetTop;
|
|
17
|
-
}
|
|
18
|
-
};
|