sprintify-ui 0.0.40 → 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 +5 -2
- 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
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
ref="input"
|
|
24
24
|
:value="modelValue"
|
|
25
25
|
:type="type"
|
|
26
|
-
:name="
|
|
26
|
+
:name="nameInternal"
|
|
27
27
|
:step="step"
|
|
28
28
|
:min="min"
|
|
29
29
|
:max="max"
|
|
30
30
|
:disabled="disabled"
|
|
31
31
|
:placeholder="placeholder"
|
|
32
|
-
:required="
|
|
32
|
+
:required="requiredInternal"
|
|
33
33
|
class="w-full border-none bg-transparent outline-none focus:z-[1] focus:ring-2 focus:ring-primary-600 focus:ring-offset-1 disabled:cursor-not-allowed disabled:text-slate-300"
|
|
34
34
|
:class="{
|
|
35
35
|
'rounded-l': !iconLeft && !prefix,
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
}"
|
|
38
38
|
:autocomplete="autocomplete ? 'on' : 'off'"
|
|
39
39
|
@keydown.enter="onEnter"
|
|
40
|
-
@input="
|
|
40
|
+
@input="emitUpdate(transformInputValue($event))"
|
|
41
41
|
@focus="$emit('focus', $event)"
|
|
42
42
|
@blur="$emit('blur', $event)"
|
|
43
43
|
/>
|
|
@@ -66,7 +66,8 @@
|
|
|
66
66
|
<script lang="ts" setup>
|
|
67
67
|
import { get, isNumber, isString, trim } from 'lodash';
|
|
68
68
|
import { PropType } from 'vue';
|
|
69
|
-
import { BaseIcon } from '
|
|
69
|
+
import { BaseIcon } from '@/index';
|
|
70
|
+
import { useField } from '@/composables/field';
|
|
70
71
|
|
|
71
72
|
const props = defineProps({
|
|
72
73
|
modelValue: {
|
|
@@ -138,7 +139,15 @@ const props = defineProps({
|
|
|
138
139
|
},
|
|
139
140
|
});
|
|
140
141
|
|
|
141
|
-
defineEmits(['update:modelValue', 'focus', 'blur']);
|
|
142
|
+
const emit = defineEmits(['update:modelValue', 'focus', 'blur']);
|
|
143
|
+
|
|
144
|
+
const { nameInternal, requiredInternal, hasErrorInternal, emitUpdate } =
|
|
145
|
+
useField({
|
|
146
|
+
name: computed(() => props.name),
|
|
147
|
+
required: computed(() => props.required),
|
|
148
|
+
hasError: computed(() => props.hasError),
|
|
149
|
+
emit: emit,
|
|
150
|
+
});
|
|
142
151
|
|
|
143
152
|
function transformInputValue(event: Event | null): string | null {
|
|
144
153
|
if (event === null) {
|
|
@@ -166,14 +175,14 @@ function onEnter(e: Event) {
|
|
|
166
175
|
}
|
|
167
176
|
|
|
168
177
|
const borderColor = computed(() => {
|
|
169
|
-
return
|
|
178
|
+
return hasErrorInternal.value ? 'border-red-500' : 'border-slate-300';
|
|
170
179
|
});
|
|
171
180
|
|
|
172
181
|
const backgroundColor = computed(() => {
|
|
173
|
-
return
|
|
182
|
+
return hasErrorInternal.value ? 'bg-red-100' : 'bg-slate-100';
|
|
174
183
|
});
|
|
175
184
|
|
|
176
185
|
const textColor = computed(() => {
|
|
177
|
-
return
|
|
186
|
+
return hasErrorInternal.value ? 'text-red-800' : 'text-slate-600';
|
|
178
187
|
});
|
|
179
188
|
</script>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import BaseInputPercent from './BaseInputPercent.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/BaseInputPercent',
|
|
5
7
|
component: BaseInputPercent,
|
|
6
8
|
args: {
|
|
7
|
-
required: true,
|
|
8
|
-
name: 'rebate',
|
|
9
9
|
placeholder: 'Enter rebate, eg: 50%',
|
|
10
10
|
step: 0.1,
|
|
11
11
|
min: 0,
|
|
@@ -15,6 +15,7 @@ export default {
|
|
|
15
15
|
|
|
16
16
|
const Template = (args) => ({
|
|
17
17
|
components: {
|
|
18
|
+
ShowValue,
|
|
18
19
|
BaseInputPercent,
|
|
19
20
|
},
|
|
20
21
|
setup() {
|
|
@@ -25,7 +26,7 @@ const Template = (args) => ({
|
|
|
25
26
|
<form @submit.prevent="" class="border-none">
|
|
26
27
|
<BaseInputPercent v-model="value" v-bind="args" class="w-full"></BaseInputPercent>
|
|
27
28
|
</form>
|
|
28
|
-
<
|
|
29
|
+
<ShowValue :value="value" />
|
|
29
30
|
`,
|
|
30
31
|
});
|
|
31
32
|
|
|
@@ -41,3 +42,9 @@ export const Error = Template.bind({});
|
|
|
41
42
|
Error.args = {
|
|
42
43
|
hasError: true,
|
|
43
44
|
};
|
|
45
|
+
|
|
46
|
+
export const Field = createFieldStory({
|
|
47
|
+
component: BaseInputPercent,
|
|
48
|
+
componentName: 'BaseInputPercent',
|
|
49
|
+
label: 'Rebate',
|
|
50
|
+
});
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
:model-value="value"
|
|
4
4
|
suffix="%"
|
|
5
5
|
type="number"
|
|
6
|
+
:required="required"
|
|
6
7
|
:prevent-submit="preventSubmit"
|
|
7
8
|
:name="name"
|
|
8
9
|
:step="step"
|
|
@@ -19,7 +20,7 @@
|
|
|
19
20
|
<script lang="ts" setup>
|
|
20
21
|
import { round } from 'lodash';
|
|
21
22
|
import { PropType } from 'vue';
|
|
22
|
-
import
|
|
23
|
+
import BaseInput from './BaseInput.vue';
|
|
23
24
|
|
|
24
25
|
const props = defineProps({
|
|
25
26
|
/**
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="">
|
|
3
|
+
<BaseInputLabel
|
|
4
|
+
v-if="label"
|
|
5
|
+
:label="label"
|
|
6
|
+
classes="form-input-label mb-2"
|
|
7
|
+
:required="required"
|
|
8
|
+
/>
|
|
9
|
+
<div class="space-y-1">
|
|
10
|
+
<div v-for="(locale, key, index) in localesInternal" :key="key">
|
|
11
|
+
<label
|
|
12
|
+
:for="name"
|
|
13
|
+
:class="[
|
|
14
|
+
layout == 'header'
|
|
15
|
+
? 'border-b-none flex items-center rounded-t border border-slate-300 bg-slate-50 py-1.5 px-2 text-sm text-slate-700'
|
|
16
|
+
: 'mb-1.5 flex items-center text-sm text-slate-700',
|
|
17
|
+
]"
|
|
18
|
+
>
|
|
19
|
+
<div
|
|
20
|
+
v-if="layout == 'header'"
|
|
21
|
+
class="mr-2 h-2.5 w-2.5 rounded-full"
|
|
22
|
+
:style="{ backgroundColor: colors[index] }"
|
|
23
|
+
/>
|
|
24
|
+
<Icon
|
|
25
|
+
v-if="layout == 'header'"
|
|
26
|
+
icon="heroicons:language-solid"
|
|
27
|
+
class="mr-1"
|
|
28
|
+
/>
|
|
29
|
+
<span>{{ locale }}</span>
|
|
30
|
+
</label>
|
|
31
|
+
<component
|
|
32
|
+
:is="component"
|
|
33
|
+
:model-value="formattedValue[key]"
|
|
34
|
+
:name="`${name}.${key}`"
|
|
35
|
+
:required="required"
|
|
36
|
+
v-bind="props"
|
|
37
|
+
:placeholder="placeholder"
|
|
38
|
+
:input-class="inputClass + ' border-t-0 rounded-t-none w-full'"
|
|
39
|
+
@update:model-value="onInput($event, key + '')"
|
|
40
|
+
/>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
<script lang="ts">
|
|
47
|
+
import { defineComponent, PropType } from 'vue';
|
|
48
|
+
import { get, isPlainObject } from 'lodash';
|
|
49
|
+
import objectHash from 'object-hash';
|
|
50
|
+
import { Locales } from '@/types';
|
|
51
|
+
import { config } from '@/index';
|
|
52
|
+
import BaseFormField from './BaseFormField';
|
|
53
|
+
import BaseInputLabel from './BaseInputLabel.vue';
|
|
54
|
+
|
|
55
|
+
type Value = { [locale: string]: string | number | boolean };
|
|
56
|
+
|
|
57
|
+
export default defineComponent({
|
|
58
|
+
components: { BaseInputLabel },
|
|
59
|
+
extends: BaseFormField,
|
|
60
|
+
props: {
|
|
61
|
+
modelValue: {
|
|
62
|
+
required: true,
|
|
63
|
+
type: [Object, null, undefined] as PropType<Value | null | undefined>,
|
|
64
|
+
},
|
|
65
|
+
locales: {
|
|
66
|
+
default: undefined,
|
|
67
|
+
type: Object as PropType<Locales>,
|
|
68
|
+
},
|
|
69
|
+
component: {
|
|
70
|
+
default: 'VInput',
|
|
71
|
+
type: String,
|
|
72
|
+
},
|
|
73
|
+
props: {
|
|
74
|
+
default: undefined,
|
|
75
|
+
type: Object,
|
|
76
|
+
},
|
|
77
|
+
defaultValue: {
|
|
78
|
+
default: '',
|
|
79
|
+
type: [String, Boolean, Number] as PropType<string | boolean | number>,
|
|
80
|
+
},
|
|
81
|
+
layout: {
|
|
82
|
+
default: 'header',
|
|
83
|
+
type: String,
|
|
84
|
+
},
|
|
85
|
+
inputClass: {
|
|
86
|
+
default: undefined,
|
|
87
|
+
type: String,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
data() {
|
|
91
|
+
return {
|
|
92
|
+
colors: ['#10b981', '#06b6d4', '#a855f7'],
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
computed: {
|
|
96
|
+
localesInternal(): Locales {
|
|
97
|
+
if (this.locales) {
|
|
98
|
+
return this.locales;
|
|
99
|
+
}
|
|
100
|
+
return config.locales;
|
|
101
|
+
},
|
|
102
|
+
formattedValue(): Value {
|
|
103
|
+
// Get current value
|
|
104
|
+
let value = {} as Value;
|
|
105
|
+
if (this.modelValue && isPlainObject(this.modelValue)) {
|
|
106
|
+
value = this.modelValue as Value;
|
|
107
|
+
}
|
|
108
|
+
// Fill missing locales
|
|
109
|
+
Object.keys(this.localesInternal).forEach((locale) => {
|
|
110
|
+
const currentValue = get(this.modelValue, locale);
|
|
111
|
+
if (!currentValue) {
|
|
112
|
+
value[locale] = this.defaultValue;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
return value;
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
created() {
|
|
119
|
+
// If formatted value is different, send event to parent
|
|
120
|
+
if (
|
|
121
|
+
!this.modelValue ||
|
|
122
|
+
objectHash(this.formattedValue) !== objectHash(this.modelValue)
|
|
123
|
+
) {
|
|
124
|
+
const formattedValue = this.formattedValue;
|
|
125
|
+
this.inputListener(formattedValue);
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
methods: {
|
|
129
|
+
getLabel(locale: string): string {
|
|
130
|
+
if (Object.keys(this.localesInternal).length > 1) {
|
|
131
|
+
return `${this.label} (${locale})`;
|
|
132
|
+
}
|
|
133
|
+
return this.label;
|
|
134
|
+
},
|
|
135
|
+
onInput(value: string, locale: string) {
|
|
136
|
+
const formattedValue = this.formattedValue;
|
|
137
|
+
formattedValue[locale] = value;
|
|
138
|
+
this.inputListener(formattedValue);
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
</script>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import BaseApp from './BaseApp.vue';
|
|
2
2
|
import BaseMediaLibrary from './BaseMediaLibrary.vue';
|
|
3
|
+
import { createFieldStory } from '@/../.storybook/utils';
|
|
3
4
|
|
|
4
5
|
const mediaModel = {
|
|
5
6
|
id: 'xxxxx',
|
|
@@ -13,10 +14,10 @@ export default {
|
|
|
13
14
|
title: 'Form/BaseMediaLibrary',
|
|
14
15
|
component: BaseMediaLibrary,
|
|
15
16
|
args: {
|
|
16
|
-
|
|
17
|
-
max: 2,
|
|
17
|
+
max: 6,
|
|
18
18
|
min: 2,
|
|
19
19
|
acceptedExtensions: ['jpg', 'png'],
|
|
20
|
+
uploadUrl: 'https://api.com/upload',
|
|
20
21
|
maxSize: 500 * 1024,
|
|
21
22
|
currentMedia: [
|
|
22
23
|
mediaModel,
|
|
@@ -74,7 +75,7 @@ Disabled.args = {
|
|
|
74
75
|
disabled: true,
|
|
75
76
|
};
|
|
76
77
|
|
|
77
|
-
export const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
};
|
|
78
|
+
export const Field = createFieldStory({
|
|
79
|
+
component: BaseMediaLibrary,
|
|
80
|
+
componentName: 'BaseMediaLibrary',
|
|
81
|
+
});
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
button-class="w-full"
|
|
8
8
|
:accept="accept"
|
|
9
9
|
:accepted-extensions="acceptedExtensions"
|
|
10
|
+
:url="uploadUrl"
|
|
10
11
|
@upload:start="$emit('upload:start', $event)"
|
|
11
12
|
@upload:end="$emit('upload:end', $event)"
|
|
12
13
|
@upload:fail="$emit('upload:fail', $event)"
|
|
@@ -49,10 +50,6 @@
|
|
|
49
50
|
</template>
|
|
50
51
|
</BaseFileUploader>
|
|
51
52
|
|
|
52
|
-
<BaseAlert v-if="globalErrorMessage" class="mt-5" color="danger" bordered>
|
|
53
|
-
{{ globalErrorMessage }}
|
|
54
|
-
</BaseAlert>
|
|
55
|
-
|
|
56
53
|
<div
|
|
57
54
|
v-if="currentMediaInternal.length + normalizedModelValue.to_add.length"
|
|
58
55
|
class="mt-5"
|
|
@@ -91,39 +88,30 @@
|
|
|
91
88
|
</template>
|
|
92
89
|
|
|
93
90
|
<script lang="ts" setup>
|
|
91
|
+
import { PropType } from 'vue';
|
|
92
|
+
import { cloneDeep, isArray, isObject, capitalize } from 'lodash';
|
|
94
93
|
import { UploadedFile } from '@/types/UploadedFile';
|
|
95
94
|
import { Media } from '@/types/Media';
|
|
96
|
-
import { cloneDeep, isArray, isObject } from 'lodash';
|
|
97
|
-
import { PropType } from 'vue';
|
|
98
95
|
import { MediaLibraryPayload } from '@/types';
|
|
99
|
-
import { useDialogsStore } from '../stores/dialogs';
|
|
100
|
-
import { useNotificationsStore } from '../stores/notifications';
|
|
101
|
-
import { capitalize } from 'lodash';
|
|
102
|
-
import BaseFileUploader from './BaseFileUploader.vue';
|
|
103
|
-
import BaseMediaItem from './BaseMediaItem.vue';
|
|
104
96
|
import { fileSizeFormat } from '@/utils';
|
|
105
|
-
import
|
|
97
|
+
import { useDialogsStore } from '@/stores/dialogs';
|
|
98
|
+
import { useNotificationsStore } from '@/stores/notifications';
|
|
99
|
+
import BaseMediaItem from '@/components/BaseMediaItem.vue';
|
|
100
|
+
import BaseFileUploader from './BaseFileUploader.vue';
|
|
101
|
+
import { useField } from '@/composables/field';
|
|
106
102
|
|
|
107
103
|
const i18n = useI18n();
|
|
108
104
|
|
|
109
105
|
const dialogs = useDialogsStore();
|
|
110
106
|
const notifications = useNotificationsStore();
|
|
111
107
|
|
|
112
|
-
const emit = defineEmits([
|
|
113
|
-
'update',
|
|
114
|
-
'upload:start',
|
|
115
|
-
'upload:success',
|
|
116
|
-
'upload:fail',
|
|
117
|
-
'upload:end',
|
|
118
|
-
]);
|
|
119
|
-
|
|
120
108
|
const props = defineProps({
|
|
121
109
|
modelValue: {
|
|
122
110
|
default: undefined,
|
|
123
111
|
type: Object as PropType<MediaLibraryPayload | null | undefined>,
|
|
124
112
|
},
|
|
125
113
|
name: {
|
|
126
|
-
|
|
114
|
+
default: undefined,
|
|
127
115
|
type: String,
|
|
128
116
|
},
|
|
129
117
|
min: {
|
|
@@ -152,9 +140,13 @@ const props = defineProps({
|
|
|
152
140
|
},
|
|
153
141
|
type: Array as PropType<Media[]>,
|
|
154
142
|
},
|
|
155
|
-
|
|
143
|
+
uploadUrl: {
|
|
156
144
|
default: undefined,
|
|
157
|
-
type:
|
|
145
|
+
type: String,
|
|
146
|
+
},
|
|
147
|
+
hasError: {
|
|
148
|
+
default: false,
|
|
149
|
+
type: Boolean,
|
|
158
150
|
},
|
|
159
151
|
disabled: {
|
|
160
152
|
default: false,
|
|
@@ -162,6 +154,22 @@ const props = defineProps({
|
|
|
162
154
|
},
|
|
163
155
|
});
|
|
164
156
|
|
|
157
|
+
const emit = defineEmits([
|
|
158
|
+
'update:modelValue',
|
|
159
|
+
'upload:start',
|
|
160
|
+
'upload:success',
|
|
161
|
+
'upload:fail',
|
|
162
|
+
'upload:end',
|
|
163
|
+
]);
|
|
164
|
+
|
|
165
|
+
const { emitUpdate } = useField({
|
|
166
|
+
name: computed(() => props.name),
|
|
167
|
+
required: computed(() => false),
|
|
168
|
+
hasError: computed(() => props.hasError),
|
|
169
|
+
emit: emit,
|
|
170
|
+
errorType: 'alert',
|
|
171
|
+
});
|
|
172
|
+
|
|
165
173
|
const currentMediaInternal = ref(cloneDeep(props.currentMedia));
|
|
166
174
|
|
|
167
175
|
const normalizedModelValue = computed(() => {
|
|
@@ -265,16 +273,9 @@ function removeMedia(index: number) {
|
|
|
265
273
|
}
|
|
266
274
|
|
|
267
275
|
function sync(modelValue: MediaLibraryPayload) {
|
|
268
|
-
|
|
276
|
+
emitUpdate(modelValue);
|
|
269
277
|
}
|
|
270
278
|
|
|
271
|
-
const globalErrorMessage = computed(() => {
|
|
272
|
-
if (props.errors && props.errors.length) {
|
|
273
|
-
return props.errors[0];
|
|
274
|
-
}
|
|
275
|
-
return '';
|
|
276
|
-
});
|
|
277
|
-
|
|
278
279
|
const maxFileText = computed(() => {
|
|
279
280
|
return i18n.t('sui.you_can_upload_up_to_n_files', { count: props.max });
|
|
280
281
|
});
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
>
|
|
26
26
|
<slot name="items" :items="items">
|
|
27
27
|
<template v-for="item in items" :key="item.label + 'link'">
|
|
28
|
-
<div v-if="item.line" class="my-1 -mx-1 flex h-px bg-
|
|
28
|
+
<div v-if="item.line" class="my-1 -mx-1 flex h-px bg-slate-200" />
|
|
29
29
|
|
|
30
30
|
<router-link
|
|
31
31
|
v-else-if="item.to"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<BaseInputLabel
|
|
4
|
+
v-if="labelValue"
|
|
5
|
+
:label="labelValue"
|
|
6
|
+
:required="required"
|
|
7
|
+
/>
|
|
8
|
+
<input
|
|
9
|
+
:value="modelValue"
|
|
10
|
+
type="number"
|
|
11
|
+
:name="name"
|
|
12
|
+
:min="min"
|
|
13
|
+
:max="max"
|
|
14
|
+
:step="step"
|
|
15
|
+
:disabled="disabled"
|
|
16
|
+
@input="inputListener(transformInputValue($event))"
|
|
17
|
+
/>
|
|
18
|
+
<BaseInputError v-if="hasError()" class="mt-1">
|
|
19
|
+
{{ errorMessage() }}
|
|
20
|
+
</BaseInputError>
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script lang="ts">
|
|
25
|
+
import { get, isNumber } from 'lodash';
|
|
26
|
+
import { defineComponent } from 'vue';
|
|
27
|
+
import BaseFormField from './BaseFormField';
|
|
28
|
+
import BaseInputError from './BaseInputError.vue';
|
|
29
|
+
import BaseInputLabel from './BaseInputLabel.vue';
|
|
30
|
+
|
|
31
|
+
export default defineComponent({
|
|
32
|
+
components: { BaseInputLabel, BaseInputError },
|
|
33
|
+
extends: BaseFormField,
|
|
34
|
+
props: {
|
|
35
|
+
modelValue: {
|
|
36
|
+
required: true,
|
|
37
|
+
validator: (value) => {
|
|
38
|
+
return isNumber(value) || value === null;
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
step: {
|
|
42
|
+
default: 1,
|
|
43
|
+
type: Number,
|
|
44
|
+
},
|
|
45
|
+
min: {
|
|
46
|
+
type: Number,
|
|
47
|
+
default: undefined,
|
|
48
|
+
},
|
|
49
|
+
max: {
|
|
50
|
+
type: Number,
|
|
51
|
+
default: undefined,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
methods: {
|
|
55
|
+
transformInputValue(event: Event | null): number | null {
|
|
56
|
+
if (event === null) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const value = get(event, 'target.value', null);
|
|
60
|
+
if (!isNumber(value)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
return value as number;
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
</script>
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import BasePassword from './BasePassword.vue';
|
|
2
|
+
import { createFieldStory } from '@/../.storybook/utils';
|
|
2
3
|
|
|
3
4
|
export default {
|
|
4
5
|
title: 'Form/BasePassword',
|
|
5
6
|
component: BasePassword,
|
|
6
|
-
args: {
|
|
7
|
-
required: true,
|
|
8
|
-
name: 'password',
|
|
9
|
-
},
|
|
7
|
+
args: {},
|
|
10
8
|
};
|
|
11
9
|
|
|
12
10
|
const Template = (args) => ({
|
|
@@ -26,6 +24,7 @@ const Template = (args) => ({
|
|
|
26
24
|
|
|
27
25
|
export const Demo = Template.bind({});
|
|
28
26
|
Demo.args = {
|
|
27
|
+
required: true,
|
|
29
28
|
placeholder: 'Enter your password',
|
|
30
29
|
};
|
|
31
30
|
|
|
@@ -34,3 +33,9 @@ Disabled.args = {
|
|
|
34
33
|
disabled: true,
|
|
35
34
|
placeholder: 'Enter your password',
|
|
36
35
|
};
|
|
36
|
+
|
|
37
|
+
export const Field = createFieldStory({
|
|
38
|
+
component: BasePassword,
|
|
39
|
+
componentName: 'BasePassword',
|
|
40
|
+
label: 'Password',
|
|
41
|
+
});
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
class="flex rounded border
|
|
4
|
-
:class="[
|
|
3
|
+
class="flex rounded border bg-white"
|
|
4
|
+
:class="[
|
|
5
|
+
disabled ? 'cursor-not-allowed text-slate-300' : '',
|
|
6
|
+
hasErrorInternal ? 'border-red-500' : 'border-slate-300',
|
|
7
|
+
]"
|
|
5
8
|
>
|
|
6
9
|
<input
|
|
7
10
|
ref="input"
|
|
8
11
|
:value="modelValue"
|
|
9
12
|
:type="showPassword ? 'text' : 'password'"
|
|
10
|
-
:name="
|
|
13
|
+
:name="nameInternal"
|
|
11
14
|
:disabled="disabled"
|
|
12
15
|
:placeholder="placeholder"
|
|
13
|
-
:required="
|
|
16
|
+
:required="requiredInternal"
|
|
14
17
|
class="grow rounded-l rounded-r-none border-none focus:ring-2 focus:ring-primary-500 disabled:cursor-not-allowed"
|
|
15
|
-
:class="inputClass"
|
|
16
18
|
@input="onInput"
|
|
17
19
|
/>
|
|
18
20
|
<div class="flex shrink-0 pl-3">
|
|
@@ -34,50 +36,53 @@
|
|
|
34
36
|
</div>
|
|
35
37
|
</template>
|
|
36
38
|
|
|
37
|
-
<script lang="ts">
|
|
39
|
+
<script lang="ts" setup>
|
|
38
40
|
import { trim } from 'lodash';
|
|
39
|
-
import { defineComponent, PropType } from 'vue';
|
|
40
41
|
import { Icon as BaseIcon } from '@iconify/vue';
|
|
42
|
+
import { PropType } from 'vue';
|
|
43
|
+
import { useField } from '@/composables/field';
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
default: '',
|
|
47
|
-
type: [String, null] as PropType<string | null>,
|
|
48
|
-
},
|
|
49
|
-
disabled: {
|
|
50
|
-
default: false,
|
|
51
|
-
type: Boolean,
|
|
52
|
-
},
|
|
53
|
-
name: {
|
|
54
|
-
default: undefined,
|
|
55
|
-
type: String,
|
|
56
|
-
},
|
|
57
|
-
placeholder: {
|
|
58
|
-
default: undefined,
|
|
59
|
-
type: String,
|
|
60
|
-
},
|
|
61
|
-
required: {
|
|
62
|
-
default: undefined,
|
|
63
|
-
type: Boolean,
|
|
64
|
-
},
|
|
65
|
-
inputClass: {
|
|
66
|
-
default: '',
|
|
67
|
-
type: String,
|
|
68
|
-
},
|
|
45
|
+
const props = defineProps({
|
|
46
|
+
modelValue: {
|
|
47
|
+
default: '',
|
|
48
|
+
type: [String, null] as PropType<string | null>,
|
|
69
49
|
},
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
showPassword: false,
|
|
74
|
-
};
|
|
50
|
+
disabled: {
|
|
51
|
+
default: false,
|
|
52
|
+
type: Boolean,
|
|
75
53
|
},
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
54
|
+
name: {
|
|
55
|
+
default: undefined,
|
|
56
|
+
type: String,
|
|
57
|
+
},
|
|
58
|
+
placeholder: {
|
|
59
|
+
default: undefined,
|
|
60
|
+
type: String,
|
|
61
|
+
},
|
|
62
|
+
required: {
|
|
63
|
+
default: false,
|
|
64
|
+
type: Boolean,
|
|
65
|
+
},
|
|
66
|
+
hasError: {
|
|
67
|
+
default: false,
|
|
68
|
+
type: Boolean,
|
|
81
69
|
},
|
|
82
70
|
});
|
|
71
|
+
|
|
72
|
+
const emit = defineEmits(['update:modelValue']);
|
|
73
|
+
|
|
74
|
+
const { nameInternal, requiredInternal, hasErrorInternal, emitUpdate } =
|
|
75
|
+
useField({
|
|
76
|
+
name: computed(() => props.name),
|
|
77
|
+
required: computed(() => props.required),
|
|
78
|
+
hasError: computed(() => props.hasError),
|
|
79
|
+
emit: emit,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const showPassword = ref(false);
|
|
83
|
+
|
|
84
|
+
function onInput(event: any) {
|
|
85
|
+
const value = event.target.value + '';
|
|
86
|
+
emitUpdate(trim(value));
|
|
87
|
+
}
|
|
83
88
|
</script>
|