@veritree/ui 0.21.1-6 → 0.21.1-7
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 +81 -0
- package/mixins/floating-ui.js +3 -1
- package/package.json +1 -1
- package/src/components/DropdownMenu/VTDropdownMenu.vue +1 -11
- package/src/components/DropdownMenu/VTDropdownMenuContent.vue +8 -70
- package/src/components/DropdownMenu/VTDropdownMenuDivider.vue +8 -5
- package/src/components/DropdownMenu/VTDropdownMenuItem.vue +31 -22
- package/src/components/DropdownMenu/VTDropdownMenuLabel.vue +3 -3
- package/src/components/DropdownMenu/VTDropdownMenuTrigger.vue +6 -8
- package/src/components/Listbox/VTListbox.vue +1 -4
- package/src/components/Listbox/VTListboxContent.vue +8 -71
- package/src/components/Listbox/VTListboxItem.vue +31 -21
- package/src/components/Listbox/VTListboxLabel.vue +0 -10
- package/src/components/Listbox/VTListboxList.vue +10 -13
- package/src/components/Listbox/VTListboxSearch.vue +5 -5
- package/src/components/Listbox/VTListboxTrigger.vue +6 -6
- 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 +5 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import FloatingUi from '../src/components/Utils/FloatingUi.vue';
|
|
2
|
+
|
|
3
|
+
export const floatingUiContentMixin = {
|
|
4
|
+
components: {
|
|
5
|
+
FloatingUi,
|
|
6
|
+
},
|
|
7
|
+
|
|
8
|
+
props: {
|
|
9
|
+
headless: {
|
|
10
|
+
type: Boolean,
|
|
11
|
+
default: false,
|
|
12
|
+
},
|
|
13
|
+
floatingUiClass: {
|
|
14
|
+
type: [String, Function],
|
|
15
|
+
default: null,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
data() {
|
|
20
|
+
return {
|
|
21
|
+
visible: false,
|
|
22
|
+
el: null,
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
mounted() {
|
|
27
|
+
document.addEventListener('click', (e) => this.onDocumentClick(e));
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
destroyed() {
|
|
31
|
+
document.removeEventListener('click', this.onDocumentClick);
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
methods: {
|
|
35
|
+
show() {
|
|
36
|
+
if (this.visible) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
this.visible = true;
|
|
41
|
+
|
|
42
|
+
this.$nextTick(() => {
|
|
43
|
+
this.component.setActive();
|
|
44
|
+
|
|
45
|
+
setTimeout(() => {
|
|
46
|
+
this.el = document.getElementById(this.id);
|
|
47
|
+
this.$emit('shown');
|
|
48
|
+
}, 100);
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
hide() {
|
|
53
|
+
if (!this.visible) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.visible = false;
|
|
58
|
+
|
|
59
|
+
this.$nextTick(() => {
|
|
60
|
+
this.component.clearActive();
|
|
61
|
+
|
|
62
|
+
setTimeout(() => {
|
|
63
|
+
this.el = document.getElementById(this.id);
|
|
64
|
+
this.$emit('hidden');
|
|
65
|
+
}, 100);
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
onDocumentClick(e) {
|
|
70
|
+
if (!e) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
e.stopPropagation();
|
|
75
|
+
|
|
76
|
+
if (this.visible && !this.el.contains(e.target)) {
|
|
77
|
+
this.componentTrigger.cancel();
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
}
|
package/mixins/floating-ui.js
CHANGED
|
@@ -43,6 +43,8 @@ export const floatingUiMixin = {
|
|
|
43
43
|
const trigger = document.getElementById(this.componentTrigger.id);
|
|
44
44
|
const content = document.getElementById(this.componentContent.id);
|
|
45
45
|
|
|
46
|
+
// console.log(this.placement);
|
|
47
|
+
|
|
46
48
|
computePosition(trigger, content, {
|
|
47
49
|
placement: this.placement,
|
|
48
50
|
middleware: [
|
|
@@ -52,7 +54,7 @@ export const floatingUiMixin = {
|
|
|
52
54
|
size({
|
|
53
55
|
apply({ rects }) {
|
|
54
56
|
Object.assign(content.style, {
|
|
55
|
-
|
|
57
|
+
minWidth: `${rects.reference.width}px`,
|
|
56
58
|
});
|
|
57
59
|
},
|
|
58
60
|
}),
|
package/package.json
CHANGED
|
@@ -15,7 +15,7 @@ export default {
|
|
|
15
15
|
|
|
16
16
|
provide() {
|
|
17
17
|
return {
|
|
18
|
-
|
|
18
|
+
apiDropdownMenu: () => {
|
|
19
19
|
const { dark: isDark, headless: isHeadless } = this;
|
|
20
20
|
|
|
21
21
|
const registerTrigger = (trigger) => {
|
|
@@ -39,8 +39,6 @@ export default {
|
|
|
39
39
|
|
|
40
40
|
return {
|
|
41
41
|
id: this.componentId,
|
|
42
|
-
isDark,
|
|
43
|
-
isHeadless,
|
|
44
42
|
component: this.component,
|
|
45
43
|
componentTrigger: this.componentTrigger,
|
|
46
44
|
componentContent: this.componentContent,
|
|
@@ -59,14 +57,6 @@ export default {
|
|
|
59
57
|
type: Boolean,
|
|
60
58
|
default: false,
|
|
61
59
|
},
|
|
62
|
-
dark: {
|
|
63
|
-
type: Boolean,
|
|
64
|
-
default: false,
|
|
65
|
-
},
|
|
66
|
-
right: {
|
|
67
|
-
type: Boolean,
|
|
68
|
-
default: false,
|
|
69
|
-
},
|
|
70
60
|
},
|
|
71
61
|
|
|
72
62
|
data() {
|
|
@@ -4,58 +4,36 @@
|
|
|
4
4
|
:id="id"
|
|
5
5
|
:headless="headless"
|
|
6
6
|
:class="{ 'dropdown-menu-content': headless }"
|
|
7
|
+
:floating-ui-class="floatingUiClass"
|
|
7
8
|
>
|
|
8
9
|
<slot></slot>
|
|
9
10
|
</FloatingUi>
|
|
10
11
|
</template>
|
|
11
12
|
|
|
12
13
|
<script>
|
|
13
|
-
import
|
|
14
|
+
import { floatingUiContentMixin } from '../../../mixins/floating-ui-content';
|
|
14
15
|
|
|
15
16
|
export default {
|
|
16
17
|
name: 'VTDropdownMenuContent',
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
FloatingUi,
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
inject: ['api'],
|
|
19
|
+
mixins: [floatingUiContentMixin],
|
|
23
20
|
|
|
24
|
-
|
|
25
|
-
headless: {
|
|
26
|
-
type: Boolean,
|
|
27
|
-
default: false,
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
data() {
|
|
32
|
-
return {
|
|
33
|
-
visible: false,
|
|
34
|
-
};
|
|
35
|
-
},
|
|
21
|
+
inject: ['apiDropdownMenu'],
|
|
36
22
|
|
|
37
23
|
computed: {
|
|
38
24
|
id() {
|
|
39
|
-
return `dropdown-menu-content-${this.
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
el() {
|
|
43
|
-
return document.getElementById(this.id);
|
|
25
|
+
return `dropdown-menu-content-${this.apiDropdownMenu().id}`;
|
|
44
26
|
},
|
|
45
27
|
|
|
46
28
|
component() {
|
|
47
|
-
return this.
|
|
29
|
+
return this.apiDropdownMenu().component;
|
|
48
30
|
},
|
|
49
31
|
|
|
50
32
|
componentTrigger() {
|
|
51
|
-
return this.
|
|
33
|
+
return this.apiDropdownMenu().componentTrigger;
|
|
52
34
|
},
|
|
53
35
|
},
|
|
54
36
|
|
|
55
|
-
created() {
|
|
56
|
-
console.log(this.$attrs);
|
|
57
|
-
},
|
|
58
|
-
|
|
59
37
|
mounted() {
|
|
60
38
|
const content = {
|
|
61
39
|
id: this.id,
|
|
@@ -63,47 +41,7 @@ export default {
|
|
|
63
41
|
show: this.show,
|
|
64
42
|
};
|
|
65
43
|
|
|
66
|
-
this.
|
|
67
|
-
|
|
68
|
-
document.addEventListener('click', (e) => {
|
|
69
|
-
if (!e) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
e.stopPropagation();
|
|
74
|
-
|
|
75
|
-
if (this.visible && !this.el.contains(e.target)) {
|
|
76
|
-
this.componentTrigger.cancel();
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
destroyed() {
|
|
82
|
-
document.removeEventListener('click', this.componentTrigger.cancel);
|
|
83
|
-
},
|
|
84
|
-
|
|
85
|
-
methods: {
|
|
86
|
-
show() {
|
|
87
|
-
if (this.visible) return;
|
|
88
|
-
|
|
89
|
-
this.visible = true;
|
|
90
|
-
|
|
91
|
-
this.$nextTick(() => {
|
|
92
|
-
this.component.setActive();
|
|
93
|
-
this.$emit('shown');
|
|
94
|
-
});
|
|
95
|
-
},
|
|
96
|
-
|
|
97
|
-
hide() {
|
|
98
|
-
if (!this.visible) return;
|
|
99
|
-
|
|
100
|
-
this.visible = false;
|
|
101
|
-
|
|
102
|
-
this.$nextTick(() => {
|
|
103
|
-
this.component.clearActive();
|
|
104
|
-
this.$emit('hidden');
|
|
105
|
-
});
|
|
106
|
-
},
|
|
44
|
+
this.apiDropdownMenu().registerContent(content);
|
|
107
45
|
},
|
|
108
46
|
};
|
|
109
47
|
</script>
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
:class="{
|
|
4
4
|
PopoverDivider: headless,
|
|
5
5
|
'-mx-3 my-2 h-[1px]': !headless,
|
|
6
|
-
'bg-gray-200': !dark,
|
|
7
|
-
'bg-fd-500': dark,
|
|
8
6
|
}"
|
|
9
7
|
></div>
|
|
10
8
|
</template>
|
|
@@ -13,15 +11,20 @@
|
|
|
13
11
|
export default {
|
|
14
12
|
name: 'VTDropdownMenuDivider',
|
|
15
13
|
|
|
16
|
-
inject: ['
|
|
14
|
+
inject: ['apiDropdownMenu'],
|
|
15
|
+
|
|
16
|
+
headless: {
|
|
17
|
+
type: Boolean,
|
|
18
|
+
default: false,
|
|
19
|
+
},
|
|
17
20
|
|
|
18
21
|
computed: {
|
|
19
22
|
dark() {
|
|
20
|
-
return this.
|
|
23
|
+
return this.apiDropdownMenu().isDark;
|
|
21
24
|
},
|
|
22
25
|
|
|
23
26
|
headless() {
|
|
24
|
-
return this.
|
|
27
|
+
return this.apiDropdownMenu().isHeadless;
|
|
25
28
|
},
|
|
26
29
|
},
|
|
27
30
|
};
|
|
@@ -3,15 +3,28 @@
|
|
|
3
3
|
:is="as"
|
|
4
4
|
:id="id"
|
|
5
5
|
:to="to"
|
|
6
|
-
:class="
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
+
]"
|
|
15
28
|
:tabindex="tabIndex"
|
|
16
29
|
:aria-disabled="disabled"
|
|
17
30
|
role="menuitem"
|
|
@@ -34,7 +47,7 @@ import { genId } from '../../utils/ids';
|
|
|
34
47
|
export default {
|
|
35
48
|
name: 'VTDropdownMenuItem',
|
|
36
49
|
|
|
37
|
-
inject: ['
|
|
50
|
+
inject: ['apiDropdownMenu'],
|
|
38
51
|
|
|
39
52
|
props: {
|
|
40
53
|
to: {
|
|
@@ -49,6 +62,10 @@ export default {
|
|
|
49
62
|
type: Boolean,
|
|
50
63
|
default: false,
|
|
51
64
|
},
|
|
65
|
+
headless: {
|
|
66
|
+
type: Boolean,
|
|
67
|
+
default: false,
|
|
68
|
+
},
|
|
52
69
|
},
|
|
53
70
|
|
|
54
71
|
data() {
|
|
@@ -61,15 +78,7 @@ export default {
|
|
|
61
78
|
|
|
62
79
|
computed: {
|
|
63
80
|
id() {
|
|
64
|
-
return `dropdown-menu-item-${this.
|
|
65
|
-
},
|
|
66
|
-
|
|
67
|
-
dark() {
|
|
68
|
-
return this.api().isDark;
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
headless() {
|
|
72
|
-
return this.api().isHeadless;
|
|
81
|
+
return `dropdown-menu-item-${this.apiDropdownMenu().id}-${genId()}`;
|
|
73
82
|
},
|
|
74
83
|
|
|
75
84
|
as() {
|
|
@@ -77,7 +86,7 @@ export default {
|
|
|
77
86
|
},
|
|
78
87
|
|
|
79
88
|
items() {
|
|
80
|
-
return this.
|
|
89
|
+
return this.apiDropdownMenu().items;
|
|
81
90
|
},
|
|
82
91
|
|
|
83
92
|
el() {
|
|
@@ -85,7 +94,7 @@ export default {
|
|
|
85
94
|
},
|
|
86
95
|
|
|
87
96
|
componentTrigger() {
|
|
88
|
-
return this.
|
|
97
|
+
return this.apiDropdownMenu().componentTrigger;
|
|
89
98
|
},
|
|
90
99
|
},
|
|
91
100
|
|
|
@@ -96,7 +105,7 @@ export default {
|
|
|
96
105
|
focus: this.focus,
|
|
97
106
|
};
|
|
98
107
|
|
|
99
|
-
this.
|
|
108
|
+
this.apiDropdownMenu().registerItem(item);
|
|
100
109
|
|
|
101
110
|
this.index = this.items.length - 1;
|
|
102
111
|
},
|
|
@@ -15,15 +15,15 @@
|
|
|
15
15
|
export default {
|
|
16
16
|
name: 'VTDropdownMenuLabel',
|
|
17
17
|
|
|
18
|
-
inject: ['
|
|
18
|
+
inject: ['apiDropdownMenu'],
|
|
19
19
|
|
|
20
20
|
computed: {
|
|
21
21
|
dark() {
|
|
22
|
-
return this.
|
|
22
|
+
return this.apiDropdownMenu().isDark;
|
|
23
23
|
},
|
|
24
24
|
|
|
25
25
|
headless() {
|
|
26
|
-
return this.
|
|
26
|
+
return this.apiDropdownMenu().isHeadless;
|
|
27
27
|
},
|
|
28
28
|
},
|
|
29
29
|
};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
:aria-controls="controls"
|
|
7
7
|
@keydown.down.prevent="onKeyDownOrUp"
|
|
8
8
|
@keydown.up.prevent="onKeyDownOrUp"
|
|
9
|
-
@keydown.esc.stop="
|
|
9
|
+
@keydown.esc.stop="onKeyEsc"
|
|
10
10
|
>
|
|
11
11
|
<slot></slot>
|
|
12
12
|
</div>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
export default {
|
|
17
17
|
name: 'VTDropdownMenuTrigger',
|
|
18
18
|
|
|
19
|
-
inject: ['
|
|
19
|
+
inject: ['apiDropdownMenu'],
|
|
20
20
|
|
|
21
21
|
data() {
|
|
22
22
|
return {
|
|
@@ -29,15 +29,15 @@ export default {
|
|
|
29
29
|
|
|
30
30
|
computed: {
|
|
31
31
|
id() {
|
|
32
|
-
return `dropdown-menu-trigger-${this.
|
|
32
|
+
return `dropdown-menu-trigger-${this.apiDropdownMenu().id}`;
|
|
33
33
|
},
|
|
34
34
|
|
|
35
35
|
componentContent() {
|
|
36
|
-
return this.
|
|
36
|
+
return this.apiDropdownMenu().componentContent;
|
|
37
37
|
},
|
|
38
38
|
|
|
39
39
|
items() {
|
|
40
|
-
return this.
|
|
40
|
+
return this.apiDropdownMenu().items;
|
|
41
41
|
},
|
|
42
42
|
|
|
43
43
|
firstMenuItem() {
|
|
@@ -52,8 +52,6 @@ export default {
|
|
|
52
52
|
watch: {
|
|
53
53
|
expanded() {
|
|
54
54
|
this.toggleAriaHasPopup();
|
|
55
|
-
if (this.expanded) {
|
|
56
|
-
}
|
|
57
55
|
},
|
|
58
56
|
},
|
|
59
57
|
|
|
@@ -65,7 +63,7 @@ export default {
|
|
|
65
63
|
focus: this.focus,
|
|
66
64
|
};
|
|
67
65
|
|
|
68
|
-
this.
|
|
66
|
+
this.apiDropdownMenu().registerTrigger(trigger);
|
|
69
67
|
|
|
70
68
|
this.setTrigger();
|
|
71
69
|
this.addTriggerEvents();
|
|
@@ -15,9 +15,7 @@ export default {
|
|
|
15
15
|
|
|
16
16
|
provide() {
|
|
17
17
|
return {
|
|
18
|
-
|
|
19
|
-
const { dark: isDark } = this;
|
|
20
|
-
|
|
18
|
+
apiListbox: () => {
|
|
21
19
|
const registerTrigger = (trigger) => {
|
|
22
20
|
if (!trigger) return;
|
|
23
21
|
this.componentTrigger = trigger;
|
|
@@ -55,7 +53,6 @@ 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,
|
|
@@ -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
|
|
16
|
+
import { floatingUiContentMixin } from '../../../mixins/floating-ui-content';
|
|
16
17
|
|
|
17
18
|
export default {
|
|
18
19
|
name: 'VTListboxContent',
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
FloatingUi,
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
inject: ['api'],
|
|
21
|
+
mixins: [floatingUiContentMixin],
|
|
25
22
|
|
|
26
|
-
|
|
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.
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
el() {
|
|
54
|
-
return document.getElementById(this.id);
|
|
33
|
+
return `listbox-content-${this.apiListbox().id}`;
|
|
55
34
|
},
|
|
56
35
|
|
|
57
36
|
component() {
|
|
58
|
-
return this.
|
|
37
|
+
return this.apiListbox().component;
|
|
59
38
|
},
|
|
60
39
|
|
|
61
40
|
componentTrigger() {
|
|
62
|
-
return this.
|
|
41
|
+
return this.apiListbox().componentTrigger;
|
|
63
42
|
},
|
|
64
43
|
},
|
|
65
44
|
|
|
@@ -71,52 +50,10 @@ export default {
|
|
|
71
50
|
setActiveDescedant: this.setActiveDescedant,
|
|
72
51
|
};
|
|
73
52
|
|
|
74
|
-
this.
|
|
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.cancel();
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
destroyed() {
|
|
91
|
-
// T-162 Create a directive or mixin for this
|
|
92
|
-
document.removeEventListener('click', this.componentTrigger.cancel);
|
|
53
|
+
this.apiListbox().registerContent(content);
|
|
93
54
|
},
|
|
94
55
|
|
|
95
56
|
methods: {
|
|
96
|
-
show() {
|
|
97
|
-
if (this.visible) {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
this.visible = true;
|
|
102
|
-
|
|
103
|
-
this.$nextTick(() => {
|
|
104
|
-
this.component.setActive();
|
|
105
|
-
});
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
hide() {
|
|
109
|
-
if (!this.visible) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
this.visible = false;
|
|
114
|
-
|
|
115
|
-
this.$nextTick(() => {
|
|
116
|
-
this.component.clearActive();
|
|
117
|
-
});
|
|
118
|
-
},
|
|
119
|
-
|
|
120
57
|
setActiveDescedant(id) {
|
|
121
58
|
this.activeDescedant = id;
|
|
122
59
|
},
|
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<li
|
|
3
3
|
:id="id"
|
|
4
|
-
:class="
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
:class="[
|
|
5
|
+
// default styles
|
|
6
|
+
headless
|
|
7
|
+
? 'listbox-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
|
+
// disabled state styles
|
|
10
|
+
headless
|
|
11
|
+
? disabled
|
|
12
|
+
? 'listbox-item--disabled'
|
|
13
|
+
: null
|
|
14
|
+
: disabled
|
|
15
|
+
? 'pointer-events-none opacity-75'
|
|
16
|
+
: null,
|
|
17
|
+
// selected state styles
|
|
18
|
+
headless
|
|
19
|
+
? selected
|
|
20
|
+
? 'lisbox-item--selected'
|
|
21
|
+
: null
|
|
22
|
+
: selected
|
|
23
|
+
? 'bg-secondary-200/10'
|
|
24
|
+
: null,
|
|
25
|
+
]"
|
|
12
26
|
:aria-disabled="disabled"
|
|
13
27
|
:aria-selected="String(selected)"
|
|
14
28
|
:tabindex="tabIndex"
|
|
@@ -35,7 +49,7 @@ import { genId } from '../../utils/ids';
|
|
|
35
49
|
export default {
|
|
36
50
|
name: 'VTListboxItem',
|
|
37
51
|
|
|
38
|
-
inject: ['
|
|
52
|
+
inject: ['apiListbox'],
|
|
39
53
|
|
|
40
54
|
props: {
|
|
41
55
|
headless: {
|
|
@@ -62,12 +76,8 @@ export default {
|
|
|
62
76
|
},
|
|
63
77
|
|
|
64
78
|
computed: {
|
|
65
|
-
dark() {
|
|
66
|
-
return this.api().isDark;
|
|
67
|
-
},
|
|
68
|
-
|
|
69
79
|
items() {
|
|
70
|
-
return this.
|
|
80
|
+
return this.apiListbox().items;
|
|
71
81
|
},
|
|
72
82
|
|
|
73
83
|
el() {
|
|
@@ -75,19 +85,19 @@ export default {
|
|
|
75
85
|
},
|
|
76
86
|
|
|
77
87
|
componentTrigger() {
|
|
78
|
-
return this.
|
|
88
|
+
return this.apiListbox().componentTrigger;
|
|
79
89
|
},
|
|
80
90
|
|
|
81
91
|
componentContent() {
|
|
82
|
-
return this.
|
|
92
|
+
return this.apiListbox().componentContent;
|
|
83
93
|
},
|
|
84
94
|
|
|
85
95
|
list() {
|
|
86
|
-
return this.
|
|
96
|
+
return this.apiListbox().list;
|
|
87
97
|
},
|
|
88
98
|
|
|
89
99
|
search() {
|
|
90
|
-
return this.
|
|
100
|
+
return this.apiListbox().search;
|
|
91
101
|
},
|
|
92
102
|
},
|
|
93
103
|
|
|
@@ -116,13 +126,13 @@ export default {
|
|
|
116
126
|
onClick: this.onClick,
|
|
117
127
|
};
|
|
118
128
|
|
|
119
|
-
this.
|
|
129
|
+
this.apiListbox().registerItem(item);
|
|
120
130
|
|
|
121
131
|
this.index = this.items.length - 1;
|
|
122
132
|
},
|
|
123
133
|
|
|
124
134
|
beforeDestroy() {
|
|
125
|
-
this.
|
|
135
|
+
this.apiListbox().unregisterItem(this.id);
|
|
126
136
|
},
|
|
127
137
|
|
|
128
138
|
methods: {
|
|
@@ -209,7 +219,7 @@ export default {
|
|
|
209
219
|
onClick() {
|
|
210
220
|
if (this.disabled) return;
|
|
211
221
|
|
|
212
|
-
this.
|
|
222
|
+
this.apiListbox().emit(this.value);
|
|
213
223
|
this.$nextTick(() => this.leaveMenu());
|
|
214
224
|
},
|
|
215
225
|
|
|
@@ -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,14 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<ul
|
|
3
|
-
:id="id"
|
|
4
|
-
:class="{
|
|
5
|
-
ListboxList: headless,
|
|
6
|
-
'-mx-3 max-h-[160px] w-auto overflow-y-auto': !headless,
|
|
7
|
-
}"
|
|
8
|
-
>
|
|
2
|
+
<ul :id="id" :class="[headless ? 'listbox-list' : 'max-h-[160px] w-auto']">
|
|
9
3
|
<slot></slot>
|
|
10
4
|
</ul>
|
|
11
5
|
</template>
|
|
12
6
|
|
|
13
7
|
<script>
|
|
14
|
-
import { genId } from "../../utils/ids";
|
|
15
|
-
|
|
16
8
|
export default {
|
|
17
|
-
name:
|
|
9
|
+
name: 'VTListboxList',
|
|
18
10
|
|
|
19
|
-
inject: [
|
|
11
|
+
inject: ['apiListbox'],
|
|
20
12
|
|
|
21
13
|
props: {
|
|
22
14
|
headless: {
|
|
@@ -27,11 +19,16 @@ export default {
|
|
|
27
19
|
|
|
28
20
|
data() {
|
|
29
21
|
return {
|
|
30
|
-
id: `listboxlist-${genId()}`,
|
|
31
22
|
isMousemove: false,
|
|
32
23
|
};
|
|
33
24
|
},
|
|
34
25
|
|
|
26
|
+
computed: {
|
|
27
|
+
id() {
|
|
28
|
+
return `listbox-list-${this.apiListbox().id}`;
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
|
|
35
32
|
mounted() {
|
|
36
33
|
const list = {
|
|
37
34
|
el: this.$el,
|
|
@@ -40,7 +37,7 @@ export default {
|
|
|
40
37
|
unsetMousemove: this.unsetMousemove,
|
|
41
38
|
};
|
|
42
39
|
|
|
43
|
-
this.
|
|
40
|
+
this.apiListbox().registerList(list);
|
|
44
41
|
},
|
|
45
42
|
|
|
46
43
|
methods: {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
export default {
|
|
20
20
|
name: 'VTListboxSearch',
|
|
21
21
|
|
|
22
|
-
inject: ['
|
|
22
|
+
inject: ['apiListbox'],
|
|
23
23
|
|
|
24
24
|
props: {
|
|
25
25
|
headless: {
|
|
@@ -37,15 +37,15 @@ export default {
|
|
|
37
37
|
|
|
38
38
|
computed: {
|
|
39
39
|
componentTrigger() {
|
|
40
|
-
return this.
|
|
40
|
+
return this.apiListbox().componentTrigger;
|
|
41
41
|
},
|
|
42
42
|
|
|
43
43
|
list() {
|
|
44
|
-
return this.
|
|
44
|
+
return this.apiListbox().list;
|
|
45
45
|
},
|
|
46
46
|
|
|
47
47
|
items() {
|
|
48
|
-
return this.
|
|
48
|
+
return this.apiListbox().items;
|
|
49
49
|
},
|
|
50
50
|
|
|
51
51
|
item() {
|
|
@@ -58,7 +58,7 @@ export default {
|
|
|
58
58
|
el: this.$el,
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
-
this.
|
|
61
|
+
this.apiListbox().registerSearch(search);
|
|
62
62
|
this.$nextTick(() => setTimeout(() => this.$el.focus(), 150));
|
|
63
63
|
},
|
|
64
64
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
:class="[
|
|
7
7
|
headless
|
|
8
8
|
? 'listbox-button'
|
|
9
|
-
: 'flex w-full justify-between border border-solid py-2 px-3 rounded text-inherit max-w-full',
|
|
9
|
+
: 'flex w-full justify-between border border-solid py-2 px-3 gap-3 rounded text-inherit max-w-full',
|
|
10
10
|
headless
|
|
11
11
|
? `listbox-button--${variant}`
|
|
12
12
|
: isError
|
|
@@ -48,7 +48,7 @@ export default {
|
|
|
48
48
|
|
|
49
49
|
components: { IconChevronDown },
|
|
50
50
|
|
|
51
|
-
inject: ['
|
|
51
|
+
inject: ['apiListbox'],
|
|
52
52
|
|
|
53
53
|
props: {
|
|
54
54
|
disabled: {
|
|
@@ -74,7 +74,7 @@ export default {
|
|
|
74
74
|
|
|
75
75
|
computed: {
|
|
76
76
|
id() {
|
|
77
|
-
return `listbox-trigger-${this.
|
|
77
|
+
return `listbox-trigger-${this.apiListbox().id}`;
|
|
78
78
|
},
|
|
79
79
|
|
|
80
80
|
isError() {
|
|
@@ -82,11 +82,11 @@ export default {
|
|
|
82
82
|
},
|
|
83
83
|
|
|
84
84
|
componentContent() {
|
|
85
|
-
return this.
|
|
85
|
+
return this.apiListbox().componentContent;
|
|
86
86
|
},
|
|
87
87
|
|
|
88
88
|
items() {
|
|
89
|
-
return this.
|
|
89
|
+
return this.apiListbox().items;
|
|
90
90
|
},
|
|
91
91
|
|
|
92
92
|
firstMenuItem() {
|
|
@@ -106,7 +106,7 @@ export default {
|
|
|
106
106
|
id: this.id,
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
-
this.
|
|
109
|
+
this.apiListbox().registerTrigger(trigger);
|
|
110
110
|
},
|
|
111
111
|
|
|
112
112
|
methods: {
|
|
@@ -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>
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
headless
|
|
17
17
|
? null
|
|
18
18
|
: 'absolute z-50 grid min-w-min overflow-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: {
|