@veritree/ui 0.21.0 → 0.21.1-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/mixins/floating-ui.js +68 -0
- package/package.json +2 -1
- package/src/components/DropdownMenu/VTDropdownMenu.vue +23 -14
- package/src/components/DropdownMenu/VTDropdownMenuContent.vue +46 -41
- package/src/components/DropdownMenu/VTDropdownMenuItem.vue +33 -12
- package/src/components/DropdownMenu/VTDropdownMenuTrigger.vue +70 -62
- package/src/components/Listbox/VTListbox.vue +22 -36
- package/src/components/Listbox/VTListboxContent.vue +38 -65
- package/src/components/Listbox/VTListboxItem.vue +12 -12
- package/src/components/Listbox/VTListboxSearch.vue +4 -8
- package/src/components/Listbox/VTListboxTrigger.vue +40 -18
- package/src/components/Popover/VTPopover.vue +23 -16
- package/src/components/Popover/VTPopoverContent.vue +52 -36
- package/src/components/Popover/VTPopoverTrigger.vue +16 -8
- package/src/components/Utils/FloatingUi.vue +56 -0
|
@@ -1,29 +1,31 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div :class="{
|
|
2
|
+
<div :class="{ listbox: headless }">
|
|
3
3
|
<slot></slot>
|
|
4
4
|
</div>
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
7
|
<script>
|
|
8
|
-
import {
|
|
8
|
+
import { floatingUiMixin } from '../../../mixins/floating-ui';
|
|
9
|
+
import { genId } from '../../utils/ids';
|
|
9
10
|
|
|
10
11
|
export default {
|
|
11
|
-
name:
|
|
12
|
+
name: 'VTListbox',
|
|
13
|
+
|
|
14
|
+
mixins: [floatingUiMixin],
|
|
12
15
|
|
|
13
16
|
provide() {
|
|
14
17
|
return {
|
|
15
18
|
api: () => {
|
|
16
|
-
const { dark: isDark
|
|
17
|
-
const { id, listbox, trigger, content, search, list, items } = this;
|
|
19
|
+
const { dark: isDark } = this;
|
|
18
20
|
|
|
19
21
|
const registerTrigger = (trigger) => {
|
|
20
22
|
if (!trigger) return;
|
|
21
|
-
this.
|
|
23
|
+
this.componentTrigger = trigger;
|
|
22
24
|
};
|
|
23
25
|
|
|
24
26
|
const registerContent = (content) => {
|
|
25
27
|
if (!content) return;
|
|
26
|
-
this.
|
|
28
|
+
this.componentContent = content;
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
const registerSearch = (search) => {
|
|
@@ -47,20 +49,19 @@ export default {
|
|
|
47
49
|
};
|
|
48
50
|
|
|
49
51
|
const emit = (value) => {
|
|
50
|
-
this.$emit(
|
|
51
|
-
this.$emit(
|
|
52
|
+
this.$emit('input', value);
|
|
53
|
+
this.$emit('change', value);
|
|
52
54
|
};
|
|
53
55
|
|
|
54
56
|
return {
|
|
55
|
-
id,
|
|
57
|
+
id: this.componentId,
|
|
56
58
|
isDark,
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
items,
|
|
59
|
+
component: this.component,
|
|
60
|
+
componentTrigger: this.componentTrigger,
|
|
61
|
+
componentContent: this.componentContent,
|
|
62
|
+
list: this.list,
|
|
63
|
+
items: this.items,
|
|
64
|
+
search: this.search,
|
|
64
65
|
registerTrigger,
|
|
65
66
|
registerContent,
|
|
66
67
|
registerSearch,
|
|
@@ -94,31 +95,16 @@ export default {
|
|
|
94
95
|
|
|
95
96
|
data() {
|
|
96
97
|
return {
|
|
97
|
-
|
|
98
|
-
listbox: null,
|
|
99
|
-
trigger: null,
|
|
100
|
-
content: null,
|
|
98
|
+
componentId: genId(),
|
|
101
99
|
search: null,
|
|
102
100
|
list: null,
|
|
103
101
|
items: [],
|
|
104
|
-
active: false,
|
|
105
|
-
};
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
mounted() {
|
|
109
|
-
this.listbox = {
|
|
110
|
-
setActive: this.setActive,
|
|
111
|
-
clearActive: this.clearActive,
|
|
112
102
|
};
|
|
113
103
|
},
|
|
114
104
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
this.
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
clearActive() {
|
|
121
|
-
this.active = null;
|
|
105
|
+
computed: {
|
|
106
|
+
id() {
|
|
107
|
+
return `listbox-${this.componentId}`;
|
|
122
108
|
},
|
|
123
109
|
},
|
|
124
110
|
};
|
|
@@ -1,43 +1,27 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@after-leave="hide"
|
|
2
|
+
<FloatingUi
|
|
3
|
+
:visible="visible"
|
|
4
|
+
:id="id"
|
|
5
|
+
:aria-activedescendant="activeDescedant"
|
|
6
|
+
:headless="headless"
|
|
7
|
+
:class="{ 'listbox-content': headless }"
|
|
8
|
+
role="listbox"
|
|
10
9
|
>
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
:id="id"
|
|
14
|
-
:aria-activedescendant="activeDescedant"
|
|
15
|
-
:class="{
|
|
16
|
-
MenuList: headless,
|
|
17
|
-
'absolute z-10 grid w-full min-w-[222px] overflow-hidden rounded-md py-2 px-3':
|
|
18
|
-
!headless,
|
|
19
|
-
'border-gray-100 bg-white shadow-300': !dark && !headless,
|
|
20
|
-
'bg-forest-default border border-solid border-gray-700 shadow-gray-700':
|
|
21
|
-
dark && !headless,
|
|
22
|
-
'left-0': !right && !headless,
|
|
23
|
-
'right-0': right && !headless,
|
|
24
|
-
'top-full mt-3': isTop && !headless,
|
|
25
|
-
'bottom-full mb-3': isBottom && !headless,
|
|
26
|
-
}"
|
|
27
|
-
role="listbox"
|
|
28
|
-
>
|
|
29
|
-
<slot></slot>
|
|
30
|
-
</div>
|
|
31
|
-
</transition>
|
|
10
|
+
<slot></slot>
|
|
11
|
+
</FloatingUi>
|
|
32
12
|
</template>
|
|
33
13
|
|
|
34
14
|
<script>
|
|
35
|
-
import
|
|
15
|
+
import FloatingUi from '../Utils/FloatingUi.vue';
|
|
36
16
|
|
|
37
17
|
export default {
|
|
38
|
-
name:
|
|
18
|
+
name: 'VTListboxContent',
|
|
39
19
|
|
|
40
|
-
|
|
20
|
+
components: {
|
|
21
|
+
FloatingUi,
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
inject: ['api'],
|
|
41
25
|
|
|
42
26
|
props: {
|
|
43
27
|
headless: {
|
|
@@ -56,41 +40,27 @@ export default {
|
|
|
56
40
|
|
|
57
41
|
data() {
|
|
58
42
|
return {
|
|
59
|
-
id: `listboxcontent-${genId()}`,
|
|
60
43
|
activeDescedant: null,
|
|
61
44
|
visible: false,
|
|
62
45
|
};
|
|
63
46
|
},
|
|
64
47
|
|
|
65
48
|
computed: {
|
|
66
|
-
|
|
67
|
-
return this.api().
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
right() {
|
|
71
|
-
return this.api().isRight;
|
|
49
|
+
id() {
|
|
50
|
+
return `listbox-content-${this.api().id}`;
|
|
72
51
|
},
|
|
73
52
|
|
|
74
|
-
|
|
75
|
-
return this.api().
|
|
53
|
+
component() {
|
|
54
|
+
return this.api().component;
|
|
76
55
|
},
|
|
77
56
|
|
|
78
|
-
|
|
79
|
-
return this.api().
|
|
57
|
+
componentTrigger() {
|
|
58
|
+
return this.api().componentTrigger;
|
|
80
59
|
},
|
|
81
60
|
|
|
82
61
|
search() {
|
|
83
62
|
return this.api().search;
|
|
84
63
|
},
|
|
85
|
-
|
|
86
|
-
// directions
|
|
87
|
-
isTop() {
|
|
88
|
-
return this.top && !this.bottom;
|
|
89
|
-
},
|
|
90
|
-
|
|
91
|
-
isBottom() {
|
|
92
|
-
return this.bottom;
|
|
93
|
-
},
|
|
94
64
|
},
|
|
95
65
|
|
|
96
66
|
mounted() {
|
|
@@ -103,16 +73,23 @@ export default {
|
|
|
103
73
|
|
|
104
74
|
this.api().registerContent(content);
|
|
105
75
|
|
|
106
|
-
//
|
|
107
|
-
document.addEventListener(
|
|
76
|
+
// T-107 Create a directive or mixin for this
|
|
77
|
+
document.addEventListener('click', (e) => {
|
|
78
|
+
if (!e) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
108
82
|
e.stopPropagation();
|
|
109
|
-
|
|
83
|
+
|
|
84
|
+
if (this.visible && !this.$el.contains(e.target)) {
|
|
85
|
+
this.componentTrigger.onClick();
|
|
86
|
+
}
|
|
110
87
|
});
|
|
111
88
|
},
|
|
112
89
|
|
|
113
90
|
destroyed() {
|
|
114
|
-
//
|
|
115
|
-
document.removeEventListener(
|
|
91
|
+
// T-162 Create a directive or mixin for this
|
|
92
|
+
document.removeEventListener('click', this.componentTrigger.onClick);
|
|
116
93
|
},
|
|
117
94
|
|
|
118
95
|
methods: {
|
|
@@ -122,8 +99,7 @@ export default {
|
|
|
122
99
|
this.visible = true;
|
|
123
100
|
|
|
124
101
|
this.$nextTick(() => {
|
|
125
|
-
this.
|
|
126
|
-
|
|
102
|
+
this.component.setActive();
|
|
127
103
|
if (this.search) this.search.el.focus();
|
|
128
104
|
});
|
|
129
105
|
},
|
|
@@ -134,12 +110,9 @@ export default {
|
|
|
134
110
|
this.visible = false;
|
|
135
111
|
|
|
136
112
|
this.$nextTick(() => {
|
|
137
|
-
this.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
this.listbox.clearActive();
|
|
141
|
-
this.trigger.contract();
|
|
142
|
-
}, 100);
|
|
113
|
+
this.componentTrigger.focus();
|
|
114
|
+
this.componentTrigger.toggleExpanded();
|
|
115
|
+
this.component.clearActive();
|
|
143
116
|
});
|
|
144
117
|
},
|
|
145
118
|
|
|
@@ -29,13 +29,13 @@
|
|
|
29
29
|
</template>
|
|
30
30
|
|
|
31
31
|
<script>
|
|
32
|
-
import { scrollElementIntoView } from
|
|
33
|
-
import { genId } from
|
|
32
|
+
import { scrollElementIntoView } from '../../utils/components';
|
|
33
|
+
import { genId } from '../../utils/ids';
|
|
34
34
|
|
|
35
35
|
export default {
|
|
36
|
-
name:
|
|
36
|
+
name: 'VTListboxItem',
|
|
37
37
|
|
|
38
|
-
inject: [
|
|
38
|
+
inject: ['api'],
|
|
39
39
|
|
|
40
40
|
props: {
|
|
41
41
|
headless: {
|
|
@@ -74,12 +74,12 @@ export default {
|
|
|
74
74
|
return this.$el;
|
|
75
75
|
},
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
return this.api().
|
|
77
|
+
componentTrigger() {
|
|
78
|
+
return this.api().componentTrigger;
|
|
79
79
|
},
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
return this.api().
|
|
81
|
+
componentContent() {
|
|
82
|
+
return this.api().componentContent;
|
|
83
83
|
},
|
|
84
84
|
|
|
85
85
|
list() {
|
|
@@ -95,8 +95,8 @@ export default {
|
|
|
95
95
|
selected(newValue) {
|
|
96
96
|
if (!newValue || !this.list) return;
|
|
97
97
|
|
|
98
|
-
if (this.
|
|
99
|
-
this.
|
|
98
|
+
if (this.componentContent) {
|
|
99
|
+
this.componentContent.setActiveDescedant(this.id);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
const isMousemove = this.list.getMousemove();
|
|
@@ -193,10 +193,10 @@ export default {
|
|
|
193
193
|
},
|
|
194
194
|
|
|
195
195
|
/**
|
|
196
|
-
* Hides
|
|
196
|
+
* Hides componentContent/menu and focus on componentTrigger
|
|
197
197
|
*/
|
|
198
198
|
leaveMenu() {
|
|
199
|
-
if (this.
|
|
199
|
+
if (this.componentContent) this.componentContent.hide();
|
|
200
200
|
},
|
|
201
201
|
|
|
202
202
|
onKeyEsc() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<input
|
|
3
3
|
v-model="search"
|
|
4
|
-
:class="{
|
|
4
|
+
:class="{ 'listbox-search': headless, 'form-control mb-1': !headless }"
|
|
5
5
|
type="text"
|
|
6
6
|
@input="onChange"
|
|
7
7
|
@keydown.down.prevent="focusNextItem"
|
|
@@ -35,12 +35,8 @@ export default {
|
|
|
35
35
|
},
|
|
36
36
|
|
|
37
37
|
computed: {
|
|
38
|
-
|
|
39
|
-
return this.api().
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
trigger() {
|
|
43
|
-
return this.api().trigger;
|
|
38
|
+
componentContent() {
|
|
39
|
+
return this.api().componentContent;
|
|
44
40
|
},
|
|
45
41
|
|
|
46
42
|
list() {
|
|
@@ -131,7 +127,7 @@ export default {
|
|
|
131
127
|
},
|
|
132
128
|
|
|
133
129
|
hide() {
|
|
134
|
-
if (this.
|
|
130
|
+
if (this.componentContent) this.componentContent.hide();
|
|
135
131
|
},
|
|
136
132
|
},
|
|
137
133
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<button
|
|
3
|
+
:id="id"
|
|
3
4
|
:aria-expanded="expanded"
|
|
4
5
|
:aria-haspopup="hasPopup"
|
|
5
6
|
:class="{
|
|
6
|
-
'
|
|
7
|
+
'listbox-button': headless,
|
|
7
8
|
'flex w-full justify-between rounded-md border border-solid py-2 px-3':
|
|
8
9
|
!headless,
|
|
9
10
|
'border-gray-300 text-gray-500': !dark && !headless,
|
|
@@ -11,14 +12,14 @@
|
|
|
11
12
|
dark && !headless,
|
|
12
13
|
}"
|
|
13
14
|
type="button"
|
|
14
|
-
@click.
|
|
15
|
+
@click.prevent="onClick"
|
|
15
16
|
@keydown.down.prevent="onKeyArrowDown"
|
|
16
17
|
@keydown.up.prevent="onKeyArrowUp"
|
|
17
18
|
@keydown.esc.stop="onKeyEsc"
|
|
18
19
|
>
|
|
19
20
|
<span
|
|
20
21
|
:class="{
|
|
21
|
-
'
|
|
22
|
+
'listbox-button__text': headless,
|
|
22
23
|
'text-left': !headless,
|
|
23
24
|
}"
|
|
24
25
|
>
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
</span>
|
|
27
28
|
<span
|
|
28
29
|
:class="{
|
|
29
|
-
'
|
|
30
|
+
'listbox-button__icon': headless,
|
|
30
31
|
}"
|
|
31
32
|
>
|
|
32
33
|
<IconChevronDown
|
|
@@ -62,12 +63,16 @@ export default {
|
|
|
62
63
|
},
|
|
63
64
|
|
|
64
65
|
computed: {
|
|
66
|
+
id() {
|
|
67
|
+
return `listbox-trigger-${this.api().id}`;
|
|
68
|
+
},
|
|
69
|
+
|
|
65
70
|
dark() {
|
|
66
71
|
return this.api().isDark;
|
|
67
72
|
},
|
|
68
73
|
|
|
69
|
-
|
|
70
|
-
return this.api().
|
|
74
|
+
componentContent() {
|
|
75
|
+
return this.api().componentContent;
|
|
71
76
|
},
|
|
72
77
|
|
|
73
78
|
items() {
|
|
@@ -85,10 +90,11 @@ export default {
|
|
|
85
90
|
|
|
86
91
|
mounted() {
|
|
87
92
|
const trigger = {
|
|
93
|
+
toggleExpanded: this.toggleExpanded,
|
|
88
94
|
el: this.$el,
|
|
89
95
|
focus: this.focus,
|
|
96
|
+
id: this.id,
|
|
90
97
|
onClick: this.onClick,
|
|
91
|
-
contract: this.contract,
|
|
92
98
|
};
|
|
93
99
|
|
|
94
100
|
this.api().registerTrigger(trigger);
|
|
@@ -100,14 +106,14 @@ export default {
|
|
|
100
106
|
*/
|
|
101
107
|
showContent() {
|
|
102
108
|
this.expanded = true;
|
|
103
|
-
this.
|
|
109
|
+
this.componentContent.show();
|
|
104
110
|
},
|
|
105
111
|
|
|
106
112
|
focus() {
|
|
107
113
|
this.$el.focus();
|
|
108
114
|
},
|
|
109
115
|
|
|
110
|
-
|
|
116
|
+
toggleExpanded() {
|
|
111
117
|
if (!this.expanded) return;
|
|
112
118
|
this.expanded = false;
|
|
113
119
|
},
|
|
@@ -119,9 +125,19 @@ export default {
|
|
|
119
125
|
* 2. Open the menu if it's closed
|
|
120
126
|
* 3. Close the menu if it's open
|
|
121
127
|
*/
|
|
122
|
-
onClick() {
|
|
123
|
-
if (!this.
|
|
124
|
-
|
|
128
|
+
onClick(e) {
|
|
129
|
+
if (!this.componentContent) return;
|
|
130
|
+
|
|
131
|
+
if (this.expanded) {
|
|
132
|
+
this.componentContent.hide();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// delay stop propagation to close other visible
|
|
137
|
+
// dropdowns and delay click event to control
|
|
138
|
+
// this dropdown visibility
|
|
139
|
+
setTimeout(() => e.stopImmediatePropagation(), 50);
|
|
140
|
+
setTimeout(() => this.showContent(), 100);
|
|
125
141
|
},
|
|
126
142
|
|
|
127
143
|
/**
|
|
@@ -131,12 +147,15 @@ export default {
|
|
|
131
147
|
* 2. if the menu is expanded, focus the first menu item
|
|
132
148
|
*/
|
|
133
149
|
onKeyArrowDown() {
|
|
134
|
-
if (!this.
|
|
150
|
+
if (!this.componentContent) return;
|
|
135
151
|
|
|
136
152
|
this.showContent();
|
|
137
153
|
|
|
154
|
+
// settimeout here is delaying the focusing the element
|
|
155
|
+
// since it is not rendered yet. All items will only
|
|
156
|
+
// be available when the content is fully visible.
|
|
138
157
|
this.$nextTick(() => {
|
|
139
|
-
this.firstMenuItem.focus();
|
|
158
|
+
setTimeout(() => this.firstMenuItem.focus(), 150);
|
|
140
159
|
});
|
|
141
160
|
},
|
|
142
161
|
|
|
@@ -147,21 +166,24 @@ export default {
|
|
|
147
166
|
* 2. if the menu is expanded, focus the last menu item
|
|
148
167
|
*/
|
|
149
168
|
onKeyArrowUp() {
|
|
150
|
-
if (!this.
|
|
169
|
+
if (!this.componentContent) return;
|
|
151
170
|
|
|
152
171
|
this.showContent();
|
|
153
172
|
|
|
173
|
+
// settimeout here is delaying the focusing the element
|
|
174
|
+
// since it is not rendered yet. All items will only
|
|
175
|
+
// be available when the content is fully visible.
|
|
154
176
|
this.$nextTick(() => {
|
|
155
|
-
this.lastMenuItem.focus();
|
|
177
|
+
setTimeout(() => this.lastMenuItem.focus(), 150);
|
|
156
178
|
});
|
|
157
179
|
},
|
|
158
180
|
|
|
159
181
|
onKeyEsc() {
|
|
160
|
-
if (!this.
|
|
182
|
+
if (!this.componentContent) return;
|
|
161
183
|
|
|
162
184
|
if (this.expanded) {
|
|
163
185
|
this.toggleExpanded();
|
|
164
|
-
this.
|
|
186
|
+
this.componentContent.hide();
|
|
165
187
|
}
|
|
166
188
|
},
|
|
167
189
|
},
|
|
@@ -2,43 +2,46 @@
|
|
|
2
2
|
<div
|
|
3
3
|
:id="id"
|
|
4
4
|
class="relative"
|
|
5
|
-
:aria-haspopup="
|
|
6
|
-
:aria-controls="
|
|
5
|
+
:aria-haspopup="componentContent ? 'true' : null"
|
|
6
|
+
:aria-controls="componentContent ? componentContent.id : null"
|
|
7
7
|
>
|
|
8
8
|
<slot></slot>
|
|
9
9
|
</div>
|
|
10
10
|
</template>
|
|
11
11
|
|
|
12
12
|
<script>
|
|
13
|
-
import {
|
|
13
|
+
import { floatingUiMixin } from '../../../mixins/floating-ui';
|
|
14
|
+
import { genId } from '../../utils/ids';
|
|
14
15
|
|
|
15
16
|
export default {
|
|
16
|
-
name:
|
|
17
|
+
name: 'VTPopover',
|
|
18
|
+
|
|
19
|
+
mixins: [floatingUiMixin],
|
|
17
20
|
|
|
18
21
|
provide() {
|
|
19
22
|
return {
|
|
20
23
|
api: () => {
|
|
21
24
|
const { dark: isDark, headless: isHeadless, right: isRight } = this;
|
|
22
|
-
const { id, button, content } = this;
|
|
23
25
|
|
|
24
|
-
const
|
|
25
|
-
if (!
|
|
26
|
-
this.
|
|
26
|
+
const registerTrigger = (trigger) => {
|
|
27
|
+
if (!trigger) return;
|
|
28
|
+
this.componentTrigger = trigger;
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
const registerContent = (content) => {
|
|
30
32
|
if (!content) return;
|
|
31
|
-
this.
|
|
33
|
+
this.componentContent = content;
|
|
32
34
|
};
|
|
33
35
|
|
|
34
36
|
return {
|
|
35
|
-
id,
|
|
37
|
+
id: this.componentId,
|
|
36
38
|
isDark,
|
|
37
39
|
isHeadless,
|
|
38
40
|
isRight,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
component: this.component,
|
|
42
|
+
componentTrigger: this.componentTrigger,
|
|
43
|
+
componentContent: this.componentContent,
|
|
44
|
+
registerTrigger,
|
|
42
45
|
registerContent,
|
|
43
46
|
};
|
|
44
47
|
},
|
|
@@ -62,10 +65,14 @@ export default {
|
|
|
62
65
|
|
|
63
66
|
data() {
|
|
64
67
|
return {
|
|
65
|
-
|
|
66
|
-
button: null,
|
|
67
|
-
content: null,
|
|
68
|
+
componentId: genId(),
|
|
68
69
|
};
|
|
69
70
|
},
|
|
71
|
+
|
|
72
|
+
computed: {
|
|
73
|
+
id() {
|
|
74
|
+
return `popover-${this.componentId}`;
|
|
75
|
+
},
|
|
76
|
+
},
|
|
70
77
|
};
|
|
71
78
|
</script>
|