@veritree/ui 0.22.3 → 0.24.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 (38) hide show
  1. package/mixins/floating-ui-content.js +0 -21
  2. package/mixins/floating-ui-item.js +88 -46
  3. package/mixins/floating-ui.js +4 -13
  4. package/mixins/form-control-icon.js +2 -2
  5. package/mixins/form-control.js +9 -5
  6. package/nuxt.js +1 -0
  7. package/package.json +1 -1
  8. package/src/components/Avatar/VTAvatarImage.vue +26 -6
  9. package/src/components/Button/VTButton.vue +5 -5
  10. package/src/components/Dialog/VTDialog.vue +6 -15
  11. package/src/components/Dialog/VTDialogClose.vue +19 -25
  12. package/src/components/Dialog/VTDialogContent.vue +18 -21
  13. package/src/components/Dialog/VTDialogFooter.vue +7 -18
  14. package/src/components/Dialog/VTDialogHeader.vue +15 -18
  15. package/src/components/Dialog/VTDialogMain.vue +11 -18
  16. package/src/components/Dialog/VTDialogOverlay.vue +14 -18
  17. package/src/components/Dialog/VTDialogTitle.vue +10 -7
  18. package/src/components/Drawer/VTDrawerContent.vue +1 -1
  19. package/src/components/Drawer/VTDrawerFooter.vue +1 -1
  20. package/src/components/DropdownMenu/VTDropdownMenu.vue +19 -0
  21. package/src/components/DropdownMenu/VTDropdownMenuContent.vue +5 -6
  22. package/src/components/DropdownMenu/VTDropdownMenuItem.vue +2 -2
  23. package/src/components/Form/VTFormFeedback.vue +5 -5
  24. package/src/components/Form/VTInput.vue +5 -2
  25. package/src/components/Form/VTTextarea.vue +5 -2
  26. package/src/components/Listbox/VTListbox.vue +35 -11
  27. package/src/components/Listbox/VTListboxContent.vue +4 -7
  28. package/src/components/Listbox/VTListboxItem.vue +117 -6
  29. package/src/components/Listbox/VTListboxList.vue +1 -24
  30. package/src/components/Listbox/VTListboxSearch.vue +58 -52
  31. package/src/components/Listbox/VTListboxTrigger.vue +7 -4
  32. package/src/components/Modal/VTModal.vue +1 -1
  33. package/src/components/Popover/VTPopover.vue +19 -0
  34. package/src/components/Popover/VTPopoverContent.vue +3 -3
  35. package/src/components/Tooltip/VTTooltip.vue +65 -0
  36. package/src/components/Tooltip/VTTooltipContent.vue +59 -0
  37. package/src/components/Tooltip/VTTooltipTrigger.vue +100 -0
  38. package/src/components/Utils/FloatingUi.vue +27 -13
@@ -1,32 +1,21 @@
1
1
  <template>
2
- <component
3
- :is="as"
4
- :class="{ 'Dialog-footer': headless, 'w-full': !headless }"
5
- >
2
+ <component :is="as" :class="[headless ? 'dialog-footer' : 'w-full']">
6
3
  <slot></slot>
7
4
  </component>
8
5
  </template>
9
6
 
10
7
  <script>
11
8
  export default {
12
- name: "VTDialogFooter",
13
-
14
- inject: ["api"],
9
+ name: 'VTDialogFooter',
15
10
 
16
11
  props: {
12
+ headless: {
13
+ type: Boolean,
14
+ default: false,
15
+ },
17
16
  as: {
18
17
  type: String,
19
- default: "footer",
20
- },
21
- },
22
-
23
- computed: {
24
- dark() {
25
- return this.api().isDark;
26
- },
27
-
28
- headless() {
29
- return this.api().isHeadless;
18
+ default: 'footer',
30
19
  },
31
20
  },
32
21
  };
@@ -2,10 +2,11 @@
2
2
  <component
3
3
  :is="as"
4
4
  :id="id"
5
- :class="{
6
- 'Dialog-header': headless,
7
- 'mb-8 flex justify-between gap-x-3 items-center w-full': !headless,
8
- }"
5
+ :class="[
6
+ headless
7
+ ? 'dialog-header'
8
+ : 'mb-8 flex w-full items-center justify-between gap-x-3',
9
+ ]"
9
10
  >
10
11
  <slot></slot>
11
12
  </component>
@@ -13,28 +14,24 @@
13
14
 
14
15
  <script>
15
16
  export default {
16
- name: "VTDialogHeader",
17
+ name: 'VTDialogHeader',
17
18
 
18
- inject: ["api"],
19
+ inject: ['apiDialog'],
19
20
 
20
21
  props: {
22
+ headless: {
23
+ type: Boolean,
24
+ default: false,
25
+ },
21
26
  as: {
22
27
  type: String,
23
- default: "header",
28
+ default: 'header',
24
29
  },
25
30
  },
26
31
 
27
32
  computed: {
28
- dark() {
29
- return this.api().isDark;
30
- },
31
-
32
- headless() {
33
- return this.api().isHeadless;
34
- },
35
-
36
33
  id() {
37
- return `${this.api().id}-header`;
34
+ return `dialog-header-${this.apiDialog().componentId}`;
38
35
  },
39
36
  },
40
37
 
@@ -45,10 +42,10 @@ export default {
45
42
  methods: {
46
43
  // In here because if there is no header, the dialog will not be labelled by
47
44
  setDialogLabelledby() {
48
- const dialog = document.getElementById(this.api().id);
45
+ const dialog = document.getElementById(this.apiDialog().id);
49
46
 
50
47
  if (dialog) {
51
- dialog.setAttribute("aria-labelledby", this.id);
48
+ dialog.setAttribute('aria-labelledby', this.id);
52
49
  }
53
50
  },
54
51
  },
@@ -2,10 +2,7 @@
2
2
  <component
3
3
  :is="as"
4
4
  :id="id"
5
- :class="{
6
- 'Dialog-body': headless,
7
- 'flex-1 w-full h-full overflow-y-auto': !headless,
8
- }"
5
+ :class="[headless ? 'dialog-body' : 'h-full w-full flex-1 overflow-y-auto']"
9
6
  >
10
7
  <slot></slot>
11
8
  </component>
@@ -13,28 +10,24 @@
13
10
 
14
11
  <script>
15
12
  export default {
16
- name: "VTDialogMain",
13
+ name: 'VTDialogMain',
17
14
 
18
- inject: ["api"],
15
+ inject: ['apiDialog'],
19
16
 
20
17
  props: {
18
+ headless: {
19
+ type: Boolean,
20
+ default: false,
21
+ },
21
22
  as: {
22
23
  type: String,
23
- default: "main",
24
+ default: 'main',
24
25
  },
25
26
  },
26
27
 
27
28
  computed: {
28
- dark() {
29
- return this.api().isDark;
30
- },
31
-
32
- headless() {
33
- return this.api().isHeadless;
34
- },
35
-
36
29
  id() {
37
- return `${this.api().id}-desc`;
30
+ return `dialog-main-${this.apiDialog().componentId}`;
38
31
  },
39
32
  },
40
33
 
@@ -45,10 +38,10 @@ export default {
45
38
  methods: {
46
39
  // In here because if there is no body, the dialog will not be described by
47
40
  setDialogDescribedby() {
48
- const dialog = document.getElementById(this.api().id);
41
+ const dialog = document.getElementById(this.apiDialog().id);
49
42
 
50
43
  if (dialog) {
51
- dialog.setAttribute("aria-describedby", this.id);
44
+ dialog.setAttribute('aria-describedby', this.id);
52
45
  }
53
46
  },
54
47
  },
@@ -3,25 +3,29 @@
3
3
  <div
4
4
  v-if="visible"
5
5
  :id="id"
6
- :class="{
7
- 'Dialog-overlay': headless,
8
- 'fixed inset-0 bg-fd-450/80': !headless,
9
- }"
10
- ></div>
6
+ :class="[headless ? 'dialog-overlay' : 'bg-primary-200/80 fixed inset-0']"
7
+ />
11
8
  </FadeInOut>
12
9
  </template>
13
10
 
14
11
  <script>
15
- import FadeInOut from "../Transitions/FadeInOut.vue";
12
+ import FadeInOut from '../Transitions/FadeInOut.vue';
16
13
 
17
14
  export default {
18
- name: "VTDialogOverlay",
15
+ name: 'VTDialogOverlay',
19
16
 
20
17
  components: {
21
18
  FadeInOut,
22
19
  },
23
20
 
24
- inject: ["api"],
21
+ inject: ['apiDialog'],
22
+
23
+ props: {
24
+ headless: {
25
+ type: Boolean,
26
+ default: false,
27
+ },
28
+ },
25
29
 
26
30
  data() {
27
31
  return {
@@ -31,21 +35,13 @@ export default {
31
35
 
32
36
  computed: {
33
37
  id() {
34
- return `dialog-overlay-${this.api().componentId}`;
35
- },
36
-
37
- dark() {
38
- return this.api().isDark;
39
- },
40
-
41
- headless() {
42
- return this.api().isHeadless;
38
+ return `dialog-overlay-${this.apiDialog().componentId}`;
43
39
  },
44
40
  },
45
41
 
46
42
  mounted() {
47
43
  this.visible = true;
48
- this.api().registerOverlay(this);
44
+ this.apiDialog().registerOverlay(this);
49
45
  },
50
46
 
51
47
  methods: {
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <span
3
3
  :id="id"
4
- :class="{ 'Dialog-header': headless, 'text-2xl font-semibold': !headless }"
4
+ :class="[headless ? 'dialog-header' : 'text-2xl font-semibold']"
5
5
  >
6
6
  <slot></slot
7
7
  ></span>
@@ -9,17 +9,20 @@
9
9
 
10
10
  <script>
11
11
  export default {
12
- name: "VTDialogTitle",
12
+ name: 'VTDialogTitle',
13
13
 
14
- inject: ["api"],
14
+ inject: ['apiDialog'],
15
15
 
16
- computed: {
17
- headless() {
18
- return this.api().isHeadless;
16
+ props: {
17
+ headless: {
18
+ type: Boolean,
19
+ default: false,
19
20
  },
21
+ },
20
22
 
23
+ computed: {
21
24
  id() {
22
- return `${this.api().id}-title`;
25
+ return `dialog-overlay-${this.apiDialog().componentId}`;
23
26
  },
24
27
  },
25
28
  };
@@ -22,7 +22,7 @@
22
22
  'bottom-0': position === 'bottom',
23
23
  }"
24
24
  tabindex="-1"
25
- @keyup.esc="hide"
25
+ @keydown.esc.stop="hide"
26
26
  >
27
27
  <slot></slot>
28
28
  </div>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <component :is="as" :class="{ 'Dialog-footer': headless }">
2
+ <component :is="as" :class="{ 'dialog-footer': headless }">
3
3
  <slot></slot>
4
4
  </component>
5
5
  </template>
@@ -56,12 +56,31 @@ export default {
56
56
  type: Boolean,
57
57
  default: false,
58
58
  },
59
+ placement: {
60
+ type: String,
61
+ default: 'bottom-start',
62
+ },
59
63
  },
60
64
 
61
65
  data() {
62
66
  return {
63
67
  componentId: genId(),
64
68
  items: [],
69
+ /**
70
+ * Explaining the need for the floatingUiMinWidth data
71
+ *
72
+ * The floating ui is a result of two items:
73
+ *
74
+ * 1. Trigger: the action button
75
+ * 2. Content: the popper/wrapper that appears after triggering the action button
76
+ *
77
+ * By default, the content will match the triggers width.
78
+ * The problem with this is that the trigger width
79
+ * might be too small causing the content to not fit
80
+ * what is inside it properly. So, to avoid this,
81
+ * a min width is needed.
82
+ */
83
+ floatingUiMinWidth: 200,
65
84
  };
66
85
  },
67
86
 
@@ -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="{ 'dropdown-menu-content': headless }"
7
- :floating-ui-class="floatingUiClass"
6
+ :portal-class="$vnode.data.staticClass"
7
+ component="dropdown"
8
8
  >
9
9
  <slot></slot>
10
10
  </FloatingUi>
@@ -16,6 +16,8 @@ import { floatingUiContentMixin } from '../../../mixins/floating-ui-content';
16
16
  export default {
17
17
  name: 'VTDropdownMenuContent',
18
18
 
19
+ inheritAttrs: false,
20
+
19
21
  mixins: [floatingUiContentMixin],
20
22
 
21
23
  inject: ['apiDropdownMenu'],
@@ -39,9 +41,6 @@ export default {
39
41
  id: this.id,
40
42
  hide: this.hide,
41
43
  show: this.show,
42
- getMousemove: this.getMousemove,
43
- setMousemove: this.setMousemove,
44
- unsetMousemove: this.unsetMousemove,
45
44
  setActiveDescedant: this.setActiveDescedant,
46
45
  };
47
46
 
@@ -9,8 +9,8 @@
9
9
  class="-mx-3"
10
10
  role="menuitem"
11
11
  @click.stop.prevent="onClick"
12
- @keydown.down.prevent="focusPreviousItem"
13
- @keydown.up.prevent="focusNextItem"
12
+ @keydown.down.prevent="focusNextItem"
13
+ @keydown.up.prevent="focusPreviousItem"
14
14
  @keydown.home.prevent="focusFirstItem"
15
15
  @keydown.end.prevent="focusLastItem"
16
16
  @keydown.esc.prevent="onKeyEsc"
@@ -1,25 +1,25 @@
1
1
  <template>
2
2
  <div
3
3
  :class="[
4
- headless ? 'form-feedback' : 'flex gap-2 mt-1 items-baseline',
4
+ headless ? 'form-feedback' : 'mt-1 flex items-baseline gap-2',
5
5
  // variant styles
6
- headless ? `form-feedback--{$variant}` : null,
6
+ headless ? `form-feedback--${variant}` : null,
7
7
  ]"
8
8
  >
9
9
  <component
10
10
  v-if="showIcon"
11
11
  :is="icon"
12
12
  :class="[
13
- headless ? 'form-feedback__icon' : 'relative top-1 shrink-0 h-4 w-4',
13
+ headless ? 'form-feedback__icon' : 'relative top-1 h-4 w-4 shrink-0',
14
14
  // variant styles
15
15
  headless
16
- ? `form-feedback__icon--{$variant}`
16
+ ? `form-feedback__icon--${variant}`
17
17
  : isError
18
18
  ? 'text-error-500'
19
19
  : null,
20
20
  ]"
21
21
  />
22
- <span :class="[headless ? 'form-feedback--text' : 'text-gray-500 text-sm']">
22
+ <span :class="[headless ? 'form-feedback--text' : 'text-sm text-gray-500']">
23
23
  <slot />
24
24
  </span>
25
25
  </div>
@@ -8,10 +8,13 @@
8
8
  </template>
9
9
 
10
10
  <script>
11
- import { formControlMixin } from '../../../mixins/form-control';
11
+ import {
12
+ formControlMixin,
13
+ formControlStyleMixin,
14
+ } from '../../../mixins/form-control';
12
15
 
13
16
  export default {
14
- mixins: [formControlMixin],
17
+ mixins: [formControlMixin, formControlStyleMixin],
15
18
 
16
19
  data() {
17
20
  return {
@@ -8,10 +8,13 @@
8
8
  </template>
9
9
 
10
10
  <script>
11
- import { formControlMixin } from '../../../mixins/form-control';
11
+ import {
12
+ formControlMixin,
13
+ formControlStyleMixin,
14
+ } from '../../../mixins/form-control';
12
15
 
13
16
  export default {
14
- mixins: [formControlMixin],
17
+ mixins: [formControlMixin, formControlStyleMixin],
15
18
 
16
19
  data() {
17
20
  return {
@@ -28,14 +28,9 @@ export default {
28
28
 
29
29
  const registerSearch = (search) => {
30
30
  if (!search) return;
31
- this.search = search;
31
+ this.componentSearch = search;
32
32
  };
33
33
 
34
- // const registerList = (list) => {
35
- // if (!list) return;
36
- // this.list = list;
37
- // };
38
-
39
34
  const registerItem = (item) => {
40
35
  if (!item) return;
41
36
  this.items.push(item);
@@ -46,6 +41,16 @@ export default {
46
41
  this.items.splice(index, 1);
47
42
  };
48
43
 
44
+ const pushSearch = (key) => {
45
+ key === 'Backspace' && this.search.length
46
+ ? (this.search = this.search.slice(0, -1))
47
+ : (this.search += key);
48
+ };
49
+
50
+ const clearSearch = () => {
51
+ this.search = '';
52
+ };
53
+
49
54
  const emit = (value) => {
50
55
  this.$emit('input', value);
51
56
  this.$emit('change', value);
@@ -56,14 +61,17 @@ export default {
56
61
  component: this.component,
57
62
  componentTrigger: this.componentTrigger,
58
63
  componentContent: this.componentContent,
64
+ componentSearch: this.componentSearch,
59
65
  items: this.items,
60
66
  search: this.search,
67
+ selectedValue: this.value,
61
68
  registerTrigger,
62
69
  registerContent,
63
70
  registerSearch,
64
- // registerList,
65
71
  registerItem,
66
72
  unregisterItem,
73
+ pushSearch,
74
+ clearSearch,
67
75
  emit,
68
76
  };
69
77
  },
@@ -79,9 +87,9 @@ export default {
79
87
  type: Boolean,
80
88
  default: false,
81
89
  },
82
- dark: {
83
- type: Boolean,
84
- default: false,
90
+ placement: {
91
+ type: String,
92
+ default: 'bottom-start',
85
93
  },
86
94
  right: {
87
95
  type: Boolean,
@@ -92,8 +100,24 @@ export default {
92
100
  data() {
93
101
  return {
94
102
  componentId: genId(),
95
- search: null,
103
+ componentSearch: null,
104
+ search: '',
96
105
  items: [],
106
+ /**
107
+ * Explaining the need for the floatingUiMinWidth data
108
+ *
109
+ * The floating ui is a result of two items:
110
+ *
111
+ * 1. Trigger: the action button
112
+ * 2. Content: the popper/wrapper that appears after triggering the action button
113
+ *
114
+ * By default, the content will match the triggers width.
115
+ * The problem with this is that the trigger width
116
+ * might be too small causing the content to not fit
117
+ * what is inside it properly. So, to avoid this,
118
+ * a min width is needed.
119
+ */
120
+ floatingUiMinWidth: 200,
97
121
  };
98
122
  },
99
123
 
@@ -1,11 +1,11 @@
1
1
  <template>
2
2
  <FloatingUi
3
- :visible="visible"
4
3
  :id="id"
5
- :aria-activedescendant="activeDescedant"
4
+ :visible="visible"
6
5
  :headless="headless"
7
- :class="{ 'listbox-content': headless }"
8
- :floating-ui-class="floatingUiClass"
6
+ :aria-activedescendant="activeDescedant"
7
+ :portal-class="$vnode.data.staticClass"
8
+ component="listbox"
9
9
  role="listbox"
10
10
  >
11
11
  <slot></slot>
@@ -47,9 +47,6 @@ export default {
47
47
  id: this.id,
48
48
  show: this.show,
49
49
  hide: this.hide,
50
- getMousemove: this.getMousemove,
51
- setMousemove: this.setMousemove,
52
- unsetMousemove: this.unsetMousemove,
53
50
  setActiveDescedant: this.setActiveDescedant,
54
51
  };
55
52