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.
- package/dist/sprintify-ui.es.js +11823 -11591
- package/dist/style.css +1 -1
- package/dist/tailwindcss/button.js +260 -0
- package/dist/tailwindcss/index.js +16 -301
- package/dist/tailwindcss/input.js +24 -0
- package/dist/tailwindcss/overlay.js +13 -0
- package/dist/tailwindcss/theme.js +46 -0
- package/dist/types/src/components/BaseActionItemButton.vue.d.ts +2 -2
- package/dist/types/src/components/BaseAddressForm.vue.d.ts +10 -0
- package/dist/types/src/components/BaseAutocomplete.vue.d.ts +10 -10
- package/dist/types/src/components/BaseAutocompleteDrawer.vue.d.ts +3 -3
- package/dist/types/src/components/BaseAutocompleteFetch.vue.d.ts +6 -6
- package/dist/types/src/components/BaseAvatarGroup.vue.d.ts +1 -1
- package/dist/types/src/components/BaseBelongsTo.vue.d.ts +6 -6
- package/dist/types/src/components/BaseBelongsToFetch.vue.d.ts +6 -6
- package/dist/types/src/components/BaseButton.vue.d.ts +12 -12
- package/dist/types/src/components/BaseButtonGroup.vue.d.ts +24 -24
- package/dist/types/src/components/BaseCalendar.vue.d.ts +1 -1
- package/dist/types/src/components/BaseColor.vue.d.ts +19 -0
- package/dist/types/src/components/BaseDataIterator.vue.d.ts +1 -1
- package/dist/types/src/components/BaseDataIteratorSectionColumns.vue.d.ts +4 -4
- package/dist/types/src/components/BaseDataTable.vue.d.ts +1 -1
- package/dist/types/src/components/BaseDropdownAutocomplete.vue.d.ts +1 -1
- package/dist/types/src/components/BaseField.vue.d.ts +13 -3
- package/dist/types/src/components/BaseHasMany.vue.d.ts +9 -0
- package/dist/types/src/components/BaseInput.vue.d.ts +35 -4
- package/dist/types/src/components/BaseInputError.vue.d.ts +14 -1
- package/dist/types/src/components/BaseInputLabel.vue.d.ts +15 -5
- package/dist/types/src/components/BaseLayoutSidebarConfigurable.vue.d.ts +1 -1
- package/dist/types/src/components/BaseLayoutStackedConfigurable.vue.d.ts +1 -1
- package/dist/types/src/components/BaseLoadingCover.vue.d.ts +1 -1
- package/dist/types/src/components/BaseMediaGallery.vue.d.ts +1 -1
- package/dist/types/src/components/BaseMediaListItem.vue.d.ts +1 -1
- package/dist/types/src/components/BaseMediaPictures.vue.d.ts +1 -1
- package/dist/types/src/components/BaseMediaPicturesItem.vue.d.ts +1 -1
- package/dist/types/src/components/BaseMenu.vue.d.ts +1 -1
- package/dist/types/src/components/BaseMenuItem.vue.d.ts +1 -1
- package/dist/types/src/components/BaseNavbar.vue.d.ts +1 -1
- package/dist/types/src/components/BaseNavbarItemContent.vue.d.ts +1 -1
- package/dist/types/src/components/BaseNavbarSideItemContent.vue.d.ts +1 -1
- package/dist/types/src/components/BaseSelect.vue.d.ts +27 -0
- package/dist/types/src/components/BaseSideNavigation.vue.d.ts +3 -3
- package/dist/types/src/components/BaseSwitch.vue.d.ts +1 -1
- package/dist/types/src/components/BaseTable.vue.d.ts +1 -1
- package/dist/types/src/components/BaseTabs.vue.d.ts +3 -3
- package/dist/types/src/components/BaseTagAutocomplete.vue.d.ts +3 -3
- package/dist/types/src/composables/field.d.ts +3 -0
- package/dist/types/src/composables/inputSize.d.ts +6 -0
- package/dist/types/src/utils/sizes.d.ts +19 -0
- package/package.json +1 -1
- package/src/assets/form.css +1 -1
- package/src/components/BaseAddressForm.stories.js +7 -2
- package/src/components/BaseAddressForm.vue +64 -37
- package/src/components/BaseAutocomplete.stories.js +1 -1
- package/src/components/BaseAutocomplete.vue +86 -96
- package/src/components/BaseAutocompleteDrawer.vue +3 -2
- package/src/components/BaseAutocompleteFetch.stories.js +1 -1
- package/src/components/BaseAutocompleteFetch.vue +3 -2
- package/src/components/BaseBelongsTo.stories.js +1 -1
- package/src/components/BaseBelongsTo.vue +3 -2
- package/src/components/BaseBelongsToFetch.vue +3 -2
- package/src/components/BaseButton.stories.js +21 -0
- package/src/components/BaseButton.vue +42 -6
- package/src/components/BaseButtonGroup.stories.js +31 -1
- package/src/components/BaseButtonGroup.vue +46 -33
- package/src/components/BaseColor.stories.js +22 -0
- package/src/components/BaseColor.vue +28 -25
- package/src/components/BaseDropdown.stories.js +2 -3
- package/src/components/BaseDropdownAutocomplete.vue +2 -2
- package/src/components/BaseField.vue +19 -8
- package/src/components/BaseHasMany.stories.js +13 -1
- package/src/components/BaseHasMany.vue +39 -1
- package/src/components/BaseInput.stories.js +35 -1
- package/src/components/BaseInput.vue +154 -74
- package/src/components/BaseInputError.vue +32 -2
- package/src/components/BaseInputLabel.vue +36 -9
- package/src/components/BaseSelect.stories.js +34 -0
- package/src/components/BaseSelect.vue +57 -8
- package/src/components/BaseTagAutocomplete.vue +3 -2
- package/src/components/BaseTimelineItem.stories.js +1 -3
- package/src/composables/field.ts +20 -0
- package/src/composables/inputSize.ts +29 -0
- package/src/utils/sizes.ts +21 -0
|
@@ -1,27 +1,23 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
class="
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
<
|
|
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
|
|
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
|
-
|
|
241
|
-
|
|
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
|
|
245
|
-
|
|
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
|
|
249
|
-
|
|
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
|
|
332
|
+
function focusAction() {
|
|
253
333
|
input.value?.focus();
|
|
254
334
|
}
|
|
255
335
|
|
|
256
|
-
function
|
|
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="
|
|
2
|
+
<p :class="classes">
|
|
3
3
|
<slot />
|
|
4
4
|
</p>
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
|
-
<script lang="ts" setup
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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="
|
|
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
|
|
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: '
|
|
181
|
-
type: String as PropType<
|
|
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
|
};
|