@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.
Files changed (74) hide show
  1. package/.prettierrc +2 -1
  2. package/index.js +64 -68
  3. package/mixins/floating-ui-content.js +6 -6
  4. package/mixins/floating-ui-item.js +266 -0
  5. package/mixins/floating-ui.js +11 -14
  6. package/mixins/form-control-icon.js +53 -0
  7. package/mixins/form-control.js +71 -0
  8. package/nuxt.js +30 -23
  9. package/package.json +8 -4
  10. package/src/components/Alert/VTAlert.vue +55 -14
  11. package/src/components/Button/VTButton.vue +20 -7
  12. package/src/components/Checkbox/VTCheckbox.vue +134 -0
  13. package/src/components/Checkbox/VTCheckboxLabel.vue +3 -0
  14. package/src/components/Checkbox/VTCheckboxText.vue +20 -0
  15. package/src/components/Dialog/VTDialog.vue +22 -32
  16. package/src/components/Dialog/VTDialogClose.vue +19 -25
  17. package/src/components/Dialog/VTDialogContent.vue +24 -19
  18. package/src/components/Dialog/VTDialogFooter.vue +11 -16
  19. package/src/components/Dialog/VTDialogHeader.vue +16 -18
  20. package/src/components/Dialog/VTDialogMain.vue +11 -18
  21. package/src/components/Dialog/VTDialogOverlay.vue +14 -18
  22. package/src/components/Dialog/VTDialogTitle.vue +10 -7
  23. package/src/components/Disclosure/VTDisclosureContent.vue +1 -1
  24. package/src/components/Disclosure/VTDisclosureDetails.vue +1 -1
  25. package/src/components/Disclosure/VTDisclosureHeader.vue +2 -2
  26. package/src/components/Disclosure/VTDisclosureIcon.vue +1 -1
  27. package/src/components/Drawer/VTDrawer.vue +6 -15
  28. package/src/components/Drawer/VTDrawerClose.vue +5 -5
  29. package/src/components/Drawer/VTDrawerContent.vue +9 -9
  30. package/src/components/Drawer/VTDrawerFooter.vue +3 -3
  31. package/src/components/Drawer/VTDrawerHeader.vue +4 -4
  32. package/src/components/Drawer/VTDrawerMain.vue +5 -5
  33. package/src/components/Drawer/VTDrawerOverlay.vue +6 -6
  34. package/src/components/Drawer/VTDrawerTitle.vue +5 -5
  35. package/src/components/DropdownMenu/VTDropdownMenu.vue +23 -5
  36. package/src/components/DropdownMenu/VTDropdownMenuContent.vue +15 -3
  37. package/src/components/DropdownMenu/VTDropdownMenuDivider.vue +7 -16
  38. package/src/components/DropdownMenu/VTDropdownMenuItem.vue +10 -148
  39. package/src/components/DropdownMenu/VTDropdownMenuLabel.vue +1 -10
  40. package/src/components/DropdownMenu/VTDropdownMenuTrigger.vue +1 -3
  41. package/src/components/Form/VTFormFeedback.vue +11 -5
  42. package/src/components/Form/VTFormGroup.vue +5 -7
  43. package/src/components/Form/VTFormLabel.vue +22 -0
  44. package/src/components/Form/VTFormRow.vue +5 -0
  45. package/src/components/Form/VTInput.vue +30 -42
  46. package/src/components/Form/VTInputIcon.vue +35 -0
  47. package/src/components/Form/VTInputPassword.vue +55 -0
  48. package/src/components/Form/VTTextarea.vue +22 -0
  49. package/src/components/Listbox/VTListbox.vue +105 -16
  50. package/src/components/Listbox/VTListboxContent.vue +3 -10
  51. package/src/components/Listbox/VTListboxItem.vue +111 -175
  52. package/src/components/Listbox/VTListboxLabel.vue +3 -4
  53. package/src/components/Listbox/VTListboxList.vue +3 -35
  54. package/src/components/Listbox/VTListboxSearch.vue +69 -62
  55. package/src/components/Listbox/VTListboxTrigger.vue +13 -40
  56. package/src/components/Popover/VTPopover.vue +19 -0
  57. package/src/components/Popover/VTPopoverItem.vue +6 -2
  58. package/src/components/ProgressBar/VTProgressBar.vue +21 -3
  59. package/src/components/Skeleton/VTSkeleton.vue +11 -0
  60. package/src/components/Skeleton/VTSkeletonItem.vue +9 -0
  61. package/src/components/Tabs/VTTab.vue +4 -3
  62. package/src/components/Tabs/VTTabGroup.vue +9 -7
  63. package/src/components/Tabs/VTTabPanel.vue +4 -5
  64. package/src/components/Tooltip/VTTooltip.vue +65 -0
  65. package/src/components/Tooltip/VTTooltipContent.vue +59 -0
  66. package/src/components/Tooltip/VTTooltipTrigger.vue +98 -0
  67. package/src/components/Transitions/FadeInOut.vue +2 -2
  68. package/src/components/Utils/FloatingUi.vue +56 -24
  69. package/src/components/Input/VTInput.vue +0 -82
  70. package/src/components/Input/VTInputDate.vue +0 -36
  71. package/src/components/Input/VTInputFile.vue +0 -60
  72. package/src/components/Input/VTInputUpload.vue +0 -54
  73. package/src/components/Modal/VTModal.vue +0 -69
  74. package/src/utils/genId.js +0 -13
@@ -2,7 +2,9 @@
2
2
  <ul
3
3
  :id="id"
4
4
  :class="[
5
- headless ? 'listbox-list' : 'max-h-[160px] w-auto overflow-y-auto -mx-3',
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
- <input
3
- v-model="search"
4
- :class="{ 'listbox-search': headless, 'form-control mb-1': !headless }"
5
- type="text"
6
- @input="onChange"
7
- @click.stop
8
- @keydown.down.prevent="focusNextItem"
9
- @keydown.up.prevent="focusPreviousItem"
10
- @keydown.home.prevent="focusFirstItem"
11
- @keydown.end.prevent="focusLastItem"
12
- @keydown.enter.prevent="onKeyEnter"
13
- @keydown.esc.stop="hide"
14
- @keydown.tab.prevent="hide"
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
- inject: ['apiListbox'],
28
+ mixins: [formControlMixin],
23
29
 
24
- props: {
25
- headless: {
26
- type: Boolean,
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 search = {
57
+ const componentSearch = {
58
58
  el: this.$el,
59
59
  };
60
60
 
61
- this.apiListbox().registerSearch(search);
62
- this.$nextTick(() => setTimeout(() => this.$el.focus(), 150));
61
+ this.apiListbox().registerSearch(componentSearch);
62
+ this.$nextTick(() => setTimeout(() => this.$refs.search.focus(), 150));
63
63
  },
64
64
 
65
- beforeDestroy() {
65
+ beforeUnmount() {
66
66
  this.search = '';
67
- this.$emit('change', this.search.trim());
67
+ this.$emit('update:modelValue', '');
68
68
  },
69
69
 
70
70
  methods: {
71
71
  focusNextItem() {
72
- if (this.index !== -1) {
73
- this.unselectItem();
74
- }
75
-
76
- this.index++;
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
- if (this.index > this.items.length - 1) {
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.unselectItem();
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.index--;
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.unselectItem();
99
- this.index = 0;
100
- this.item.select();
92
+ const selectedIndex = this.getItemSelectedIndex();
93
+ const newSelectedIndex = 0;
94
+
95
+ this.selectItem(selectedIndex, newSelectedIndex);
101
96
  },
102
97
 
103
98
  focusLastItem() {
104
- this.unselectItem();
105
- this.index = this.items.length - 1;
106
- this.item.select();
99
+ const selectedIndex = this.getItemSelectedIndex();
100
+ const newSelectedIndex = this.items.length - 1;
101
+
102
+ this.selectItem(selectedIndex, newSelectedIndex);
107
103
  },
108
104
 
109
- unselectItem() {
110
- const isMousemove = this.list.getMousemove();
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
- if (isMousemove) {
113
- this.list.unsetMousemove();
114
- this.items.forEach((item) => item.unselect());
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
- onChange() {
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('change', this.search.trim());
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
- inject: ['apiListbox'],
38
+ mixins: [formControlMixin, formControlStyleMixin],
52
39
 
53
- props: {
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 hover:bg-secondary-200/10',
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 ? 'a' : this.to ? 'NuxtLink' : 'button';
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="100"
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 bg-secondary-300 transition-all',
18
+ : 'bg-secondary-200 absolute left-0 h-full transition-all',
19
19
  ]"
20
- :style="{ width: `${value}%` }"
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>
@@ -0,0 +1,11 @@
1
+ <template>
2
+ <div class="animate-pulse">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ export default {
9
+ name: 'VTSkeleton',
10
+ };
11
+ </script>
@@ -0,0 +1,9 @@
1
+ <template>
2
+ <div class="bg-gray-300" />
3
+ </template>
4
+
5
+ <script>
6
+ export default {
7
+ name: 'VTSkeletonItem',
8
+ };
9
+ </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
- beforeDestroy() {
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
- vertical: {
22
- type: Boolean,
23
- default: false
24
- }
22
+ vertical: {
23
+ type: Boolean,
24
+ default: false,
25
+ },
25
26
  },
27
+
26
28
  provide() {
27
29
  return {
28
- api: () => {
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: ['api'],
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.api.registerTabPanel(this);
33
+ this.apiTabs().registerTabPanel(this);
35
34
  },
36
35
 
37
- beforeDestroy() {
38
- this.api.unregisterTabPanel(this.id);
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>