sprintify-ui 0.6.32 → 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 +11685 -11472
- 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/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/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
|
@@ -2,10 +2,18 @@ import { createFieldStory } from '../../.storybook/utils';
|
|
|
2
2
|
import BaseColor from './BaseColor.vue';
|
|
3
3
|
import ShowValue from '../../.storybook/components/ShowValue.vue';
|
|
4
4
|
|
|
5
|
+
const sizes = ['xs', 'sm', 'md'];
|
|
6
|
+
|
|
5
7
|
export default {
|
|
6
8
|
title: 'Form/BaseColor',
|
|
7
9
|
component: BaseColor,
|
|
8
10
|
decorators: [() => ({ template: '<div class="mb-36"><story/></div>' })],
|
|
11
|
+
argTypes: {
|
|
12
|
+
size: {
|
|
13
|
+
control: { type: 'select' },
|
|
14
|
+
options: sizes,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
9
17
|
};
|
|
10
18
|
|
|
11
19
|
const Template = (args) => ({
|
|
@@ -39,6 +47,20 @@ Disabled.args = {
|
|
|
39
47
|
modelValue: '#16a34a',
|
|
40
48
|
};
|
|
41
49
|
|
|
50
|
+
export const Sizes = (args) => ({
|
|
51
|
+
components: { BaseColor },
|
|
52
|
+
setup() {
|
|
53
|
+
const value = ref(null);
|
|
54
|
+
return { args, sizes, value };
|
|
55
|
+
},
|
|
56
|
+
template: `
|
|
57
|
+
<div v-for="size in sizes" :key="size" class="mb-4">
|
|
58
|
+
<p class="text-xs text-slate-600 leading-tight mb-1"> {{ size }}</p>
|
|
59
|
+
<BaseColor v-bind="args" v-model="value" :size="size"></BaseColor>
|
|
60
|
+
</div>
|
|
61
|
+
`,
|
|
62
|
+
});
|
|
63
|
+
|
|
42
64
|
export const Field = createFieldStory({
|
|
43
65
|
component: BaseColor,
|
|
44
66
|
componentName: 'BaseColor',
|
|
@@ -5,36 +5,28 @@
|
|
|
5
5
|
:disabled="disabled"
|
|
6
6
|
value-key="value"
|
|
7
7
|
label-key="label"
|
|
8
|
+
:size="size"
|
|
8
9
|
:required="required"
|
|
9
10
|
:options="colorOptions"
|
|
10
11
|
:multiple="multiple"
|
|
11
12
|
:button-type="buttonType"
|
|
12
|
-
|
|
13
|
-
tw-button
|
|
14
|
-
tw-button-unselected=""
|
|
13
|
+
:button-color="buttonColor"
|
|
14
|
+
tw-button="p-0 aspect-1 justify-center items-center"
|
|
15
15
|
@update:model-value="(value) => transformModelValue(value)"
|
|
16
16
|
>
|
|
17
17
|
<template #option="option">
|
|
18
18
|
<div
|
|
19
|
-
|
|
20
|
-
class="
|
|
21
|
-
:class="[
|
|
22
|
-
option.selected.value
|
|
23
|
-
? option.option.option.hasLowContrast
|
|
24
|
-
? 'text-slate-700'
|
|
25
|
-
: 'text-white'
|
|
26
|
-
: 'text-transparent',
|
|
27
|
-
disabled ? ' cursor-not-allowed opacity-50' : '',
|
|
28
|
-
option.option.option.hasLowContrast
|
|
29
|
-
? 'border-slate-400'
|
|
30
|
-
: 'border-transparent',
|
|
31
|
-
]"
|
|
19
|
+
v-if="option.selected.value"
|
|
20
|
+
class="overflow-hidden flex h-full"
|
|
32
21
|
>
|
|
33
22
|
<BaseIcon
|
|
34
23
|
icon="heroicons-solid:check-circle"
|
|
35
|
-
class="
|
|
24
|
+
class="block leading-none"
|
|
25
|
+
width="18"
|
|
26
|
+
height="18"
|
|
36
27
|
/>
|
|
37
28
|
</div>
|
|
29
|
+
<div v-else />
|
|
38
30
|
</template>
|
|
39
31
|
</BaseButtonGroup>
|
|
40
32
|
</div>
|
|
@@ -48,6 +40,7 @@ import { useField } from '@/composables/field';
|
|
|
48
40
|
import { Option } from '@/types';
|
|
49
41
|
import { palette } from '@/utils/colors';
|
|
50
42
|
import { getContrast } from 'color2k';
|
|
43
|
+
import { Size } from '@/utils/sizes';
|
|
51
44
|
|
|
52
45
|
const props = defineProps({
|
|
53
46
|
modelValue: {
|
|
@@ -64,6 +57,10 @@ const props = defineProps({
|
|
|
64
57
|
default: false,
|
|
65
58
|
type: Boolean,
|
|
66
59
|
},
|
|
60
|
+
size: {
|
|
61
|
+
default: undefined,
|
|
62
|
+
type: String as PropType<Size>,
|
|
63
|
+
},
|
|
67
64
|
buttonType: {
|
|
68
65
|
default: 'button',
|
|
69
66
|
type: String as PropType<'button' | 'submit'>,
|
|
@@ -74,6 +71,12 @@ const props = defineProps({
|
|
|
74
71
|
return Object.values(palette).map((p) => p.high.backgroundColor);
|
|
75
72
|
},
|
|
76
73
|
},
|
|
74
|
+
buttonColor: {
|
|
75
|
+
default(option: Option, selected: boolean) {
|
|
76
|
+
return option.value;
|
|
77
|
+
},
|
|
78
|
+
type: Function as PropType<(option: Option, selected: boolean) => string>,
|
|
79
|
+
},
|
|
77
80
|
multiple: {
|
|
78
81
|
default: false,
|
|
79
82
|
type: Boolean,
|
|
@@ -107,15 +110,15 @@ function hasLowContrast(color: string): boolean {
|
|
|
107
110
|
const normalizeModelValue = computed(() => {
|
|
108
111
|
return isArray(props.modelValue)
|
|
109
112
|
? props.modelValue?.map((m) => {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
};
|
|
114
|
-
})
|
|
115
|
-
: {
|
|
116
|
-
label: props.modelValue,
|
|
117
|
-
value: props.modelValue,
|
|
113
|
+
return {
|
|
114
|
+
label: m,
|
|
115
|
+
value: m,
|
|
118
116
|
};
|
|
117
|
+
})
|
|
118
|
+
: {
|
|
119
|
+
label: props.modelValue,
|
|
120
|
+
value: props.modelValue,
|
|
121
|
+
};
|
|
119
122
|
});
|
|
120
123
|
|
|
121
124
|
const { emitUpdate } = useField({
|
|
@@ -93,7 +93,6 @@ export const WithAutocomplete = (args) => ({
|
|
|
93
93
|
size="xs"
|
|
94
94
|
:options="options"
|
|
95
95
|
:inline="true"
|
|
96
|
-
:visibleFocus="false"
|
|
97
96
|
dropdownShow="always"
|
|
98
97
|
:showModelValue="false"
|
|
99
98
|
focus-on-mount
|
|
@@ -143,14 +142,14 @@ export const ModalWithScroll = (args) => ({
|
|
|
143
142
|
},
|
|
144
143
|
template: `
|
|
145
144
|
<BaseModalCenter v-model="open">
|
|
146
|
-
<div class="p-10
|
|
145
|
+
<div class="p-10">
|
|
147
146
|
<BaseDropdown v-bind="args">
|
|
148
147
|
<template #button>
|
|
149
148
|
<div class="btn btn-primary">Click me</div>
|
|
150
149
|
</template>
|
|
151
150
|
<template #dropdown>
|
|
152
151
|
<div
|
|
153
|
-
class="bg-white shadow py-1 px-1 rounded"
|
|
152
|
+
class="bg-white ring-1 shadow-xl ring-black ring-opacity-10 py-1 px-1 rounded-md"
|
|
154
153
|
style="max-height: 200px; overflow: auto;"
|
|
155
154
|
data-scroll-lock-scrollable>
|
|
156
155
|
<button type="button" v-for="item in items" :key="item.label" class="block text-sm px-4 py-1.5">{{ item.label }}</button>
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
</template>
|
|
14
14
|
<template #dropdown="{ close }">
|
|
15
15
|
<div
|
|
16
|
-
class="inline-block w-[320px] overflow-hidden rounded
|
|
16
|
+
class="inline-block w-[320px] overflow-hidden input-rounded ring-1 ring-black ring-opacity-10 bg-white px-2 pt-2 shadow-2xl"
|
|
17
17
|
>
|
|
18
18
|
<component
|
|
19
19
|
:is="componentName"
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
<template #option="optionProps">
|
|
34
34
|
<div
|
|
35
35
|
:class="[optionProps.active ? 'bg-slate-100' : 'bg-white']"
|
|
36
|
-
class="mb-px flex items-center rounded px-1 py-1"
|
|
36
|
+
class="mb-px flex items-center input-rounded px-1 py-1"
|
|
37
37
|
>
|
|
38
38
|
<div class="flex grow items-center">
|
|
39
39
|
<slot
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
v-if="labelNormalized"
|
|
5
5
|
:label="labelNormalized"
|
|
6
6
|
:required="required"
|
|
7
|
-
:
|
|
7
|
+
:size="inputSize.size.value"
|
|
8
|
+
:class="labelClassInternalMerged"
|
|
8
9
|
:help="help"
|
|
9
10
|
/>
|
|
10
11
|
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
<template v-if="errorMessage">
|
|
20
21
|
<BaseInputError
|
|
21
22
|
v-if="errorTypeInternal == 'default'"
|
|
23
|
+
:size="inputSize.size.value"
|
|
22
24
|
class="mt-1"
|
|
23
25
|
>
|
|
24
26
|
{{ errorMessage }}
|
|
@@ -40,6 +42,9 @@ import { PropType } from 'vue';
|
|
|
40
42
|
import BaseAlert from './BaseAlert.vue';
|
|
41
43
|
import BaseInputError from './BaseInputError.vue';
|
|
42
44
|
import BaseInputLabel from './BaseInputLabel.vue';
|
|
45
|
+
import { useInputSize } from '@/composables/inputSize';
|
|
46
|
+
import { Size } from '@/utils/sizes';
|
|
47
|
+
import { ClassNameValue, twMerge } from 'tailwind-merge';
|
|
43
48
|
|
|
44
49
|
const props = defineProps({
|
|
45
50
|
name: {
|
|
@@ -50,6 +55,10 @@ const props = defineProps({
|
|
|
50
55
|
type: String,
|
|
51
56
|
default: '',
|
|
52
57
|
},
|
|
58
|
+
size: {
|
|
59
|
+
default: undefined,
|
|
60
|
+
type: String as PropType<Size>,
|
|
61
|
+
},
|
|
53
62
|
required: {
|
|
54
63
|
type: Boolean,
|
|
55
64
|
default: false,
|
|
@@ -68,25 +77,26 @@ const props = defineProps({
|
|
|
68
77
|
},
|
|
69
78
|
labelClass: {
|
|
70
79
|
default: '',
|
|
71
|
-
type: [String, Array
|
|
72
|
-
string | string[] | Record<string, boolean>
|
|
73
|
-
>,
|
|
80
|
+
type: [String, Array] as PropType<ClassNameValue>,
|
|
74
81
|
},
|
|
75
82
|
});
|
|
76
83
|
|
|
77
84
|
const errorTypeInternal = ref(props.errorType);
|
|
78
85
|
|
|
86
|
+
const inputSize = useInputSize(computed(() => props.size));
|
|
87
|
+
|
|
79
88
|
function setErrorType(errorType: 'default' | 'alert' | null) {
|
|
80
89
|
if (errorType != null) {
|
|
81
90
|
errorTypeInternal.value = errorType;
|
|
82
91
|
}
|
|
83
92
|
}
|
|
84
93
|
|
|
85
|
-
const labelClassInternal = ref(
|
|
94
|
+
const labelClassInternal = ref<any>('');
|
|
95
|
+
const labelClassInternalMerged = computed(() => {
|
|
96
|
+
return twMerge(labelClassInternal.value, props.labelClass);
|
|
97
|
+
});
|
|
86
98
|
|
|
87
|
-
function setLabelClass(
|
|
88
|
-
labelClass: string | string[] | Record<string, boolean> | null
|
|
89
|
-
) {
|
|
99
|
+
function setLabelClass(labelClass: ClassNameValue) {
|
|
90
100
|
if (labelClass != null) {
|
|
91
101
|
labelClassInternal.value = labelClass;
|
|
92
102
|
}
|
|
@@ -122,6 +132,7 @@ const errorMessage = computed((): string | null | undefined => {
|
|
|
122
132
|
|
|
123
133
|
provide('field:name', readonly(ref(props.name)));
|
|
124
134
|
provide('field:required', readonly(ref(props.required)));
|
|
135
|
+
provide('field:size', readonly(inputSize.size));
|
|
125
136
|
provide('field:onUpdate', fieldOnUpdate);
|
|
126
137
|
provide('field:errorMessage', errorMessage);
|
|
127
138
|
provide('field:setErrorType', setErrorType);
|
|
@@ -19,6 +19,10 @@ export default {
|
|
|
19
19
|
placeholder: 'Enter your name',
|
|
20
20
|
},
|
|
21
21
|
argTypes: {
|
|
22
|
+
size: {
|
|
23
|
+
control: { type: 'select' },
|
|
24
|
+
options: ['xs', 'sm', 'md'],
|
|
25
|
+
},
|
|
22
26
|
mask: {
|
|
23
27
|
control: { type: 'select' },
|
|
24
28
|
options: [
|
|
@@ -144,12 +148,42 @@ CustomMask.args = {
|
|
|
144
148
|
mask: (value) => (value.startsWith('1') ? '#-#' : '##-##'),
|
|
145
149
|
};
|
|
146
150
|
|
|
151
|
+
const CustomClassesT = (args) => ({
|
|
152
|
+
components: { BaseInput, ShowValue },
|
|
153
|
+
setup() {
|
|
154
|
+
const value = ref(null);
|
|
155
|
+
return { args, value };
|
|
156
|
+
},
|
|
157
|
+
template: `
|
|
158
|
+
<BaseInput v-model="value" v-bind="args" suffix="$$$" 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"></BaseInput>
|
|
159
|
+
<ShowValue :value="value" />
|
|
160
|
+
`,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
export const CustomClasses = CustomClassesT.bind({});
|
|
164
|
+
|
|
147
165
|
export const Field = createFieldStory({
|
|
148
166
|
component: BaseInput,
|
|
149
167
|
componentName: 'BaseInput',
|
|
150
168
|
label: 'Name',
|
|
151
169
|
});
|
|
152
170
|
|
|
171
|
+
const TemplateSizes = (args) => ({
|
|
172
|
+
components: { BaseInput, ShowValue },
|
|
173
|
+
setup() {
|
|
174
|
+
const value = ref(null);
|
|
175
|
+
const sizes = ['xs', 'sm', 'md'];
|
|
176
|
+
return { args, value, sizes };
|
|
177
|
+
},
|
|
178
|
+
template: `
|
|
179
|
+
<div v-for="size in sizes" :key="size" class="mb-4">
|
|
180
|
+
<p class="text-xs text-slate-600 leading-tight mb-1">btn {{ size }}</p>
|
|
181
|
+
<BaseInput v-model="value" v-bind="args" :size="size" class="w-full"></BaseInput>
|
|
182
|
+
</div>
|
|
183
|
+
`,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
export const Sizes = TemplateSizes.bind({});
|
|
153
187
|
|
|
154
188
|
const FocusTemplate = (args) => ({
|
|
155
189
|
components: { BaseInput },
|
|
@@ -161,7 +195,7 @@ const FocusTemplate = (args) => ({
|
|
|
161
195
|
template: `
|
|
162
196
|
<BaseInput ref="input" v-model="value" v-bind="args" class="w-full"></BaseInput>
|
|
163
197
|
|
|
164
|
-
<button @click="input.focus()" class="mt-4
|
|
198
|
+
<button @click="input.focus()" class="mt-4 btn btn-sm">Focus</button>
|
|
165
199
|
`,
|
|
166
200
|
});
|
|
167
201
|
|
|
@@ -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>
|