@veritree/ui 0.21.1-8 → 0.22.0-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/.prettierrc +2 -1
- package/index.js +64 -68
- package/mixins/floating-ui-content.js +6 -6
- package/mixins/floating-ui-item.js +266 -0
- package/mixins/floating-ui.js +11 -14
- package/mixins/form-control-icon.js +53 -0
- package/mixins/form-control.js +71 -0
- package/nuxt.js +30 -23
- package/package.json +8 -4
- package/src/components/Alert/VTAlert.vue +55 -14
- package/src/components/Button/VTButton.vue +20 -7
- package/src/components/Checkbox/VTCheckbox.vue +134 -0
- package/src/components/Checkbox/VTCheckboxLabel.vue +3 -0
- package/src/components/Checkbox/VTCheckboxText.vue +20 -0
- package/src/components/Dialog/VTDialog.vue +22 -32
- package/src/components/Dialog/VTDialogClose.vue +19 -25
- package/src/components/Dialog/VTDialogContent.vue +24 -19
- package/src/components/Dialog/VTDialogFooter.vue +11 -16
- package/src/components/Dialog/VTDialogHeader.vue +16 -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/Disclosure/VTDisclosureContent.vue +1 -1
- package/src/components/Disclosure/VTDisclosureDetails.vue +1 -1
- package/src/components/Disclosure/VTDisclosureHeader.vue +2 -2
- package/src/components/Disclosure/VTDisclosureIcon.vue +1 -1
- package/src/components/Drawer/VTDrawer.vue +6 -15
- package/src/components/Drawer/VTDrawerClose.vue +5 -5
- package/src/components/Drawer/VTDrawerContent.vue +9 -9
- package/src/components/Drawer/VTDrawerFooter.vue +3 -3
- package/src/components/Drawer/VTDrawerHeader.vue +4 -4
- package/src/components/Drawer/VTDrawerMain.vue +5 -5
- package/src/components/Drawer/VTDrawerOverlay.vue +6 -6
- package/src/components/Drawer/VTDrawerTitle.vue +5 -5
- package/src/components/DropdownMenu/VTDropdownMenu.vue +23 -5
- package/src/components/DropdownMenu/VTDropdownMenuContent.vue +15 -3
- package/src/components/DropdownMenu/VTDropdownMenuDivider.vue +7 -16
- package/src/components/DropdownMenu/VTDropdownMenuItem.vue +10 -148
- package/src/components/DropdownMenu/VTDropdownMenuLabel.vue +1 -10
- package/src/components/DropdownMenu/VTDropdownMenuTrigger.vue +1 -3
- package/src/components/Form/VTFormFeedback.vue +11 -5
- package/src/components/Form/VTFormGroup.vue +5 -7
- package/src/components/Form/VTFormLabel.vue +22 -0
- package/src/components/Form/VTFormRow.vue +5 -0
- package/src/components/Form/VTInput.vue +30 -42
- package/src/components/Form/VTInputIcon.vue +35 -0
- package/src/components/Form/VTInputPassword.vue +55 -0
- package/src/components/Form/VTTextarea.vue +22 -0
- package/src/components/Listbox/VTListbox.vue +105 -16
- package/src/components/Listbox/VTListboxContent.vue +3 -10
- package/src/components/Listbox/VTListboxItem.vue +111 -175
- package/src/components/Listbox/VTListboxLabel.vue +3 -4
- package/src/components/Listbox/VTListboxList.vue +3 -35
- package/src/components/Listbox/VTListboxSearch.vue +69 -62
- package/src/components/Listbox/VTListboxTrigger.vue +13 -40
- package/src/components/Popover/VTPopover.vue +19 -0
- package/src/components/Popover/VTPopoverItem.vue +6 -2
- package/src/components/ProgressBar/VTProgressBar.vue +21 -3
- package/src/components/Skeleton/VTSkeleton.vue +11 -0
- package/src/components/Skeleton/VTSkeletonItem.vue +9 -0
- package/src/components/Tabs/VTTab.vue +4 -3
- package/src/components/Tabs/VTTabGroup.vue +9 -7
- package/src/components/Tabs/VTTabPanel.vue +4 -5
- package/src/components/Tooltip/VTTooltip.vue +65 -0
- package/src/components/Tooltip/VTTooltipContent.vue +59 -0
- package/src/components/Tooltip/VTTooltipTrigger.vue +98 -0
- package/src/components/Transitions/FadeInOut.vue +2 -2
- package/src/components/Utils/FloatingUi.vue +56 -24
- package/src/components/Input/VTInput.vue +0 -82
- package/src/components/Input/VTInputDate.vue +0 -36
- package/src/components/Input/VTInputFile.vue +0 -60
- package/src/components/Input/VTInputUpload.vue +0 -54
- package/src/components/Modal/VTModal.vue +0 -69
- package/src/utils/genId.js +0 -13
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
<ul
|
|
3
3
|
:id="id"
|
|
4
4
|
:class="[
|
|
5
|
-
headless
|
|
5
|
+
headless
|
|
6
|
+
? 'listbox-list'
|
|
7
|
+
: '-mx-3 max-h-[160px] w-auto overflow-y-auto scroll-auto',
|
|
6
8
|
]"
|
|
7
9
|
>
|
|
8
10
|
<slot></slot>
|
|
@@ -22,44 +24,10 @@ export default {
|
|
|
22
24
|
},
|
|
23
25
|
},
|
|
24
26
|
|
|
25
|
-
data() {
|
|
26
|
-
return {
|
|
27
|
-
isMousemove: false,
|
|
28
|
-
};
|
|
29
|
-
},
|
|
30
|
-
|
|
31
27
|
computed: {
|
|
32
28
|
id() {
|
|
33
29
|
return `listbox-list-${this.apiListbox().id}`;
|
|
34
30
|
},
|
|
35
31
|
},
|
|
36
|
-
|
|
37
|
-
mounted() {
|
|
38
|
-
const list = {
|
|
39
|
-
el: this.$el,
|
|
40
|
-
getMousemove: this.getMousemove,
|
|
41
|
-
setMousemove: this.setMousemove,
|
|
42
|
-
unsetMousemove: this.unsetMousemove,
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
this.apiListbox().registerList(list);
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
methods: {
|
|
49
|
-
// Mousemove instead of mouseover to support keyboard navigation.
|
|
50
|
-
// The problem with mouseover is that when scrolling (scrollIntoView),
|
|
51
|
-
// mouseover event gets triggered.
|
|
52
|
-
setMousemove() {
|
|
53
|
-
this.isMousemove = true;
|
|
54
|
-
},
|
|
55
|
-
|
|
56
|
-
unsetMousemove() {
|
|
57
|
-
this.isMousemove = false;
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
getMousemove() {
|
|
61
|
-
return this.isMousemove;
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
32
|
};
|
|
65
33
|
</script>
|
|
@@ -1,37 +1,41 @@
|
|
|
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
|
+
:value="modelValue"
|
|
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="onInput"
|
|
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
|
+
import { formControlMixin } from '../../../mixins/form-control';
|
|
24
|
+
|
|
19
25
|
export default {
|
|
20
26
|
name: 'VTListboxSearch',
|
|
21
27
|
|
|
22
|
-
|
|
28
|
+
mixins: [formControlMixin],
|
|
23
29
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
default: false,
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
+
inheritAttrs: false,
|
|
31
|
+
|
|
32
|
+
inject: ['apiListbox'],
|
|
30
33
|
|
|
31
34
|
data() {
|
|
32
35
|
return {
|
|
36
|
+
name: 'listbox-search',
|
|
37
|
+
index: null,
|
|
33
38
|
search: '',
|
|
34
|
-
index: -1,
|
|
35
39
|
};
|
|
36
40
|
},
|
|
37
41
|
|
|
@@ -40,10 +44,6 @@ export default {
|
|
|
40
44
|
return this.apiListbox().componentTrigger;
|
|
41
45
|
},
|
|
42
46
|
|
|
43
|
-
list() {
|
|
44
|
-
return this.apiListbox().list;
|
|
45
|
-
},
|
|
46
|
-
|
|
47
47
|
items() {
|
|
48
48
|
return this.apiListbox().items;
|
|
49
49
|
},
|
|
@@ -54,74 +54,81 @@ export default {
|
|
|
54
54
|
},
|
|
55
55
|
|
|
56
56
|
mounted() {
|
|
57
|
-
const
|
|
57
|
+
const componentSearch = {
|
|
58
58
|
el: this.$el,
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
-
this.apiListbox().registerSearch(
|
|
62
|
-
this.$nextTick(() => setTimeout(() => this.$
|
|
61
|
+
this.apiListbox().registerSearch(componentSearch);
|
|
62
|
+
this.$nextTick(() => setTimeout(() => this.$refs.search.focus(), 150));
|
|
63
63
|
},
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
beforeUnmount() {
|
|
66
66
|
this.search = '';
|
|
67
|
-
this.$emit('
|
|
67
|
+
this.$emit('update:modelValue', '');
|
|
68
68
|
},
|
|
69
69
|
|
|
70
70
|
methods: {
|
|
71
71
|
focusNextItem() {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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;
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
this.index = 0;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (this.item) this.item.select();
|
|
78
|
+
this.selectItem(selectedIndex, newSelectedIndex);
|
|
83
79
|
},
|
|
84
80
|
|
|
85
81
|
focusPreviousItem() {
|
|
86
|
-
this.
|
|
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;
|
|
87
87
|
|
|
88
|
-
this.
|
|
89
|
-
|
|
90
|
-
if (this.index < 0) {
|
|
91
|
-
this.index = this.items.length - 1;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
this.item.select();
|
|
88
|
+
this.selectItem(selectedIndex, newSelectedIndex);
|
|
95
89
|
},
|
|
96
90
|
|
|
97
91
|
focusFirstItem() {
|
|
98
|
-
this.
|
|
99
|
-
|
|
100
|
-
|
|
92
|
+
const selectedIndex = this.getItemSelectedIndex();
|
|
93
|
+
const newSelectedIndex = 0;
|
|
94
|
+
|
|
95
|
+
this.selectItem(selectedIndex, newSelectedIndex);
|
|
101
96
|
},
|
|
102
97
|
|
|
103
98
|
focusLastItem() {
|
|
104
|
-
this.
|
|
105
|
-
|
|
106
|
-
|
|
99
|
+
const selectedIndex = this.getItemSelectedIndex();
|
|
100
|
+
const newSelectedIndex = this.items.length - 1;
|
|
101
|
+
|
|
102
|
+
this.selectItem(selectedIndex, newSelectedIndex);
|
|
107
103
|
},
|
|
108
104
|
|
|
109
|
-
|
|
110
|
-
|
|
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();
|
|
110
|
+
}
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
this.
|
|
112
|
+
// select new item
|
|
113
|
+
if (this.items[newSelectedIndex]) {
|
|
114
|
+
this.index = newSelectedIndex;
|
|
115
|
+
this.items[newSelectedIndex].select();
|
|
115
116
|
}
|
|
117
|
+
},
|
|
116
118
|
|
|
119
|
+
unselectItem() {
|
|
117
120
|
if (this.item) this.item.unselect();
|
|
118
121
|
},
|
|
119
122
|
|
|
120
|
-
|
|
123
|
+
getItemSelectedIndex() {
|
|
124
|
+
return this.items.findIndex((item) => item.isSelected());
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
onInput(event) {
|
|
121
128
|
this.index = 0;
|
|
122
129
|
if (this.item) this.item.select();
|
|
123
130
|
|
|
124
|
-
this.$emit('
|
|
131
|
+
this.$emit('update:modelValue', event.target.value);
|
|
125
132
|
},
|
|
126
133
|
|
|
127
134
|
onKeyEnter() {
|
|
@@ -1,37 +1,20 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<button
|
|
3
3
|
:id="id"
|
|
4
|
+
:class="classComputed"
|
|
5
|
+
:disabled="disabled"
|
|
4
6
|
:aria-expanded="expanded"
|
|
5
7
|
:aria-haspopup="hasPopup"
|
|
6
|
-
:class="[
|
|
7
|
-
headless
|
|
8
|
-
? 'listbox-button'
|
|
9
|
-
: 'flex w-full justify-between border border-solid py-2 px-3 gap-3 rounded text-inherit max-w-full',
|
|
10
|
-
headless
|
|
11
|
-
? `listbox-button--${variant}`
|
|
12
|
-
: isError
|
|
13
|
-
? 'border-error-300'
|
|
14
|
-
: 'border-gray-300',
|
|
15
|
-
]"
|
|
16
8
|
type="button"
|
|
17
9
|
@click.prevent="onClick"
|
|
18
10
|
@keydown.down.prevent="onKeyDownOrUp"
|
|
19
11
|
@keydown.up.prevent="onKeyDownOrUp"
|
|
20
12
|
@keydown.esc.stop="onKeyEsc"
|
|
21
13
|
>
|
|
22
|
-
<span
|
|
23
|
-
:class="{
|
|
24
|
-
'listbox-button__text': headless,
|
|
25
|
-
'text-left': !headless,
|
|
26
|
-
}"
|
|
27
|
-
>
|
|
14
|
+
<span :class="[headless ? 'listbox-button__text' : 'truncate text-left']">
|
|
28
15
|
<slot></slot>
|
|
29
16
|
</span>
|
|
30
|
-
<span
|
|
31
|
-
:class="{
|
|
32
|
-
'listbox-button__icon': headless,
|
|
33
|
-
}"
|
|
34
|
-
>
|
|
17
|
+
<span :class="[headless ? 'listbox-button__icon' : 'shrink-0']">
|
|
35
18
|
<IconChevronDown
|
|
36
19
|
class="transition-transform"
|
|
37
20
|
:class="{ 'rotate-180': expanded }"
|
|
@@ -41,6 +24,10 @@
|
|
|
41
24
|
</template>
|
|
42
25
|
|
|
43
26
|
<script>
|
|
27
|
+
import {
|
|
28
|
+
formControlMixin,
|
|
29
|
+
formControlStyleMixin,
|
|
30
|
+
} from '../../../mixins/form-control';
|
|
44
31
|
import { IconChevronDown } from '@veritree/icons';
|
|
45
32
|
|
|
46
33
|
export default {
|
|
@@ -48,25 +35,13 @@ export default {
|
|
|
48
35
|
|
|
49
36
|
components: { IconChevronDown },
|
|
50
37
|
|
|
51
|
-
|
|
38
|
+
mixins: [formControlMixin, formControlStyleMixin],
|
|
52
39
|
|
|
53
|
-
|
|
54
|
-
disabled: {
|
|
55
|
-
type: Boolean,
|
|
56
|
-
default: false,
|
|
57
|
-
},
|
|
58
|
-
headless: {
|
|
59
|
-
type: Boolean,
|
|
60
|
-
default: false,
|
|
61
|
-
},
|
|
62
|
-
variant: {
|
|
63
|
-
type: [String, Object, Function],
|
|
64
|
-
default: '',
|
|
65
|
-
},
|
|
66
|
-
},
|
|
40
|
+
inject: ['apiListbox'],
|
|
67
41
|
|
|
68
42
|
data() {
|
|
69
43
|
return {
|
|
44
|
+
name: 'listbox-trigger',
|
|
70
45
|
expanded: false,
|
|
71
46
|
hasPopup: false,
|
|
72
47
|
};
|
|
@@ -77,10 +52,6 @@ export default {
|
|
|
77
52
|
return `listbox-trigger-${this.apiListbox().id}`;
|
|
78
53
|
},
|
|
79
54
|
|
|
80
|
-
isError() {
|
|
81
|
-
return this.variant === 'error';
|
|
82
|
-
},
|
|
83
|
-
|
|
84
55
|
componentContent() {
|
|
85
56
|
return this.apiListbox().componentContent;
|
|
86
57
|
},
|
|
@@ -133,6 +104,7 @@ export default {
|
|
|
133
104
|
if (!this.componentContent) {
|
|
134
105
|
return;
|
|
135
106
|
}
|
|
107
|
+
|
|
136
108
|
this.expanded = false;
|
|
137
109
|
|
|
138
110
|
this.hideComponentContent();
|
|
@@ -152,6 +124,7 @@ export default {
|
|
|
152
124
|
|
|
153
125
|
onClick(e) {
|
|
154
126
|
this.init(e);
|
|
127
|
+
this.$emit('click');
|
|
155
128
|
},
|
|
156
129
|
|
|
157
130
|
onKeyDownOrUp(e) {
|
|
@@ -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
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// default styles
|
|
6
6
|
headless
|
|
7
7
|
? 'popover-item'
|
|
8
|
-
: 'relative z-10 -mx-3 flex items-center gap-2 px-3 py-2 text-inherit no-underline
|
|
8
|
+
: 'hover:bg-secondary-200/10 relative z-10 -mx-3 flex items-center gap-2 px-3 py-2 text-inherit no-underline',
|
|
9
9
|
]"
|
|
10
10
|
@click="onClick"
|
|
11
11
|
>
|
|
@@ -44,7 +44,11 @@ export default {
|
|
|
44
44
|
},
|
|
45
45
|
|
|
46
46
|
as() {
|
|
47
|
-
return this.href
|
|
47
|
+
return this.href
|
|
48
|
+
? 'a'
|
|
49
|
+
: this.to
|
|
50
|
+
? resolveComponent('NuxtLink')
|
|
51
|
+
: 'button';
|
|
48
52
|
},
|
|
49
53
|
},
|
|
50
54
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
]"
|
|
8
8
|
role="progressbar"
|
|
9
9
|
aria-valuemin="0"
|
|
10
|
-
aria-valuemax="
|
|
10
|
+
:aria-valuemax="max"
|
|
11
11
|
:aria-valuenow="value"
|
|
12
12
|
:aria-label="label"
|
|
13
13
|
>
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
:class="[
|
|
16
16
|
headless
|
|
17
17
|
? 'progress-bar__indicator'
|
|
18
|
-
: 'absolute left-0 h-full
|
|
18
|
+
: 'bg-secondary-200 absolute left-0 h-full transition-all',
|
|
19
19
|
]"
|
|
20
|
-
:style="{ width: `${
|
|
20
|
+
:style="{ width: `${percentageComputed}%` }"
|
|
21
21
|
></div>
|
|
22
22
|
</div>
|
|
23
23
|
</template>
|
|
@@ -37,6 +37,24 @@ export default {
|
|
|
37
37
|
type: [String, Number],
|
|
38
38
|
default: 0,
|
|
39
39
|
},
|
|
40
|
+
max: {
|
|
41
|
+
type: [String, Number],
|
|
42
|
+
default: 100,
|
|
43
|
+
},
|
|
44
|
+
percentage: {
|
|
45
|
+
type: [String, Number],
|
|
46
|
+
default: null,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
computed: {
|
|
51
|
+
percentageComputed() {
|
|
52
|
+
if (typeof this.percentage !== 'undefined' && this.percentage !== null) {
|
|
53
|
+
return this.percentage;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return (this.value / this.max) * 100;
|
|
57
|
+
},
|
|
40
58
|
},
|
|
41
59
|
};
|
|
42
60
|
</script>
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
]"
|
|
19
19
|
role="tab"
|
|
20
20
|
type="button"
|
|
21
|
-
@click.stop="onClick"
|
|
21
|
+
@click.prevent.stop="onClick"
|
|
22
22
|
@keydown="onKeyDown"
|
|
23
23
|
@blur="onBlur"
|
|
24
24
|
>
|
|
25
|
-
<slot></slot>
|
|
25
|
+
<slot :selected="selected"></slot>
|
|
26
26
|
</button>
|
|
27
27
|
</template>
|
|
28
28
|
|
|
@@ -60,7 +60,7 @@ export default {
|
|
|
60
60
|
this.api.registerTab(this);
|
|
61
61
|
},
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
beforeUnmount() {
|
|
64
64
|
this.api.unregisterTab(this.index);
|
|
65
65
|
},
|
|
66
66
|
|
|
@@ -106,6 +106,7 @@ export default {
|
|
|
106
106
|
onClick() {
|
|
107
107
|
this.api.selectTab(this.index);
|
|
108
108
|
this.$emit('change');
|
|
109
|
+
this.$emit('click');
|
|
109
110
|
},
|
|
110
111
|
|
|
111
112
|
onKeyDown(event) {
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
7
|
<style scoped>
|
|
8
|
-
.TabGroup.vertical{
|
|
8
|
+
.TabGroup.vertical {
|
|
9
9
|
display: flex;
|
|
10
10
|
}
|
|
11
|
-
.TabGroup.vertical .TabList{
|
|
11
|
+
.TabGroup.vertical .TabList {
|
|
12
12
|
display: flex;
|
|
13
13
|
flex-direction: column;
|
|
14
14
|
}
|
|
@@ -17,15 +17,17 @@
|
|
|
17
17
|
<script>
|
|
18
18
|
export default {
|
|
19
19
|
name: 'VTTabGroup',
|
|
20
|
+
|
|
20
21
|
props: {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
vertical: {
|
|
23
|
+
type: Boolean,
|
|
24
|
+
default: false,
|
|
25
|
+
},
|
|
25
26
|
},
|
|
27
|
+
|
|
26
28
|
provide() {
|
|
27
29
|
return {
|
|
28
|
-
|
|
30
|
+
apiTabs: () => {
|
|
29
31
|
const registerTab = (tab) => {
|
|
30
32
|
_register(this.tabs, tab);
|
|
31
33
|
if (tab.index === 0) tab.select();
|
|
@@ -14,11 +14,10 @@
|
|
|
14
14
|
export default {
|
|
15
15
|
name: 'VTTabPanel',
|
|
16
16
|
|
|
17
|
-
inject: ['
|
|
17
|
+
inject: ['apiTabs'],
|
|
18
18
|
|
|
19
19
|
data() {
|
|
20
20
|
return {
|
|
21
|
-
api: this.api(),
|
|
22
21
|
index: null,
|
|
23
22
|
visible: false,
|
|
24
23
|
};
|
|
@@ -31,11 +30,11 @@ export default {
|
|
|
31
30
|
},
|
|
32
31
|
|
|
33
32
|
mounted() {
|
|
34
|
-
this.
|
|
33
|
+
this.apiTabs().registerTabPanel(this);
|
|
35
34
|
},
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
this.
|
|
36
|
+
beforeUnmount() {
|
|
37
|
+
this.apiTabs().unregisterTabPanel(this.id);
|
|
39
38
|
},
|
|
40
39
|
|
|
41
40
|
methods: {
|
|
@@ -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>
|