sprintify-ui 0.6.31 → 0.6.33

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 (83) hide show
  1. package/dist/sprintify-ui.es.js +11823 -11591
  2. package/dist/style.css +1 -1
  3. package/dist/tailwindcss/button.js +260 -0
  4. package/dist/tailwindcss/index.js +16 -301
  5. package/dist/tailwindcss/input.js +24 -0
  6. package/dist/tailwindcss/overlay.js +13 -0
  7. package/dist/tailwindcss/theme.js +46 -0
  8. package/dist/types/src/components/BaseActionItemButton.vue.d.ts +2 -2
  9. package/dist/types/src/components/BaseAddressForm.vue.d.ts +10 -0
  10. package/dist/types/src/components/BaseAutocomplete.vue.d.ts +10 -10
  11. package/dist/types/src/components/BaseAutocompleteDrawer.vue.d.ts +3 -3
  12. package/dist/types/src/components/BaseAutocompleteFetch.vue.d.ts +6 -6
  13. package/dist/types/src/components/BaseAvatarGroup.vue.d.ts +1 -1
  14. package/dist/types/src/components/BaseBelongsTo.vue.d.ts +6 -6
  15. package/dist/types/src/components/BaseBelongsToFetch.vue.d.ts +6 -6
  16. package/dist/types/src/components/BaseButton.vue.d.ts +12 -12
  17. package/dist/types/src/components/BaseButtonGroup.vue.d.ts +24 -24
  18. package/dist/types/src/components/BaseCalendar.vue.d.ts +1 -1
  19. package/dist/types/src/components/BaseColor.vue.d.ts +19 -0
  20. package/dist/types/src/components/BaseDataIterator.vue.d.ts +1 -1
  21. package/dist/types/src/components/BaseDataIteratorSectionColumns.vue.d.ts +4 -4
  22. package/dist/types/src/components/BaseDataTable.vue.d.ts +1 -1
  23. package/dist/types/src/components/BaseDropdownAutocomplete.vue.d.ts +1 -1
  24. package/dist/types/src/components/BaseField.vue.d.ts +13 -3
  25. package/dist/types/src/components/BaseHasMany.vue.d.ts +9 -0
  26. package/dist/types/src/components/BaseInput.vue.d.ts +35 -4
  27. package/dist/types/src/components/BaseInputError.vue.d.ts +14 -1
  28. package/dist/types/src/components/BaseInputLabel.vue.d.ts +15 -5
  29. package/dist/types/src/components/BaseLayoutSidebarConfigurable.vue.d.ts +1 -1
  30. package/dist/types/src/components/BaseLayoutStackedConfigurable.vue.d.ts +1 -1
  31. package/dist/types/src/components/BaseLoadingCover.vue.d.ts +1 -1
  32. package/dist/types/src/components/BaseMediaGallery.vue.d.ts +1 -1
  33. package/dist/types/src/components/BaseMediaListItem.vue.d.ts +1 -1
  34. package/dist/types/src/components/BaseMediaPictures.vue.d.ts +1 -1
  35. package/dist/types/src/components/BaseMediaPicturesItem.vue.d.ts +1 -1
  36. package/dist/types/src/components/BaseMenu.vue.d.ts +1 -1
  37. package/dist/types/src/components/BaseMenuItem.vue.d.ts +1 -1
  38. package/dist/types/src/components/BaseNavbar.vue.d.ts +1 -1
  39. package/dist/types/src/components/BaseNavbarItemContent.vue.d.ts +1 -1
  40. package/dist/types/src/components/BaseNavbarSideItemContent.vue.d.ts +1 -1
  41. package/dist/types/src/components/BaseSelect.vue.d.ts +27 -0
  42. package/dist/types/src/components/BaseSideNavigation.vue.d.ts +3 -3
  43. package/dist/types/src/components/BaseSwitch.vue.d.ts +1 -1
  44. package/dist/types/src/components/BaseTable.vue.d.ts +1 -1
  45. package/dist/types/src/components/BaseTabs.vue.d.ts +3 -3
  46. package/dist/types/src/components/BaseTagAutocomplete.vue.d.ts +3 -3
  47. package/dist/types/src/composables/field.d.ts +3 -0
  48. package/dist/types/src/composables/inputSize.d.ts +6 -0
  49. package/dist/types/src/utils/sizes.d.ts +19 -0
  50. package/package.json +1 -1
  51. package/src/assets/form.css +1 -1
  52. package/src/components/BaseAddressForm.stories.js +7 -2
  53. package/src/components/BaseAddressForm.vue +64 -37
  54. package/src/components/BaseAutocomplete.stories.js +1 -1
  55. package/src/components/BaseAutocomplete.vue +86 -96
  56. package/src/components/BaseAutocompleteDrawer.vue +3 -2
  57. package/src/components/BaseAutocompleteFetch.stories.js +1 -1
  58. package/src/components/BaseAutocompleteFetch.vue +3 -2
  59. package/src/components/BaseBelongsTo.stories.js +1 -1
  60. package/src/components/BaseBelongsTo.vue +3 -2
  61. package/src/components/BaseBelongsToFetch.vue +3 -2
  62. package/src/components/BaseButton.stories.js +21 -0
  63. package/src/components/BaseButton.vue +42 -6
  64. package/src/components/BaseButtonGroup.stories.js +31 -1
  65. package/src/components/BaseButtonGroup.vue +46 -33
  66. package/src/components/BaseColor.stories.js +22 -0
  67. package/src/components/BaseColor.vue +28 -25
  68. package/src/components/BaseDropdown.stories.js +2 -3
  69. package/src/components/BaseDropdownAutocomplete.vue +2 -2
  70. package/src/components/BaseField.vue +19 -8
  71. package/src/components/BaseHasMany.stories.js +13 -1
  72. package/src/components/BaseHasMany.vue +39 -1
  73. package/src/components/BaseInput.stories.js +35 -1
  74. package/src/components/BaseInput.vue +154 -74
  75. package/src/components/BaseInputError.vue +32 -2
  76. package/src/components/BaseInputLabel.vue +36 -9
  77. package/src/components/BaseSelect.stories.js +34 -0
  78. package/src/components/BaseSelect.vue +57 -8
  79. package/src/components/BaseTagAutocomplete.vue +3 -2
  80. package/src/components/BaseTimelineItem.stories.js +1 -3
  81. package/src/composables/field.ts +20 -0
  82. package/src/composables/inputSize.ts +29 -0
  83. package/src/utils/sizes.ts +21 -0
@@ -1,27 +1,23 @@
1
1
  <template>
2
- <div
3
- class="inline-flex rounded border"
4
- :class="[borderColor]"
5
- >
6
- <div
7
- v-if="iconLeft"
8
- class="flex shrink-0 items-center justify-center rounded-l border-r px-3 transition-colors"
9
- :class="[borderColor, backgroundColor, textColor]"
10
- >
11
- <BaseIcon :icon="iconLeft" />
12
- </div>
13
- <div
14
- v-if="prefix"
15
- class="flex shrink-0 items-center justify-center border-r px-4 transition-colors"
16
- :class="[
17
- iconLeft ? '' : 'rounded-l',
18
- borderColor,
19
- backgroundColor,
20
- textColor,
21
- ]"
22
- >
23
- {{ prefix }}
2
+ <div :class="classes">
3
+ <div :class="decorationWrapClasses">
4
+ <div
5
+ v-if="iconLeft"
6
+ :class="decorationClasses"
7
+ >
8
+ <BaseIcon
9
+ :icon="iconLeft"
10
+ :class="iconClasses"
11
+ />
12
+ </div>
13
+ <div
14
+ v-if="prefix"
15
+ :class="decorationClasses"
16
+ >
17
+ {{ prefix }}
18
+ </div>
24
19
  </div>
20
+
25
21
  <input
26
22
  v-if="maskOptions"
27
23
  ref="input"
@@ -30,12 +26,10 @@
30
26
  :value="modelValue"
31
27
  :type="type"
32
28
  :autocomplete="autocomplete ? 'on' : 'off'"
33
- :class="{
34
- 'rounded-l': emptyLeft,
35
- 'rounded-r': emptyRight,
36
- 'w-full border-none bg-white outline-none focus:z-[1] focus:ring-2 focus:ring-primary-600 focus:ring-offset-1 disabled:cursor-not-allowed disabled:text-slate-300': true,
37
- }"
38
- @input="update"
29
+ :class="baseClasses"
30
+ @input="onInput"
31
+ @click="onClick"
32
+ @keydown="onKeydown"
39
33
  >
40
34
  <input
41
35
  v-else
@@ -44,31 +38,27 @@
44
38
  :value="modelValue"
45
39
  :type="type"
46
40
  :autocomplete="autocomplete ? 'on' : 'off'"
47
- :class="{
48
- 'rounded-l': emptyLeft,
49
- 'rounded-r': emptyRight,
50
- 'w-full border-none bg-white outline-none focus:z-[1] focus:ring-2 focus:ring-primary-600 focus:ring-offset-1 disabled:cursor-not-allowed disabled:text-slate-300': true,
51
- }"
52
- @input="update"
53
- >
54
- <div
55
- v-if="suffix"
56
- class="flex shrink-0 items-center justify-center border-l px-4 transition-colors"
57
- :class="[
58
- iconRight ? '' : 'rounded-r',
59
- borderColor,
60
- backgroundColor,
61
- textColor,
62
- ]"
41
+ :class="baseClasses"
42
+ @input="onInput"
43
+ @click="onClick"
44
+ @keydown="onKeydown"
63
45
  >
64
- {{ suffix }}
65
- </div>
66
- <div
67
- v-if="iconRight"
68
- class="flex shrink-0 items-center justify-center rounded-r border-l px-3 transition-colors"
69
- :class="[borderColor, backgroundColor, textColor]"
70
- >
71
- <BaseIcon :icon="iconRight" />
46
+ <div :class="decorationWrapClasses">
47
+ <div
48
+ v-if="suffix"
49
+ :class="decorationClasses"
50
+ >
51
+ {{ suffix }}
52
+ </div>
53
+ <div
54
+ v-if="iconRight"
55
+ :class="decorationClasses"
56
+ >
57
+ <BaseIcon
58
+ :icon="iconRight"
59
+ :class="iconClasses"
60
+ />
61
+ </div>
72
62
  </div>
73
63
  </div>
74
64
  </template>
@@ -79,12 +69,22 @@ import { PropType } from 'vue';
79
69
  import { Icon as BaseIcon } from '@iconify/vue';
80
70
  import { useField } from '@/composables/field';
81
71
  import { vMaska } from 'maska';
72
+ import { Size, sizes } from '@/utils/sizes';
73
+ import { twMerge } from 'tailwind-merge';
74
+
75
+ defineOptions({
76
+ inheritAttrs: false,
77
+ })
82
78
 
83
79
  const props = defineProps({
84
80
  modelValue: {
85
81
  default: '',
86
82
  type: [String, Number, null] as PropType<string | number | null>,
87
83
  },
84
+ class: {
85
+ default: '',
86
+ type: [String, Array] as PropType<string | string[]>,
87
+ },
88
88
  type: {
89
89
  type: String,
90
90
  default: 'text',
@@ -140,6 +140,10 @@ const props = defineProps({
140
140
  default: false,
141
141
  type: Boolean,
142
142
  },
143
+ size: {
144
+ default: undefined,
145
+ type: String as PropType<Size>,
146
+ },
143
147
  min: {
144
148
  default: undefined,
145
149
  type: [null, Number] as PropType<undefined | null | number>,
@@ -154,9 +158,22 @@ const props = defineProps({
154
158
  return null;
155
159
  },
156
160
  },
161
+ visibleFocus: {
162
+ default: true,
163
+ type: Boolean,
164
+ },
157
165
  });
158
166
 
159
167
  const input = ref<HTMLInputElement | null>(null);
168
+ const focus = ref(false);
169
+
170
+ const hasLeftDecoration = computed(() => {
171
+ return props.iconLeft || props.prefix;
172
+ });
173
+
174
+ const hasRightDecoration = computed(() => {
175
+ return props.iconRight || props.suffix;
176
+ });
160
177
 
161
178
  const maskOptions = computed(() => {
162
179
  if (props.mask) {
@@ -167,14 +184,6 @@ const maskOptions = computed(() => {
167
184
  return undefined;
168
185
  });
169
186
 
170
- const emptyLeft = computed(() => {
171
- return !props.iconLeft && !props.prefix;
172
- });
173
-
174
- const emptyRight = computed(() => {
175
- return !props.iconRight && !props.suffix;
176
- });
177
-
178
187
  const bindings = computed<any>(() => {
179
188
  return {
180
189
  name: nameInternal.value,
@@ -184,7 +193,6 @@ const bindings = computed<any>(() => {
184
193
  disabled: props.disabled,
185
194
  placeholder: props.placeholder,
186
195
  required: requiredInternal.value,
187
-
188
196
  onKeydown(event: KeyboardEvent) {
189
197
  if (event.code == 'Enter' && props.preventSubmit) {
190
198
  event.preventDefault();
@@ -192,9 +200,11 @@ const bindings = computed<any>(() => {
192
200
  },
193
201
  onFocus: (e: Event) => {
194
202
  emit('focus', e);
203
+ focus.value = true;
195
204
  },
196
205
  onBlur: (e: Event) => {
197
206
  emit('blur', e);
207
+ focus.value = false;
198
208
  },
199
209
  };
200
210
  });
@@ -219,17 +229,18 @@ const maskInternal = computed(() => {
219
229
  return props.mask;
220
230
  });
221
231
 
222
- const emit = defineEmits(['update:modelValue', 'focus', 'blur']);
232
+ const emit = defineEmits(['update:modelValue', 'focus', 'blur', 'click', 'keydown']);
223
233
 
224
- const { nameInternal, requiredInternal, hasErrorInternal, emitUpdate } =
234
+ const { nameInternal, requiredInternal, hasErrorInternal, emitUpdate, sizeInternal } =
225
235
  useField({
226
236
  name: computed(() => props.name),
227
237
  required: computed(() => props.required),
228
238
  hasError: computed(() => props.hasError),
239
+ size: computed(() => props.size),
229
240
  emit: emit,
230
241
  });
231
242
 
232
- function update(event: any | null) {
243
+ function onInput(event: any | null) {
233
244
  if (event === null) {
234
245
  emitUpdate(null);
235
246
  }
@@ -237,28 +248,97 @@ function update(event: any | null) {
237
248
  return emitUpdate(get(event, 'target.value', ''));
238
249
  }
239
250
 
240
- const borderColor = computed(() => {
241
- return hasErrorInternal.value ? 'border-red-500' : 'border-slate-300';
251
+ function onClick(event: MouseEvent) {
252
+ emit('click', event);
253
+ }
254
+
255
+ function onKeydown(event: KeyboardEvent) {
256
+ emit('keydown', event);
257
+ }
258
+
259
+ const classes = computed(() => {
260
+ const base = `inline-flex bg-white input-rounded border transition-colors duration-200`;
261
+ const border = hasErrorInternal.value ? 'border-red-500' : 'border-slate-300';
262
+ const disabled = props.disabled ? 'cursor-not-allowed text-slate-300' : '';
263
+ const sizeConfig = sizes[sizeInternal.value];
264
+ let focusClass = '';
265
+ if (props.visibleFocus) {
266
+ if (hasErrorInternal.value) {
267
+ focusClass = 'focus-within:input-focus-error';
268
+ } else {
269
+ focusClass = 'focus-within:input-focus';
270
+ }
271
+ }
272
+
273
+ return twMerge([
274
+ base,
275
+ border,
276
+ disabled,
277
+ sizeConfig.height,
278
+ sizeConfig.fontSize,
279
+ focusClass,
280
+ props.class,
281
+ ]);
242
282
  });
243
283
 
244
- const backgroundColor = computed(() => {
245
- return hasErrorInternal.value ? 'bg-red-100' : 'bg-slate-100';
284
+ const baseClasses = computed(() => {
285
+ const base = `border-none outline-none bg-transparent grow min-w-0 focus:border-none focus:outline-none focus:ring-0`;
286
+ const sizeConfig = sizes[sizeInternal.value];
287
+ const disabled = props.disabled ? 'cursor-not-allowed text-slate-300' : '';
288
+
289
+ const paddingX = {
290
+ xs: [hasLeftDecoration.value ? 'pl-1.5' : 'pl-2', hasRightDecoration.value ? 'pr-1.5' : 'pr-2'],
291
+ sm: [hasLeftDecoration.value ? 'pl-2' : 'pl-2.5', hasRightDecoration.value ? 'pr-2' : 'pr-2.5'],
292
+ md: [hasLeftDecoration.value ? 'pl-2.5' : 'pl-3', hasRightDecoration.value ? 'pr-2.5' : 'pr-3'],
293
+ }[sizeInternal.value];
294
+
295
+ return [
296
+ base,
297
+ disabled,
298
+ paddingX,
299
+ sizeConfig.fontSize,
300
+ ];
301
+ });
302
+
303
+ const decorationWrapClasses = computed(() => {
304
+ const base = `flex items-center justify-center empty:hidden`;
305
+ const spacing = {
306
+ xs: 'first:pl-2 last:pr-2 gap-1',
307
+ sm: 'first:pl-2.5 last:pr-2.5 gap-2',
308
+ md: 'first:pl-3 last:pr-3 gap-3',
309
+ }[sizeInternal.value];
310
+
311
+ return [
312
+ base,
313
+ spacing,
314
+ ];
246
315
  });
247
316
 
248
- const textColor = computed(() => {
249
- return hasErrorInternal.value ? 'text-red-800' : 'text-slate-600';
317
+ const decorationClasses = computed(() => {
318
+ const base = `flex items-center justify-center`;
319
+ const textColor = hasErrorInternal.value ? 'text-red-800' : 'text-slate-500';
320
+
321
+ return `${base} ${textColor}`;
322
+ });
323
+
324
+ const iconClasses = computed(() => {
325
+ const sizeConfig = sizes[sizeInternal.value];
326
+
327
+ return [
328
+ sizeConfig.iconSize,
329
+ ];
250
330
  });
251
331
 
252
- function focus() {
332
+ function focusAction() {
253
333
  input.value?.focus();
254
334
  }
255
335
 
256
- function blur() {
336
+ function blurAction() {
257
337
  input.value?.blur();
258
338
  }
259
339
 
260
340
  defineExpose({
261
- focus,
262
- blur,
341
+ focus: focusAction,
342
+ blur: blurAction,
263
343
  });
264
344
  </script>
@@ -1,7 +1,37 @@
1
1
  <template>
2
- <p class="block text-sm font-medium leading-tight text-red-600">
2
+ <p :class="classes">
3
3
  <slot />
4
4
  </p>
5
5
  </template>
6
6
 
7
- <script lang="ts" setup></script>
7
+ <script lang="ts" setup>
8
+ import { useInputSize } from '@/composables/inputSize';
9
+ import { Size } from '@/utils/sizes';
10
+ import { PropType } from 'vue';
11
+
12
+
13
+ const props = defineProps({
14
+ size: {
15
+ default: undefined,
16
+ type: String as PropType<Size>,
17
+ },
18
+ });
19
+
20
+ const { size } = useInputSize(computed(() => props.size));
21
+
22
+ const classes = computed(() => {
23
+ const base = 'block font-medium leading-tight text-red-600';
24
+
25
+ const responsive = {
26
+ 'xs': 'text-xs mb-0.5',
27
+ 'sm': 'text-xs mb-1',
28
+ 'md': 'text-sm mb-1',
29
+ }[size.value];
30
+
31
+ return [
32
+ base,
33
+ responsive,
34
+ ]
35
+ });
36
+
37
+ </script>
@@ -2,25 +2,27 @@
2
2
  <label :class="classes">
3
3
  <div
4
4
  ref="labelRef"
5
- class="relative inline-flex"
5
+ class="relative inline-flex items-center flex-wrap"
6
6
  :class="[help ? 'cursor-help' : 'cursor-default']"
7
7
  >
8
8
  <div> {{ label }}</div>
9
9
 
10
+ <div
11
+ v-if="required"
12
+ class="ml-0.5 text-red-600"
13
+ > *</div>
14
+
10
15
  <BaseTooltip
11
16
  v-if="help"
12
17
  :text="help"
13
18
  >
14
19
  <BaseIcon
15
- class="block relative text-slate-900 -bottom-[3px] ml-1 mr-1 h-[13px] w-[13px]"
20
+ class="block relative text-slate-900 ml-1 mr-1 h-[13px] w-[13px]"
16
21
  icon="mdi:information-outline"
17
22
  />
18
23
  </BaseTooltip>
19
24
 
20
- <div
21
- v-if="required"
22
- class="ml-0.5 text-red-600"
23
- > *</div>
25
+
24
26
  </div>
25
27
  </label>
26
28
  </template>
@@ -29,8 +31,15 @@
29
31
  import { PropType } from 'vue';
30
32
  import { Icon as BaseIcon } from '@iconify/vue';
31
33
  import BaseTooltip from './BaseTooltip.vue';
34
+ import { Size } from '@/utils/sizes';
35
+ import { useInputSize } from '@/composables/inputSize';
36
+ import { ClassNameValue, twMerge } from 'tailwind-merge';
32
37
 
33
- defineProps({
38
+ defineOptions({
39
+ inheritAttrs: false,
40
+ });
41
+
42
+ const props = defineProps({
34
43
  required: {
35
44
  default: false,
36
45
  type: Boolean,
@@ -39,16 +48,34 @@ defineProps({
39
48
  required: true,
40
49
  type: String,
41
50
  },
42
- classes: {
51
+ class: {
43
52
  default: 'mb-1 block text-sm',
44
- type: String,
53
+ type: [String, Array] as PropType<ClassNameValue>,
45
54
  },
46
55
  help: {
47
56
  default: null,
48
57
  type: [String, null] as PropType<string | null>,
49
58
  },
59
+ size: {
60
+ default: undefined,
61
+ type: String as PropType<Size>,
62
+ },
50
63
  });
51
64
 
52
65
  const labelRef = ref();
53
66
 
67
+ const { size } = useInputSize(computed(() => props.size));
68
+
69
+ const classes = computed(() => {
70
+ const base = 'mb-1 block text-sm';
71
+
72
+ const responsive = {
73
+ 'xs': 'text-xs mb-[0.2rem]',
74
+ 'sm': 'text-sm mb-1',
75
+ 'md': 'text-sm mb-1',
76
+ }[size.value];
77
+
78
+ return twMerge(base, responsive, props.class);
79
+ });
80
+
54
81
  </script>
@@ -78,6 +78,23 @@ Disabled.args = {
78
78
  disabled: true,
79
79
  };
80
80
 
81
+ const TemplateSizes = (args) => ({
82
+ components: { BaseSelect },
83
+ setup() {
84
+ const value = ref(null);
85
+ const sizes = ['xs', 'sm', 'md'];
86
+ return { args, value, sizes, options };
87
+ },
88
+ template: `
89
+ <div v-for="size in sizes" :key="size" class="mb-4">
90
+ <p class="text-xs text-slate-600 leading-tight mb-1">btn {{ size }}</p>
91
+ <BaseSelect v-model="value" v-bind="args" :options="options" label-key="label" value-key="value" :size="size" class="w-full"></BaseSelect>
92
+ </div>
93
+ `,
94
+ });
95
+
96
+ export const Sizes = TemplateSizes.bind({});
97
+
81
98
  export const OptionsAsProps = (args) => ({
82
99
  components: { BaseSelect, ShowValue },
83
100
  setup() {
@@ -106,6 +123,23 @@ OptionsAsProps.args = {
106
123
  valueKey: 'label',
107
124
  };
108
125
 
126
+ const CustomClassesT = (args) => ({
127
+ components: { BaseSelect },
128
+ setup() {
129
+ const value = ref(null);
130
+ return { args, value };
131
+ },
132
+ template: `
133
+ <BaseSelect
134
+ v-model="value"
135
+ v-bind="args"
136
+ class="w-full bg-green-100 text-green-700 border-green-700 focus-within:border-red-500 focus-within:ring-red-800 placeholder:text-green-400 shadow-xl"
137
+ ></BaseSelect>
138
+ `,
139
+ });
140
+
141
+ export const CustomClasses = CustomClassesT.bind({});
142
+
109
143
  export const Field = createFieldStory({
110
144
  component: BaseSelect,
111
145
  componentName: 'BaseSelect',
@@ -5,11 +5,7 @@
5
5
  :name="nameInternal"
6
6
  :disabled="disabled"
7
7
  :required="requiredInternal"
8
- class="rounded border disabled:cursor-not-allowed disabled:text-slate-300 disabled:opacity-100"
9
- :class="[
10
- !modelValue && requiredInternal ? 'text-slate-400' : '',
11
- hasErrorInternal ? 'border-red-600' : 'border-slate-300',
12
- ]"
8
+ :class="classes"
13
9
  @change="onChange($event)"
14
10
  >
15
11
  <template v-if="requiredInternal">
@@ -43,17 +39,22 @@
43
39
 
44
40
  <script lang="ts" setup>
45
41
  import { PropType } from 'vue';
46
- import { get, isArray, isEqual } from 'lodash';
47
- import { useMutationObserver } from '@vueuse/core';
42
+ import { get, isArray } from 'lodash';
48
43
  import { useField } from '@/composables/field';
49
44
  import { NormalizedOption, OptionValue, Option } from '@/types';
50
45
  import { t } from '@/i18n';
46
+ import { twMerge } from 'tailwind-merge';
47
+ import { Size, sizes } from '@/utils/sizes';
51
48
 
52
49
  type SelectOption = string | number | null;
53
50
 
54
51
  const EMPTY_VALUE_INTERNAL = '';
55
52
  const EMPTY_VALUE_EXTERNAL = null;
56
53
 
54
+ defineOptions({
55
+ inheritAttrs: false,
56
+ });
57
+
57
58
  const props = defineProps({
58
59
  modelValue: {
59
60
  default: undefined,
@@ -63,6 +64,14 @@ const props = defineProps({
63
64
  default: undefined,
64
65
  type: String,
65
66
  },
67
+ class: {
68
+ default: '',
69
+ type: [String, Array] as PropType<string | string[]>,
70
+ },
71
+ size: {
72
+ default: undefined,
73
+ type: String as PropType<Size>,
74
+ },
66
75
  placeholder: {
67
76
  default: '',
68
77
  type: String,
@@ -91,16 +100,21 @@ const props = defineProps({
91
100
  default: undefined,
92
101
  type: String,
93
102
  },
103
+ visibleFocus: {
104
+ default: true,
105
+ type: Boolean,
106
+ },
94
107
  });
95
108
 
96
109
  const select = ref<HTMLSelectElement | null>(null);
97
110
 
98
111
  const emit = defineEmits(['update:modelValue']);
99
112
 
100
- const { nameInternal, requiredInternal, hasErrorInternal, emitUpdate } =
113
+ const { nameInternal, requiredInternal, hasErrorInternal, emitUpdate, sizeInternal } =
101
114
  useField({
102
115
  name: computed(() => props.name),
103
116
  required: computed(() => props.required),
117
+ size: computed(() => props.size),
104
118
  hasError: computed(() => props.hasError),
105
119
  emit: emit,
106
120
  });
@@ -182,4 +196,39 @@ const normalizedOptions = computed<NormalizedOption[] | undefined>(() => {
182
196
  } as NormalizedOption;
183
197
  });
184
198
  });
199
+
200
+ const classes = computed(() => {
201
+ const base = 'input-rounded border transition-colors duration-200 py-0';
202
+
203
+ let focusClass = '';
204
+ if (props.visibleFocus) {
205
+ if (hasErrorInternal.value) {
206
+ focusClass = 'focus-within:input-focus-error';
207
+ } else {
208
+ focusClass = 'focus-within:input-focus';
209
+ }
210
+ }
211
+
212
+ const disabled = 'disabled:cursor-not-allowed disabled:text-slate-300 disabled:opacity-100';
213
+ const error = hasErrorInternal.value ? 'border-red-600' : 'border-slate-300';
214
+ const textColor = !props.modelValue && requiredInternal ? 'text-slate-400' : '';
215
+ const sizeConfig = sizes[sizeInternal.value];
216
+ const padding = {
217
+ xs: 'pl-2 pr-7',
218
+ sm: 'pl-2.5 pr-8',
219
+ md: 'pl-3 pr-10',
220
+ }[sizeInternal.value];
221
+
222
+ return twMerge([
223
+ base,
224
+ focusClass,
225
+ disabled,
226
+ error,
227
+ textColor,
228
+ sizeConfig.height,
229
+ sizeConfig.fontSize,
230
+ padding,
231
+ props.class,
232
+ ]);
233
+ });
185
234
  </script>
@@ -116,6 +116,7 @@ import { useNotificationsStore } from '@/stores/notifications';
116
116
  import BaseAutocompleteDrawer from './BaseAutocompleteDrawer.vue';
117
117
  import { twMerge } from 'tailwind-merge';
118
118
  import { t } from '@/i18n';
119
+ import { Size } from '@/utils/sizes';
119
120
 
120
121
  const notifications = useNotificationsStore();
121
122
 
@@ -177,8 +178,8 @@ const props = defineProps({
177
178
  type: Boolean,
178
179
  },
179
180
  size: {
180
- default: 'base',
181
- type: String as PropType<'xs' | 'sm' | 'base'>,
181
+ default: 'md',
182
+ type: String as PropType<Size>,
182
183
  },
183
184
  dropdownShow: {
184
185
  default: 'focus',
@@ -68,9 +68,7 @@ WithDescription.args = {
68
68
  item: {
69
69
  title: 'Advanced to phone screening by Bethany Blake',
70
70
  icon: 'heroicons:shield-check-20-solid',
71
- description:
72
- 'Lorem nostrud quis aute elit ea Lorem magna eiusmod ipsum. Eu ipsum eiusmod ad minim adipisicing irure. Fugiat ut adipisicing consequat dolor.',
73
- color: 'primary',
71
+ description: 'Lorem nostrud quis aute elit ea Lorem magna eiusmod ipsum. Eu ipsum eiusmod ad minim adipisicing irure. Fugiat ut adipisicing consequat dolor.',
74
72
  date: '15 Jan',
75
73
  },
76
74
  };