v-uixy 1.3.0 → 1.4.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 (37) hide show
  1. package/package.json +1 -1
  2. package/templates/assets/css/main.css +8 -0
  3. package/templates/components/Avatar/Avatar.component.vue +7 -3
  4. package/templates/components/Avatar/Avatar.styles.ts +3 -3
  5. package/templates/components/Badge/Badge.styles.ts +2 -2
  6. package/templates/components/Button/Button.styles.ts +3 -3
  7. package/templates/components/Calendar/Calendar.component.vue +26 -15
  8. package/templates/components/Calendar/Calendar.styles.ts +11 -3
  9. package/templates/components/Calendar/Calendar.types.ts +1 -0
  10. package/templates/components/ColorPicker/ColorPicker.component.vue +287 -0
  11. package/templates/components/ColorPicker/ColorPicker.types.ts +3 -0
  12. package/templates/components/ColorPicker/index.ts +4 -0
  13. package/templates/components/Command/Command.component.vue +6 -6
  14. package/templates/components/Command/Command.types.ts +1 -0
  15. package/templates/components/Input/Input.component.vue +14 -4
  16. package/templates/components/Input/Input.styles.ts +4 -4
  17. package/templates/components/Modal/Modal.component.vue +28 -4
  18. package/templates/components/Modal/Modal.types.ts +3 -1
  19. package/templates/components/Popover/Popover.component.vue +33 -2
  20. package/templates/components/Popover/Popover.types.ts +1 -0
  21. package/templates/components/Select/Select.component.vue +241 -87
  22. package/templates/components/Select/Select.styles.ts +5 -5
  23. package/templates/components/Select/Select.types.ts +12 -1
  24. package/templates/components/Select/SelectTreeItem.vue +154 -0
  25. package/templates/components/Select/composables/useSelect.composable.ts +260 -42
  26. package/templates/components/Sheet/Sheet.component.vue +6 -0
  27. package/templates/components/Tabs/Tabs.component.vue +11 -2
  28. package/templates/components/Tabs/Tabs.types.ts +1 -0
  29. package/templates/components/Textarea/Textarea.component.vue +10 -13
  30. package/templates/components/Textarea/Textarea.styles.ts +3 -3
  31. package/templates/components/TimePicker/TimePicker.component.vue +21 -18
  32. package/templates/components/TimePicker/TimePicker.styles.ts +1 -1
  33. package/templates/components/TimePicker/TimePicker.types.ts +1 -0
  34. package/templates/components/index.ts +35 -0
  35. package/templates/components.json +9 -3
  36. package/templates/composables/useInputValidation.ts +89 -0
  37. package/templates/composables/usePosition.ts +17 -2
@@ -0,0 +1,154 @@
1
+ <template>
2
+ <li class="flex flex-col">
3
+ <div
4
+ :tabIndex="option.disabled ? -1 : 0"
5
+ :class="
6
+ itemStyles({
7
+ selected: isSelected(option),
8
+ disabled: !!option.disabled,
9
+ })
10
+ "
11
+ :style="{
12
+ paddingLeft: `${depth * 28 + 12}px`,
13
+ cursor: option.disabled ? 'not-allowed' : 'pointer',
14
+ }"
15
+ @click.stop="handleItemClick"
16
+ >
17
+ <div class="flex items-center gap-2 flex-1 min-h-6">
18
+ <div
19
+ v-if="hasChildren"
20
+ class="shrink-0 size-6 flex items-center justify-center -ml-1 hover:bg-gray-200 dark:hover:bg-gray-800 rounded transition cursor-pointer"
21
+ @click.stop="$emit('toggle-expand', getValue(option))"
22
+ >
23
+ <uixy-icon
24
+ name="chevron-right"
25
+ class="size-4.5 transition-transform"
26
+ :class="{ 'rotate-90': isExpanded }"
27
+ />
28
+ </div>
29
+ <span
30
+ class="truncate"
31
+ :class="{
32
+ 'text-gray-400 dark:text-gray-600':
33
+ isNotSelectable && !option.disabled,
34
+ }"
35
+ >{{ option.label }}</span
36
+ >
37
+ </div>
38
+ <uixy-icon
39
+ v-if="isSelected(option)"
40
+ name="check"
41
+ class="absolute right-2 top-1/2 -translate-y-1/2 size-4"
42
+ />
43
+ <div
44
+ v-else-if="isPartiallySelected?.(option)"
45
+ class="absolute right-2 top-1/2 -translate-y-1/2 size-4 flex items-center justify-center"
46
+ >
47
+ <div class="h-0.5 w-2.5 rounded bg-current"></div>
48
+ </div>
49
+ </div>
50
+
51
+ <animate-presence>
52
+ <motion.ul
53
+ v-if="hasChildren && isExpanded"
54
+ class="flex flex-col gap-1 pt-1 overflow-hidden"
55
+ :initial="{ opacity: 0, height: 0 }"
56
+ :animate="{ opacity: 1, height: 'auto' }"
57
+ :exit="{ opacity: 0, height: 0 }"
58
+ :transition="{ duration: 0.15 }"
59
+ >
60
+ <SelectTreeItem
61
+ v-for="child in option.children"
62
+ :key="child.value ?? child.label"
63
+ :option="child"
64
+ :depth="depth + 1"
65
+ :is-selected="isSelected"
66
+ :is-partially-selected="isPartiallySelected"
67
+ :expanded-items="expandedItems"
68
+ :leaf-only="leafOnly"
69
+ :multiple="multiple"
70
+ :search-term="searchTerm"
71
+ :original-options="originalOptions"
72
+ @toggle-expand="$emit('toggle-expand', $event)"
73
+ @select="$emit('select', $event)"
74
+ />
75
+ </motion.ul>
76
+ </animate-presence>
77
+ </li>
78
+ </template>
79
+
80
+ <script setup lang="ts">
81
+ import { motion, AnimatePresence } from "motion-v";
82
+ import { itemStyles } from "./Select.styles";
83
+ import type { UixySelectOptionType } from "./Select.types";
84
+ import { UixyIcon } from "../Icon";
85
+
86
+ const props = defineProps<{
87
+ option: UixySelectOptionType;
88
+ depth: number;
89
+ isSelected: (option: UixySelectOptionType) => boolean;
90
+ isPartiallySelected?: (option: UixySelectOptionType) => boolean;
91
+ expandedItems: string[];
92
+ leafOnly?: boolean;
93
+ multiple?: boolean;
94
+ searchTerm?: string;
95
+ originalOptions?: UixySelectOptionType[];
96
+ }>();
97
+
98
+ const emit = defineEmits<{
99
+ (e: "toggle-expand", value: string): void;
100
+ (e: "select", value: string): void;
101
+ }>();
102
+
103
+ const getValue = (option: UixySelectOptionType) =>
104
+ option.value ?? option.label;
105
+
106
+ const hasChildren = computed(
107
+ () => props.option.children && props.option.children.length > 0,
108
+ );
109
+
110
+ const hasChildrenInOriginal = computed(() => {
111
+ if (!props.originalOptions) return hasChildren.value;
112
+
113
+ const findInTree = (
114
+ options: UixySelectOptionType[],
115
+ value: string,
116
+ ): UixySelectOptionType | null => {
117
+ for (const opt of options) {
118
+ if ((opt.value ?? opt.label) === value) return opt;
119
+ if (opt.children && opt.children.length > 0) {
120
+ const found = findInTree(opt.children, value);
121
+ if (found) return found;
122
+ }
123
+ }
124
+ return null;
125
+ };
126
+
127
+ const originalOption = findInTree(
128
+ props.originalOptions,
129
+ getValue(props.option),
130
+ );
131
+ return originalOption?.children && originalOption.children.length > 0;
132
+ });
133
+
134
+ const isExpanded = computed(() =>
135
+ props.expandedItems.includes(getValue(props.option)),
136
+ );
137
+
138
+ const isNotSelectable = computed(
139
+ () =>
140
+ props.option.disabled ||
141
+ (props.leafOnly && hasChildrenInOriginal.value && !props.multiple),
142
+ );
143
+
144
+ const handleItemClick = () => {
145
+ if (props.option.disabled) return;
146
+
147
+ if (props.leafOnly && hasChildrenInOriginal.value && !props.multiple) {
148
+ emit("toggle-expand", getValue(props.option));
149
+ return;
150
+ }
151
+
152
+ emit("select", getValue(props.option));
153
+ };
154
+ </script>
@@ -1,33 +1,238 @@
1
- import { onWatcherCleanup, watch } from "vue";
2
- import type { UixySelectOptionType } from "../Select.types";
1
+ import {
2
+ onWatcherCleanup,
3
+ watch,
4
+ computed,
5
+ ref,
6
+ onUnmounted,
7
+ type Ref,
8
+ } from "vue";
9
+ import type { UixySelectOptionType, UixySelectProps } from "../Select.types";
3
10
 
4
- export const useSelect = <T>(
11
+ const normalizeForSearch = (str: string): string =>
12
+ str.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "");
13
+
14
+ const getValue = (option: UixySelectOptionType): string =>
15
+ option.value ?? option.label;
16
+
17
+ const collectLeafValues = (option: UixySelectOptionType): string[] => {
18
+ if (!option.children || option.children.length === 0)
19
+ return [getValue(option)];
20
+ return option.children.flatMap(collectLeafValues);
21
+ };
22
+
23
+ const findInTree = (
5
24
  options: UixySelectOptionType[],
6
- disabled: boolean,
7
- onChange: (e: string) => void
25
+ value: string,
26
+ ): UixySelectOptionType | null => {
27
+ for (const opt of options) {
28
+ if (getValue(opt) === value) return opt;
29
+ if (opt.children?.length) {
30
+ const found = findInTree(opt.children, value);
31
+ if (found) return found;
32
+ }
33
+ }
34
+ return null;
35
+ };
36
+
37
+ export const useSelect = <T extends string | string[]>(
38
+ props: UixySelectProps,
39
+ model: Ref<T>,
40
+ emit: (event: "change", v: string) => void,
41
+ dropdownRef?: Ref<HTMLElement | undefined>,
8
42
  ) => {
9
43
  const open = ref(false);
10
44
  const search = ref("");
11
45
  const refOptions = ref<HTMLDivElement>();
46
+ const lastScrollAt = ref(0);
47
+ const expandedItems = ref<string[]>([]);
12
48
 
13
- const filteredOptions = computed(() =>
14
- options.filter((option) =>
15
- option.label.toLowerCase().includes(search.value.toLowerCase())
16
- )
17
- );
49
+ const filterTreeOptions = (
50
+ items: UixySelectOptionType[],
51
+ searchTerm: string,
52
+ ): UixySelectOptionType[] => {
53
+ if (!searchTerm) return items;
54
+ const normSearch = normalizeForSearch(searchTerm);
55
+ const filterNode = (
56
+ node: UixySelectOptionType,
57
+ ): UixySelectOptionType | null => {
58
+ const matches = normalizeForSearch(node.label).includes(normSearch);
59
+ let filteredChildren: UixySelectOptionType[] = [];
60
+ if (node.children?.length) {
61
+ filteredChildren = node.children
62
+ .map(filterNode)
63
+ .filter((n): n is UixySelectOptionType => n !== null);
64
+ }
65
+ if (matches || filteredChildren.length > 0) {
66
+ return { ...node, children: filteredChildren };
67
+ }
68
+ return null;
69
+ };
70
+ return items
71
+ .map(filterNode)
72
+ .filter((n): n is UixySelectOptionType => n !== null);
73
+ };
18
74
 
19
- const handleOpen = (e?: MouseEvent) => {
20
- if (disabled || (e && "badge" in (e?.target as HTMLDivElement).dataset))
75
+ const filteredOptions = computed(() => {
76
+ const list = props.options ?? [];
77
+ if (props.tree) return filterTreeOptions(list, search.value);
78
+ return list.filter((option) =>
79
+ normalizeForSearch(option.label).includes(
80
+ normalizeForSearch(search.value),
81
+ ),
82
+ );
83
+ });
84
+
85
+ const collectParentsWithMatchingDescendants = (
86
+ items: UixySelectOptionType[],
87
+ searchTerm: string,
88
+ ): string[] => {
89
+ if (!searchTerm) return [];
90
+ const lowerSearch = searchTerm.toLowerCase();
91
+ const parentsToExpand: string[] = [];
92
+ const checkNode = (node: UixySelectOptionType): boolean => {
93
+ const nodeMatches = node.label.toLowerCase().includes(lowerSearch);
94
+ let hasMatchingDescendant = false;
95
+ if (node.children?.length) {
96
+ for (const child of node.children) {
97
+ if (checkNode(child)) hasMatchingDescendant = true;
98
+ }
99
+ }
100
+ if (hasMatchingDescendant) parentsToExpand.push(getValue(node));
101
+ return nodeMatches || hasMatchingDescendant;
102
+ };
103
+ for (const item of items) checkNode(item);
104
+ return parentsToExpand;
105
+ };
106
+
107
+ const effectiveExpandedItems = computed(() => {
108
+ if (!search.value) return expandedItems.value;
109
+ const autoExpanded = collectParentsWithMatchingDescendants(
110
+ props.options,
111
+ search.value,
112
+ );
113
+ return Array.from(new Set([...expandedItems.value, ...autoExpanded]));
114
+ });
115
+
116
+ const toggleExpand = (value: string) => {
117
+ const idx = expandedItems.value.indexOf(value);
118
+ if (idx >= 0) expandedItems.value.splice(idx, 1);
119
+ else expandedItems.value.push(value);
120
+ };
121
+
122
+ const isSelected = (option: UixySelectOptionType): boolean => {
123
+ const v = getValue(option);
124
+ if (props.tree && props.multiple && option.children?.length) {
125
+ const leaves = collectLeafValues(option);
126
+ const arr = (model.value as string[]) ?? [];
127
+ return leaves.length > 0 && leaves.every((l) => arr.includes(l));
128
+ }
129
+ return Array.isArray(model.value)
130
+ ? (model.value as string[]).includes(v)
131
+ : (model.value as string) === v;
132
+ };
133
+
134
+ const isPartiallySelected = (option: UixySelectOptionType): boolean => {
135
+ if (!props.tree || !props.multiple || !option.children?.length)
136
+ return false;
137
+ const leaves = collectLeafValues(option);
138
+ const arr = (model.value as string[]) ?? [];
139
+ const selected = leaves.filter((l) => arr.includes(l)).length;
140
+ return selected > 0 && selected < leaves.length;
141
+ };
142
+
143
+ const setMultiValues = (next: string[]) => {
144
+ (model.value as string[]) = next;
145
+ };
146
+
147
+ const toggleMultiValue = (v: string) => {
148
+ const prev = Array.isArray(model.value) ? (model.value as string[]) : [];
149
+ if (prev.includes(v)) {
150
+ setMultiValues(prev.filter((item) => item !== v));
151
+ return;
152
+ }
153
+ if (props.max && prev.length >= props.max) {
154
+ setMultiValues([...prev.slice(1), v]);
21
155
  return;
156
+ }
157
+ setMultiValues([...prev, v]);
158
+ };
159
+
160
+ const cascadeMultiTree = (option: UixySelectOptionType) => {
161
+ const leaves = collectLeafValues(option);
162
+ const prev = Array.isArray(model.value) ? (model.value as string[]) : [];
163
+ const allSelected = leaves.every((l) => prev.includes(l));
164
+ if (allSelected) {
165
+ setMultiValues(prev.filter((v) => !leaves.includes(v)));
166
+ return;
167
+ }
168
+ const merged = Array.from(new Set([...prev, ...leaves]));
169
+ if (props.max && merged.length > props.max) {
170
+ setMultiValues(merged.slice(merged.length - props.max));
171
+ return;
172
+ }
173
+ setMultiValues(merged);
174
+ };
22
175
 
176
+ const applyChange = (v: string) => {
177
+ if (!props.multiple) {
178
+ (model.value as string) = v;
179
+ return;
180
+ }
181
+ if (props.tree) {
182
+ const node = findInTree(props.options, v);
183
+ if (node?.children?.length) {
184
+ cascadeMultiTree(node);
185
+ return;
186
+ }
187
+ }
188
+ toggleMultiValue(v);
189
+ };
190
+
191
+ const handleClick = (value: string) => {
192
+ applyChange(value);
193
+ emit("change", value);
194
+ if (props.clearSearchOnSelect) search.value = "";
195
+ if (!props.multiple) open.value = false;
196
+ };
197
+
198
+ const handleClickSelectedOption = (item: string) => {
199
+ applyChange(item);
200
+ emit("change", item);
201
+ };
202
+
203
+ const handleDeselect = () => {
204
+ (model.value as string) = "";
205
+ };
206
+
207
+ const getLabel = (value: string): string => {
208
+ if (!value) return "";
209
+ return findInTree(props.options, value)?.label ?? value;
210
+ };
211
+
212
+ const customAddVisible = computed(() => {
213
+ if (!props.allowCustom || !search.value) return false;
214
+ const lowerSearch = search.value.toLowerCase();
215
+ return !filteredOptions.value.some(
216
+ (opt) => opt.label.toLowerCase() === lowerSearch,
217
+ );
218
+ });
219
+
220
+ const handleCustomAdd = () => {
221
+ handleClick(search.value);
222
+ };
223
+
224
+ const handleOpen = (e?: MouseEvent) => {
225
+ if (
226
+ props.disabled ||
227
+ (e && "badge" in (e?.target as HTMLDivElement).dataset)
228
+ )
229
+ return;
23
230
  open.value = !open.value;
24
231
  };
25
232
 
26
233
  const handleKeydownSelect = (e: KeyboardEvent) => {
27
234
  const key = e.key;
28
-
29
235
  if (key === "Tab") return;
30
-
31
236
  if (key === "ArrowDown" || key === "Enter" || key === " ") {
32
237
  e.preventDefault();
33
238
  open.value = !open.value;
@@ -36,73 +241,76 @@ export const useSelect = <T>(
36
241
 
37
242
  const handleKeydownOption = (
38
243
  e: KeyboardEvent,
39
- option: UixySelectOptionType
244
+ option: UixySelectOptionType,
40
245
  ) => {
41
246
  const key = e.key;
42
-
43
247
  if ((key === "Enter" || key === " ") && !option.disabled) {
44
248
  e.preventDefault();
45
- return onChange(option.label);
249
+ return handleClick(getValue(option));
46
250
  }
47
-
48
251
  if (key !== "ArrowDown" && key !== "ArrowUp") return;
49
-
50
- const container = refOptions.value;
51
-
252
+ const container = dropdownRef?.value ?? refOptions.value;
52
253
  if (!container) return;
53
-
54
254
  const focusables = Array.from(
55
255
  container.querySelectorAll<HTMLElement>(
56
- "[select-dropdown] input, [select-dropdown] li"
57
- )
256
+ "[select-dropdown] input, [select-dropdown] li",
257
+ ),
58
258
  ).filter(
59
259
  (el) =>
60
260
  el.tabIndex >= 0 &&
61
261
  el.getAttribute("aria-disabled") !== "true" &&
62
- !el.hasAttribute("disabled")
262
+ !el.hasAttribute("disabled"),
63
263
  );
64
-
65
264
  if (!focusables.length) return;
66
-
67
265
  const currentEl = (e.currentTarget as HTMLElement) || null;
68
266
  let index = currentEl ? focusables.indexOf(currentEl) : -1;
69
-
70
267
  e.preventDefault();
71
-
72
268
  const val = key === "ArrowDown" ? 1 : -1;
73
-
74
269
  index = (index + val + focusables.length) % focusables.length;
75
-
76
- const nextEl = focusables[index];
77
- nextEl?.focus();
270
+ focusables[index]?.focus();
78
271
  };
79
272
 
80
- const handleClickSelectedOption = (item: string) => {
81
- onChange(item);
273
+ const handleClickOutside = (e: MouseEvent) => {
274
+ if (Date.now() - lastScrollAt.value < 250) return;
275
+ const target = e.target as Node;
276
+ if (refOptions.value?.contains(target)) return;
277
+ if (dropdownRef?.value?.contains(target)) return;
278
+ const el = target as HTMLElement;
279
+ if (el.closest?.("[select-dropdown]")) return;
280
+ open.value = false;
82
281
  };
83
282
 
84
- const handleClickOutside = (e: MouseEvent) => {
85
- if (refOptions.value && !refOptions.value.contains(e.target as Node))
86
- open.value = false;
283
+ const markScroll = () => {
284
+ lastScrollAt.value = Date.now();
87
285
  };
88
286
 
89
287
  watch(
90
288
  () => open.value,
91
- (_, prevVal) => {
289
+ (isOpen, prevVal) => {
290
+ if (!isOpen) {
291
+ expandedItems.value = [];
292
+ }
92
293
  if (prevVal) return;
93
-
94
294
  requestAnimationFrame(() => {
95
295
  window.addEventListener("click", handleClickOutside);
296
+ window.addEventListener("scroll", markScroll, true);
297
+ window.addEventListener("wheel", markScroll, { passive: true });
298
+ window.addEventListener("touchmove", markScroll, { passive: true });
96
299
  });
97
-
98
300
  onWatcherCleanup(() => {
99
301
  window.removeEventListener("click", handleClickOutside);
302
+ window.removeEventListener("scroll", markScroll, true);
303
+ window.removeEventListener("wheel", markScroll as any);
304
+ window.removeEventListener("touchmove", markScroll as any);
100
305
  });
101
- }
306
+ },
102
307
  );
103
308
 
104
309
  onUnmounted(() => {
105
310
  window.removeEventListener("click", handleClickOutside);
311
+ window.removeEventListener("scroll", markScroll, true);
312
+ window.removeEventListener("wheel", markScroll as any);
313
+ window.removeEventListener("touchmove", markScroll as any);
106
314
  });
107
315
 
108
316
  return {
@@ -110,9 +318,19 @@ export const useSelect = <T>(
110
318
  search,
111
319
  refOptions,
112
320
  filteredOptions,
321
+ expandedItems,
322
+ effectiveExpandedItems,
323
+ customAddVisible,
324
+ isSelected,
325
+ isPartiallySelected,
326
+ getLabel,
327
+ toggleExpand,
113
328
  handleOpen,
114
329
  handleKeydownSelect,
115
330
  handleKeydownOption,
116
331
  handleClickSelectedOption,
332
+ handleClick,
333
+ handleDeselect,
334
+ handleCustomAdd,
117
335
  };
118
336
  };
@@ -79,4 +79,10 @@
79
79
  setScale(1);
80
80
  }
81
81
  );
82
+
83
+ onUnmounted(async () => {
84
+ await nextTick();
85
+
86
+ setScale(1);
87
+ });
82
88
  </script>
@@ -23,7 +23,16 @@
23
23
  </div>
24
24
  <slot name="nav-extra" />
25
25
  <div>
26
- <animate-presence mode="wait" :initial="false">
26
+ <template v-if="props.keepAlive">
27
+ <div
28
+ v-for="tab in props.tabs"
29
+ :key="tab.value"
30
+ v-show="active === tab.value"
31
+ >
32
+ <slot :name="tab.value" />
33
+ </div>
34
+ </template>
35
+ <animate-presence v-else mode="wait" :initial="false">
27
36
  <motion.div
28
37
  :key="active"
29
38
  :initial="{ opacity: 0, y: 4 }"
@@ -55,6 +64,6 @@
55
64
  const layoutId = computed(() => `uixy-tabs-${uuidv4()}`);
56
65
 
57
66
  onBeforeMount(() => {
58
- if (props.tabs.length > 0) active.value = props.tabs[0].value;
67
+ if (props.tabs.length > 0) active.value = props.tabs[0]?.value;
59
68
  });
60
69
  </script>
@@ -4,4 +4,5 @@ export interface UixyTabsProps {
4
4
  value: string | number;
5
5
  }[];
6
6
  variant: "primary" | "secondary";
7
+ keepAlive?: boolean;
7
8
  }
@@ -9,17 +9,19 @@
9
9
  :auto-focus="props.autoFocus"
10
10
  :rows="props.rows ?? 3"
11
11
  :max-length="props.maxLength"
12
- :class="textareaStyles(
13
- { status, noResize: !!props.noResize },
14
- $attrs.class as string,
15
- )"
12
+ :class="
13
+ textareaStyles(
14
+ { status, noResize: !!props.noResize },
15
+ $attrs.class as string,
16
+ )
17
+ "
16
18
  v-bind="filteredAttrs"
17
19
  />
18
20
  <label v-if="props.label" :for="id" :class="labelStyles({ status })">
19
21
  {{ props.label }}
20
22
  </label>
21
23
  </div>
22
- <div class="h-4" v-if="!props.hideHelper">
24
+ <div v-if="!props.hideHelper" class="h-4">
23
25
  <animate-presence mode="wait" :initial="false">
24
26
  <motion.div
25
27
  :initial="{ opacity: 0, y: '-4px' }"
@@ -29,12 +31,7 @@
29
31
  :key="status"
30
32
  >
31
33
  <p
32
- :class="
33
- helperStyles({
34
- status,
35
- align: props.alignText ?? 'left',
36
- })
37
- "
34
+ :class="helperStyles({ status, align: props.alignText ?? 'left' })"
38
35
  >
39
36
  {{ text }}
40
37
  </p>
@@ -68,7 +65,7 @@
68
65
  const id = useId();
69
66
 
70
67
  const status = computed(() =>
71
- props.disabled ? "disabled" : props.status ?? "default"
68
+ props.disabled ? "disabled" : (props.status ?? "default"),
72
69
  );
73
70
 
74
71
  const text = computed(
@@ -78,6 +75,6 @@
78
75
  default: props.helperText,
79
76
  disabled: "",
80
77
  valid: "",
81
- }[props.status ?? "default"])
78
+ })[props.status ?? "default"],
82
79
  );
83
80
  </script>
@@ -17,7 +17,7 @@ export const textareaStyles = styles(
17
17
  true: "resize-none",
18
18
  false: "",
19
19
  },
20
- }
20
+ },
21
21
  );
22
22
 
23
23
  export const labelStyles = styles(
@@ -29,7 +29,7 @@ export const labelStyles = styles(
29
29
  disabled: "text-gray-500 dark:text-gray-700",
30
30
  valid: "",
31
31
  },
32
- }
32
+ },
33
33
  );
34
34
 
35
35
  export const helperStyles = styles(
@@ -46,5 +46,5 @@ export const helperStyles = styles(
46
46
  right: "text-right",
47
47
  center: "text-center",
48
48
  },
49
- }
49
+ },
50
50
  );