sprintify-ui 0.6.32 → 0.6.34
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 +13449 -13183
- package/dist/style.css +1 -1
- package/dist/tailwindcss/button.js +261 -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 +53 -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/BasePassword.vue.d.ts +13 -0
- 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/components/BaseTextarea.vue.d.ts +18 -0
- package/dist/types/src/components/BaseTextareaAutoresize.vue.d.ts +9 -0
- 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/BaseDataTable.vue +5 -5
- package/src/components/BaseDataTableRowAction.vue +6 -6
- package/src/components/BaseDatePicker.vue +6 -3
- package/src/components/BaseDraggable.vue +5 -1
- 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 +36 -2
- package/src/components/BaseInput.vue +199 -74
- package/src/components/BaseInputError.vue +32 -2
- package/src/components/BaseInputLabel.vue +36 -9
- package/src/components/BasePassword.stories.js +25 -0
- package/src/components/BasePassword.vue +35 -55
- 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/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 +27 -8
- package/src/components/BaseTimelineItem.stories.js +1 -3
- package/src/composables/field.ts +20 -0
- package/src/composables/inputSize.ts +29 -0
- package/src/utils/sizes.ts +21 -0
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div ref="autocomplete">
|
|
3
3
|
<div class="relative z-[1]">
|
|
4
|
-
<div
|
|
5
|
-
|
|
4
|
+
<div
|
|
5
|
+
class="flex"
|
|
6
|
+
:class="[select ? '-space-x-px' : '']"
|
|
7
|
+
>
|
|
8
|
+
<BaseSelect
|
|
6
9
|
v-if="select"
|
|
7
10
|
v-model="selection"
|
|
8
11
|
:disabled="disabled"
|
|
9
|
-
class="rounded-
|
|
10
|
-
:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@change="onSelectChange"
|
|
12
|
+
class="rounded-r-none border-r-transparent min-w-0"
|
|
13
|
+
:size="sizeInternal"
|
|
14
|
+
:has-error="hasErrorInternal"
|
|
15
|
+
required
|
|
16
|
+
@update:model-value="onSelectChange"
|
|
15
17
|
>
|
|
16
18
|
<option
|
|
17
19
|
v-for="option in select.options"
|
|
@@ -20,60 +22,38 @@
|
|
|
20
22
|
>
|
|
21
23
|
{{ option.label }}
|
|
22
24
|
</option>
|
|
23
|
-
</
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
: 'focus:border-slate-300',
|
|
42
|
-
]
|
|
43
|
-
: '',
|
|
44
|
-
]"
|
|
45
|
-
autocomplete="off"
|
|
46
|
-
:disabled="disabled"
|
|
47
|
-
@click="open"
|
|
48
|
-
@input="onTextInput"
|
|
49
|
-
@keydown="onTextKeydown"
|
|
50
|
-
>
|
|
51
|
-
<div
|
|
52
|
-
class="pointer-events-none absolute left-0 top-0 flex h-full items-center justify-center"
|
|
53
|
-
:class="[iconWrapClass]"
|
|
54
|
-
>
|
|
55
|
-
<BaseIcon
|
|
56
|
-
class="text-slate-400"
|
|
57
|
-
:class="[iconClass]"
|
|
58
|
-
icon="heroicons:magnifying-glass-solid"
|
|
59
|
-
/>
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
25
|
+
</BaseSelect>
|
|
26
|
+
<BaseInput
|
|
27
|
+
ref="inputElement"
|
|
28
|
+
:model-value="keywords"
|
|
29
|
+
type="text"
|
|
30
|
+
:visible-focus="visibleFocus"
|
|
31
|
+
:placeholder="placeholder ? placeholder : t('sui.autocomplete_placeholder')"
|
|
32
|
+
class="w-full grow min-w-0"
|
|
33
|
+
:class="inputClasses"
|
|
34
|
+
:has-error="hasErrorInternal"
|
|
35
|
+
:size="sizeInternal"
|
|
36
|
+
:autocomplete="false"
|
|
37
|
+
:disabled="disabled"
|
|
38
|
+
icon-left="heroicons:magnifying-glass-solid"
|
|
39
|
+
@focus="open"
|
|
40
|
+
@update:model-value="onTextInput"
|
|
41
|
+
@keydown="onTextKeydown"
|
|
42
|
+
/>
|
|
62
43
|
</div>
|
|
63
44
|
|
|
64
45
|
<div
|
|
65
46
|
v-if="showDeleteButton"
|
|
66
|
-
class="absolute right-0 top-0 flex h-full
|
|
47
|
+
class="absolute right-0 top-0 flex h-full p-1"
|
|
67
48
|
>
|
|
68
49
|
<button
|
|
69
50
|
type="button"
|
|
70
|
-
class="group flex
|
|
51
|
+
class="group flex rounded items-center justify-center aspect-1 enabled:hover:bg-slate-200"
|
|
71
52
|
@click="clear()"
|
|
72
53
|
>
|
|
73
54
|
<BaseIcon
|
|
74
55
|
icon="heroicons:x-mark"
|
|
75
|
-
class="
|
|
76
|
-
:class="[iconClass]"
|
|
56
|
+
:class="deleteButtonIconClasses"
|
|
77
57
|
/>
|
|
78
58
|
</button>
|
|
79
59
|
</div>
|
|
@@ -86,7 +66,7 @@
|
|
|
86
66
|
:class="[
|
|
87
67
|
inline
|
|
88
68
|
? 'relative'
|
|
89
|
-
: 'absolute top-
|
|
69
|
+
: 'absolute top-0.5 z-menu min-h-[110px] w-full overflow-hidden input-rounded border border-slate-300 bg-white shadow-2xl',
|
|
90
70
|
]"
|
|
91
71
|
>
|
|
92
72
|
<BaseAutocompleteDrawer
|
|
@@ -136,6 +116,8 @@ import { useClickOutside } from '@/composables/clickOutside';
|
|
|
136
116
|
import BaseAutocompleteDrawer from './BaseAutocompleteDrawer.vue';
|
|
137
117
|
import { SelectConfiguration } from '@/types';
|
|
138
118
|
import { t } from '@/i18n';
|
|
119
|
+
import { Size, sizes } from '@/utils/sizes';
|
|
120
|
+
import { BaseInput, BaseSelect } from '.';
|
|
139
121
|
|
|
140
122
|
const props = defineProps({
|
|
141
123
|
modelValue: {
|
|
@@ -158,6 +140,10 @@ const props = defineProps({
|
|
|
158
140
|
default: undefined,
|
|
159
141
|
type: String,
|
|
160
142
|
},
|
|
143
|
+
size: {
|
|
144
|
+
default: undefined,
|
|
145
|
+
type: String as PropType<Size>,
|
|
146
|
+
},
|
|
161
147
|
placeholder: {
|
|
162
148
|
default: undefined,
|
|
163
149
|
type: String,
|
|
@@ -190,10 +176,6 @@ const props = defineProps({
|
|
|
190
176
|
default: false,
|
|
191
177
|
type: Boolean,
|
|
192
178
|
},
|
|
193
|
-
size: {
|
|
194
|
-
default: 'base',
|
|
195
|
-
type: String as PropType<'xs' | 'sm' | 'base'>,
|
|
196
|
-
},
|
|
197
179
|
dropdownShow: {
|
|
198
180
|
default: 'focus',
|
|
199
181
|
type: String as PropType<'focus' | 'always'>,
|
|
@@ -237,10 +219,11 @@ const emit = defineEmits([
|
|
|
237
219
|
'select',
|
|
238
220
|
]);
|
|
239
221
|
|
|
240
|
-
const { hasErrorInternal, emitUpdate } = useField({
|
|
222
|
+
const { hasErrorInternal, emitUpdate, sizeInternal } = useField({
|
|
241
223
|
name: computed(() => props.name),
|
|
242
224
|
required: computed(() => props.required),
|
|
243
225
|
hasError: computed(() => props.hasError),
|
|
226
|
+
size: computed(() => props.size),
|
|
244
227
|
emit: emit,
|
|
245
228
|
});
|
|
246
229
|
|
|
@@ -335,6 +318,31 @@ useClickOutside(autocomplete, () => {
|
|
|
335
318
|
close();
|
|
336
319
|
});
|
|
337
320
|
|
|
321
|
+
onMounted(() => {
|
|
322
|
+
window.addEventListener('keydown', onWindowKeydown);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
onBeforeUnmount(() => {
|
|
326
|
+
window.removeEventListener('keydown', onWindowKeydown);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
function onWindowKeydown(event: KeyboardEvent) {
|
|
330
|
+
|
|
331
|
+
if (!opened.value) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const key = event.key;
|
|
336
|
+
|
|
337
|
+
if (key === 'Tab') {
|
|
338
|
+
close();
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (key === 'Escape') {
|
|
342
|
+
close();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
338
346
|
function open() {
|
|
339
347
|
clearInterval(timerId);
|
|
340
348
|
// Always focus as a safety
|
|
@@ -368,10 +376,10 @@ function close() {
|
|
|
368
376
|
emit('close');
|
|
369
377
|
}
|
|
370
378
|
|
|
371
|
-
const onTextInput = (
|
|
379
|
+
const onTextInput = (text: string) => {
|
|
372
380
|
open();
|
|
373
381
|
shouldFilter.value = true;
|
|
374
|
-
setKeywords(
|
|
382
|
+
setKeywords(text);
|
|
375
383
|
emit('typing', keywords.value);
|
|
376
384
|
|
|
377
385
|
// If keywords is empty, emit null
|
|
@@ -463,8 +471,7 @@ watch(
|
|
|
463
471
|
{ immediate: true }
|
|
464
472
|
);
|
|
465
473
|
|
|
466
|
-
function onSelectChange(
|
|
467
|
-
const value = get(event, 'target.value', null);
|
|
474
|
+
function onSelectChange(value: string | number | null | undefined) {
|
|
468
475
|
const option = props.select?.options.find((option) => option.value == value);
|
|
469
476
|
|
|
470
477
|
if (props.select && props.select.onChange) {
|
|
@@ -491,45 +498,28 @@ const slotProps = {
|
|
|
491
498
|
|
|
492
499
|
// Element Classes
|
|
493
500
|
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
return 'xs:text-xs xs:pl-7 text-base pl-9';
|
|
497
|
-
}
|
|
498
|
-
if (props.size == 'sm') {
|
|
499
|
-
return 'xs:text-sm xs:pl-8 text-base pl-9';
|
|
500
|
-
}
|
|
501
|
-
return 'text-base pl-9';
|
|
502
|
-
});
|
|
501
|
+
const deleteButtonIconClasses = computed(() => {
|
|
502
|
+
const base = 'text-slate-500 group-hover:text-slate-700';
|
|
503
503
|
|
|
504
|
-
const
|
|
505
|
-
if (props.size == 'xs') {
|
|
506
|
-
return 'xs:text-xs text-base';
|
|
507
|
-
}
|
|
508
|
-
if (props.size == 'sm') {
|
|
509
|
-
return 'xs:text-sm text-base';
|
|
510
|
-
}
|
|
511
|
-
return 'text-base';
|
|
512
|
-
});
|
|
504
|
+
const sizeConfig = sizes[sizeInternal.value];
|
|
513
505
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
if (props.size == 'sm') {
|
|
519
|
-
return 'xs:h-5 xs:w-5 h-5 w-5';
|
|
520
|
-
}
|
|
521
|
-
return 'h-5 w-5';
|
|
506
|
+
return [
|
|
507
|
+
base,
|
|
508
|
+
sizeConfig.iconSize,
|
|
509
|
+
];
|
|
522
510
|
});
|
|
523
511
|
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
512
|
+
const inputClasses = computed(() => {
|
|
513
|
+
const base = 'w-full';
|
|
514
|
+
const paddingRight = showDeleteButton.value ? 'pr-5' : '';
|
|
515
|
+
const withSelect = props.select ? 'rounded-l-none' : '';
|
|
516
|
+
|
|
517
|
+
return [
|
|
518
|
+
base,
|
|
519
|
+
withSelect,
|
|
520
|
+
paddingRight
|
|
521
|
+
];
|
|
522
|
+
})
|
|
533
523
|
|
|
534
524
|
const showDeleteButton = computed(() => {
|
|
535
525
|
return normalizedModelValue.value && !props.disabled && props.showModelValue;
|
|
@@ -121,6 +121,7 @@ import BaseSkeleton from './BaseSkeleton.vue';
|
|
|
121
121
|
import { Icon as BaseIcon } from '@iconify/vue';
|
|
122
122
|
import BaseSpinnerSmall from '../svg/BaseSpinnerSmall.vue';
|
|
123
123
|
import { t } from '@/i18n';
|
|
124
|
+
import { Size } from '@/utils/sizes';
|
|
124
125
|
|
|
125
126
|
const props = defineProps({
|
|
126
127
|
selected: {
|
|
@@ -148,8 +149,8 @@ const props = defineProps({
|
|
|
148
149
|
default: false,
|
|
149
150
|
},
|
|
150
151
|
size: {
|
|
151
|
-
type: String as PropType<
|
|
152
|
-
default: '
|
|
152
|
+
type: String as PropType<Size>,
|
|
153
|
+
default: 'md',
|
|
153
154
|
},
|
|
154
155
|
twDrawer: {
|
|
155
156
|
type: String,
|
|
@@ -3,7 +3,7 @@ import ShowValue from '@/../.storybook/components/ShowValue.vue';
|
|
|
3
3
|
import { options } from '@/../.storybook/utils';
|
|
4
4
|
import { createFieldStory } from '../../.storybook/utils';
|
|
5
5
|
|
|
6
|
-
const sizes = ['xs', 'sm', '
|
|
6
|
+
const sizes = ['xs', 'sm', 'md'];
|
|
7
7
|
|
|
8
8
|
export default {
|
|
9
9
|
title: 'Form/BaseAutocompleteFetch',
|
|
@@ -74,6 +74,7 @@ import {
|
|
|
74
74
|
import BaseAutocomplete from './BaseAutocomplete.vue';
|
|
75
75
|
import { useHasPaginatedData } from '@/composables/paginatedData';
|
|
76
76
|
import { t } from '@/i18n';
|
|
77
|
+
import { Size } from '@/utils/sizes';
|
|
77
78
|
|
|
78
79
|
/**
|
|
79
80
|
* Behavior notes
|
|
@@ -130,8 +131,8 @@ const props = defineProps({
|
|
|
130
131
|
type: Boolean,
|
|
131
132
|
},
|
|
132
133
|
size: {
|
|
133
|
-
default:
|
|
134
|
-
type: String as PropType<
|
|
134
|
+
default: undefined,
|
|
135
|
+
type: String as PropType<Size>,
|
|
135
136
|
},
|
|
136
137
|
dropdownShow: {
|
|
137
138
|
default: 'focus',
|
|
@@ -3,7 +3,7 @@ import ShowValue from '@/../.storybook/components/ShowValue.vue';
|
|
|
3
3
|
import { options } from '@/../.storybook/utils';
|
|
4
4
|
import { createFieldStory } from '../../.storybook/utils';
|
|
5
5
|
|
|
6
|
-
const sizes = ['xs', 'sm', '
|
|
6
|
+
const sizes = ['xs', 'sm', 'md'];
|
|
7
7
|
|
|
8
8
|
export default {
|
|
9
9
|
title: 'Form/BaseBelongsTo',
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
import { PropType } from 'vue';
|
|
47
47
|
import BaseAutocomplete from './BaseAutocomplete.vue';
|
|
48
48
|
import { Option, SelectConfiguration } from '@/types';
|
|
49
|
+
import { Size } from '@/utils/sizes';
|
|
49
50
|
|
|
50
51
|
const props = defineProps({
|
|
51
52
|
modelValue: {
|
|
@@ -91,8 +92,8 @@ const props = defineProps({
|
|
|
91
92
|
type: Boolean,
|
|
92
93
|
},
|
|
93
94
|
size: {
|
|
94
|
-
default:
|
|
95
|
-
type: String as PropType<
|
|
95
|
+
default: undefined,
|
|
96
|
+
type: String as PropType<Size>,
|
|
96
97
|
},
|
|
97
98
|
dropdownShow: {
|
|
98
99
|
default: 'focus',
|
|
@@ -48,6 +48,7 @@ import { AxiosResponse } from 'axios';
|
|
|
48
48
|
import { config } from '@/index';
|
|
49
49
|
import BaseAutocompleteFetch from './BaseAutocompleteFetch.vue';
|
|
50
50
|
import { Option, SelectConfiguration } from '@/types';
|
|
51
|
+
import { Size } from '@/utils/sizes';
|
|
51
52
|
|
|
52
53
|
const props = defineProps({
|
|
53
54
|
modelValue: {
|
|
@@ -101,8 +102,8 @@ const props = defineProps({
|
|
|
101
102
|
type: Boolean,
|
|
102
103
|
},
|
|
103
104
|
size: {
|
|
104
|
-
default:
|
|
105
|
-
type: String as PropType<
|
|
105
|
+
default: undefined,
|
|
106
|
+
type: String as PropType<Size>,
|
|
106
107
|
},
|
|
107
108
|
dropdownShow: {
|
|
108
109
|
default: 'focus',
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import BaseButton from './BaseButton.vue';
|
|
2
2
|
|
|
3
3
|
const colors = [
|
|
4
|
+
'#6fc3fc',
|
|
5
|
+
'#ff00ff',
|
|
6
|
+
'#111111',
|
|
7
|
+
'#f1f1f1',
|
|
4
8
|
'primary',
|
|
5
9
|
'secondary',
|
|
6
10
|
'secondary-outline',
|
|
@@ -109,3 +113,20 @@ export const Sizes = (args) => ({
|
|
|
109
113
|
</div>
|
|
110
114
|
`,
|
|
111
115
|
});
|
|
116
|
+
|
|
117
|
+
const IconTemplate = (args) => ({
|
|
118
|
+
components: { BaseButton },
|
|
119
|
+
setup() {
|
|
120
|
+
return { args };
|
|
121
|
+
},
|
|
122
|
+
template: `
|
|
123
|
+
<BaseButton v-bind="args">
|
|
124
|
+
</BaseButton>
|
|
125
|
+
`,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
export const Icon = IconTemplate.bind({});
|
|
129
|
+
Icon.args = {
|
|
130
|
+
icon: 'mdi:plus',
|
|
131
|
+
color: 'primary',
|
|
132
|
+
};
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
:is="as"
|
|
4
4
|
ref="button"
|
|
5
5
|
:class="classes"
|
|
6
|
+
:style="styles"
|
|
6
7
|
:to="to"
|
|
7
8
|
:disabled="disabled"
|
|
8
9
|
:type="type"
|
|
@@ -46,11 +47,14 @@
|
|
|
46
47
|
import { ClassNameValue, twMerge } from 'tailwind-merge';
|
|
47
48
|
import { RouteLocationRaw } from 'vue-router';
|
|
48
49
|
import { BaseIcon } from '.';
|
|
50
|
+
import { getColorConfig } from '@/utils/colors';
|
|
49
51
|
|
|
50
52
|
defineOptions({
|
|
51
53
|
inheritAttrs: false,
|
|
52
54
|
});
|
|
53
55
|
|
|
56
|
+
const slots = useSlots();
|
|
57
|
+
|
|
54
58
|
const sizes = {
|
|
55
59
|
'xs': 'btn-xs',
|
|
56
60
|
'sm': 'btn-sm',
|
|
@@ -89,7 +93,7 @@ const props = withDefaults(defineProps<{
|
|
|
89
93
|
type?: 'button' | 'submit' | 'reset';
|
|
90
94
|
iconPosition?: 'start' | 'end';
|
|
91
95
|
size?: keyof typeof sizes;
|
|
92
|
-
color?: keyof typeof colors;
|
|
96
|
+
color?: keyof typeof colors | string;
|
|
93
97
|
to?: RouteLocationRaw;
|
|
94
98
|
}>(), {
|
|
95
99
|
as: 'button',
|
|
@@ -106,20 +110,50 @@ const props = withDefaults(defineProps<{
|
|
|
106
110
|
|
|
107
111
|
defineEmits(['click']);
|
|
108
112
|
|
|
113
|
+
const iconOnly = computed(() => {
|
|
114
|
+
return props.icon && !slots.default;
|
|
115
|
+
});
|
|
116
|
+
|
|
109
117
|
const classes = computed(() => {
|
|
110
118
|
const classes = ['btn'];
|
|
111
119
|
|
|
112
120
|
if (props.color) {
|
|
113
|
-
|
|
121
|
+
|
|
122
|
+
if (props.color in colors) {
|
|
123
|
+
classes.push(colors[props.color as keyof typeof colors]);
|
|
124
|
+
} else {
|
|
125
|
+
classes.push('hover:opacity-80');
|
|
126
|
+
}
|
|
127
|
+
|
|
114
128
|
}
|
|
115
129
|
|
|
116
130
|
if (props.size) {
|
|
117
131
|
classes.push(sizes[props.size]);
|
|
118
132
|
}
|
|
119
133
|
|
|
134
|
+
if (iconOnly.value) {
|
|
135
|
+
classes.push('p-0 aspect-1 justify-center');
|
|
136
|
+
}
|
|
137
|
+
|
|
120
138
|
return twMerge(classes, props.class);
|
|
121
139
|
});
|
|
122
140
|
|
|
141
|
+
const styles = computed(() => {
|
|
142
|
+
if (props.color && !(props.color in colors)) {
|
|
143
|
+
const config = getColorConfig(props.color);
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
backgroundColor: config.backgroundColor,
|
|
147
|
+
color: config.textColor,
|
|
148
|
+
borderColor: config.borderColor,
|
|
149
|
+
borderWidth: '1px',
|
|
150
|
+
borderStyle: 'solid',
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return {};
|
|
155
|
+
});
|
|
156
|
+
|
|
123
157
|
const containerClass = computed(() => {
|
|
124
158
|
const classes = ['flex items-center justify-center'];
|
|
125
159
|
|
|
@@ -147,10 +181,12 @@ const containerClass = computed(() => {
|
|
|
147
181
|
const iconClass = computed(() => {
|
|
148
182
|
const classes = [''];
|
|
149
183
|
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
184
|
+
if (!iconOnly.value) {
|
|
185
|
+
if (props.iconPosition === 'start') {
|
|
186
|
+
classes.push('-translate-x-[20%]');
|
|
187
|
+
} else if (props.iconPosition === 'end') {
|
|
188
|
+
classes.push('translate-x-[20%]');
|
|
189
|
+
}
|
|
154
190
|
}
|
|
155
191
|
|
|
156
192
|
if (props.size === 'xs') {
|
|
@@ -2,10 +2,21 @@ import { createFieldStory, options } from '../../.storybook/utils';
|
|
|
2
2
|
import BaseButtonGroup from './BaseButtonGroup.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/BaseButtonGroup',
|
|
7
9
|
component: BaseButtonGroup,
|
|
8
|
-
argTypes: {
|
|
10
|
+
argTypes: {
|
|
11
|
+
spacing: {
|
|
12
|
+
control: { type: 'select' },
|
|
13
|
+
options: ['none', 'default'],
|
|
14
|
+
},
|
|
15
|
+
size: {
|
|
16
|
+
control: { type: 'select' },
|
|
17
|
+
options: sizes,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
9
20
|
args: {
|
|
10
21
|
labelKey: 'label',
|
|
11
22
|
valueKey: 'value',
|
|
@@ -45,6 +56,11 @@ Disabled.args = {
|
|
|
45
56
|
modelValue: { label: 'Dark Maul', value: 'darth_maul' },
|
|
46
57
|
};
|
|
47
58
|
|
|
59
|
+
export const NoSpace = Template.bind({});
|
|
60
|
+
NoSpace.args = {
|
|
61
|
+
spacing: 'none',
|
|
62
|
+
};
|
|
63
|
+
|
|
48
64
|
export const SlotOption = (args) => ({
|
|
49
65
|
components: { BaseButtonGroup },
|
|
50
66
|
setup() {
|
|
@@ -79,6 +95,20 @@ export const SlotOption = (args) => ({
|
|
|
79
95
|
`,
|
|
80
96
|
});
|
|
81
97
|
|
|
98
|
+
export const Sizes = (args) => ({
|
|
99
|
+
components: { BaseButtonGroup },
|
|
100
|
+
setup() {
|
|
101
|
+
const value = ref(null);
|
|
102
|
+
return { value, args, sizes };
|
|
103
|
+
},
|
|
104
|
+
template: `
|
|
105
|
+
<div v-for="size in sizes" :key="size" class="mb-4">
|
|
106
|
+
<p class="text-xs text-slate-600 leading-tight mb-1"> {{ size }}</p>
|
|
107
|
+
<BaseButtonGroup v-bind="args" v-model="value" :size="size"></BaseButtonGroup>
|
|
108
|
+
</div>
|
|
109
|
+
`,
|
|
110
|
+
});
|
|
111
|
+
|
|
82
112
|
export const Field = createFieldStory({
|
|
83
113
|
component: BaseButtonGroup,
|
|
84
114
|
componentName: 'BaseButtonGroup',
|
|
@@ -1,32 +1,27 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
class="flex flex-wrap"
|
|
4
|
-
:
|
|
3
|
+
class="flex items-center flex-wrap"
|
|
4
|
+
:class="wrapperClasses"
|
|
5
5
|
>
|
|
6
|
-
<
|
|
6
|
+
<BaseButton
|
|
7
7
|
v-for="option in normalizedOptions"
|
|
8
8
|
:key="option.value ? option.value : 'null'"
|
|
9
|
-
:
|
|
9
|
+
:type="buttonType"
|
|
10
|
+
:disabled="disabled"
|
|
11
|
+
:size="sizeInternal"
|
|
12
|
+
:class="twButton"
|
|
13
|
+
:color="buttonColor(option, isSelected(option))"
|
|
14
|
+
@click="onSelect(option)"
|
|
10
15
|
>
|
|
11
|
-
<
|
|
12
|
-
|
|
16
|
+
<slot
|
|
17
|
+
name="option"
|
|
18
|
+
:selected="computed(() => isSelected(option))"
|
|
19
|
+
:option="option"
|
|
13
20
|
:disabled="disabled"
|
|
14
|
-
:class="[
|
|
15
|
-
twButton,
|
|
16
|
-
isSelected(option) ? twButtonSelected : twButtonUnselected,
|
|
17
|
-
]"
|
|
18
|
-
@click="onSelect(option)"
|
|
19
21
|
>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
:option="option"
|
|
24
|
-
:disabled="disabled"
|
|
25
|
-
>
|
|
26
|
-
{{ option.label }}
|
|
27
|
-
</slot>
|
|
28
|
-
</button>
|
|
29
|
-
</div>
|
|
22
|
+
{{ option.label }}
|
|
23
|
+
</slot>
|
|
24
|
+
</BaseButton>
|
|
30
25
|
</div>
|
|
31
26
|
</template>
|
|
32
27
|
|
|
@@ -36,6 +31,8 @@ import { NormalizedOption, Option } from '@/types';
|
|
|
36
31
|
import { cloneDeep, isArray } from 'lodash';
|
|
37
32
|
import { useHasOptions } from '@/composables/hasOptions';
|
|
38
33
|
import { useField } from '@/composables/field';
|
|
34
|
+
import BaseButton from '@/components/BaseButton.vue';
|
|
35
|
+
import { Size } from '@/utils/sizes';
|
|
39
36
|
|
|
40
37
|
const props = defineProps({
|
|
41
38
|
modelValue: {
|
|
@@ -52,25 +49,27 @@ const props = defineProps({
|
|
|
52
49
|
default: false,
|
|
53
50
|
type: Boolean,
|
|
54
51
|
},
|
|
52
|
+
size: {
|
|
53
|
+
default: undefined,
|
|
54
|
+
type: String as PropType<Size>,
|
|
55
|
+
},
|
|
55
56
|
buttonType: {
|
|
56
57
|
default: 'button',
|
|
57
58
|
type: String as PropType<'button' | 'submit'>,
|
|
58
59
|
},
|
|
59
|
-
|
|
60
|
-
default:
|
|
61
|
-
|
|
60
|
+
buttonColor: {
|
|
61
|
+
default(option: Option, selected: boolean) {
|
|
62
|
+
return selected ? 'primary' : ''
|
|
63
|
+
},
|
|
64
|
+
type: Function as PropType<(option: Option, selected: boolean) => string>,
|
|
62
65
|
},
|
|
63
|
-
|
|
64
|
-
default: 'btn-primary',
|
|
65
|
-
type: String,
|
|
66
|
-
},
|
|
67
|
-
twButtonUnselected: {
|
|
66
|
+
twButton: {
|
|
68
67
|
default: '',
|
|
69
|
-
type: String,
|
|
68
|
+
type: [String, Array] as PropType<string | string[]>,
|
|
70
69
|
},
|
|
71
70
|
spacing: {
|
|
72
|
-
default: '
|
|
73
|
-
type: String
|
|
71
|
+
default: 'default',
|
|
72
|
+
type: String as PropType<'none' | 'default'>,
|
|
74
73
|
},
|
|
75
74
|
options: {
|
|
76
75
|
required: true,
|
|
@@ -100,9 +99,10 @@ const props = defineProps({
|
|
|
100
99
|
|
|
101
100
|
const emit = defineEmits(['update:modelValue']);
|
|
102
101
|
|
|
103
|
-
const { requiredInternal, emitUpdate } = useField({
|
|
102
|
+
const { requiredInternal, emitUpdate, sizeInternal } = useField({
|
|
104
103
|
name: computed(() => props.name),
|
|
105
104
|
required: computed(() => props.required),
|
|
105
|
+
size: computed(() => props.size),
|
|
106
106
|
hasError: computed(() => props.hasError),
|
|
107
107
|
emit: emit,
|
|
108
108
|
});
|
|
@@ -147,4 +147,17 @@ function onSelect(option: NormalizedOption) {
|
|
|
147
147
|
emitUpdate(newOption);
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
|
+
|
|
151
|
+
const wrapperClasses = computed(() => {
|
|
152
|
+
|
|
153
|
+
if (props.spacing == 'none') {
|
|
154
|
+
return 'btn-group';
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
"xs": "gap-0.5",
|
|
159
|
+
"sm": "gap-1",
|
|
160
|
+
"md": "gap-1.5",
|
|
161
|
+
}[sizeInternal.value];
|
|
162
|
+
});
|
|
150
163
|
</script>
|