@veritree/ui 0.27.0 → 0.28.0-1

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 (116) hide show
  1. package/.claude/settings.local.json +10 -0
  2. package/index.js +105 -75
  3. package/mixins/floating-ui-content.js +17 -4
  4. package/mixins/floating-ui-item.js +31 -15
  5. package/mixins/floating-ui.js +142 -24
  6. package/mixins/form-control-icon.js +3 -3
  7. package/mixins/form-control.js +45 -20
  8. package/nuxt.js +38 -26
  9. package/package.json +17 -6
  10. package/src/components/Alert/VTAlert.vue +55 -14
  11. package/src/components/Avatar/VTAvatarImage.vue +6 -26
  12. package/src/components/Badge/VTBadge.vue +60 -0
  13. package/src/components/Badge/VTBadgeNew.vue +60 -0
  14. package/src/components/Breadcrumb/VTBreadcrumbItem.vue +11 -0
  15. package/src/components/Breadcrumb/VTBreadcrumbLink.vue +40 -0
  16. package/src/components/Breadcrumb/VTBreadcrumbList.vue +11 -0
  17. package/src/components/Breadcrumb/VTBreadcrumbRoot.vue +11 -0
  18. package/src/components/Breadcrumb/VTBreadcrumbSeparator.vue +19 -0
  19. package/src/components/Button/VTButton.vue +104 -56
  20. package/src/components/Carousel/VTCarousel.vue +69 -0
  21. package/src/components/Carousel/VTCarouselBackward.vue +36 -0
  22. package/src/components/Carousel/VTCarouselForward.vue +38 -0
  23. package/src/components/Carousel/VTCarouselTracker.vue +80 -0
  24. package/src/components/Checkbox/VTCheckbox.vue +134 -0
  25. package/src/components/Checkbox/VTCheckboxLabel.vue +3 -0
  26. package/src/components/Checkbox/VTCheckboxText.vue +20 -0
  27. package/src/components/Chip/VTChip.vue +29 -0
  28. package/src/components/Dialog/VTDialog.vue +59 -25
  29. package/src/components/Dialog/VTDialogClose.vue +3 -2
  30. package/src/components/Dialog/VTDialogContent.vue +29 -7
  31. package/src/components/Dialog/VTDialogFooter.vue +17 -2
  32. package/src/components/Dialog/VTDialogHeader.vue +2 -1
  33. package/src/components/Dialog/VTDialogMain.vue +5 -1
  34. package/src/components/Dialog/VTDialogOverlay.vue +5 -1
  35. package/src/components/Dialog/VTDialogTitle.vue +1 -1
  36. package/src/components/Disclosure/VTDisclosure.vue +2 -11
  37. package/src/components/Disclosure/VTDisclosureContent.vue +26 -52
  38. package/src/components/Disclosure/VTDisclosureDetails.vue +27 -2
  39. package/src/components/Disclosure/VTDisclosureHeader.vue +56 -89
  40. package/src/components/Disclosure/VTDisclosureIcon.vue +42 -31
  41. package/src/components/Divider/VTDivider.vue +9 -0
  42. package/src/components/Drawer/VTDrawer.vue +6 -15
  43. package/src/components/Drawer/VTDrawerClose.vue +5 -5
  44. package/src/components/Drawer/VTDrawerContent.vue +10 -10
  45. package/src/components/Drawer/VTDrawerFooter.vue +4 -4
  46. package/src/components/Drawer/VTDrawerHeader.vue +4 -4
  47. package/src/components/Drawer/VTDrawerMain.vue +5 -5
  48. package/src/components/Drawer/VTDrawerOverlay.vue +6 -6
  49. package/src/components/Drawer/VTDrawerTitle.vue +5 -5
  50. package/src/components/DropdownMenu/VTDropdownMenu.vue +0 -6
  51. package/src/components/DropdownMenu/VTDropdownMenuContent.vue +10 -1
  52. package/src/components/DropdownMenu/VTDropdownMenuDivider.vue +7 -16
  53. package/src/components/DropdownMenu/VTDropdownMenuItem.vue +5 -1
  54. package/src/components/DropdownMenu/VTDropdownMenuLabel.vue +1 -10
  55. package/src/components/DropdownMenu/VTDropdownMenuTrigger.vue +2 -4
  56. package/src/components/Form/VTFieldset.vue +5 -0
  57. package/src/components/Form/VTForm.vue +11 -0
  58. package/src/components/Form/VTFormCol.vue +20 -0
  59. package/src/components/Form/VTFormFeedback.vue +7 -1
  60. package/src/components/Form/VTFormGroup.vue +5 -7
  61. package/src/components/Form/VTFormLabel.vue +22 -0
  62. package/src/components/Form/VTFormLabelHelper.vue +22 -0
  63. package/src/components/Form/VTFormRow.vue +5 -0
  64. package/src/components/Form/VTInput.vue +2 -5
  65. package/src/components/Form/VTInputDate.vue +602 -0
  66. package/src/components/Form/VTInputIcon.vue +3 -9
  67. package/src/components/Form/VTInputNumber.vue +198 -0
  68. package/src/components/Form/VTInputPassword.vue +14 -5
  69. package/src/components/Form/VTInputRange.vue +92 -0
  70. package/src/components/Form/VTLegend.vue +24 -0
  71. package/src/components/Form/VTTextarea.vue +2 -2
  72. package/src/components/Image/VTImage.vue +10 -10
  73. package/src/components/Listbox/VTListbox.vue +128 -9
  74. package/src/components/Listbox/VTListboxContent.vue +14 -1
  75. package/src/components/Listbox/VTListboxDivider.vue +21 -0
  76. package/src/components/Listbox/VTListboxGroup.vue +9 -0
  77. package/src/components/Listbox/VTListboxItem.vue +57 -15
  78. package/src/components/Listbox/VTListboxLabel.vue +5 -4
  79. package/src/components/Listbox/VTListboxList.vue +1 -6
  80. package/src/components/Listbox/VTListboxPlaceholder.vue +25 -0
  81. package/src/components/Listbox/VTListboxSearch.vue +12 -8
  82. package/src/components/Listbox/VTListboxTrigger.vue +87 -6
  83. package/src/components/Listbox/VTListboxTriggerHighlight.vue +204 -0
  84. package/src/components/Listbox/VTListboxViewport.vue +33 -0
  85. package/src/components/Popover/VTPopoverContent.vue +3 -3
  86. package/src/components/Popover/VTPopoverDivider.vue +1 -1
  87. package/src/components/Popover/VTPopoverItem.vue +6 -2
  88. package/src/components/ProgressBar/VTProgressBar.vue +35 -10
  89. package/src/components/ProgressBar/VTProgressBarIndicator.vue +53 -0
  90. package/src/components/ScrollShadows/VTScrollShadows.vue +76 -0
  91. package/src/components/Separator/VTSeparator.vue +13 -0
  92. package/src/components/Switch/VTSwitch.vue +61 -0
  93. package/src/components/Tabs/VTTab.vue +6 -5
  94. package/src/components/Tabs/VTTabGroup.vue +88 -9
  95. package/src/components/Tabs/VTTabPanel.vue +4 -5
  96. package/src/components/Toast/README.md +263 -0
  97. package/src/components/Toast/VTToast.vue +145 -0
  98. package/src/components/Toast/VTToastAction.vue +25 -0
  99. package/src/components/Toast/VTToastClose.vue +52 -0
  100. package/src/components/Toast/VTToastContent.vue +25 -0
  101. package/src/components/Toast/VTToastDescription.vue +36 -0
  102. package/src/components/Toast/VTToastIcon.vue +72 -0
  103. package/src/components/Toast/VTToastItem.vue +180 -0
  104. package/src/components/Toast/VTToastTitle.vue +34 -0
  105. package/src/components/Tooltip/VTTooltipTrigger.vue +3 -5
  106. package/src/components/Transitions/FadeInOut.vue +2 -2
  107. package/src/components/Utils/FloatingUi.vue +31 -13
  108. package/src/helpers/currency.js +21 -0
  109. package/src/utils/components.js +18 -0
  110. package/src/utils/images.js +31 -12
  111. package/src/components/Input/VTInput.vue +0 -82
  112. package/src/components/Input/VTInputDate.vue +0 -36
  113. package/src/components/Input/VTInputFile.vue +0 -60
  114. package/src/components/Input/VTInputUpload.vue +0 -54
  115. package/src/components/Modal/VTModal.vue +0 -69
  116. package/src/utils/genId.js +0 -13
@@ -0,0 +1,180 @@
1
+ <template>
2
+ <transition
3
+ appear
4
+ :appear-class="enterClass"
5
+ appear-active-class="duration-300 ease-out"
6
+ appear-to-class="translate-y-0 opacity-100"
7
+ enter-active-class="duration-300 ease-out"
8
+ :enter-class="enterClass"
9
+ enter-to-class="translate-y-0 opacity-100"
10
+ leave-active-class="duration-300 ease-in"
11
+ :leave-to-class="leaveClass"
12
+ @after-leave="onAfterLeave"
13
+ >
14
+ <component
15
+ :is="as"
16
+ v-if="visible"
17
+ :id="id"
18
+ :class="[
19
+ headless
20
+ ? 'toast-item'
21
+ : 'grid grid-cols-[auto_1fr_auto] items-center gap-3 rounded border border-solid pt-2 pb-2.5 px-3 shadow-lg bg-gray-800 ',
22
+ headless ? `toast-item--${variant}` : null,
23
+ ]"
24
+ role="alert"
25
+ aria-live="polite"
26
+ @mouseenter="pauseTimer"
27
+ @mouseleave="resumeTimer"
28
+ >
29
+ <slot></slot>
30
+ </component>
31
+ </transition>
32
+ </template>
33
+
34
+ <script>
35
+ import { genId } from '../../utils/ids';
36
+
37
+ export default {
38
+ name: 'VTToastItem',
39
+
40
+ inject: ['apiToast'],
41
+
42
+ provide() {
43
+ return {
44
+ apiToastItem: () => {
45
+ return {
46
+ componentId: this.componentId,
47
+ variant: this.variant,
48
+ close: this.dismiss,
49
+ };
50
+ },
51
+ };
52
+ },
53
+
54
+ props: {
55
+ headless: {
56
+ type: Boolean,
57
+ default: false,
58
+ },
59
+ variant: {
60
+ type: String,
61
+ default: 'default',
62
+ validator: (value) => {
63
+ return ['default', 'success', 'error', 'warning'].includes(value);
64
+ },
65
+ },
66
+ duration: {
67
+ type: Number,
68
+ default: 5000,
69
+ },
70
+ as: {
71
+ type: String,
72
+ default: 'div',
73
+ },
74
+ },
75
+
76
+ data() {
77
+ return {
78
+ componentId: genId(),
79
+ visible: true,
80
+ timer: null,
81
+ };
82
+ },
83
+
84
+ computed: {
85
+ id() {
86
+ return `toast-item-${this.componentId}`;
87
+ },
88
+
89
+ isSuccess() {
90
+ return this.variant === 'success';
91
+ },
92
+
93
+ isError() {
94
+ return this.variant === 'error';
95
+ },
96
+
97
+ isWarning() {
98
+ return this.variant === 'warning';
99
+ },
100
+
101
+ position() {
102
+ return this.apiToast().position;
103
+ },
104
+
105
+ enterClass() {
106
+ // Slide from top/bottom edge based on position
107
+ if (this.position.includes('top')) {
108
+ return '-translate-y-full opacity-0';
109
+ } else if (this.position.includes('bottom')) {
110
+ return 'translate-y-full opacity-0';
111
+ }
112
+
113
+ return 'opacity-0';
114
+ },
115
+
116
+ leaveClass() {
117
+ // Slide to top/bottom edge based on position
118
+ if (this.position.includes('top')) {
119
+ return '-translate-y-full opacity-0';
120
+ } else if (this.position.includes('bottom')) {
121
+ return 'translate-y-full opacity-0';
122
+ }
123
+
124
+ return 'opacity-0';
125
+ },
126
+ },
127
+
128
+ mounted() {
129
+ this.apiToast().registerItem(this);
130
+
131
+ if (this.duration > 0) {
132
+ this.startTimer();
133
+ }
134
+ },
135
+
136
+ beforeDestroy() {
137
+ this.clearTimer();
138
+
139
+ // Only unregister if not already hidden (avoid double unregister)
140
+ if (this.visible) {
141
+ this.apiToast().unregisterItem(this);
142
+ }
143
+ },
144
+
145
+ methods: {
146
+ startTimer() {
147
+ if (this.duration > 0) {
148
+ this.timer = setTimeout(() => {
149
+ this.dismiss();
150
+ }, this.duration);
151
+ }
152
+ },
153
+
154
+ clearTimer() {
155
+ if (this.timer) {
156
+ clearTimeout(this.timer);
157
+ this.timer = null;
158
+ }
159
+ },
160
+
161
+ pauseTimer() {
162
+ this.clearTimer();
163
+ },
164
+
165
+ resumeTimer() {
166
+ this.startTimer();
167
+ },
168
+
169
+ dismiss() {
170
+ // this.visible = false;
171
+ this.$emit('close');
172
+ },
173
+
174
+ onAfterLeave() {
175
+ // Unregister from parent after transition completes
176
+ this.apiToast().unregisterItem(this);
177
+ },
178
+ },
179
+ };
180
+ </script>
@@ -0,0 +1,34 @@
1
+ <template>
2
+ <component
3
+ :is="as"
4
+ :id="id"
5
+ :class="[headless ? 'toast-title' : 'text-sm font-semibold text-white']"
6
+ >
7
+ <slot></slot>
8
+ </component>
9
+ </template>
10
+
11
+ <script>
12
+ export default {
13
+ name: 'VTToastTitle',
14
+
15
+ inject: ['apiToastItem'],
16
+
17
+ props: {
18
+ headless: {
19
+ type: Boolean,
20
+ default: false,
21
+ },
22
+ as: {
23
+ type: String,
24
+ default: 'div',
25
+ },
26
+ },
27
+
28
+ computed: {
29
+ id() {
30
+ return `toast-title-${this.apiToastItem().componentId}`;
31
+ },
32
+ },
33
+ };
34
+ </script>
@@ -2,9 +2,8 @@
2
2
  <div
3
3
  :id="id"
4
4
  :aria-describedby="ariaDescribedBy"
5
- class="inline-flex"
6
5
  @mouseenter="onMouseenter"
7
- @mouseleave="onMouseleave"
6
+ @mouseleave="onmouseout"
8
7
  >
9
8
  <slot />
10
9
  </div>
@@ -55,7 +54,7 @@ export default {
55
54
  }, this.apiTooltip().delayDuration);
56
55
  },
57
56
 
58
- onMouseleave() {
57
+ onmouseout() {
59
58
  clearTimeout(tooltipTriggerTimeout);
60
59
  this.cancel();
61
60
  },
@@ -75,8 +74,7 @@ export default {
75
74
  // delay stop propagation to close other visible
76
75
  // dropdowns and delay click event to control
77
76
  // this dropdown visibility
78
- setTimeout(() => e.stopImmediatePropagation(), 50);
79
- setTimeout(() => this.showComponentContent(), 100);
77
+ this.showComponentContent();
80
78
  },
81
79
 
82
80
  cancel() {
@@ -1,10 +1,10 @@
1
1
  <template>
2
2
  <transition
3
3
  enter-active-class="duration-300 ease-out"
4
- enter-class="transform opacity-0"
4
+ enter-from-class="transform opacity-0"
5
5
  enter-to-class="opacity-100"
6
6
  leave-active-class="duration-300 ease-in"
7
- leave-class="opacity-100"
7
+ leave-from-class="opacity-100"
8
8
  leave-to-class="transform opacity-0"
9
9
  @after-enter="afterEnter"
10
10
  @after-leave="afterLeave"
@@ -1,12 +1,12 @@
1
1
  <template>
2
- <Portal>
2
+ <Teleport to="body">
3
3
  <transition
4
4
  enter-active-class="duration-200 ease-out"
5
- enter-class="translate-y-[15px] opacity-0"
6
- enter-to-class="translate-y-0 opacity-100"
5
+ :enter-from-class="transitionEnterClass"
6
+ :enter-to-class="transitionEnterToClass"
7
7
  leave-active-class="duration-200 ease-in"
8
- leave-class="translate-y-0 opacity-100"
9
- leave-to-class="translate-y-[15px] opacity-0"
8
+ :leave-from-class="transitionLeaveClass"
9
+ :leave-to-class="transitionLeaveToClass"
10
10
  @after-leave="hidden"
11
11
  @after-enter="shown"
12
12
  >
@@ -15,23 +15,19 @@
15
15
  :class="[
16
16
  headless
17
17
  ? `${this.component}-content`
18
- : `shadow-300 absolute z-50 grid overflow-x-hidden rounded-md py-2 px-3 ${this.classes} ${this.portalClass}`,
18
+ : `absolute z-50 grid overflow-hidden rounded-md shadow-md ${this.classes} ${this.portalClass}`,
19
19
  ]"
20
20
  v-bind="$attrs"
21
21
  >
22
22
  <slot />
23
23
  </div>
24
24
  </transition>
25
- </Portal>
25
+ </Teleport>
26
26
  </template>
27
27
 
28
28
  <script>
29
- import { Portal } from '@linusborg/vue-simple-portal';
30
-
31
29
  export default {
32
- components: {
33
- Portal,
34
- },
30
+ inheritAttrs: false,
35
31
 
36
32
  props: {
37
33
  component: {
@@ -46,6 +42,10 @@ export default {
46
42
  type: Boolean,
47
43
  default: false,
48
44
  },
45
+ ariaActivedescendant: {
46
+ type: String,
47
+ default: null,
48
+ },
49
49
  portalClass: {
50
50
  type: [String, Function],
51
51
  default: null,
@@ -58,7 +58,25 @@ export default {
58
58
  },
59
59
 
60
60
  classes() {
61
- return this.isTooltip ? 'bg-gray-800 text-sm text-white' : 'bg-white';
61
+ return this.isTooltip
62
+ ? 'bg-gray-800 text-sm text-white py-1 px-2'
63
+ : 'bg-white py-2 px-3';
64
+ },
65
+
66
+ transitionEnterClass() {
67
+ return this.isTooltip ? 'opacity-0' : 'translate-y-[15px] opacity-0';
68
+ },
69
+
70
+ transitionEnterToClass() {
71
+ return this.isTooltip ? 'opacity-100' : 'translate-y-0 opacity-100';
72
+ },
73
+
74
+ transitionLeaveClass() {
75
+ return this.transitionEnterToClass;
76
+ },
77
+
78
+ transitionLeaveToClass() {
79
+ return this.transitionEnterClass;
62
80
  },
63
81
  },
64
82
 
@@ -0,0 +1,21 @@
1
+ export const currencyFormatter = ({ locale = 'en-US', currency = 'USD' }) => {
2
+ return Intl.NumberFormat(locale, {
3
+ style: 'currency',
4
+ currency,
5
+ });
6
+ };
7
+
8
+ export const getCurrencySymbol = (options) => {
9
+ const formatter = currencyFormatter(options);
10
+ const parts = formatter.formatToParts(0); // can be any number
11
+ const currency = parts.find((part) => part.type === 'currency');
12
+ const symbol = currency.value;
13
+
14
+ return removeLetters(symbol);
15
+ };
16
+
17
+ // Remove all letters (for example, change CA$ to $)
18
+ const removeLetters = (value) => {
19
+ const lettersRegex = /[A-Za-z]/g;
20
+ return value.replaceAll(lettersRegex, '');
21
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ *
3
+ * @param {HTMLElement} el
4
+ * @param {HTMLElement} parent
5
+ */
6
+ export const scrollElementIntoView = (el, parent) => {
7
+ // this works better than scrollIntoView
8
+ if (parent.scrollHeight <= parent.clientHeight) return;
9
+
10
+ const scrollBottom = parent.clientHeight + parent.scrollTop;
11
+ const elBottom = el.offsetTop + el.offsetHeight;
12
+
13
+ if (elBottom > scrollBottom) {
14
+ parent.scrollTop = elBottom - parent.clientHeight;
15
+ } else if (el.offsetTop < parent.scrollTop) {
16
+ parent.scrollTop = el.offsetTop;
17
+ }
18
+ };
@@ -1,18 +1,37 @@
1
+ /**
2
+ * Resizes an image by adjusting its width and returns the modified URL.
3
+ *
4
+ * This method processes image URLs from CloudFront CDN. If the URL is a valid
5
+ * string or object, it decodes the URL, adjusts the width in the `resize` edits,
6
+ * and returns the updated encoded URL. If no width is provided, a default value
7
+ * of 450 is used. If an error occurs, the original CDN URL is returned.
8
+ *
9
+ * @param {(string|object)} url - The URL of the image, either as a string or object.
10
+ * @param {number} [width=450] - The width to resize the image to (default is 450 if not specified).
11
+ *
12
+ * @returns {string} The updated image URL with the resized width, or the original URL if an error occurs.
13
+ */
1
14
  export const handleImageResizing = (url, width) => {
2
15
  // early exit if url is null for whatever reason
3
16
  if (!url) {
4
17
  return '';
5
18
  }
6
-
19
+
7
20
  let cdn;
8
-
21
+
9
22
  try {
10
- if (typeof url === 'object') {
11
- cdn = url.cdn_url;
12
- }
23
+ // new code
24
+ if (typeof url === 'string' && url.includes('cloudfront.net/')) {
25
+ cdn = url;
26
+ } else {
27
+ // old code
28
+ if (typeof url === 'object') {
29
+ cdn = url.cdn_url;
30
+ }
13
31
 
14
- if (typeof url === 'string') {
15
- cdn = JSON.parse(url).cdn_url;
32
+ if (typeof url === 'string') {
33
+ cdn = JSON.parse(url).cdn_url;
34
+ }
16
35
  }
17
36
 
18
37
  const base64String = cdn.split('net/');
@@ -23,7 +42,7 @@ export const handleImageResizing = (url, width) => {
23
42
  // browsers.
24
43
  //
25
44
  // Details: https://developer.mozilla.org/en-US/docs/Web/API/atob
26
- const decodedString = JSON.parse(atob(base64String[1]));
45
+ const decodedString = JSON.parse(atob(base64String[1]));
27
46
 
28
47
  width
29
48
  ? (decodedString.edits.resize.width = width)
@@ -37,8 +56,8 @@ export const handleImageResizing = (url, width) => {
37
56
  const encodedString = btoa(JSON.stringify(decodedString));
38
57
  const encodedUrl = `${firstPart}net/${encodedString}`;
39
58
 
40
- return encodedUrl
41
- } catch(error) {
42
- console.error(`${error} in ${cdn}`);
59
+ return encodedUrl;
60
+ } catch (error) {
61
+ return cdn;
43
62
  }
44
- };
63
+ };
@@ -1,82 +0,0 @@
1
- <template>
2
- <input
3
- :class="classes"
4
- class="form-control"
5
- :data-theme="theme"
6
- :type="type"
7
- :value="value"
8
- v-on="listeners"
9
- />
10
- </template>
11
-
12
- <script>
13
- export default {
14
- name: 'VTInput',
15
-
16
- props: {
17
- lazy: {
18
- type: Boolean,
19
- default: false,
20
- },
21
- type: {
22
- type: String,
23
- default: 'text',
24
- },
25
- theme: {
26
- type: String,
27
- default: null,
28
- validator(value) {
29
- return ['dark'].includes(value);
30
- },
31
- },
32
- variant: {
33
- type: [String, Object],
34
- default: '',
35
- validator(value) {
36
- if (value === '' || typeof value === 'object') {
37
- return true;
38
- }
39
-
40
- return ['success', 'warning', 'error'].includes(value);
41
- },
42
- },
43
- value: {
44
- type: [String, Number, Object, Array],
45
- default: null,
46
- },
47
- },
48
-
49
- computed: {
50
- classes() {
51
- const classes = {};
52
-
53
- if (this.variant) {
54
- classes[`form-control--${this.variant}`] = true;
55
- }
56
-
57
- return classes;
58
- },
59
-
60
- listeners() {
61
- // `Object.assign` merges objects together to form a new object
62
- return Object.assign(
63
- {},
64
- // We add all the listeners from the parent
65
- this.$listeners,
66
- // Then we can add custom listeners or override the
67
- // behavior of some listeners.
68
- {
69
- // This ensures that the component works with v-model
70
- input: (event) => {
71
- if (this.lazy) return;
72
- this.$emit('input', event.target.value);
73
- },
74
- blur: (event) => {
75
- this.$emit('blur', event);
76
- },
77
- }
78
- );
79
- },
80
- },
81
- };
82
- </script>
@@ -1,36 +0,0 @@
1
- <template>
2
- <VTInput v-model="date" type="date" />
3
- </template>
4
-
5
- <script>
6
- import VTInput from './VTInput.vue';
7
-
8
- export default {
9
- name: 'VTInputDate',
10
-
11
- components: { VTInput },
12
-
13
- model: {
14
- prop: 'value',
15
- event: 'input',
16
- },
17
-
18
- props: {
19
- value: {
20
- type: String,
21
- default: '',
22
- },
23
- },
24
-
25
- computed: {
26
- date: {
27
- get() {
28
- return this.$date.format(this.value, 'YYYY-MM-DD');
29
- },
30
- set(newDate) {
31
- this.$emit('input', newDate);
32
- },
33
- },
34
- },
35
- };
36
- </script>
@@ -1,60 +0,0 @@
1
- <template>
2
- <div class="flex items-stretch gap-2">
3
- <VTInput
4
- ref="input"
5
- type="file"
6
- :value="value"
7
- :theme="theme"
8
- v-bind="$attrs"
9
- @change="onChange"
10
- />
11
- <VTButton :theme="theme" @click.stop="onButtonClick">Browse</VTButton>
12
- </div>
13
- </template>
14
-
15
- <script>
16
- import VTButton from '../Button/VTButton.vue';
17
- import VTInput from './VTInput.vue';
18
-
19
- export default {
20
- name: 'VTInputFile',
21
-
22
- components: {
23
- VTInput,
24
- VTButton,
25
- },
26
-
27
- inheritAttrs: false,
28
-
29
- props: {
30
- theme: {
31
- type: String,
32
- default: null,
33
- validator(value) {
34
- return ['dark'].includes(value);
35
- },
36
- },
37
- multiple: {
38
- type: Boolean,
39
- default: false,
40
- },
41
- },
42
-
43
- data() {
44
- return {
45
- value: null,
46
- };
47
- },
48
-
49
- methods: {
50
- onChange(event) {
51
- this.value = this.$refs.input.$el.value;
52
- this.$emit('change', event);
53
- },
54
-
55
- onButtonClick() {
56
- this.$refs.input.$el.click();
57
- },
58
- },
59
- };
60
- </script>
@@ -1,54 +0,0 @@
1
- <template>
2
- <label
3
- class="flex h-full w-full flex-col items-center justify-center rounded border-2 border-dotted border-white p-4 text-center hover:border-fl-500 hover:bg-fd-500"
4
- :class="{ 'border-fl-500 bg-fd-500': isDraggingOver }"
5
- @drop.prevent="onDrop"
6
- @dragover.prevent="onDragOver"
7
- @dragleave.prevent="onDragLeave"
8
- >
9
- <IconImagePlaceholder class="mb-3" />
10
- <span>Drop your images here, or click to browse</span>
11
- <VTInput type="file" class="sr-only" v-bind="$attrs" @change="onChange" />
12
- </label>
13
- </template>
14
-
15
- <script>
16
- import { IconImagePlaceholder } from '@veritree/icons';
17
- import VTInput from './VTInput.vue';
18
-
19
- export default {
20
- name: 'VTInputFile',
21
-
22
- components: {
23
- VTInput,
24
- IconImagePlaceholder,
25
- },
26
-
27
- inheritAttrs: false,
28
-
29
- data() {
30
- return {
31
- isDraggingOver: false,
32
- };
33
- },
34
-
35
- methods: {
36
- onDrop(event) {
37
- this.isDraggingOver = false;
38
- this.$emit('drop', event);
39
- },
40
-
41
- onDragOver() {
42
- this.isDraggingOver = true;
43
- },
44
-
45
- onDragLeave() {
46
- this.isDraggingOver = false;
47
- },
48
-
49
- onChange(event) {
50
- this.$emit('change', event);
51
- },
52
- },
53
- };
54
- </script>