@veritree/ui 0.22.3 → 0.23.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-content.js +0 -21
- package/mixins/floating-ui-item.js +88 -46
- package/mixins/floating-ui.js +4 -13
- package/mixins/form-control-icon.js +2 -2
- package/mixins/form-control.js +9 -5
- package/nuxt.js +1 -0
- package/package.json +1 -1
- package/src/components/Button/VTButton.vue +5 -5
- package/src/components/Dialog/VTDialog.vue +6 -15
- package/src/components/Dialog/VTDialogClose.vue +19 -25
- package/src/components/Dialog/VTDialogContent.vue +18 -21
- package/src/components/Dialog/VTDialogFooter.vue +7 -18
- package/src/components/Dialog/VTDialogHeader.vue +15 -18
- package/src/components/Dialog/VTDialogMain.vue +11 -18
- package/src/components/Dialog/VTDialogOverlay.vue +14 -18
- package/src/components/Dialog/VTDialogTitle.vue +10 -7
- package/src/components/Drawer/VTDrawerContent.vue +1 -1
- package/src/components/Drawer/VTDrawerFooter.vue +1 -1
- package/src/components/DropdownMenu/VTDropdownMenu.vue +19 -0
- package/src/components/DropdownMenu/VTDropdownMenuContent.vue +5 -6
- package/src/components/DropdownMenu/VTDropdownMenuItem.vue +2 -2
- package/src/components/Form/VTFormFeedback.vue +5 -5
- package/src/components/Form/VTInput.vue +5 -2
- package/src/components/Form/VTTextarea.vue +5 -2
- package/src/components/Listbox/VTListbox.vue +35 -11
- package/src/components/Listbox/VTListboxContent.vue +4 -7
- package/src/components/Listbox/VTListboxItem.vue +117 -6
- package/src/components/Listbox/VTListboxList.vue +1 -24
- package/src/components/Listbox/VTListboxSearch.vue +58 -52
- package/src/components/Listbox/VTListboxTrigger.vue +7 -4
- package/src/components/Modal/VTModal.vue +1 -1
- package/src/components/Popover/VTPopover.vue +19 -0
- package/src/components/Popover/VTPopoverContent.vue +3 -3
- package/src/components/Tooltip/VTTooltip.vue +65 -0
- package/src/components/Tooltip/VTTooltipContent.vue +59 -0
- package/src/components/Tooltip/VTTooltipTrigger.vue +100 -0
- package/src/components/Utils/FloatingUi.vue +27 -13
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<ul
|
|
3
3
|
:id="id"
|
|
4
4
|
:class="[
|
|
5
|
-
headless ? 'listbox-list' : 'max-h-[160px] w-auto overflow-y-auto
|
|
5
|
+
headless ? 'listbox-list' : '-mx-3 max-h-[160px] w-auto overflow-y-auto',
|
|
6
6
|
]"
|
|
7
7
|
>
|
|
8
8
|
<slot></slot>
|
|
@@ -27,28 +27,5 @@ export default {
|
|
|
27
27
|
return `listbox-list-${this.apiListbox().id}`;
|
|
28
28
|
},
|
|
29
29
|
},
|
|
30
|
-
|
|
31
|
-
// mounted() {
|
|
32
|
-
// const list = {
|
|
33
|
-
// el: this.$el,
|
|
34
|
-
// };
|
|
35
|
-
|
|
36
|
-
// this.apiListbox().registerList(list);
|
|
37
|
-
// },
|
|
38
|
-
|
|
39
|
-
methods: {
|
|
40
|
-
// Mousemove instead of mouseover to support keyboard navigation.
|
|
41
|
-
// The problem with mouseover is that when scrolling (scrollIntoView),
|
|
42
|
-
// mouseover event gets triggered.
|
|
43
|
-
// setMousemove() {
|
|
44
|
-
// this.isMousemove = true;
|
|
45
|
-
// },
|
|
46
|
-
// unsetMousemove() {
|
|
47
|
-
// this.isMousemove = false;
|
|
48
|
-
// },
|
|
49
|
-
// getMousemove() {
|
|
50
|
-
// return this.isMousemove;
|
|
51
|
-
// },
|
|
52
|
-
},
|
|
53
30
|
};
|
|
54
31
|
</script>
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
2
|
+
<div class="-mx-3 -mt-2">
|
|
3
|
+
<input
|
|
4
|
+
ref="search"
|
|
5
|
+
v-model="search"
|
|
6
|
+
v-bind="$attrs"
|
|
7
|
+
type="text"
|
|
8
|
+
class="leading-0 font-inherit w-full max-w-full appearance-none border-b border-solid border-gray-300 py-2 px-3 text-base text-inherit"
|
|
9
|
+
@input="onChange"
|
|
10
|
+
@click.stop
|
|
11
|
+
@keydown.down.prevent="focusNextItem"
|
|
12
|
+
@keydown.up.prevent="focusPreviousItem"
|
|
13
|
+
@keydown.home.prevent="focusFirstItem"
|
|
14
|
+
@keydown.end.prevent="focusLastItem"
|
|
15
|
+
@keydown.enter.prevent="onKeyEnter"
|
|
16
|
+
@keydown.esc.stop="hide"
|
|
17
|
+
@keydown.tab.prevent="hide"
|
|
18
|
+
/>
|
|
19
|
+
</div>
|
|
16
20
|
</template>
|
|
17
21
|
|
|
18
22
|
<script>
|
|
@@ -23,12 +27,14 @@ export default {
|
|
|
23
27
|
|
|
24
28
|
mixins: [formControlMixin],
|
|
25
29
|
|
|
30
|
+
inheritAttrs: false,
|
|
31
|
+
|
|
26
32
|
inject: ['apiListbox'],
|
|
27
33
|
|
|
28
34
|
data() {
|
|
29
35
|
return {
|
|
30
36
|
name: 'listbox-search',
|
|
31
|
-
index:
|
|
37
|
+
index: null,
|
|
32
38
|
search: '',
|
|
33
39
|
};
|
|
34
40
|
},
|
|
@@ -38,10 +44,6 @@ export default {
|
|
|
38
44
|
return this.apiListbox().componentTrigger;
|
|
39
45
|
},
|
|
40
46
|
|
|
41
|
-
componentContent() {
|
|
42
|
-
return this.apiListbox().componentContent;
|
|
43
|
-
},
|
|
44
|
-
|
|
45
47
|
items() {
|
|
46
48
|
return this.apiListbox().items;
|
|
47
49
|
},
|
|
@@ -52,12 +54,12 @@ export default {
|
|
|
52
54
|
},
|
|
53
55
|
|
|
54
56
|
mounted() {
|
|
55
|
-
const
|
|
57
|
+
const componentSearch = {
|
|
56
58
|
el: this.$el,
|
|
57
59
|
};
|
|
58
60
|
|
|
59
|
-
this.apiListbox().registerSearch(
|
|
60
|
-
this.$nextTick(() => setTimeout(() => this.$
|
|
61
|
+
this.apiListbox().registerSearch(componentSearch);
|
|
62
|
+
this.$nextTick(() => setTimeout(() => this.$refs.search.focus(), 150));
|
|
61
63
|
},
|
|
62
64
|
|
|
63
65
|
beforeDestroy() {
|
|
@@ -67,54 +69,58 @@ export default {
|
|
|
67
69
|
|
|
68
70
|
methods: {
|
|
69
71
|
focusNextItem() {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
const selectedIndex = this.getItemSelectedIndex();
|
|
73
|
+
const isLast = selectedIndex === this.items.length - 1;
|
|
74
|
+
const firstItemIndex = 0;
|
|
75
|
+
const nextItemIndex = selectedIndex + 1;
|
|
76
|
+
const newSelectedIndex = isLast ? firstItemIndex : nextItemIndex;
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
this.index = 0;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (this.item) this.item.select();
|
|
78
|
+
this.selectItem(selectedIndex, newSelectedIndex);
|
|
81
79
|
},
|
|
82
80
|
|
|
83
81
|
focusPreviousItem() {
|
|
84
|
-
this.
|
|
85
|
-
|
|
86
|
-
this.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
this.index = this.items.length - 1;
|
|
90
|
-
}
|
|
82
|
+
const selectedIndex = this.getItemSelectedIndex();
|
|
83
|
+
const isFirst = selectedIndex === 0;
|
|
84
|
+
const lastItemIndex = this.items.length - 1;
|
|
85
|
+
const previousItemIndex = selectedIndex - 1;
|
|
86
|
+
const newSelectedIndex = isFirst ? lastItemIndex : previousItemIndex;
|
|
91
87
|
|
|
92
|
-
this.
|
|
88
|
+
this.selectItem(selectedIndex, newSelectedIndex);
|
|
93
89
|
},
|
|
94
90
|
|
|
95
91
|
focusFirstItem() {
|
|
96
|
-
this.
|
|
97
|
-
|
|
98
|
-
|
|
92
|
+
const selectedIndex = this.getItemSelectedIndex();
|
|
93
|
+
const newSelectedIndex = 0;
|
|
94
|
+
|
|
95
|
+
this.selectItem(selectedIndex, newSelectedIndex);
|
|
99
96
|
},
|
|
100
97
|
|
|
101
98
|
focusLastItem() {
|
|
102
|
-
this.
|
|
103
|
-
|
|
104
|
-
this.item.select();
|
|
105
|
-
},
|
|
99
|
+
const selectedIndex = this.getItemSelectedIndex();
|
|
100
|
+
const newSelectedIndex = this.items.length - 1;
|
|
106
101
|
|
|
107
|
-
|
|
108
|
-
|
|
102
|
+
this.selectItem(selectedIndex, newSelectedIndex);
|
|
103
|
+
},
|
|
109
104
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
105
|
+
selectItem(selectedIndex, newSelectedIndex) {
|
|
106
|
+
// before selecting, let's unselect selected
|
|
107
|
+
// item that were previously focused
|
|
108
|
+
if (selectedIndex >= 0) {
|
|
109
|
+
this.items[selectedIndex].unselect();
|
|
113
110
|
}
|
|
114
111
|
|
|
112
|
+
// select new item
|
|
113
|
+
this.items[newSelectedIndex].select();
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
unselectItem() {
|
|
115
117
|
if (this.item) this.item.unselect();
|
|
116
118
|
},
|
|
117
119
|
|
|
120
|
+
getItemSelectedIndex() {
|
|
121
|
+
return this.items.findIndex((item) => item.isSelected());
|
|
122
|
+
},
|
|
123
|
+
|
|
118
124
|
onChange() {
|
|
119
125
|
this.index = 0;
|
|
120
126
|
if (this.item) this.item.select();
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
@keydown.up.prevent="onKeyDownOrUp"
|
|
12
12
|
@keydown.esc.stop="onKeyEsc"
|
|
13
13
|
>
|
|
14
|
-
<span :class="[headless ? 'listbox-button__text' : 'text-left
|
|
14
|
+
<span :class="[headless ? 'listbox-button__text' : 'truncate text-left']">
|
|
15
15
|
<slot></slot>
|
|
16
16
|
</span>
|
|
17
17
|
<span :class="[headless ? 'listbox-button__icon' : 'shrink-0']">
|
|
@@ -24,7 +24,10 @@
|
|
|
24
24
|
</template>
|
|
25
25
|
|
|
26
26
|
<script>
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
formControlMixin,
|
|
29
|
+
formControlStyleMixin,
|
|
30
|
+
} from '../../../mixins/form-control';
|
|
28
31
|
import { IconChevronDown } from '@veritree/icons';
|
|
29
32
|
|
|
30
33
|
export default {
|
|
@@ -32,13 +35,12 @@ export default {
|
|
|
32
35
|
|
|
33
36
|
components: { IconChevronDown },
|
|
34
37
|
|
|
35
|
-
mixins: [formControlMixin],
|
|
38
|
+
mixins: [formControlMixin, formControlStyleMixin],
|
|
36
39
|
|
|
37
40
|
inject: ['apiListbox'],
|
|
38
41
|
|
|
39
42
|
data() {
|
|
40
43
|
return {
|
|
41
|
-
name: 'listbox-button',
|
|
42
44
|
expanded: false,
|
|
43
45
|
hasPopup: false,
|
|
44
46
|
};
|
|
@@ -101,6 +103,7 @@ export default {
|
|
|
101
103
|
if (!this.componentContent) {
|
|
102
104
|
return;
|
|
103
105
|
}
|
|
106
|
+
|
|
104
107
|
this.expanded = false;
|
|
105
108
|
|
|
106
109
|
this.hideComponentContent();
|
|
@@ -48,11 +48,30 @@ export default {
|
|
|
48
48
|
type: Boolean,
|
|
49
49
|
default: false,
|
|
50
50
|
},
|
|
51
|
+
placement: {
|
|
52
|
+
type: String,
|
|
53
|
+
default: 'bottom-start',
|
|
54
|
+
},
|
|
51
55
|
},
|
|
52
56
|
|
|
53
57
|
data() {
|
|
54
58
|
return {
|
|
55
59
|
componentId: genId(),
|
|
60
|
+
/**
|
|
61
|
+
* Explaining the need for the floatingUiMinWidth data
|
|
62
|
+
*
|
|
63
|
+
* The floating ui is a result of two items:
|
|
64
|
+
*
|
|
65
|
+
* 1. Trigger: the action button
|
|
66
|
+
* 2. Content: the popper/wrapper that appears after triggering the action button
|
|
67
|
+
*
|
|
68
|
+
* By default, the content will match the triggers width.
|
|
69
|
+
* The problem with this is that the trigger width
|
|
70
|
+
* might be too small causing the content to not fit
|
|
71
|
+
* what is inside it properly. So, to avoid this,
|
|
72
|
+
* a min width is needed.
|
|
73
|
+
*/
|
|
74
|
+
floatingUiMinWidth: 200,
|
|
56
75
|
};
|
|
57
76
|
},
|
|
58
77
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<FloatingUi
|
|
3
|
-
:visible="visible"
|
|
4
3
|
:id="id"
|
|
4
|
+
:visible="visible"
|
|
5
5
|
:headless="headless"
|
|
6
|
-
:class="
|
|
7
|
-
|
|
6
|
+
:portal-class="$vnode.data.staticClass"
|
|
7
|
+
component="popover"
|
|
8
8
|
>
|
|
9
9
|
<slot></slot>
|
|
10
10
|
</FloatingUi>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script>
|
|
8
|
+
import { floatingUiMixin } from '../../../mixins/floating-ui';
|
|
9
|
+
import { genId } from '../../utils/ids';
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
name: 'VTTooltip',
|
|
13
|
+
|
|
14
|
+
mixins: [floatingUiMixin],
|
|
15
|
+
|
|
16
|
+
props: {
|
|
17
|
+
delayDuration: {
|
|
18
|
+
type: [String, Number],
|
|
19
|
+
default: 500,
|
|
20
|
+
},
|
|
21
|
+
placement: {
|
|
22
|
+
type: String,
|
|
23
|
+
default: 'bottom',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
data() {
|
|
28
|
+
return {
|
|
29
|
+
floatingUiMinWidth: 0,
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
provide() {
|
|
34
|
+
return {
|
|
35
|
+
apiTooltip: () => {
|
|
36
|
+
const registerTrigger = (trigger) => {
|
|
37
|
+
if (!trigger) return;
|
|
38
|
+
this.componentTrigger = trigger;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const registerContent = (content) => {
|
|
42
|
+
if (!content) return;
|
|
43
|
+
this.componentContent = content;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
id: this.componentId,
|
|
48
|
+
component: this.component,
|
|
49
|
+
componentTrigger: this.componentTrigger,
|
|
50
|
+
componentContent: this.componentContent,
|
|
51
|
+
delayDuration: this.delayDuration,
|
|
52
|
+
registerTrigger,
|
|
53
|
+
registerContent,
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
data() {
|
|
60
|
+
return {
|
|
61
|
+
componentId: genId(),
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
</script>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<FloatingUi
|
|
3
|
+
:id="id"
|
|
4
|
+
:visible="visible"
|
|
5
|
+
:headless="headless"
|
|
6
|
+
component="tooltip"
|
|
7
|
+
>
|
|
8
|
+
<slot></slot>
|
|
9
|
+
</FloatingUi>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script>
|
|
13
|
+
import { floatingUiContentMixin } from '../../../mixins/floating-ui-content';
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
name: 'VTTooltipContent',
|
|
17
|
+
|
|
18
|
+
inheritAttrs: false,
|
|
19
|
+
|
|
20
|
+
mixins: [floatingUiContentMixin],
|
|
21
|
+
|
|
22
|
+
inject: ['apiTooltip'],
|
|
23
|
+
|
|
24
|
+
data() {
|
|
25
|
+
return {
|
|
26
|
+
visible: false,
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
computed: {
|
|
31
|
+
id() {
|
|
32
|
+
return `tooltip-content-${this.apiTooltip().id}`;
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
component() {
|
|
36
|
+
return this.apiTooltip().component;
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
componentTrigger() {
|
|
40
|
+
return this.apiTooltip().componentTrigger;
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
ariaLabelledby() {
|
|
44
|
+
if (!this.componentTrigger) return null;
|
|
45
|
+
return this.visible ? this.componentTrigger.id : null;
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
mounted() {
|
|
50
|
+
const content = {
|
|
51
|
+
id: this.id,
|
|
52
|
+
hide: this.hide,
|
|
53
|
+
show: this.show,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
this.apiTooltip().registerContent(content);
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
</script>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:id="id"
|
|
4
|
+
:aria-describedby="ariaDescribedBy"
|
|
5
|
+
class="inline-flex"
|
|
6
|
+
@mouseenter="onMouseenter"
|
|
7
|
+
@mouseleave="onMouseleave"
|
|
8
|
+
>
|
|
9
|
+
<slot />
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script>
|
|
14
|
+
let tooltipTriggerTimeout = null;
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
|
+
name: 'VTTooltipTrigger',
|
|
18
|
+
|
|
19
|
+
inject: ['apiTooltip'],
|
|
20
|
+
|
|
21
|
+
data() {
|
|
22
|
+
return {
|
|
23
|
+
expanded: false,
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
computed: {
|
|
28
|
+
id() {
|
|
29
|
+
return `tooltip-trigger-${this.apiTooltip().id}`;
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
componentContent() {
|
|
33
|
+
return this.apiTooltip().componentContent;
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
ariaDescribedBy() {
|
|
37
|
+
if (!this.componentContent) return null;
|
|
38
|
+
return this.expanded ? this.componentContent.id : null;
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
mounted() {
|
|
43
|
+
const trigger = {
|
|
44
|
+
cancel: this.cancel,
|
|
45
|
+
id: this.id,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
this.apiTooltip().registerTrigger(trigger);
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
methods: {
|
|
52
|
+
onMouseenter(e) {
|
|
53
|
+
tooltipTriggerTimeout = setTimeout(() => {
|
|
54
|
+
this.init(e);
|
|
55
|
+
}, this.apiTooltip().delayDuration);
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
onMouseleave() {
|
|
59
|
+
clearTimeout(tooltipTriggerTimeout);
|
|
60
|
+
this.cancel();
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
init(e) {
|
|
64
|
+
if (!this.componentContent) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (this.expanded) {
|
|
69
|
+
this.cancel();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
this.expanded = true;
|
|
74
|
+
|
|
75
|
+
// delay stop propagation to close other visible
|
|
76
|
+
// dropdowns and delay click event to control
|
|
77
|
+
// this dropdown visibility
|
|
78
|
+
setTimeout(() => e.stopImmediatePropagation(), 50);
|
|
79
|
+
setTimeout(() => this.showComponentContent(), 100);
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
cancel() {
|
|
83
|
+
if (!this.componentContent) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.expanded = false;
|
|
88
|
+
this.hideComponentContent();
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
showComponentContent() {
|
|
92
|
+
this.componentContent.show();
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
hideComponentContent() {
|
|
96
|
+
this.componentContent.hide();
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
</script>
|
|
@@ -7,19 +7,19 @@
|
|
|
7
7
|
leave-active-class="duration-200 ease-in"
|
|
8
8
|
leave-class="translate-y-0 opacity-100"
|
|
9
9
|
leave-to-class="translate-y-[15px] opacity-0"
|
|
10
|
-
@after-leave="
|
|
10
|
+
@after-leave="hidden"
|
|
11
|
+
@after-enter="shown"
|
|
11
12
|
>
|
|
12
13
|
<div
|
|
13
14
|
v-if="visible"
|
|
14
|
-
:id="id"
|
|
15
15
|
:class="[
|
|
16
16
|
headless
|
|
17
|
-
?
|
|
18
|
-
:
|
|
19
|
-
floatingUiClass ? floatingUiClass : null,
|
|
17
|
+
? `${this.component}-content`
|
|
18
|
+
: `shadow-300 absolute z-50 grid overflow-x-hidden rounded-md py-2 px-3 ${this.classes} ${this.portalClass}`,
|
|
20
19
|
]"
|
|
20
|
+
v-bind="$attrs"
|
|
21
21
|
>
|
|
22
|
-
<slot
|
|
22
|
+
<slot />
|
|
23
23
|
</div>
|
|
24
24
|
</transition>
|
|
25
25
|
</Portal>
|
|
@@ -34,27 +34,41 @@ export default {
|
|
|
34
34
|
},
|
|
35
35
|
|
|
36
36
|
props: {
|
|
37
|
+
component: {
|
|
38
|
+
type: String,
|
|
39
|
+
default: null,
|
|
40
|
+
},
|
|
37
41
|
headless: {
|
|
38
42
|
type: Boolean,
|
|
39
43
|
default: false,
|
|
40
44
|
},
|
|
41
|
-
id: {
|
|
42
|
-
type: [String, Number],
|
|
43
|
-
default: null,
|
|
44
|
-
},
|
|
45
45
|
visible: {
|
|
46
46
|
type: Boolean,
|
|
47
47
|
default: false,
|
|
48
48
|
},
|
|
49
|
-
|
|
49
|
+
portalClass: {
|
|
50
50
|
type: [String, Function],
|
|
51
51
|
default: null,
|
|
52
52
|
},
|
|
53
53
|
},
|
|
54
54
|
|
|
55
|
+
computed: {
|
|
56
|
+
isTooltip() {
|
|
57
|
+
return this.component === 'tooltip';
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
classes() {
|
|
61
|
+
return this.isTooltip ? 'bg-gray-800 text-sm text-white' : 'bg-white';
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
|
|
55
65
|
methods: {
|
|
56
|
-
|
|
57
|
-
this.$emit('
|
|
66
|
+
shown() {
|
|
67
|
+
this.$emit('shown');
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
hidden() {
|
|
71
|
+
this.$emit('hidden');
|
|
58
72
|
},
|
|
59
73
|
},
|
|
60
74
|
};
|