sprintify-ui 0.0.41 → 0.0.42
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 +6033 -5518
- package/dist/types/src/components/BaseAutocomplete.vue.d.ts +32 -12
- package/dist/types/src/components/BaseAutocompleteFetch.vue.d.ts +28 -28
- package/dist/types/src/components/BaseBelongsTo.vue.d.ts +35 -35
- package/dist/types/src/components/BaseButtonGroup.vue.d.ts +46 -8
- package/dist/types/src/components/BaseDatePicker.vue.d.ts +18 -9
- package/dist/types/src/components/BaseDateSelect.vue.d.ts +14 -5
- package/dist/types/src/components/BaseField.vue.d.ts +151 -0
- package/dist/types/src/components/BaseFieldI18n.vue.d.ts +93 -0
- package/dist/types/src/components/BaseForm.vue.d.ts +267 -0
- package/dist/types/src/components/BaseFormField.d.ts +81 -0
- package/dist/types/src/components/BaseHasMany.vue.d.ts +31 -31
- package/dist/types/src/components/BaseInput.vue.d.ts +1 -1
- package/dist/types/src/components/BaseInputError.vue.d.ts +48 -0
- package/dist/types/src/components/BaseInputPercent.vue.d.ts +1 -1
- package/dist/types/src/components/BaseLocaleForm.vue.d.ts +420 -0
- package/dist/types/src/components/BaseMediaLibrary.vue.d.ts +46 -24
- package/dist/types/src/components/BaseNumberForm.vue.d.ts +382 -0
- package/dist/types/src/components/BasePassword.vue.d.ts +10 -14
- package/dist/types/src/components/BasePasswordForm.vue.d.ts +365 -0
- package/dist/types/src/components/BaseRadioGroup.vue.d.ts +23 -4
- package/dist/types/src/components/BaseSelect.vue.d.ts +20 -1
- package/dist/types/src/components/BaseSwitch.vue.d.ts +155 -23
- package/dist/types/src/components/BaseTagAutocomplete.vue.d.ts +31 -12
- package/dist/types/src/components/BaseTagAutocompleteFetch.vue.d.ts +20 -20
- package/dist/types/src/components/BaseTextarea.vue.d.ts +9 -0
- package/dist/types/src/components/BaseTextareaAutoresize.vue.d.ts +18 -0
- package/dist/types/src/components/BaseTextareaForm.vue.d.ts +394 -0
- package/dist/types/src/components/index.d.ts +4 -1
- package/dist/types/src/composables/field.d.ts +17 -0
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/types/index.d.ts +11 -0
- package/package.json +4 -1
- package/src/components/BaseAutocomplete.stories.js +56 -51
- package/src/components/BaseAutocomplete.vue +25 -8
- package/src/components/BaseAutocompleteFetch.stories.js +67 -65
- package/src/components/BaseAutocompleteFetch.vue +9 -29
- package/src/components/BaseBelongsTo.stories.js +72 -82
- package/src/components/BaseBelongsTo.vue +10 -11
- package/src/components/BaseButtonGroup.stories.js +11 -10
- package/src/components/BaseButtonGroup.vue +22 -9
- package/src/components/BaseCharacterCounter.stories.js +1 -1
- package/src/components/BaseDatePicker.stories.js +13 -9
- package/src/components/BaseDatePicker.vue +25 -8
- package/src/components/BaseDateSelect.stories.js +15 -9
- package/src/components/BaseDateSelect.vue +20 -8
- package/src/components/BaseField.vue +109 -0
- package/src/components/BaseFieldI18n.stories.js +38 -0
- package/src/components/BaseFieldI18n.vue +162 -0
- package/src/components/BaseFileUploader.stories.js +3 -3
- package/src/components/BaseFileUploader.vue +3 -3
- package/src/components/BaseForm.vue +298 -0
- package/src/components/BaseFormField.ts +117 -0
- package/src/components/BaseHasMany.stories.js +25 -10
- package/src/components/BaseHasMany.vue +9 -9
- package/src/components/BaseInput.stories.js +27 -14
- package/src/components/BaseInput.vue +17 -8
- package/src/components/BaseInputError.vue +7 -0
- package/src/components/BaseInputPercent.stories.js +10 -3
- package/src/components/BaseInputPercent.vue +2 -1
- package/src/components/BaseLocaleForm.vue +142 -0
- package/src/components/BaseMediaLibrary.stories.js +7 -6
- package/src/components/BaseMediaLibrary.vue +32 -31
- package/src/components/BaseMenu.vue +1 -1
- package/src/components/BaseNumberForm.vue +67 -0
- package/src/components/BasePassword.stories.js +9 -4
- package/src/components/BasePassword.vue +49 -44
- package/src/components/BasePasswordForm.vue +59 -0
- package/src/components/BaseRadioGroup.stories.js +9 -8
- package/src/components/BaseRadioGroup.vue +17 -3
- package/src/components/BaseSelect.stories.js +15 -2
- package/src/components/BaseSelect.vue +26 -10
- package/src/components/BaseSwitch.stories.js +7 -0
- package/src/components/BaseSwitch.vue +134 -124
- package/src/components/BaseTagAutocomplete.stories.js +21 -14
- package/src/components/BaseTagAutocomplete.vue +25 -14
- package/src/components/BaseTagAutocompleteFetch.stories.js +37 -21
- package/src/components/BaseTagAutocompleteFetch.vue +5 -5
- package/src/components/BaseTextarea.stories.js +11 -3
- package/src/components/BaseTextarea.vue +20 -6
- package/src/components/BaseTextareaAutoresize.stories.js +11 -2
- package/src/components/BaseTextareaAutoresize.vue +28 -4
- package/src/components/BaseTextareaForm.vue +101 -0
- package/src/components/BaseTimeline.vue +1 -1
- package/src/components/BaseTimelineItem.vue +4 -4
- package/src/components/index.ts +6 -0
- package/src/composables/field.ts +100 -0
- package/src/index.ts +11 -1
- package/src/types/index.ts +12 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
|
-
<div
|
|
3
|
+
<div
|
|
4
|
+
class="min-h-[42px] rounded border p-1"
|
|
5
|
+
:class="[hasErrorInternal ? 'border-red-600' : 'border-slate-300']"
|
|
6
|
+
>
|
|
4
7
|
<div class="-m-0.5 flex flex-wrap">
|
|
5
8
|
<div
|
|
6
9
|
v-for="selection in normalizedModelValue"
|
|
@@ -38,8 +41,7 @@
|
|
|
38
41
|
:value="keywords"
|
|
39
42
|
type="text"
|
|
40
43
|
:placeholder="$t('sui.select_an_item')"
|
|
41
|
-
class="h-[32px] w-full min-w-[50px] border-none p-0 pl-1 shadow-none outline-none focus:border-none focus:shadow-none focus:outline-none focus:ring-0"
|
|
42
|
-
:class="inputClass"
|
|
44
|
+
class="h-[32px] w-full min-w-[50px] border-none p-0 pl-1 shadow-none outline-none focus:border-none focus:shadow-none focus:outline-none focus:ring-0 disabled:cursor-not-allowed"
|
|
43
45
|
@focus="onTextFocus"
|
|
44
46
|
@blur="onTextBlur"
|
|
45
47
|
@input="onTextInput"
|
|
@@ -129,13 +131,14 @@ import { PropType, Ref, ComputedRef } from 'vue';
|
|
|
129
131
|
import { NormalizedOption, Option } from '@/types';
|
|
130
132
|
import { useInfiniteScroll, useMutationObserver } from '@vueuse/core';
|
|
131
133
|
import { useNotificationsStore } from '@/stores/notifications';
|
|
132
|
-
import BaseSkeleton from '
|
|
134
|
+
import BaseSkeleton from '@/components/BaseSkeleton.vue';
|
|
133
135
|
import { useHasOptions } from '@/composables/hasOptions';
|
|
136
|
+
import { useField } from '@/composables/field';
|
|
134
137
|
|
|
135
138
|
const props = defineProps({
|
|
136
139
|
modelValue: {
|
|
137
140
|
required: true,
|
|
138
|
-
type: Array as PropType<Option[]>,
|
|
141
|
+
type: [Array, null] as PropType<Option[] | null>,
|
|
139
142
|
},
|
|
140
143
|
options: {
|
|
141
144
|
required: true,
|
|
@@ -149,7 +152,7 @@ const props = defineProps({
|
|
|
149
152
|
required: true,
|
|
150
153
|
type: String,
|
|
151
154
|
},
|
|
152
|
-
|
|
155
|
+
name: {
|
|
153
156
|
default: undefined,
|
|
154
157
|
type: String,
|
|
155
158
|
},
|
|
@@ -177,6 +180,10 @@ const props = defineProps({
|
|
|
177
180
|
default: undefined,
|
|
178
181
|
type: Function as PropType<(option: NormalizedOption) => boolean>,
|
|
179
182
|
},
|
|
183
|
+
hasError: {
|
|
184
|
+
default: false,
|
|
185
|
+
type: Boolean,
|
|
186
|
+
},
|
|
180
187
|
});
|
|
181
188
|
|
|
182
189
|
const emit = defineEmits([
|
|
@@ -186,6 +193,13 @@ const emit = defineEmits([
|
|
|
186
193
|
'scrollBottom',
|
|
187
194
|
]);
|
|
188
195
|
|
|
196
|
+
const { hasErrorInternal, emitUpdate } = useField({
|
|
197
|
+
name: computed(() => props.name),
|
|
198
|
+
required: computed(() => props.required),
|
|
199
|
+
hasError: computed(() => props.hasError),
|
|
200
|
+
emit: emit,
|
|
201
|
+
});
|
|
202
|
+
|
|
189
203
|
const i18n = useI18n();
|
|
190
204
|
const notifications = useNotificationsStore();
|
|
191
205
|
|
|
@@ -377,7 +391,7 @@ const addOption = (option: NormalizedOption) => {
|
|
|
377
391
|
|
|
378
392
|
beforeAddOption();
|
|
379
393
|
|
|
380
|
-
|
|
394
|
+
update([...normalizedModelValue.value, option]);
|
|
381
395
|
|
|
382
396
|
setKeywords('');
|
|
383
397
|
};
|
|
@@ -405,16 +419,13 @@ const attemptRemoveLastSelection = () => {
|
|
|
405
419
|
const removeOption = (option: NormalizedOption) => {
|
|
406
420
|
let newModelValue = cloneDeep(normalizedModelValue.value);
|
|
407
421
|
newModelValue = newModelValue.filter((v) => v.value != option.value);
|
|
408
|
-
|
|
422
|
+
update(newModelValue);
|
|
409
423
|
};
|
|
410
424
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
'update:modelValue',
|
|
414
|
-
value.map((v) => v.option)
|
|
415
|
-
);
|
|
425
|
+
function update(value: NormalizedOption[]) {
|
|
426
|
+
emitUpdate(value.map((v) => v.option));
|
|
416
427
|
afterUpdate();
|
|
417
|
-
}
|
|
428
|
+
}
|
|
418
429
|
|
|
419
430
|
const beforeAddOption = () => {
|
|
420
431
|
selectionToDelete.value = null;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import BaseTagAutocompleteFetch from './BaseTagAutocompleteFetch.vue';
|
|
2
|
-
import
|
|
2
|
+
import ShowValue from '@/../.storybook/components/ShowValue.vue';
|
|
3
|
+
import { createFieldStory, options } from '../../.storybook/utils';
|
|
4
|
+
import BaseAppNotifications from './BaseAppNotifications.vue';
|
|
3
5
|
|
|
4
6
|
export default {
|
|
5
7
|
title: 'Form/BaseTagAutocompleteFetch',
|
|
@@ -9,25 +11,24 @@ export default {
|
|
|
9
11
|
url: 'https://effettandem.com/api/content/articles',
|
|
10
12
|
labelKey: 'title',
|
|
11
13
|
valueKey: 'id',
|
|
12
|
-
disabled: false,
|
|
13
14
|
},
|
|
14
15
|
decorators: [() => ({ template: '<div class="mb-36"><story/></div>' })],
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
const Template = (args) => {
|
|
18
19
|
return {
|
|
19
|
-
components: { BaseTagAutocompleteFetch,
|
|
20
|
+
components: { BaseTagAutocompleteFetch, ShowValue, BaseAppNotifications },
|
|
20
21
|
setup() {
|
|
21
22
|
const value = ref([]);
|
|
22
23
|
return { args, value };
|
|
23
24
|
},
|
|
24
25
|
template: `
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
<BaseTagAutocompleteFetch
|
|
27
|
+
v-model="value"
|
|
28
|
+
v-bind="args"
|
|
29
|
+
></BaseTagAutocompleteFetch>
|
|
30
|
+
<ShowValue :value="value" />
|
|
31
|
+
<BaseAppNotifications />
|
|
31
32
|
`,
|
|
32
33
|
};
|
|
33
34
|
};
|
|
@@ -35,12 +36,19 @@ const Template = (args) => {
|
|
|
35
36
|
export const Demo = Template.bind({});
|
|
36
37
|
Demo.args = {};
|
|
37
38
|
|
|
38
|
-
export const Disabled =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
export const Disabled = (args) => {
|
|
40
|
+
return {
|
|
41
|
+
components: { BaseTagAutocompleteFetch },
|
|
42
|
+
setup() {
|
|
43
|
+
const value = ref([]);
|
|
44
|
+
return { args, value };
|
|
45
|
+
},
|
|
46
|
+
template: `<BaseTagAutocompleteFetch
|
|
47
|
+
v-bind="args"
|
|
48
|
+
v-model="value"
|
|
49
|
+
:disabled="true"
|
|
50
|
+
></BaseTagAutocompleteFetch>`,
|
|
51
|
+
};
|
|
44
52
|
};
|
|
45
53
|
|
|
46
54
|
export const Maximum = Template.bind({});
|
|
@@ -50,7 +58,7 @@ Maximum.args = {
|
|
|
50
58
|
|
|
51
59
|
export const SlotOption = (args) => {
|
|
52
60
|
return {
|
|
53
|
-
components: {
|
|
61
|
+
components: {},
|
|
54
62
|
setup() {
|
|
55
63
|
const value = ref([]);
|
|
56
64
|
return { args, value };
|
|
@@ -83,11 +91,13 @@ export const SlotOption = (args) => {
|
|
|
83
91
|
|
|
84
92
|
export const SlotFooter = (args) => {
|
|
85
93
|
return {
|
|
86
|
-
components: {
|
|
94
|
+
components: {},
|
|
87
95
|
setup() {
|
|
88
96
|
const value = ref([]);
|
|
89
97
|
function onClick() {
|
|
90
|
-
|
|
98
|
+
setTimeout(() => {
|
|
99
|
+
alert(1);
|
|
100
|
+
}, 300);
|
|
91
101
|
}
|
|
92
102
|
return { args, value, onClick };
|
|
93
103
|
},
|
|
@@ -98,7 +108,7 @@ export const SlotFooter = (args) => {
|
|
|
98
108
|
>
|
|
99
109
|
<template #footer>
|
|
100
110
|
<div class="text-center p-2 border-t">
|
|
101
|
-
<button @click=onClick class="btn btn-sm w-full btn-slate-200-outline">This is the footer 💯</button>
|
|
111
|
+
<button type="button" @click=onClick class="btn btn-sm w-full btn-slate-200-outline">This is the footer 💯</button>
|
|
102
112
|
</div>
|
|
103
113
|
</template>
|
|
104
114
|
</BaseTagAutocompleteFetch>
|
|
@@ -108,7 +118,7 @@ export const SlotFooter = (args) => {
|
|
|
108
118
|
|
|
109
119
|
export const SlotEmpty = (args) => {
|
|
110
120
|
return {
|
|
111
|
-
components: {
|
|
121
|
+
components: {},
|
|
112
122
|
setup() {
|
|
113
123
|
const value = ref([]);
|
|
114
124
|
return { args, value };
|
|
@@ -121,10 +131,16 @@ export const SlotEmpty = (args) => {
|
|
|
121
131
|
<template #empty="props">
|
|
122
132
|
<div>
|
|
123
133
|
<div v-if="props.firstSearch" class="text-center py-10 p-6">🤓🤓🤓</div>
|
|
124
|
-
<div v-else class="text-center
|
|
134
|
+
<div v-else class="text-center px-6 py-20">Start your search... 🔎</div>
|
|
125
135
|
</div>
|
|
126
136
|
</template>
|
|
127
137
|
</BaseTagAutocompleteFetch>
|
|
128
138
|
`,
|
|
129
139
|
};
|
|
130
140
|
};
|
|
141
|
+
|
|
142
|
+
export const Field = createFieldStory({
|
|
143
|
+
component: BaseTagAutocompleteFetch,
|
|
144
|
+
componentName: 'BaseTagAutocompleteFetch',
|
|
145
|
+
label: 'Name',
|
|
146
|
+
});
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
:options="options"
|
|
8
8
|
:value-key="valueKey"
|
|
9
9
|
:label-key="labelKey"
|
|
10
|
-
:
|
|
10
|
+
:has-error="hasError"
|
|
11
11
|
:max="max"
|
|
12
12
|
:filter="() => true"
|
|
13
13
|
@focus="onFocus"
|
|
@@ -60,10 +60,6 @@ const props = defineProps({
|
|
|
60
60
|
required: true,
|
|
61
61
|
type: String,
|
|
62
62
|
},
|
|
63
|
-
inputClass: {
|
|
64
|
-
default: undefined,
|
|
65
|
-
type: String,
|
|
66
|
-
},
|
|
67
63
|
placeholder: {
|
|
68
64
|
default: undefined,
|
|
69
65
|
type: String,
|
|
@@ -84,6 +80,10 @@ const props = defineProps({
|
|
|
84
80
|
default: 'search',
|
|
85
81
|
type: String,
|
|
86
82
|
},
|
|
83
|
+
hasError: {
|
|
84
|
+
default: false,
|
|
85
|
+
type: Boolean,
|
|
86
|
+
},
|
|
87
87
|
});
|
|
88
88
|
|
|
89
89
|
defineEmits(['update:modelValue', 'typing', 'focus', 'scrollBottom']);
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
+
import { createFieldStory } from '../../.storybook/utils';
|
|
1
2
|
import BaseTextarea from './BaseTextarea.vue';
|
|
2
3
|
|
|
3
4
|
export default {
|
|
4
5
|
title: 'Form/BaseTextarea',
|
|
5
6
|
component: BaseTextarea,
|
|
6
7
|
args: {
|
|
7
|
-
name: 'bio',
|
|
8
|
-
required: true,
|
|
9
8
|
placeholder: 'Describe your complete life in 4 sentences...',
|
|
10
9
|
},
|
|
11
10
|
};
|
|
@@ -21,15 +20,24 @@ const Template = (args) => ({
|
|
|
21
20
|
template: `
|
|
22
21
|
<form @submit.prevent="" class="border-none">
|
|
23
22
|
<BaseTextarea v-model="value" v-bind="args" class="w-full"></BaseTextarea>
|
|
23
|
+
<button type="submit" class="btn btn-primary mt-4">Submit</button>
|
|
24
24
|
</form>
|
|
25
25
|
`,
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
export const Demo = Template.bind({});
|
|
29
|
-
Demo.args = {
|
|
29
|
+
Demo.args = {
|
|
30
|
+
required: true,
|
|
31
|
+
};
|
|
30
32
|
|
|
31
33
|
export const Disabled = Template.bind({});
|
|
32
34
|
Disabled.args = {
|
|
33
35
|
modelValue: 'Lorem ipsum...',
|
|
34
36
|
disabled: true,
|
|
35
37
|
};
|
|
38
|
+
|
|
39
|
+
export const Field = createFieldStory({
|
|
40
|
+
component: BaseTextarea,
|
|
41
|
+
componentName: 'BaseTextarea',
|
|
42
|
+
label: 'Biography',
|
|
43
|
+
});
|
|
@@ -2,21 +2,23 @@
|
|
|
2
2
|
<textarea
|
|
3
3
|
:value="modelValue"
|
|
4
4
|
:type="type"
|
|
5
|
-
:name="
|
|
5
|
+
:name="nameInternal"
|
|
6
6
|
:placeholder="placeholder"
|
|
7
7
|
:disabled="disabled"
|
|
8
|
-
:required="
|
|
8
|
+
:required="requiredInternal"
|
|
9
9
|
:rows="rows"
|
|
10
|
-
class="
|
|
11
|
-
|
|
10
|
+
:class="[hasErrorInternal ? 'border-red-500' : 'border-slate-300']"
|
|
11
|
+
class="mb-0 block rounded disabled:cursor-not-allowed disabled:text-slate-300"
|
|
12
|
+
@input="emitUpdate(transformInputValue($event))"
|
|
12
13
|
/>
|
|
13
14
|
</template>
|
|
14
15
|
|
|
15
16
|
<script lang="ts" setup>
|
|
17
|
+
import { useField } from '@/composables/field';
|
|
16
18
|
import { get, isString, trim } from 'lodash';
|
|
17
19
|
import { PropType } from 'vue';
|
|
18
20
|
|
|
19
|
-
defineProps({
|
|
21
|
+
const props = defineProps({
|
|
20
22
|
modelValue: {
|
|
21
23
|
default: undefined,
|
|
22
24
|
type: String as PropType<string | undefined>,
|
|
@@ -53,9 +55,21 @@ defineProps({
|
|
|
53
55
|
default: undefined,
|
|
54
56
|
type: Number,
|
|
55
57
|
},
|
|
58
|
+
hasError: {
|
|
59
|
+
default: false,
|
|
60
|
+
type: Boolean,
|
|
61
|
+
},
|
|
56
62
|
});
|
|
57
63
|
|
|
58
|
-
defineEmits(['update:modelValue']);
|
|
64
|
+
const emit = defineEmits(['update:modelValue']);
|
|
65
|
+
|
|
66
|
+
const { nameInternal, requiredInternal, hasErrorInternal, emitUpdate } =
|
|
67
|
+
useField({
|
|
68
|
+
name: computed(() => props.name),
|
|
69
|
+
required: computed(() => props.required),
|
|
70
|
+
hasError: computed(() => props.hasError),
|
|
71
|
+
emit: emit,
|
|
72
|
+
});
|
|
59
73
|
|
|
60
74
|
function transformInputValue(event: Event | null): string | null {
|
|
61
75
|
if (event === null) {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import BaseTextareaAutoresize from './BaseTextareaAutoresize.vue';
|
|
2
|
+
import ShowValue from '@/../.storybook/components/ShowValue.vue';
|
|
3
|
+
import { createFieldStory } from '../../.storybook/utils';
|
|
2
4
|
|
|
3
5
|
export default {
|
|
4
6
|
title: 'Form/BaseTextareaAutoresize',
|
|
@@ -12,12 +14,13 @@ export default {
|
|
|
12
14
|
const Template = (args) => ({
|
|
13
15
|
components: {
|
|
14
16
|
BaseTextareaAutoresize,
|
|
17
|
+
ShowValue,
|
|
15
18
|
},
|
|
16
19
|
setup() {
|
|
17
20
|
function onSubmit() {
|
|
18
21
|
alert('submit');
|
|
19
22
|
}
|
|
20
|
-
const value = ref(
|
|
23
|
+
const value = ref(null);
|
|
21
24
|
return { args, value, onSubmit };
|
|
22
25
|
},
|
|
23
26
|
template: `
|
|
@@ -30,7 +33,7 @@ const Template = (args) => ({
|
|
|
30
33
|
></BaseTextareaAutoresize>
|
|
31
34
|
</form>
|
|
32
35
|
|
|
33
|
-
<
|
|
36
|
+
<ShowValue :value="value" />
|
|
34
37
|
`,
|
|
35
38
|
});
|
|
36
39
|
|
|
@@ -47,3 +50,9 @@ Disabled.args = {
|
|
|
47
50
|
modelValue: 'Lorem ipsum...',
|
|
48
51
|
disabled: true,
|
|
49
52
|
};
|
|
53
|
+
|
|
54
|
+
export const Field = createFieldStory({
|
|
55
|
+
component: BaseTextareaAutoresize,
|
|
56
|
+
componentName: 'BaseTextareaAutoresize',
|
|
57
|
+
label: 'Biography',
|
|
58
|
+
});
|
|
@@ -6,11 +6,15 @@
|
|
|
6
6
|
>
|
|
7
7
|
<textarea
|
|
8
8
|
:value="modelValue"
|
|
9
|
-
:name="
|
|
9
|
+
:name="nameInternal"
|
|
10
10
|
:placeholder="placeholder"
|
|
11
11
|
:disabled="disabled"
|
|
12
|
+
:required="requiredInternal"
|
|
12
13
|
class="resize-none"
|
|
13
|
-
:class="
|
|
14
|
+
:class="[
|
|
15
|
+
BASE_TEXTAREA_CLASSES,
|
|
16
|
+
hasErrorInternal ? 'border-red-500' : 'border-slate-300',
|
|
17
|
+
]"
|
|
14
18
|
:style="{ maxHeight: maxHeight + 'px', gridArea: BASE_GRID_AREA }"
|
|
15
19
|
rows="1"
|
|
16
20
|
@input="onInput"
|
|
@@ -20,7 +24,10 @@
|
|
|
20
24
|
/>
|
|
21
25
|
<div
|
|
22
26
|
class="invisible whitespace-pre-wrap"
|
|
23
|
-
:class="
|
|
27
|
+
:class="[
|
|
28
|
+
BASE_TEXTAREA_CLASSES,
|
|
29
|
+
hasErrorInternal ? 'border-red-500' : 'border-slate-300',
|
|
30
|
+
]"
|
|
24
31
|
:style="{
|
|
25
32
|
content: DIV_CONTENT,
|
|
26
33
|
maxHeight: maxHeight + 'px',
|
|
@@ -33,10 +40,11 @@
|
|
|
33
40
|
</template>
|
|
34
41
|
|
|
35
42
|
<script lang="ts" setup>
|
|
43
|
+
import { useField } from '@/composables/field';
|
|
36
44
|
import { Ref } from 'vue';
|
|
37
45
|
|
|
38
46
|
const BASE_TEXTAREA_CLASSES =
|
|
39
|
-
'py-2 px-3 font-normal text-base disabled:cursor-not-allowed disabled:text-slate-300 font-sans rounded leading-normal tracking-normal border
|
|
47
|
+
'py-2 px-3 font-normal text-base disabled:cursor-not-allowed disabled:text-slate-300 font-sans rounded leading-normal tracking-normal border';
|
|
40
48
|
|
|
41
49
|
const BASE_GRID_AREA = '1 / 1 / 2 / 2';
|
|
42
50
|
|
|
@@ -56,6 +64,10 @@ const props = defineProps({
|
|
|
56
64
|
required: true,
|
|
57
65
|
type: String,
|
|
58
66
|
},
|
|
67
|
+
required: {
|
|
68
|
+
default: false,
|
|
69
|
+
type: Boolean,
|
|
70
|
+
},
|
|
59
71
|
maxHeight: {
|
|
60
72
|
default: 100,
|
|
61
73
|
type: Number,
|
|
@@ -72,10 +84,22 @@ const props = defineProps({
|
|
|
72
84
|
default: false,
|
|
73
85
|
type: Boolean,
|
|
74
86
|
},
|
|
87
|
+
hasError: {
|
|
88
|
+
default: false,
|
|
89
|
+
type: Boolean,
|
|
90
|
+
},
|
|
75
91
|
});
|
|
76
92
|
|
|
77
93
|
const emit = defineEmits(['update:modelValue', 'submit', 'focus', 'input']);
|
|
78
94
|
|
|
95
|
+
const { nameInternal, requiredInternal, hasErrorInternal, emitUpdate } =
|
|
96
|
+
useField({
|
|
97
|
+
name: computed(() => props.name),
|
|
98
|
+
required: computed(() => props.required),
|
|
99
|
+
hasError: computed(() => props.hasError),
|
|
100
|
+
emit: emit,
|
|
101
|
+
});
|
|
102
|
+
|
|
79
103
|
const wrapper = ref(null) as Ref<null | HTMLDivElement>;
|
|
80
104
|
|
|
81
105
|
const keys = {} as { [key: string]: boolean };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<BaseInputLabel
|
|
4
|
+
v-if="labelValue"
|
|
5
|
+
:label="labelValue"
|
|
6
|
+
:required="required"
|
|
7
|
+
/>
|
|
8
|
+
<BaseTextarea
|
|
9
|
+
:model-value="normalizedModelValue"
|
|
10
|
+
:type="type"
|
|
11
|
+
:name="name"
|
|
12
|
+
:placeholder="placeholder"
|
|
13
|
+
:disabled="disabled"
|
|
14
|
+
:required="required"
|
|
15
|
+
:rows="rows"
|
|
16
|
+
:class="[
|
|
17
|
+
{
|
|
18
|
+
'border-red-600': hasError(),
|
|
19
|
+
'border-slate-300': !hasError(),
|
|
20
|
+
},
|
|
21
|
+
]"
|
|
22
|
+
@update:model-value="inputListener($event)"
|
|
23
|
+
/>
|
|
24
|
+
<div class="mt-1 flex flex-wrap sm:space-x-3">
|
|
25
|
+
<BaseInputError v-if="hasError()" class="order-2 grow sm:order-1">
|
|
26
|
+
{{ errorMessage() }}
|
|
27
|
+
</BaseInputError>
|
|
28
|
+
<div
|
|
29
|
+
class="order-1 w-full grow text-left sm:order-2 sm:w-auto sm:text-right"
|
|
30
|
+
>
|
|
31
|
+
<BaseCharacterCounter
|
|
32
|
+
:text="normalizedModelValue"
|
|
33
|
+
:min="min"
|
|
34
|
+
:max="max"
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</template>
|
|
40
|
+
|
|
41
|
+
<script lang="ts">
|
|
42
|
+
import { defineComponent, PropType } from 'vue';
|
|
43
|
+
import BaseFormField from './BaseFormField';
|
|
44
|
+
import { get, isString, trim } from 'lodash';
|
|
45
|
+
import BaseTextarea from './BaseTextarea.vue';
|
|
46
|
+
import BaseInputLabel from './BaseInputLabel.vue';
|
|
47
|
+
import BaseCharacterCounter from './BaseCharacterCounter.vue';
|
|
48
|
+
import BaseInputError from './BaseInputError.vue';
|
|
49
|
+
|
|
50
|
+
export default defineComponent({
|
|
51
|
+
components: {
|
|
52
|
+
BaseTextarea,
|
|
53
|
+
BaseInputLabel,
|
|
54
|
+
BaseCharacterCounter,
|
|
55
|
+
BaseInputError,
|
|
56
|
+
},
|
|
57
|
+
extends: BaseFormField,
|
|
58
|
+
props: {
|
|
59
|
+
modelValue: {
|
|
60
|
+
required: true,
|
|
61
|
+
type: [String, null] as PropType<string | null>,
|
|
62
|
+
},
|
|
63
|
+
type: {
|
|
64
|
+
type: String,
|
|
65
|
+
default: 'text',
|
|
66
|
+
},
|
|
67
|
+
rows: {
|
|
68
|
+
default: 3,
|
|
69
|
+
type: Number,
|
|
70
|
+
},
|
|
71
|
+
min: {
|
|
72
|
+
default: undefined,
|
|
73
|
+
type: Number,
|
|
74
|
+
},
|
|
75
|
+
max: {
|
|
76
|
+
default: undefined,
|
|
77
|
+
type: Number,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
computed: {
|
|
81
|
+
normalizedModelValue(): string {
|
|
82
|
+
if (isString(this.modelValue)) {
|
|
83
|
+
return this.modelValue;
|
|
84
|
+
}
|
|
85
|
+
return '';
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
methods: {
|
|
89
|
+
transformInputValue(event: Event | null): string | null {
|
|
90
|
+
if (event === null) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const value = get(event, 'target.value', null);
|
|
94
|
+
if (!isString(value)) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
return trim(value as string);
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
</script>
|
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
]"
|
|
9
9
|
>
|
|
10
10
|
<BaseIcon
|
|
11
|
-
class="
|
|
11
|
+
class="h-5 w-5 text-white"
|
|
12
12
|
aria-hidden="true"
|
|
13
13
|
:icon="item.icon"
|
|
14
14
|
/>
|
|
15
15
|
</span>
|
|
16
16
|
</div>
|
|
17
17
|
<div
|
|
18
|
-
class="flex
|
|
18
|
+
class="flex min-w-0 flex-1 justify-between space-x-4"
|
|
19
19
|
:class="{ 'pt-1.5': !item.description }"
|
|
20
20
|
>
|
|
21
21
|
<div>
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
</div>
|
|
32
32
|
<div
|
|
33
33
|
v-if="item.date"
|
|
34
|
-
class="
|
|
34
|
+
class="whitespace-nowrap text-right text-sm text-slate-500"
|
|
35
35
|
>
|
|
36
36
|
<time :datetime="item.date">{{ item.date }}</time>
|
|
37
37
|
</div>
|
|
@@ -74,6 +74,6 @@ const iconBackgroundClass = computed((): string => {
|
|
|
74
74
|
if (props.item.color == Colors.success) {
|
|
75
75
|
return 'bg-green-500';
|
|
76
76
|
}
|
|
77
|
-
return 'bg-
|
|
77
|
+
return 'bg-slate-500';
|
|
78
78
|
});
|
|
79
79
|
</script>
|
package/src/components/index.ts
CHANGED
|
@@ -25,8 +25,11 @@ import BaseDateSelect from './BaseDateSelect.vue';
|
|
|
25
25
|
import BaseDescriptionList from './BaseDescriptionList.vue';
|
|
26
26
|
import BaseDescriptionListItem from './BaseDescriptionListItem.vue';
|
|
27
27
|
import BaseDialog from './BaseDialog.vue';
|
|
28
|
+
import BaseField from './BaseField.vue';
|
|
29
|
+
import BaseFieldI18n from './BaseFieldI18n.vue';
|
|
28
30
|
import BaseFilePicker from './BaseFilePicker.vue';
|
|
29
31
|
import BaseFileUploader from './BaseFileUploader.vue';
|
|
32
|
+
import BaseForm from './BaseForm.vue';
|
|
30
33
|
import BaseHasMany from './BaseHasMany.vue';
|
|
31
34
|
import { Icon as BaseIcon } from '@iconify/vue';
|
|
32
35
|
import BaseInput from './BaseInput.vue';
|
|
@@ -100,8 +103,11 @@ export {
|
|
|
100
103
|
BaseDescriptionList,
|
|
101
104
|
BaseDescriptionListItem,
|
|
102
105
|
BaseDialog,
|
|
106
|
+
BaseField,
|
|
107
|
+
BaseFieldI18n,
|
|
103
108
|
BaseFilePicker,
|
|
104
109
|
BaseFileUploader,
|
|
110
|
+
BaseForm,
|
|
105
111
|
BaseHasMany,
|
|
106
112
|
BaseIcon,
|
|
107
113
|
BaseInput,
|