@veritree/ui 0.20.1 → 0.21.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 (78) hide show
  1. package/.prettierrc +2 -1
  2. package/index.js +64 -68
  3. package/mixins/floating-ui-content.js +81 -0
  4. package/mixins/floating-ui-item.js +266 -0
  5. package/mixins/floating-ui.js +67 -0
  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 +9 -4
  10. package/src/components/Alert/VTAlert.vue +55 -14
  11. package/src/components/Avatar/VTAvatar.vue +32 -29
  12. package/src/components/Button/VTButton.vue +20 -7
  13. package/src/components/Checkbox/VTCheckbox.vue +134 -0
  14. package/src/components/Checkbox/VTCheckboxLabel.vue +3 -0
  15. package/src/components/Checkbox/VTCheckboxText.vue +20 -0
  16. package/src/components/Dialog/VTDialog.vue +22 -32
  17. package/src/components/Dialog/VTDialogClose.vue +19 -25
  18. package/src/components/Dialog/VTDialogContent.vue +24 -19
  19. package/src/components/Dialog/VTDialogFooter.vue +11 -16
  20. package/src/components/Dialog/VTDialogHeader.vue +16 -18
  21. package/src/components/Dialog/VTDialogMain.vue +11 -18
  22. package/src/components/Dialog/VTDialogOverlay.vue +14 -18
  23. package/src/components/Dialog/VTDialogTitle.vue +10 -7
  24. package/src/components/Disclosure/VTDisclosureContent.vue +1 -1
  25. package/src/components/Disclosure/VTDisclosureDetails.vue +1 -1
  26. package/src/components/Disclosure/VTDisclosureHeader.vue +2 -2
  27. package/src/components/Disclosure/VTDisclosureIcon.vue +1 -1
  28. package/src/components/Drawer/VTDrawer.vue +6 -15
  29. package/src/components/Drawer/VTDrawerClose.vue +5 -5
  30. package/src/components/Drawer/VTDrawerContent.vue +9 -9
  31. package/src/components/Drawer/VTDrawerFooter.vue +3 -3
  32. package/src/components/Drawer/VTDrawerHeader.vue +4 -4
  33. package/src/components/Drawer/VTDrawerMain.vue +5 -5
  34. package/src/components/Drawer/VTDrawerOverlay.vue +6 -6
  35. package/src/components/Drawer/VTDrawerTitle.vue +5 -5
  36. package/src/components/DropdownMenu/VTDropdownMenu.vue +45 -28
  37. package/src/components/DropdownMenu/VTDropdownMenuContent.vue +29 -64
  38. package/src/components/DropdownMenu/VTDropdownMenuDivider.vue +8 -14
  39. package/src/components/DropdownMenu/VTDropdownMenuItem.vue +11 -124
  40. package/src/components/DropdownMenu/VTDropdownMenuLabel.vue +3 -12
  41. package/src/components/DropdownMenu/VTDropdownMenuTrigger.vue +91 -121
  42. package/src/components/Form/VTFormFeedback.vue +39 -22
  43. package/src/components/Form/VTFormGroup.vue +5 -7
  44. package/src/components/Form/VTFormLabel.vue +22 -0
  45. package/src/components/Form/VTFormRow.vue +5 -0
  46. package/src/components/Form/VTInput.vue +40 -0
  47. package/src/components/Form/VTInputIcon.vue +35 -0
  48. package/src/components/Form/VTInputPassword.vue +55 -0
  49. package/src/components/Form/VTTextarea.vue +22 -0
  50. package/src/components/Listbox/VTListbox.vue +122 -50
  51. package/src/components/Listbox/VTListboxContent.vue +20 -116
  52. package/src/components/Listbox/VTListboxItem.vue +115 -166
  53. package/src/components/Listbox/VTListboxLabel.vue +3 -14
  54. package/src/components/Listbox/VTListboxList.vue +10 -40
  55. package/src/components/Listbox/VTListboxSearch.vue +76 -68
  56. package/src/components/Listbox/VTListboxTrigger.vue +75 -86
  57. package/src/components/Popover/VTPopover.vue +42 -29
  58. package/src/components/Popover/VTPopoverContent.vue +24 -59
  59. package/src/components/Popover/VTPopoverDivider.vue +4 -11
  60. package/src/components/Popover/VTPopoverItem.vue +21 -14
  61. package/src/components/Popover/VTPopoverTrigger.vue +126 -21
  62. package/src/components/ProgressBar/VTProgressBar.vue +21 -3
  63. package/src/components/Skeleton/VTSkeleton.vue +11 -0
  64. package/src/components/Skeleton/VTSkeletonItem.vue +9 -0
  65. package/src/components/Tabs/VTTab.vue +4 -3
  66. package/src/components/Tabs/VTTabGroup.vue +9 -7
  67. package/src/components/Tabs/VTTabPanel.vue +4 -5
  68. package/src/components/Tooltip/VTTooltip.vue +65 -0
  69. package/src/components/Tooltip/VTTooltipContent.vue +59 -0
  70. package/src/components/Tooltip/VTTooltipTrigger.vue +98 -0
  71. package/src/components/Transitions/FadeInOut.vue +2 -2
  72. package/src/components/Utils/FloatingUi.vue +93 -0
  73. package/src/components/Input/VTInput.vue +0 -82
  74. package/src/components/Input/VTInputDate.vue +0 -36
  75. package/src/components/Input/VTInputFile.vue +0 -60
  76. package/src/components/Input/VTInputUpload.vue +0 -54
  77. package/src/components/Modal/VTModal.vue +0 -69
  78. package/src/utils/genId.js +0 -13
package/.prettierrc CHANGED
@@ -1,4 +1,5 @@
1
1
  {
2
2
  "semi": true,
3
- "singleQuote": true
3
+ "singleQuote": true,
4
+ "quoteProps": "preserve"
4
5
  }
package/index.js CHANGED
@@ -1,67 +1,65 @@
1
- import VTAlert from "./src/components/Alert/VTAlert.vue";
2
- import VTAvatar from "./src/components/Avatar/VTAvatar.vue";
3
- import VTAvatarImage from "./src/components/Avatar/VTAvatarImage.vue";
4
- import VTAvatarText from "./src/components/Avatar/VTAvatarText.vue";
5
- import VTButton from "./src/components/Button/VTButton.vue";
6
- import VTImage from "./src/components/Image/VTImage.vue";
7
- import VTImageCounter from "./src/components/Image/VTImageCounter.vue";
8
- import VTImageHover from "./src/components/Image/VTImageHover.vue";
9
- import VTDropdownMenu from "./src/components/DropdownMenu/VTDropdownMenu.vue";
10
- import VTDropdownMenuContent from "./src/components/DropdownMenu/VTDropdownMenuContent.vue";
11
- import VTDropdownMenuDivider from "./src/components/DropdownMenu/VTDropdownMenuDivider.vue";
12
- import VTDropdownMenuGroup from "./src/components/DropdownMenu/VTDropdownMenuGroup.vue";
13
- import VTDropdownMenuItem from "./src/components/DropdownMenu/VTDropdownMenuItem.vue";
14
- import VTDropdownMenuLabel from "./src/components/DropdownMenu/VTDropdownMenuLabel.vue";
15
- import VTDropdownMenuTrigger from "./src/components/DropdownMenu/VTDropdownMenuTrigger.vue";
16
- import VTPopover from "./src/components/Popover/VTPopover.vue";
17
- import VTPopoverContent from "./src/components/Popover/VTPopoverContent.vue";
18
- import VTPopoverDivider from "./src/components/Popover/VTPopoverDivider.vue";
19
- import VTPopoverGroup from "./src/components/Popover/VTPopoverGroup.vue";
20
- import VTPopoverItem from "./src/components/Popover/VTPopoverItem.vue";
21
- import VTPopoverTrigger from "./src/components/Popover/VTPopoverTrigger.vue";
22
- import VTFormFeedback from "./src/components/Form/VTFormFeedback.vue";
23
- import VTFormGroup from "./src/components/Form/VTFormGroup.vue";
24
- import VTListbox from "./src/components/Listbox/VTListbox.vue";
25
- import VTListboxContent from "./src/components/Listbox/VTListboxContent.vue";
26
- import VTListboxItem from "./src/components/Listbox/VTListboxItem.vue";
27
- import VTListboxLabel from "./src/components/Listbox/VTListboxLabel.vue";
28
- import VTListboxList from "./src/components/Listbox/VTListboxList.vue";
29
- import VTListboxSearch from "./src/components/Listbox/VTListboxSearch.vue";
30
- import VTListboxTrigger from "./src/components/Listbox/VTListboxTrigger.vue";
31
- import VTSpinner from "./src/components/Spinner/VTSpinner.vue";
32
- import VTInput from "./src/components/Input/VTInput.vue";
33
- import VTInputDate from "./src/components/Input/VTInputDate.vue";
34
- import VTInputFile from "./src/components/Input/VTInputFile.vue";
35
- import VTInputUpload from "./src/components/Input/VTInputUpload.vue";
36
- import VTProgressBar from "./src/components/ProgressBar/VTProgressBar.vue";
37
- import VTTextarea from "./src/components/Textarea/VTTextarea.vue";
38
- import VTModal from "./src/components/Modal/VTModal.vue";
39
- import VTTab from "./src/components/Tabs/VTTab.vue";
40
- import VTTabGroup from "./src/components/Tabs/VTTabGroup.vue";
41
- import VTTabList from "./src/components/Tabs/VTTabList.vue";
42
- import VTTabPanel from "./src/components/Tabs/VTTabPanel.vue";
43
- import VTTabPanels from "./src/components/Tabs/VTTabPanels.vue";
44
- import VTDialog from "./src/components/Dialog/VTDialog.vue";
45
- import VTDialogClose from "./src/components/Dialog/VTDialogClose.vue";
46
- import VTDialogContent from "./src/components/Dialog/VTDialogContent.vue";
47
- import VTDialogFooter from "./src/components/Dialog/VTDialogFooter.vue";
48
- import VTDialogHeader from "./src/components/Dialog/VTDialogHeader.vue";
49
- import VTDialogMain from "./src/components/Dialog/VTDialogMain.vue";
50
- import VTDialogOverlay from "./src/components/Dialog/VTDialogOverlay.vue";
51
- import VTDialogTitle from "./src/components/Dialog/VTDialogTitle.vue";
52
- import VTDrawer from "./src/components/Drawer/VTDrawer.vue";
53
- import VTDrawerClose from "./src/components/Drawer/VTDrawerClose.vue";
54
- import VTDrawerContent from "./src/components/Drawer/VTDrawerContent.vue";
55
- import VTDrawerFooter from "./src/components/Drawer/VTDrawerFooter.vue";
56
- import VTDrawerHeader from "./src/components/Drawer/VTDrawerHeader.vue";
57
- import VTDrawerTitle from "./src/components/Drawer/VTDrawerTitle.vue";
58
- import VTDrawerMain from "./src/components/Drawer/VTDrawerMain.vue";
59
- import VTDrawerOverlay from "./src/components/Drawer/VTDrawerOverlay.vue";
60
- import VTDisclosure from "./src/components/Disclosure/VTDisclosure.vue";
61
- import VTDisclosureDetails from "./src/components/Disclosure/VTDisclosureDetails.vue";
62
- import VTDisclosureHeader from "./src/components/Disclosure/VTDisclosureHeader.vue";
63
- import VTDisclosureIcon from "./src/components/Disclosure/VTDisclosureIcon.vue";
64
- import VTDisclosureContent from "./src/components/Disclosure/VTDisclosureContent.vue";
1
+ import VTAlert from './src/components/Alert/VTAlert.vue';
2
+ import VTAvatar from './src/components/Avatar/VTAvatar.vue';
3
+ import VTAvatarImage from './src/components/Avatar/VTAvatarImage.vue';
4
+ import VTAvatarText from './src/components/Avatar/VTAvatarText.vue';
5
+ import VTButton from './src/components/Button/VTButton.vue';
6
+ import VTImage from './src/components/Image/VTImage.vue';
7
+ import VTImageCounter from './src/components/Image/VTImageCounter.vue';
8
+ import VTImageHover from './src/components/Image/VTImageHover.vue';
9
+ import VTDropdownMenu from './src/components/DropdownMenu/VTDropdownMenu.vue';
10
+ import VTDropdownMenuContent from './src/components/DropdownMenu/VTDropdownMenuContent.vue';
11
+ import VTDropdownMenuDivider from './src/components/DropdownMenu/VTDropdownMenuDivider.vue';
12
+ import VTDropdownMenuGroup from './src/components/DropdownMenu/VTDropdownMenuGroup.vue';
13
+ import VTDropdownMenuItem from './src/components/DropdownMenu/VTDropdownMenuItem.vue';
14
+ import VTDropdownMenuLabel from './src/components/DropdownMenu/VTDropdownMenuLabel.vue';
15
+ import VTDropdownMenuTrigger from './src/components/DropdownMenu/VTDropdownMenuTrigger.vue';
16
+ import VTPopover from './src/components/Popover/VTPopover.vue';
17
+ import VTPopoverContent from './src/components/Popover/VTPopoverContent.vue';
18
+ import VTPopoverDivider from './src/components/Popover/VTPopoverDivider.vue';
19
+ import VTPopoverGroup from './src/components/Popover/VTPopoverGroup.vue';
20
+ import VTPopoverItem from './src/components/Popover/VTPopoverItem.vue';
21
+ import VTPopoverTrigger from './src/components/Popover/VTPopoverTrigger.vue';
22
+ import VTFormFeedback from './src/components/Form/VTFormFeedback.vue';
23
+ import VTFormGroup from './src/components/Form/VTFormGroup.vue';
24
+ import VTListbox from './src/components/Listbox/VTListbox.vue';
25
+ import VTListboxContent from './src/components/Listbox/VTListboxContent.vue';
26
+ import VTListboxItem from './src/components/Listbox/VTListboxItem.vue';
27
+ import VTListboxLabel from './src/components/Listbox/VTListboxLabel.vue';
28
+ import VTListboxList from './src/components/Listbox/VTListboxList.vue';
29
+ import VTListboxSearch from './src/components/Listbox/VTListboxSearch.vue';
30
+ import VTListboxTrigger from './src/components/Listbox/VTListboxTrigger.vue';
31
+ import VTSpinner from './src/components/Spinner/VTSpinner.vue';
32
+ import VTInput from './src/components/Form/VTInput.vue';
33
+ import VTProgressBar from './src/components/ProgressBar/VTProgressBar.vue';
34
+ import VTTextarea from './src/components/Textarea/VTTextarea.vue';
35
+ import VTTab from './src/components/Tabs/VTTab.vue';
36
+ import VTTabGroup from './src/components/Tabs/VTTabGroup.vue';
37
+ import VTTabList from './src/components/Tabs/VTTabList.vue';
38
+ import VTTabPanel from './src/components/Tabs/VTTabPanel.vue';
39
+ import VTTabPanels from './src/components/Tabs/VTTabPanels.vue';
40
+ import VTDialog from './src/components/Dialog/VTDialog.vue';
41
+ import VTDialogClose from './src/components/Dialog/VTDialogClose.vue';
42
+ import VTDialogContent from './src/components/Dialog/VTDialogContent.vue';
43
+ import VTDialogFooter from './src/components/Dialog/VTDialogFooter.vue';
44
+ import VTDialogHeader from './src/components/Dialog/VTDialogHeader.vue';
45
+ import VTDialogMain from './src/components/Dialog/VTDialogMain.vue';
46
+ import VTDialogOverlay from './src/components/Dialog/VTDialogOverlay.vue';
47
+ import VTDialogTitle from './src/components/Dialog/VTDialogTitle.vue';
48
+ import VTDrawer from './src/components/Drawer/VTDrawer.vue';
49
+ import VTDrawerClose from './src/components/Drawer/VTDrawerClose.vue';
50
+ import VTDrawerContent from './src/components/Drawer/VTDrawerContent.vue';
51
+ import VTDrawerFooter from './src/components/Drawer/VTDrawerFooter.vue';
52
+ import VTDrawerHeader from './src/components/Drawer/VTDrawerHeader.vue';
53
+ import VTDrawerTitle from './src/components/Drawer/VTDrawerTitle.vue';
54
+ import VTDrawerMain from './src/components/Drawer/VTDrawerMain.vue';
55
+ import VTDrawerOverlay from './src/components/Drawer/VTDrawerOverlay.vue';
56
+ import VTDisclosure from './src/components/Disclosure/VTDisclosure.vue';
57
+ import VTDisclosureDetails from './src/components/Disclosure/VTDisclosureDetails.vue';
58
+ import VTDisclosureHeader from './src/components/Disclosure/VTDisclosureHeader.vue';
59
+ import VTDisclosureIcon from './src/components/Disclosure/VTDisclosureIcon.vue';
60
+ import VTDisclosureContent from './src/components/Disclosure/VTDisclosureContent.vue';
61
+ import VTSkeleton from './src/components/Skeleton/VTSkeleton.vue';
62
+ import VTSkeletonItem from './src/components/Skeleton/VTSkeletonItem.vue';
65
63
 
66
64
  export {
67
65
  VTAvatar,
@@ -96,12 +94,8 @@ export {
96
94
  VTListboxTrigger,
97
95
  VTButton,
98
96
  VTInput,
99
- VTInputDate,
100
- VTInputFile,
101
- VTInputUpload,
102
97
  VTProgressBar,
103
98
  VTTextarea,
104
- VTModal,
105
99
  VTTab,
106
100
  VTTabGroup,
107
101
  VTTabList,
@@ -128,4 +122,6 @@ export {
128
122
  VTDisclosureHeader,
129
123
  VTDisclosureIcon,
130
124
  VTDisclosureContent,
125
+ VTSkeleton,
126
+ VTSkeletonItem,
131
127
  };
@@ -0,0 +1,81 @@
1
+ import FloatingUi from '../src/components/Utils/FloatingUi.vue';
2
+
3
+ export const floatingUiContentMixin = {
4
+ components: {
5
+ FloatingUi,
6
+ },
7
+
8
+ props: {
9
+ headless: {
10
+ type: Boolean,
11
+ default: false,
12
+ },
13
+ },
14
+
15
+ data() {
16
+ return {
17
+ el: null,
18
+ visible: false,
19
+ };
20
+ },
21
+
22
+ mounted() {
23
+ document.addEventListener('click', this.onDocumentClick);
24
+ },
25
+
26
+ destroyed() {
27
+ document.removeEventListener('click', this.onDocumentClick);
28
+ },
29
+
30
+ methods: {
31
+ show() {
32
+ if (this.visible) {
33
+ return;
34
+ }
35
+
36
+ this.visible = true;
37
+
38
+ this.$nextTick(() => {
39
+ this.component.setActive();
40
+
41
+ setTimeout(() => {
42
+ this.el = document.getElementById(this.id);
43
+ this.$emit('shown');
44
+ }, 100);
45
+ });
46
+ },
47
+
48
+ hide() {
49
+ if (!this.visible) {
50
+ return;
51
+ }
52
+
53
+ this.visible = false;
54
+
55
+ this.$nextTick(() => {
56
+ this.component.clearActive();
57
+
58
+ setTimeout(() => {
59
+ this.el = document.getElementById(this.id);
60
+ this.$emit('hidden');
61
+ }, 100);
62
+ });
63
+ },
64
+
65
+ onDocumentClick(e) {
66
+ if (!e) {
67
+ return;
68
+ }
69
+
70
+ e.stopPropagation();
71
+
72
+ if (this.visible && !this.el.contains(e.target)) {
73
+ this.componentTrigger.cancel();
74
+ }
75
+ },
76
+
77
+ setActiveDescedant(id) {
78
+ this.activeDescedant = id;
79
+ },
80
+ },
81
+ }
@@ -0,0 +1,266 @@
1
+ export const floatingUiItemMixin = {
2
+ props: {
3
+ headless: {
4
+ type: Boolean,
5
+ default: false,
6
+ },
7
+ disabled: {
8
+ type: Boolean,
9
+ default: false,
10
+ },
11
+ },
12
+
13
+ computed: {
14
+ id() {
15
+ return `${this.componentName}-${this.apiInjected().id}-${this.index}`;
16
+ },
17
+
18
+ items() {
19
+ return this.apiInjected().items;
20
+ },
21
+
22
+ componentContent() {
23
+ return this.apiInjected().componentContent;
24
+ },
25
+
26
+ componentTrigger() {
27
+ return this.apiInjected().componentTrigger;
28
+ },
29
+
30
+ classComputed() {
31
+ return [
32
+ // default styles
33
+ this.headless
34
+ ? `${this.componentName}`
35
+ : 'relative z-10 flex items-center gap-2 px-3 py-2 text-inherit no-underline cursor-pointer',
36
+ // disabled state styles
37
+ this.headless
38
+ ? this.disabled
39
+ ? `${this.componentName}--disabled`
40
+ : null
41
+ : this.disabled
42
+ ? 'pointer-events-none opacity-75'
43
+ : null,
44
+ // selected state styles
45
+ this.headless
46
+ ? this.selected
47
+ ? `${this.componentName}--selected`
48
+ : null
49
+ : this.selected
50
+ ? 'bg-gray-200'
51
+ : null,
52
+ ];
53
+ },
54
+ },
55
+
56
+ data() {
57
+ return {
58
+ index: null,
59
+ selected: false,
60
+ tabIndex: 0,
61
+ eventType: null,
62
+ };
63
+ },
64
+
65
+ watch: {
66
+ selected(newValue) {
67
+ if (!newValue || !this.componentContent) {
68
+ return;
69
+ }
70
+
71
+ this.componentContent.setActiveDescedant(this.id);
72
+
73
+ // do not scroll into view if it's a mouse event
74
+ if (this.eventType && this.eventType.includes('mouse')) {
75
+ return;
76
+ }
77
+
78
+ this.$el.scrollIntoView({ block: 'nearest' });
79
+ },
80
+ },
81
+
82
+ mounted() {
83
+ this.init();
84
+ this.addMouseEventListeners();
85
+ },
86
+
87
+ beforeUnmount() {
88
+ this.apiInjected().unregisterItem(this.id);
89
+ this.removeMouseEventListeners();
90
+ },
91
+
92
+ methods: {
93
+ init() {
94
+ this.index = this.items.length;
95
+
96
+ const item = {
97
+ id: this.id,
98
+ text: '',
99
+ select: this.select,
100
+ unselect: this.unselect,
101
+ isSelected: this.isSelected,
102
+ focus: this.focus,
103
+ onClick: this.onClick,
104
+ };
105
+
106
+ this.apiInjected().registerItem(item);
107
+ },
108
+
109
+ /**
110
+ * mousemove and mouseleave events will be attached to
111
+ * the element here instead of using vue @mousemove
112
+ * and @mouseleave in the element since they did
113
+ * not worker properly
114
+ */
115
+ addMouseEventListeners() {
116
+ this.$el.addEventListener('mousemove', this.onMousemove);
117
+ this.$el.addEventListener('mouseleave', this.onMouseleave);
118
+ },
119
+
120
+ /**
121
+ * remove the mousemove and mouseleave added for
122
+ * avoiding memory leaks, if @mousemove and
123
+ * @mouseleave were in the element, this
124
+ * wound't be needed
125
+ */
126
+ removeMouseEventListeners() {
127
+ this.$el.removeEventListener('mousemove', this.onMousemove);
128
+ this.$el.removeEventListener('mouseleave', this.onMouseleave);
129
+ },
130
+
131
+ select(eventType) {
132
+ this.eventType = eventType;
133
+ this.selected = true;
134
+ },
135
+
136
+ unselect() {
137
+ this.selected = false;
138
+ },
139
+
140
+ isSelected() {
141
+ return this.selected;
142
+ },
143
+
144
+ focus() {
145
+ if (!this.$el) return;
146
+
147
+ this.tabIndex = -1;
148
+ this.selected = true;
149
+ this.$el.focus();
150
+ },
151
+
152
+ focusFirstItem() {
153
+ const selectedIndex = this.getItemSelectedIndex();
154
+ const newSelectedIndex = 0;
155
+
156
+ this.setFocusToItem(selectedIndex, newSelectedIndex);
157
+ },
158
+
159
+ focusLastItem() {
160
+ const selectedIndex = this.getItemSelectedIndex();
161
+ const newSelectedIndex = this.items.length - 1;
162
+
163
+ this.setFocusToItem(selectedIndex, newSelectedIndex);
164
+ },
165
+
166
+ /**
167
+ * Focus the next item in the menu.
168
+ * If is the last item, jump to the first item.
169
+ */
170
+ focusNextItem() {
171
+ const selectedIndex = this.getItemSelectedIndex();
172
+ const isLast = selectedIndex === this.items.length - 1;
173
+ const firstItemIndex = 0;
174
+ const nextItemIndex = selectedIndex + 1;
175
+ const newSelectedIndex = isLast ? firstItemIndex : nextItemIndex;
176
+
177
+ this.setFocusToItem(selectedIndex, newSelectedIndex);
178
+ },
179
+
180
+ /**
181
+ * Focus the previous item in the menu.
182
+ * If is the first item, jump to the last item.
183
+ */
184
+ focusPreviousItem() {
185
+ const selectedIndex = this.getItemSelectedIndex();
186
+ const isFirst = selectedIndex === 0;
187
+ const lastItemIndex = this.items.length - 1;
188
+ const previousItemIndex = selectedIndex - 1;
189
+ const newSelectedIndex = isFirst ? lastItemIndex : previousItemIndex;
190
+
191
+ this.setFocusToItem(selectedIndex, newSelectedIndex);
192
+ },
193
+
194
+ /**
195
+ * Focus/select item by removing its tabindex and calling
196
+ * focus to the element.
197
+ *
198
+ * @param {Number, String} selectedIndex
199
+ * @param {Number, String} newSelectedIndex
200
+ */
201
+ setFocusToItem(selectedIndex, newSelectedIndex) {
202
+ this.tabIndex = 0;
203
+
204
+ // before focusing, let's unselect selected
205
+ // item that were previously focused
206
+ if (selectedIndex >= 0) {
207
+ this.items[selectedIndex].unselect();
208
+ }
209
+
210
+ // focus new item
211
+ if (this.items[newSelectedIndex]) {
212
+ this.items[newSelectedIndex].focus();
213
+ }
214
+ },
215
+
216
+ getItemSelectedIndex() {
217
+ return this.items.findIndex((item) => item.isSelected());
218
+ },
219
+
220
+ leaveMenu() {
221
+ if (this.componentTrigger) {
222
+ this.componentTrigger.cancel();
223
+ this.componentTrigger.focus();
224
+ }
225
+ },
226
+
227
+ onClick() {
228
+ if (this.disabled) {
229
+ return;
230
+ }
231
+
232
+ this.value !== undefined
233
+ ? this.apiInjected().emit(this.value)
234
+ : this.$emit('click');
235
+
236
+ this.$nextTick(() => this.leaveMenu());
237
+ },
238
+
239
+ onKeyEsc() {
240
+ this.leaveMenu();
241
+ },
242
+
243
+ onMousemove(event) {
244
+ if (this.selected) {
245
+ return;
246
+ }
247
+
248
+ // unselect all items before selecting new one
249
+ for (const item of this.items) {
250
+ item.unselect();
251
+ }
252
+
253
+ /**
254
+ * Select item passing the event type to decide if
255
+ * scrolling will be disabled or not. It is
256
+ * expected that on mouse move event, the scroll
257
+ * doesn't happen automatically.
258
+ */
259
+ this.select(event.type);
260
+ },
261
+
262
+ onMouseleave() {
263
+ this.unselect();
264
+ },
265
+ },
266
+ };
@@ -0,0 +1,67 @@
1
+ import { computePosition, flip, shift, offset, size } from '@floating-ui/dom';
2
+
3
+ export const floatingUiMixin = {
4
+ data() {
5
+ return {
6
+ component: null,
7
+ componentTrigger: null,
8
+ componentContent: null,
9
+ active: false,
10
+ };
11
+ },
12
+
13
+ watch: {
14
+ active(newVal) {
15
+ if (newVal) this.$nextTick(() => this.positionContentToTrigger());
16
+ },
17
+ },
18
+
19
+ mounted() {
20
+ this.component = {
21
+ setActive: this.setActive,
22
+ clearActive: this.clearActive,
23
+ };
24
+ },
25
+
26
+ methods: {
27
+ setActive() {
28
+ this.active = true;
29
+ },
30
+
31
+ clearActive() {
32
+ this.active = null;
33
+ },
34
+
35
+ positionContentToTrigger() {
36
+ const trigger = document.getElementById(this.componentTrigger.id);
37
+ const content = document.getElementById(this.componentContent.id);
38
+ const minWidthLimit = Number(this.floatingUiMinWidth);
39
+
40
+ computePosition(trigger, content, {
41
+ placement: this.placement,
42
+ middleware: [
43
+ offset(5),
44
+ flip(),
45
+ shift({ padding: 5 }),
46
+ size({
47
+ apply({ rects }) {
48
+ if (!minWidthLimit) return;
49
+
50
+ const width = rects.reference.width;
51
+ const minWidth = width < minWidthLimit ? minWidthLimit : width;
52
+
53
+ Object.assign(content.style, {
54
+ minWidth: `${minWidth}px`,
55
+ });
56
+ },
57
+ }),
58
+ ],
59
+ }).then(({ x, y }) => {
60
+ Object.assign(content.style, {
61
+ left: `${x}px`,
62
+ top: `${y}px`,
63
+ });
64
+ });
65
+ },
66
+ },
67
+ };
@@ -0,0 +1,53 @@
1
+ import { formControlMixin, formControlStyleMixin } from '../mixins/form-control';
2
+
3
+ export const formControlIconMixin = {
4
+ inheritAttrs: false,
5
+
6
+ mixins: [formControlMixin, formControlStyleMixin],
7
+
8
+ data() {
9
+ return {
10
+ name: 'input',
11
+ };
12
+ },
13
+
14
+ computed: {
15
+ isLeft() {
16
+ return this.iconPlacement === 'left';
17
+ },
18
+
19
+ isRight() {
20
+ return this.iconPlacement === 'right';
21
+ },
22
+
23
+ classComputedWrapper() {
24
+ return [
25
+ this.headless ? 'form-control-wrapper' : 'relative',
26
+ // placement styles
27
+ this.headless
28
+ ? `form-control-icon--${this.placement}`
29
+ : this.isLeft
30
+ ? '[&_input]:pl-9'
31
+ : this.isRight
32
+ ? '[&_input]:pr-9'
33
+ : null,
34
+ ];
35
+ },
36
+
37
+ classComputedWrapperIcon() {
38
+ return [
39
+ this.headless
40
+ ? 'form-control-wrapper__icon'
41
+ : `absolute top-0 bottom-0 flex w-9 justify-center items-center text-gray-400`,
42
+ // placement styles
43
+ this.headless
44
+ ? null
45
+ : this.isLeft
46
+ ? 'left-1'
47
+ : this.isRight
48
+ ? 'right-1'
49
+ : null,
50
+ ];
51
+ },
52
+ },
53
+ };