sprintify-ui 0.6.33 → 0.6.35
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 +12984 -12888
- package/dist/style.css +1 -1
- package/dist/tailwindcss/button.js +3 -2
- package/dist/tailwindcss/theme.js +14 -7
- package/dist/types/src/components/BaseButtonGroup.vue.d.ts +9 -0
- package/dist/types/src/components/BaseDataIterator.vue.d.ts +3 -3
- package/dist/types/src/components/BaseDataIteratorSectionButton.vue.d.ts +2 -2
- package/dist/types/src/components/BaseDataIteratorSectionColumns.vue.d.ts +8 -8
- package/dist/types/src/components/BaseDataTable.vue.d.ts +3 -3
- package/dist/types/src/components/BaseDataTableRowAction.vue.d.ts +4 -3
- package/dist/types/src/components/BaseDatePicker.vue.d.ts +10 -0
- package/dist/types/src/components/BaseDateSelect.vue.d.ts +9 -0
- package/dist/types/src/components/BaseInput.vue.d.ts +18 -0
- package/dist/types/src/components/BasePassword.vue.d.ts +13 -0
- package/dist/types/src/components/BaseRichText.vue.d.ts +9 -0
- package/dist/types/src/components/BaseTable.vue.d.ts +3 -3
- package/dist/types/src/components/BaseTagAutocomplete.vue.d.ts +11 -2
- package/dist/types/src/components/BaseTextarea.vue.d.ts +18 -0
- package/dist/types/src/components/BaseTextareaAutoresize.vue.d.ts +12 -2
- package/dist/types/src/stories/PageInputSizes.vue.d.ts +2 -0
- package/dist/types/src/utils/slots.d.ts +1 -0
- package/package.json +1 -1
- package/src/assets/base-rich-text.css +148 -26
- package/src/components/BaseButton.vue +3 -2
- package/src/components/BaseButtonGroup.vue +37 -9
- package/src/components/BaseColor.vue +29 -31
- package/src/components/BaseDataIterator.stories.js +8 -1
- package/src/components/BaseDataIterator.vue +36 -76
- package/src/components/BaseDataIteratorSectionButton.vue +8 -19
- package/src/components/BaseDataTable.vue +25 -18
- package/src/components/BaseDataTableRowAction.vue +8 -7
- package/src/components/BaseDatePicker.stories.js +23 -0
- package/src/components/BaseDatePicker.vue +71 -11
- package/src/components/BaseDateSelect.stories.js +25 -1
- package/src/components/BaseDateSelect.vue +80 -101
- package/src/components/BaseDraggable.vue +5 -1
- package/src/components/BaseDropdownAutocomplete.stories.js +30 -15
- package/src/components/BaseHasMany.stories.js +22 -1
- package/src/components/BaseInput.stories.js +4 -4
- package/src/components/BaseInput.vue +61 -15
- package/src/components/BaseMediaPicturesItem.vue +2 -2
- package/src/components/BasePassword.stories.js +25 -0
- package/src/components/BasePassword.vue +35 -55
- package/src/components/BaseRichText.stories.js +6 -0
- package/src/components/BaseRichText.vue +12 -2
- package/src/components/BaseSelect.vue +5 -0
- package/src/components/BaseTable.vue +2 -1
- package/src/components/BaseTagAutocomplete.stories.js +1 -1
- package/src/components/BaseTagAutocomplete.vue +143 -88
- package/src/components/BaseTagAutocompleteFetch.stories.js +22 -1
- package/src/components/BaseTextarea.stories.js +25 -0
- package/src/components/BaseTextarea.vue +34 -3
- package/src/components/BaseTextareaAutoresize.stories.js +27 -2
- package/src/components/BaseTextareaAutoresize.vue +28 -9
- package/src/composables/inputSize.ts +5 -1
- package/src/stories/InputSizes.stories.js +22 -0
- package/src/stories/PageInputSizes.vue +205 -0
- package/src/utils/slots.ts +13 -0
|
@@ -2,10 +2,17 @@ import BaseDateSelect from './BaseDateSelect.vue';
|
|
|
2
2
|
import ShowValue from '@/../.storybook/components/ShowValue.vue';
|
|
3
3
|
import { createFieldStory } from '../../.storybook/utils';
|
|
4
4
|
|
|
5
|
+
const sizes = ['xs', 'sm', 'md'];
|
|
6
|
+
|
|
5
7
|
export default {
|
|
6
8
|
title: 'Form/BaseDateSelect',
|
|
7
9
|
component: BaseDateSelect,
|
|
8
|
-
argTypes: {
|
|
10
|
+
argTypes: {
|
|
11
|
+
size: {
|
|
12
|
+
control: { type: 'select' },
|
|
13
|
+
options: sizes,
|
|
14
|
+
}
|
|
15
|
+
},
|
|
9
16
|
};
|
|
10
17
|
|
|
11
18
|
const Template = (args) => ({
|
|
@@ -40,6 +47,23 @@ Disabled.args = {
|
|
|
40
47
|
disabled: true,
|
|
41
48
|
};
|
|
42
49
|
|
|
50
|
+
const TemplateSizes = (args) => ({
|
|
51
|
+
components: { BaseDateSelect },
|
|
52
|
+
setup() {
|
|
53
|
+
const value = ref(null);
|
|
54
|
+
const sizes = ['xs', 'sm', 'md'];
|
|
55
|
+
return { args, value, sizes };
|
|
56
|
+
},
|
|
57
|
+
template: `
|
|
58
|
+
<div v-for="size in sizes" :key="size" class="mb-4">
|
|
59
|
+
<p class="text-xs text-slate-600 leading-tight mb-1">{{ size }}</p>
|
|
60
|
+
<BaseDateSelect v-model="value" v-bind="args" :size="size" class="w-full"></BaseDateSelect>
|
|
61
|
+
</div>
|
|
62
|
+
`,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export const Sizes = TemplateSizes.bind({});
|
|
66
|
+
|
|
43
67
|
export const Field = createFieldStory({
|
|
44
68
|
component: BaseDateSelect,
|
|
45
69
|
componentName: 'BaseDateSelect',
|
|
@@ -1,114 +1,82 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
|
-
<div
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
3
|
+
<div
|
|
4
|
+
class="flex"
|
|
5
|
+
:class="[
|
|
6
|
+
sizeInternal === 'xs' ? 'gap-0.5' : '',
|
|
7
|
+
sizeInternal === 'sm' ? 'gap-0.5' : '',
|
|
8
|
+
sizeInternal === 'md' ? 'gap-1' : '',
|
|
9
|
+
]"
|
|
10
|
+
>
|
|
11
|
+
<BaseSelect
|
|
12
|
+
v-model="date.year"
|
|
13
|
+
:disabled="disabled"
|
|
14
|
+
:required="requiredInternal"
|
|
15
|
+
data-cy="year"
|
|
16
|
+
:has-error="hasErrorInternal"
|
|
17
|
+
:placeholder="t('sui.year')"
|
|
18
|
+
:size="sizeInternal"
|
|
19
|
+
@update:model-value="update()"
|
|
20
|
+
>
|
|
21
|
+
<option
|
|
22
|
+
v-for="year in years"
|
|
23
|
+
:key="year"
|
|
24
|
+
:value="year"
|
|
19
25
|
>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
<select
|
|
40
|
-
v-model="date.month"
|
|
41
|
-
:disabled="disabled"
|
|
42
|
-
:required="required"
|
|
43
|
-
data-cy="month"
|
|
44
|
-
class="w-full rounded capitalize"
|
|
45
|
-
:class="[
|
|
46
|
-
{
|
|
47
|
-
'cursor-not-allowed bg-slate-100 text-slate-500': disabled,
|
|
48
|
-
},
|
|
49
|
-
[hasErrorInternal ? 'border-red-500' : 'border-slate-300'],
|
|
50
|
-
]"
|
|
51
|
-
:placeholder="t('sui.month')"
|
|
52
|
-
@change="update()"
|
|
26
|
+
{{ year }}
|
|
27
|
+
</option>
|
|
28
|
+
</BaseSelect>
|
|
29
|
+
|
|
30
|
+
<BaseSelect
|
|
31
|
+
v-model="date.month"
|
|
32
|
+
:disabled="disabled"
|
|
33
|
+
:required="requiredInternal"
|
|
34
|
+
data-cy="month"
|
|
35
|
+
class="capitalize"
|
|
36
|
+
:has-error="hasErrorInternal"
|
|
37
|
+
:placeholder="t('sui.month')"
|
|
38
|
+
:size="sizeInternal"
|
|
39
|
+
@update:model-value="update()"
|
|
40
|
+
>
|
|
41
|
+
<option
|
|
42
|
+
v-for="(month, i) in months"
|
|
43
|
+
:key="month"
|
|
44
|
+
:value="i + 1"
|
|
53
45
|
>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
<div class="w-auto p-0.5">
|
|
73
|
-
<select
|
|
74
|
-
v-model="date.day"
|
|
75
|
-
:disabled="dayDisabled"
|
|
76
|
-
:required="required"
|
|
77
|
-
data-cy="day"
|
|
78
|
-
class="w-full rounded capitalize"
|
|
79
|
-
:class="[
|
|
80
|
-
{
|
|
81
|
-
'cursor-not-allowed bg-slate-100 text-slate-500': dayDisabled,
|
|
82
|
-
},
|
|
83
|
-
[hasErrorInternal ? 'border-red-500' : 'border-slate-300'],
|
|
84
|
-
]"
|
|
85
|
-
:placeholder="t('sui.day')"
|
|
86
|
-
@change="update()"
|
|
46
|
+
{{ month }}
|
|
47
|
+
</option>
|
|
48
|
+
</BaseSelect>
|
|
49
|
+
|
|
50
|
+
<BaseSelect
|
|
51
|
+
v-model="date.day"
|
|
52
|
+
:disabled="dayDisabled"
|
|
53
|
+
:required="requiredInternal"
|
|
54
|
+
data-cy="day"
|
|
55
|
+
:has-error="hasErrorInternal"
|
|
56
|
+
:placeholder="t('sui.day')"
|
|
57
|
+
:size="sizeInternal"
|
|
58
|
+
@update:model-value="update()"
|
|
59
|
+
>
|
|
60
|
+
<option
|
|
61
|
+
v-for="day in days"
|
|
62
|
+
:key="day"
|
|
63
|
+
:value="day"
|
|
87
64
|
>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
hidden
|
|
92
|
-
:value="null"
|
|
93
|
-
>
|
|
94
|
-
{{ t('sui.day') }}
|
|
95
|
-
</option>
|
|
96
|
-
<option
|
|
97
|
-
v-for="day in days"
|
|
98
|
-
:key="day"
|
|
99
|
-
:value="day"
|
|
100
|
-
>
|
|
101
|
-
{{ day }}
|
|
102
|
-
</option>
|
|
103
|
-
</select>
|
|
104
|
-
</div>
|
|
65
|
+
{{ day }}
|
|
66
|
+
</option>
|
|
67
|
+
</BaseSelect>
|
|
105
68
|
</div>
|
|
106
69
|
|
|
107
70
|
<button
|
|
108
71
|
v-if="clearable"
|
|
109
72
|
type="button"
|
|
110
73
|
:disabled="disabled"
|
|
111
|
-
class="mt-1 appearance-none border-transparent bg-transparent text-
|
|
74
|
+
class="mt-1 appearance-none border-transparent bg-transparent text-slate-700 underline outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
|
75
|
+
:class="[
|
|
76
|
+
sizeInternal === 'xs' ? 'text-xs' : '',
|
|
77
|
+
sizeInternal === 'sm' ? 'text-xs' : '',
|
|
78
|
+
sizeInternal === 'md' ? 'text-sm' : '',
|
|
79
|
+
]"
|
|
112
80
|
@click="clear()"
|
|
113
81
|
>
|
|
114
82
|
<span>{{ t('sui.clear') }}</span>
|
|
@@ -123,6 +91,8 @@ import { DateTime, Info } from 'luxon';
|
|
|
123
91
|
import { useField } from '@/composables/field';
|
|
124
92
|
import { t } from '@/i18n';
|
|
125
93
|
import { useI18nStore } from '@/stores/i18n';
|
|
94
|
+
import BaseSelect from './BaseSelect.vue';
|
|
95
|
+
import { Size } from '@/utils/sizes';
|
|
126
96
|
|
|
127
97
|
const props = defineProps({
|
|
128
98
|
modelValue: {
|
|
@@ -133,6 +103,10 @@ const props = defineProps({
|
|
|
133
103
|
default: false,
|
|
134
104
|
type: Boolean,
|
|
135
105
|
},
|
|
106
|
+
size: {
|
|
107
|
+
default: undefined,
|
|
108
|
+
type: String as PropType<Size>,
|
|
109
|
+
},
|
|
136
110
|
disabled: {
|
|
137
111
|
default: false,
|
|
138
112
|
type: Boolean,
|
|
@@ -161,15 +135,18 @@ const props = defineProps({
|
|
|
161
135
|
|
|
162
136
|
const emit = defineEmits(['update:modelValue']);
|
|
163
137
|
|
|
164
|
-
const { hasErrorInternal, emitUpdate } = useField({
|
|
138
|
+
const { hasErrorInternal, emitUpdate, requiredInternal, sizeInternal } = useField({
|
|
165
139
|
name: computed(() => props.name),
|
|
166
140
|
required: computed(() => props.required),
|
|
141
|
+
size: computed(() => props.size),
|
|
167
142
|
hasError: computed(() => props.hasError),
|
|
168
143
|
emit: emit,
|
|
169
144
|
});
|
|
170
145
|
|
|
171
146
|
const years = range(props.maxYear, props.minYear) as number[];
|
|
147
|
+
|
|
172
148
|
const months = Info.months('short', { locale: useI18nStore().locale });
|
|
149
|
+
|
|
173
150
|
const days = computed(() => {
|
|
174
151
|
if (!date.value.year) {
|
|
175
152
|
return [];
|
|
@@ -197,6 +174,8 @@ watch(
|
|
|
197
174
|
date.value.year = datetime.year;
|
|
198
175
|
date.value.month = datetime.month;
|
|
199
176
|
date.value.day = datetime.day;
|
|
177
|
+
} else {
|
|
178
|
+
clear();
|
|
200
179
|
}
|
|
201
180
|
},
|
|
202
181
|
{ immediate: true }
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div ref="elementsRef">
|
|
3
3
|
<slot
|
|
4
4
|
v-for="(element, index) in modelValue"
|
|
5
|
-
:key="element
|
|
5
|
+
:key="getKey(element, index)"
|
|
6
6
|
name="item"
|
|
7
7
|
:element="element"
|
|
8
8
|
:index="index"
|
|
@@ -25,6 +25,10 @@ const emit = defineEmits(['update:modelValue']);
|
|
|
25
25
|
|
|
26
26
|
const elementsRef = ref<HTMLElement | null>(null);
|
|
27
27
|
|
|
28
|
+
function getKey(element: any, index: number) {
|
|
29
|
+
return element[props.itemKey] ?? index;
|
|
30
|
+
}
|
|
31
|
+
|
|
28
32
|
let sortable = null as Sortable | null;
|
|
29
33
|
|
|
30
34
|
onMounted(() => {
|
|
@@ -19,6 +19,35 @@ export default {
|
|
|
19
19
|
},
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
+
const template = `<template #button="{ newValue }">
|
|
23
|
+
<div class="btn btn-sm">
|
|
24
|
+
<div v-if="newValue" class="flex items-center gap-2 text-sm">
|
|
25
|
+
<div class="h-2 w-2 bg-red-500 rounded-full"></div>
|
|
26
|
+
<span>
|
|
27
|
+
{{ newValue.label }}
|
|
28
|
+
</span>
|
|
29
|
+
</div>
|
|
30
|
+
<div v-else class="">
|
|
31
|
+
Select a character
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</template>
|
|
35
|
+
<template #option="{ option }">
|
|
36
|
+
<div class="flex items-center gap-2 text-sm px-2">
|
|
37
|
+
<div
|
|
38
|
+
class="h-2 w-2 rounded-full"
|
|
39
|
+
:class="{
|
|
40
|
+
'bg-red-500': option?.type === 'sith',
|
|
41
|
+
'bg-green-500': option?.type === 'jedi',
|
|
42
|
+
'bg-slate-300': option?.type === undefined,
|
|
43
|
+
}"
|
|
44
|
+
></div>
|
|
45
|
+
<span>
|
|
46
|
+
{{ option.label }}
|
|
47
|
+
</span>
|
|
48
|
+
</div>
|
|
49
|
+
</template>`
|
|
50
|
+
|
|
22
51
|
export const Autocomplete = (args) => ({
|
|
23
52
|
components: { BaseDropdownAutocomplete, BaseBadge, ShowValue },
|
|
24
53
|
setup() {
|
|
@@ -30,21 +59,7 @@ export const Autocomplete = (args) => ({
|
|
|
30
59
|
v-bind="args"
|
|
31
60
|
v-model="value"
|
|
32
61
|
>
|
|
33
|
-
|
|
34
|
-
<BaseBadge v-if="newValue">
|
|
35
|
-
{{ newValue.label }}
|
|
36
|
-
</BaseBadge>
|
|
37
|
-
<div v-else>
|
|
38
|
-
<BaseBadge contrast="low">
|
|
39
|
-
Nothing
|
|
40
|
-
</BaseBadge>
|
|
41
|
-
</div>
|
|
42
|
-
</template>
|
|
43
|
-
<template #option="{ option }">
|
|
44
|
-
<BaseBadge :contrast="option?.value ? 'high' : 'low'">
|
|
45
|
-
{{ option.label }}
|
|
46
|
-
</BaseBadge>
|
|
47
|
-
</template>
|
|
62
|
+
${template}
|
|
48
63
|
</BaseDropdownAutocomplete>
|
|
49
64
|
<ShowValue :value="value" />
|
|
50
65
|
`,
|
|
@@ -4,10 +4,17 @@ import { createFieldStory, options } from '../../.storybook/utils';
|
|
|
4
4
|
import BaseAppNotifications from './BaseAppNotifications.vue';
|
|
5
5
|
import QueryString from 'qs';
|
|
6
6
|
|
|
7
|
+
const sizes = ['xs', 'sm', 'md'];
|
|
8
|
+
|
|
7
9
|
export default {
|
|
8
10
|
title: 'Form/BaseHasMany',
|
|
9
11
|
component: BaseHasMany,
|
|
10
|
-
argTypes: {
|
|
12
|
+
argTypes: {
|
|
13
|
+
size: {
|
|
14
|
+
control: { type: 'select' },
|
|
15
|
+
options: sizes,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
11
18
|
args: {
|
|
12
19
|
url: 'https://effettandem.com/api/content/articles',
|
|
13
20
|
field: 'title',
|
|
@@ -72,6 +79,20 @@ ShowRouteUrl.args = {
|
|
|
72
79
|
},
|
|
73
80
|
};
|
|
74
81
|
|
|
82
|
+
export const Sizes = (args) => ({
|
|
83
|
+
components: { BaseHasMany },
|
|
84
|
+
setup() {
|
|
85
|
+
const value = ref([]);
|
|
86
|
+
return { args, sizes, value };
|
|
87
|
+
},
|
|
88
|
+
template: `
|
|
89
|
+
<div v-for="size in sizes" class="mb-1">
|
|
90
|
+
<p class="text-xs text-slate-600 leading-tight">{{ size }}</p>
|
|
91
|
+
<BaseHasMany v-model="value" v-bind="args" :size="size"></BaseHasMany>
|
|
92
|
+
</div>
|
|
93
|
+
`,
|
|
94
|
+
});
|
|
95
|
+
|
|
75
96
|
export const SlotOption = (args) => {
|
|
76
97
|
return {
|
|
77
98
|
components: { BaseHasMany },
|
|
@@ -54,13 +54,13 @@ export const Demo = Template.bind({});
|
|
|
54
54
|
|
|
55
55
|
export const IconLeft = Template.bind({});
|
|
56
56
|
IconLeft.args = {
|
|
57
|
-
iconLeft: 'heroicons:
|
|
57
|
+
iconLeft: 'heroicons:calendar',
|
|
58
58
|
placeholder: 'Enter your phone',
|
|
59
59
|
};
|
|
60
60
|
|
|
61
61
|
export const IconRight = Template.bind({});
|
|
62
62
|
IconRight.args = {
|
|
63
|
-
iconRight: 'mdi:email
|
|
63
|
+
iconRight: 'mdi:email',
|
|
64
64
|
placeholder: 'Enter your email',
|
|
65
65
|
};
|
|
66
66
|
|
|
@@ -169,7 +169,7 @@ export const Field = createFieldStory({
|
|
|
169
169
|
});
|
|
170
170
|
|
|
171
171
|
const TemplateSizes = (args) => ({
|
|
172
|
-
components: { BaseInput
|
|
172
|
+
components: { BaseInput },
|
|
173
173
|
setup() {
|
|
174
174
|
const value = ref(null);
|
|
175
175
|
const sizes = ['xs', 'sm', 'md'];
|
|
@@ -177,7 +177,7 @@ const TemplateSizes = (args) => ({
|
|
|
177
177
|
},
|
|
178
178
|
template: `
|
|
179
179
|
<div v-for="size in sizes" :key="size" class="mb-4">
|
|
180
|
-
<p class="text-xs text-slate-600 leading-tight mb-1">
|
|
180
|
+
<p class="text-xs text-slate-600 leading-tight mb-1">{{ size }}</p>
|
|
181
181
|
<BaseInput v-model="value" v-bind="args" :size="size" class="w-full"></BaseInput>
|
|
182
182
|
</div>
|
|
183
183
|
`,
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div :class="classes">
|
|
3
3
|
<div :class="decorationWrapClasses">
|
|
4
|
-
<
|
|
4
|
+
<component
|
|
5
|
+
:is="hasLeftListener ? 'button' : 'div'"
|
|
5
6
|
v-if="iconLeft"
|
|
6
|
-
:
|
|
7
|
+
:type="hasLeftListener ? 'button' : undefined"
|
|
8
|
+
:class="[decorationClasses, hasLeftListener ? 'hover:bg-slate-100' : '']"
|
|
9
|
+
@click="onIconLeftClickInternal"
|
|
7
10
|
>
|
|
8
11
|
<BaseIcon
|
|
9
12
|
:icon="iconLeft"
|
|
10
13
|
:class="iconClasses"
|
|
11
14
|
/>
|
|
12
|
-
</
|
|
15
|
+
</component>
|
|
13
16
|
<div
|
|
14
17
|
v-if="prefix"
|
|
15
18
|
:class="decorationClasses"
|
|
19
|
+
@click="focusAction"
|
|
16
20
|
>
|
|
17
21
|
{{ prefix }}
|
|
18
22
|
</div>
|
|
@@ -47,18 +51,22 @@
|
|
|
47
51
|
<div
|
|
48
52
|
v-if="suffix"
|
|
49
53
|
:class="decorationClasses"
|
|
54
|
+
@click="focusAction"
|
|
50
55
|
>
|
|
51
56
|
{{ suffix }}
|
|
52
57
|
</div>
|
|
53
|
-
<
|
|
58
|
+
<component
|
|
59
|
+
:is="hasRightListener ? 'button' : 'div'"
|
|
54
60
|
v-if="iconRight"
|
|
55
|
-
:
|
|
61
|
+
:type="hasRightListener ? 'button' : undefined"
|
|
62
|
+
:class="[decorationClasses, hasRightListener ? 'hover:bg-slate-100' : '']"
|
|
63
|
+
@click="onIconRightClickInternal"
|
|
56
64
|
>
|
|
57
65
|
<BaseIcon
|
|
58
66
|
:icon="iconRight"
|
|
59
67
|
:class="iconClasses"
|
|
60
68
|
/>
|
|
61
|
-
</
|
|
69
|
+
</component>
|
|
62
70
|
</div>
|
|
63
71
|
</div>
|
|
64
72
|
</template>
|
|
@@ -162,6 +170,14 @@ const props = defineProps({
|
|
|
162
170
|
default: true,
|
|
163
171
|
type: Boolean,
|
|
164
172
|
},
|
|
173
|
+
onIconLeftClick: {
|
|
174
|
+
default: undefined,
|
|
175
|
+
type: Function as PropType<() => void>,
|
|
176
|
+
},
|
|
177
|
+
onIconRightClick: {
|
|
178
|
+
default: undefined,
|
|
179
|
+
type: Function as PropType<() => void>,
|
|
180
|
+
},
|
|
165
181
|
});
|
|
166
182
|
|
|
167
183
|
const input = ref<HTMLInputElement | null>(null);
|
|
@@ -175,6 +191,14 @@ const hasRightDecoration = computed(() => {
|
|
|
175
191
|
return props.iconRight || props.suffix;
|
|
176
192
|
});
|
|
177
193
|
|
|
194
|
+
const hasLeftListener = computed(() => {
|
|
195
|
+
return props.onIconLeftClick !== undefined;
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const hasRightListener = computed(() => {
|
|
199
|
+
return props.onIconRightClick !== undefined;
|
|
200
|
+
});
|
|
201
|
+
|
|
178
202
|
const maskOptions = computed(() => {
|
|
179
203
|
if (props.mask) {
|
|
180
204
|
return {
|
|
@@ -260,6 +284,7 @@ const classes = computed(() => {
|
|
|
260
284
|
const base = `inline-flex bg-white input-rounded border transition-colors duration-200`;
|
|
261
285
|
const border = hasErrorInternal.value ? 'border-red-500' : 'border-slate-300';
|
|
262
286
|
const disabled = props.disabled ? 'cursor-not-allowed text-slate-300' : '';
|
|
287
|
+
|
|
263
288
|
const sizeConfig = sizes[sizeInternal.value];
|
|
264
289
|
let focusClass = '';
|
|
265
290
|
if (props.visibleFocus) {
|
|
@@ -287,9 +312,9 @@ const baseClasses = computed(() => {
|
|
|
287
312
|
const disabled = props.disabled ? 'cursor-not-allowed text-slate-300' : '';
|
|
288
313
|
|
|
289
314
|
const paddingX = {
|
|
290
|
-
xs: [hasLeftDecoration.value ? 'pl-
|
|
291
|
-
sm: [hasLeftDecoration.value ? 'pl-
|
|
292
|
-
md: [hasLeftDecoration.value ? 'pl-
|
|
315
|
+
xs: [hasLeftDecoration.value ? 'pl-0' : 'pl-2', hasRightDecoration.value ? 'pr-0' : 'pr-2'],
|
|
316
|
+
sm: [hasLeftDecoration.value ? 'pl-0' : 'pl-2.5', hasRightDecoration.value ? 'pr-0' : 'pr-2.5'],
|
|
317
|
+
md: [hasLeftDecoration.value ? 'pl-0' : 'pl-3', hasRightDecoration.value ? 'pr-1' : 'pr-3'],
|
|
293
318
|
}[sizeInternal.value];
|
|
294
319
|
|
|
295
320
|
return [
|
|
@@ -301,11 +326,11 @@ const baseClasses = computed(() => {
|
|
|
301
326
|
});
|
|
302
327
|
|
|
303
328
|
const decorationWrapClasses = computed(() => {
|
|
304
|
-
const base = `flex
|
|
329
|
+
const base = `flex justify-center empty:hidden`;
|
|
305
330
|
const spacing = {
|
|
306
|
-
xs: 'first:pl-
|
|
307
|
-
sm: 'first:pl-
|
|
308
|
-
md: 'first:pl-
|
|
331
|
+
xs: 'first:pl-0.5 last:pr-0.5 py-0.5',
|
|
332
|
+
sm: 'first:pl-1 last:pr-1 py-1',
|
|
333
|
+
md: 'first:pl-1 last:pr-1 py-1',
|
|
309
334
|
}[sizeInternal.value];
|
|
310
335
|
|
|
311
336
|
return [
|
|
@@ -315,10 +340,15 @@ const decorationWrapClasses = computed(() => {
|
|
|
315
340
|
});
|
|
316
341
|
|
|
317
342
|
const decorationClasses = computed(() => {
|
|
318
|
-
const base = `flex items-center justify-center`;
|
|
343
|
+
const base = `flex items-center justify-center rounded-md`;
|
|
319
344
|
const textColor = hasErrorInternal.value ? 'text-red-800' : 'text-slate-500';
|
|
345
|
+
const padding = {
|
|
346
|
+
xs: 'p-1',
|
|
347
|
+
sm: 'p-1.5',
|
|
348
|
+
md: 'p-2',
|
|
349
|
+
}[sizeInternal.value];
|
|
320
350
|
|
|
321
|
-
return `${base} ${textColor}`;
|
|
351
|
+
return `${base} ${textColor} ${padding}`;
|
|
322
352
|
});
|
|
323
353
|
|
|
324
354
|
const iconClasses = computed(() => {
|
|
@@ -337,6 +367,22 @@ function blurAction() {
|
|
|
337
367
|
input.value?.blur();
|
|
338
368
|
}
|
|
339
369
|
|
|
370
|
+
function onIconLeftClickInternal() {
|
|
371
|
+
if (props.onIconLeftClick) {
|
|
372
|
+
props.onIconLeftClick();
|
|
373
|
+
} else {
|
|
374
|
+
focusAction();
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function onIconRightClickInternal() {
|
|
379
|
+
if (props.onIconRightClick) {
|
|
380
|
+
props.onIconRightClick();
|
|
381
|
+
} else {
|
|
382
|
+
blurAction();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
340
386
|
defineExpose({
|
|
341
387
|
focus: focusAction,
|
|
342
388
|
blur: blurAction,
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
v-if="url"
|
|
23
23
|
:href="url"
|
|
24
24
|
target="_blank"
|
|
25
|
-
class="btn btn-white rounded-full
|
|
25
|
+
class="btn btn-white btn-sm rounded-full aspect-1 p-0 shadow-sm ring-1 ring-black ring-opacity-[0.15]"
|
|
26
26
|
>
|
|
27
27
|
<BaseIcon
|
|
28
28
|
class="h-4 w-4"
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
v-if="showRemove"
|
|
34
34
|
type="button"
|
|
35
35
|
:disabled="disabled"
|
|
36
|
-
class="btn btn-white rounded-full
|
|
36
|
+
class="btn btn-white btn-sm rounded-full aspect-1 p-0 shadow-sm ring-1 ring-black ring-opacity-[0.15] disabled:bg-white disabled:opacity-70"
|
|
37
37
|
@click="$emit('remove')"
|
|
38
38
|
>
|
|
39
39
|
<BaseIcon
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import BasePassword from './BasePassword.vue';
|
|
2
2
|
import { createFieldStory } from '@/../.storybook/utils';
|
|
3
3
|
|
|
4
|
+
const sizes = ['xs', 'sm', 'md'];
|
|
5
|
+
|
|
4
6
|
export default {
|
|
5
7
|
title: 'Form/BasePassword',
|
|
6
8
|
component: BasePassword,
|
|
7
9
|
args: {},
|
|
10
|
+
argTypes: {
|
|
11
|
+
size: {
|
|
12
|
+
control: { type: 'select' },
|
|
13
|
+
options: sizes,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
8
16
|
};
|
|
9
17
|
|
|
10
18
|
const Template = (args) => ({
|
|
@@ -55,4 +63,21 @@ const FocusTemplate = (args) => ({
|
|
|
55
63
|
`,
|
|
56
64
|
});
|
|
57
65
|
|
|
66
|
+
const TemplateSizes = (args) => ({
|
|
67
|
+
components: { BasePassword },
|
|
68
|
+
setup() {
|
|
69
|
+
const value = ref(null);
|
|
70
|
+
const sizes = ['xs', 'sm', 'md'];
|
|
71
|
+
return { args, value, sizes };
|
|
72
|
+
},
|
|
73
|
+
template: `
|
|
74
|
+
<div v-for="size in sizes" :key="size" class="mb-4">
|
|
75
|
+
<p class="text-xs text-slate-600 leading-tight mb-1">{{ size }}</p>
|
|
76
|
+
<BasePassword v-model="value" v-bind="args" :size="size" class="w-full"></BasePassword>
|
|
77
|
+
</div>
|
|
78
|
+
`,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
export const Sizes = TemplateSizes.bind({});
|
|
82
|
+
|
|
58
83
|
export const Focus = FocusTemplate.bind({});
|