sprintify-ui 0.0.96 → 0.0.99
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 +8035 -7945
- package/dist/style.css +2 -2
- package/dist/types/src/components/BaseAutocomplete.vue.d.ts +32 -10
- package/dist/types/src/components/BaseAutocompleteDropdown.vue.d.ts +88 -0
- package/dist/types/src/components/BaseAutocompleteFetch.vue.d.ts +20 -5
- package/dist/types/src/components/BaseBelongsTo.vue.d.ts +18 -3
- package/dist/types/src/components/BaseDatePicker.vue.d.ts +1 -1
- package/dist/types/src/components/BaseHasMany.vue.d.ts +17 -0
- package/dist/types/src/components/BaseLoadingCover.vue.d.ts +1 -1
- package/dist/types/src/components/BaseSwitch.vue.d.ts +1 -1
- package/dist/types/src/components/BaseTagAutocomplete.vue.d.ts +66 -6
- package/dist/types/src/components/BaseTagAutocompleteFetch.vue.d.ts +19 -2
- package/dist/types/src/composables/clickOutside.d.ts +1 -1
- package/dist/types/src/index.d.ts +2 -0
- package/package.json +10 -10
- package/src/assets/main.css +0 -1
- package/src/components/BaseAutocomplete.vue +53 -183
- package/src/components/BaseAutocompleteDropdown.vue +344 -0
- package/src/components/BaseAutocompleteFetch.vue +8 -3
- package/src/components/BaseDatePicker.vue +155 -1
- package/src/components/BaseDropdown.stories.js +25 -0
- package/src/components/BaseDropdown.vue +2 -2
- package/src/components/BaseModalSide.stories.js +1 -3
- package/src/components/BaseModalSide.vue +5 -5
- package/src/components/BaseTagAutocomplete.stories.js +46 -1
- package/src/components/BaseTagAutocomplete.vue +184 -253
- package/src/components/BaseTagAutocompleteFetch.stories.js +4 -4
- package/src/components/BaseTagAutocompleteFetch.vue +10 -5
- package/src/composables/clickOutside.ts +13 -8
- package/src/index.ts +3 -1
- package/src/assets/pikaday.css +0 -134
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="relative w-full overflow-hidden">
|
|
3
|
+
<div
|
|
4
|
+
ref="dropdown"
|
|
5
|
+
data-scroll-lock-scrollable
|
|
6
|
+
class="max-h-[214px] w-full overflow-y-auto"
|
|
7
|
+
>
|
|
8
|
+
<!-- Slot empty -->
|
|
9
|
+
<slot v-if="options.length == 0" name="empty">
|
|
10
|
+
<div
|
|
11
|
+
class="flex items-center justify-center px-5 py-10 text-center text-slate-600"
|
|
12
|
+
>
|
|
13
|
+
{{ $t('sui.nothing_found') }}
|
|
14
|
+
</div>
|
|
15
|
+
</slot>
|
|
16
|
+
|
|
17
|
+
<!-- Option list -->
|
|
18
|
+
<ul v-else :class="dropdownClass">
|
|
19
|
+
<li
|
|
20
|
+
v-for="(option, index) in options"
|
|
21
|
+
:key="option.value"
|
|
22
|
+
ref="optionRefs"
|
|
23
|
+
class="block"
|
|
24
|
+
>
|
|
25
|
+
<button
|
|
26
|
+
class="block w-full cursor-pointer select-none appearance-none border-none text-left focus:outline-none"
|
|
27
|
+
type="button"
|
|
28
|
+
tabindex="-1"
|
|
29
|
+
@click="onSelect(option)"
|
|
30
|
+
@mouseenter="onOptionMouseEnter(index)"
|
|
31
|
+
@mousemove="onOptionMouseMove(index)"
|
|
32
|
+
>
|
|
33
|
+
<slot
|
|
34
|
+
name="option"
|
|
35
|
+
:option="option.option"
|
|
36
|
+
:selected="isSelected(option)"
|
|
37
|
+
:active="focusOption && focusOption.value == option.value"
|
|
38
|
+
>
|
|
39
|
+
<div
|
|
40
|
+
class="flex items-center rounded px-[0.75em] py-[0.5em]"
|
|
41
|
+
:class="[optionClass(option), optionSizeClass]"
|
|
42
|
+
>
|
|
43
|
+
<div class="grow">
|
|
44
|
+
{{ option.label }}
|
|
45
|
+
</div>
|
|
46
|
+
<div class="shrink-0">
|
|
47
|
+
<BaseIcon
|
|
48
|
+
v-if="isSelected(option)"
|
|
49
|
+
icon="heroicons:check-20-solid"
|
|
50
|
+
:class="optionIconClass"
|
|
51
|
+
></BaseIcon>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</slot>
|
|
55
|
+
</button>
|
|
56
|
+
</li>
|
|
57
|
+
</ul>
|
|
58
|
+
|
|
59
|
+
<!-- Loading bottom -->
|
|
60
|
+
<!-- This component should always take the same amount of space whether it's visible or not -->
|
|
61
|
+
<!-- This is to prevent infinite scroll jumps -->
|
|
62
|
+
<div
|
|
63
|
+
:class="[loadingBottom ? 'opacity-100' : 'opacity-0']"
|
|
64
|
+
class="relative py-3 text-center"
|
|
65
|
+
>
|
|
66
|
+
<div
|
|
67
|
+
class="absolute inset-0 h-full w-full bg-gradient-to-b from-transparent to-white"
|
|
68
|
+
></div>
|
|
69
|
+
<BaseSpinnerSmall
|
|
70
|
+
class="mx-auto h-7 w-7 text-slate-300"
|
|
71
|
+
></BaseSpinnerSmall>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div ref="footer">
|
|
76
|
+
<div v-if="$slots.footer" class="bg-white">
|
|
77
|
+
<slot :options="options" name="footer" />
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<div
|
|
82
|
+
v-if="loading"
|
|
83
|
+
class="absolute inset-0 h-full w-full space-y-1 bg-white p-2"
|
|
84
|
+
>
|
|
85
|
+
<div class="space-y-1">
|
|
86
|
+
<BaseSkeleton class="h-7 w-full" delay="0ms"></BaseSkeleton>
|
|
87
|
+
<BaseSkeleton class="h-7 w-full opacity-70" delay="50ms"></BaseSkeleton>
|
|
88
|
+
<BaseSkeleton
|
|
89
|
+
class="h-7 w-full opacity-30"
|
|
90
|
+
delay="100ms"
|
|
91
|
+
></BaseSkeleton>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</template>
|
|
96
|
+
|
|
97
|
+
<script lang="ts" setup>
|
|
98
|
+
import { Ref, PropType } from 'vue';
|
|
99
|
+
import { useInfiniteScroll } from '@vueuse/core';
|
|
100
|
+
import { NormalizedOption } from '@/types';
|
|
101
|
+
import { isArray, isObject, throttle } from 'lodash';
|
|
102
|
+
import BaseSkeleton from './BaseSkeleton.vue';
|
|
103
|
+
import { BaseIcon } from '.';
|
|
104
|
+
import BaseSpinnerSmall from '../svg/BaseSpinnerSmall.vue';
|
|
105
|
+
|
|
106
|
+
const props = defineProps({
|
|
107
|
+
selected: {
|
|
108
|
+
type: [Array, Object, null, undefined] as PropType<
|
|
109
|
+
NormalizedOption[] | NormalizedOption | null | undefined
|
|
110
|
+
>,
|
|
111
|
+
default: undefined,
|
|
112
|
+
},
|
|
113
|
+
options: {
|
|
114
|
+
type: Array as PropType<NormalizedOption[]>,
|
|
115
|
+
default() {
|
|
116
|
+
return [];
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
keywords: {
|
|
120
|
+
type: String,
|
|
121
|
+
default: '',
|
|
122
|
+
},
|
|
123
|
+
loading: {
|
|
124
|
+
type: Boolean,
|
|
125
|
+
default: false,
|
|
126
|
+
},
|
|
127
|
+
loadingBottom: {
|
|
128
|
+
type: Boolean,
|
|
129
|
+
default: false,
|
|
130
|
+
},
|
|
131
|
+
size: {
|
|
132
|
+
type: String as PropType<'xs' | 'sm' | 'base'>,
|
|
133
|
+
default: 'base',
|
|
134
|
+
},
|
|
135
|
+
dropdownClass: {
|
|
136
|
+
type: String,
|
|
137
|
+
default: '',
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const emit = defineEmits(['scrollBottom', 'select']);
|
|
142
|
+
|
|
143
|
+
const dropdown = ref(null) as Ref<HTMLDivElement | null>;
|
|
144
|
+
const optionRefs = ref([]) as Ref<HTMLElement[]>;
|
|
145
|
+
|
|
146
|
+
let mouseIsMoving = false;
|
|
147
|
+
|
|
148
|
+
// Focus
|
|
149
|
+
|
|
150
|
+
const focusIndex = ref(0);
|
|
151
|
+
|
|
152
|
+
const focusOption = computed(() => {
|
|
153
|
+
return (
|
|
154
|
+
props.options[Math.min(focusIndex.value, props.options.length - 1)] ?? null
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Selecting
|
|
159
|
+
|
|
160
|
+
function onSelect(option: NormalizedOption) {
|
|
161
|
+
emit('select', option);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function isSelected(option: NormalizedOption): boolean {
|
|
165
|
+
if (isArray(props.selected)) {
|
|
166
|
+
return props.selected.some((modelValue) => {
|
|
167
|
+
return modelValue.value === option.value;
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (isObject(props.selected)) {
|
|
172
|
+
return props.selected.value == option.value;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Arrow actions
|
|
179
|
+
|
|
180
|
+
window.addEventListener('keydown', onKeyDown);
|
|
181
|
+
|
|
182
|
+
onBeforeUnmount(() => {
|
|
183
|
+
window.removeEventListener('keydown', onKeyDown);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
function onKeyDown(event: KeyboardEvent) {
|
|
187
|
+
const key = event.key;
|
|
188
|
+
|
|
189
|
+
mouseIsMoving = false;
|
|
190
|
+
|
|
191
|
+
if (props.loading) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (key === 'ArrowDown') {
|
|
196
|
+
if (focusIndex.value < props.options.length - 1) {
|
|
197
|
+
focusIndex.value++;
|
|
198
|
+
} else {
|
|
199
|
+
focusIndex.value = 0;
|
|
200
|
+
}
|
|
201
|
+
scrollToFocus();
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (key === 'ArrowUp') {
|
|
206
|
+
if (focusIndex.value > 0) {
|
|
207
|
+
focusIndex.value--;
|
|
208
|
+
} else {
|
|
209
|
+
focusIndex.value = Math.max(0, props.options.length - 1);
|
|
210
|
+
}
|
|
211
|
+
scrollToFocus();
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (key === 'Enter') {
|
|
216
|
+
event.preventDefault();
|
|
217
|
+
if (focusOption.value) {
|
|
218
|
+
onSelect(focusOption.value);
|
|
219
|
+
}
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Infinite scroll
|
|
225
|
+
|
|
226
|
+
onMounted(() => {
|
|
227
|
+
useInfiniteScroll(
|
|
228
|
+
dropdown.value,
|
|
229
|
+
() => {
|
|
230
|
+
emit('scrollBottom');
|
|
231
|
+
},
|
|
232
|
+
{ distance: 60 }
|
|
233
|
+
);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
function scrollToFocus() {
|
|
237
|
+
if (!optionRefs.value) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const option = optionRefs.value[focusIndex.value] ?? null;
|
|
242
|
+
|
|
243
|
+
if (!option) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
option.scrollIntoView({
|
|
248
|
+
block: 'center',
|
|
249
|
+
inline: 'center',
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Validate focus index
|
|
254
|
+
|
|
255
|
+
watch(
|
|
256
|
+
() => props.options,
|
|
257
|
+
() => {
|
|
258
|
+
focusIndex.value = Math.max(
|
|
259
|
+
0,
|
|
260
|
+
Math.min(focusIndex.value, props.options.length - 1)
|
|
261
|
+
);
|
|
262
|
+
},
|
|
263
|
+
{ immediate: true }
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
// Update the focusIndex when the selected value changes
|
|
267
|
+
|
|
268
|
+
watch(
|
|
269
|
+
() => props.selected,
|
|
270
|
+
() => {
|
|
271
|
+
if (
|
|
272
|
+
props.selected &&
|
|
273
|
+
isObject(props.selected) &&
|
|
274
|
+
!isArray(props.selected)
|
|
275
|
+
) {
|
|
276
|
+
const selected = props.selected as NormalizedOption;
|
|
277
|
+
const index = props.options.findIndex(
|
|
278
|
+
(option) => option.value === selected.value
|
|
279
|
+
);
|
|
280
|
+
if (index >= 0) {
|
|
281
|
+
focusIndex.value = index;
|
|
282
|
+
}
|
|
283
|
+
} else {
|
|
284
|
+
focusIndex.value = 0;
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
{ immediate: true }
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
// Scroll top when the keywords change
|
|
291
|
+
|
|
292
|
+
watch(
|
|
293
|
+
() => props.keywords,
|
|
294
|
+
() => {
|
|
295
|
+
if (dropdown.value) {
|
|
296
|
+
dropdown.value.scrollTop = 0;
|
|
297
|
+
}
|
|
298
|
+
// Reset the focusIndex
|
|
299
|
+
focusIndex.value = 0;
|
|
300
|
+
}
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
const onOptionMouseEnter = (index: number) => {
|
|
304
|
+
if (mouseIsMoving) {
|
|
305
|
+
focusIndex.value = index;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const onOptionMouseMove = throttle((index: number) => {
|
|
310
|
+
mouseIsMoving = true;
|
|
311
|
+
}, 10);
|
|
312
|
+
|
|
313
|
+
// Classes
|
|
314
|
+
|
|
315
|
+
const optionClass = (option: NormalizedOption) => {
|
|
316
|
+
const focus = focusOption.value && focusOption.value.value == option.value;
|
|
317
|
+
|
|
318
|
+
if (focus) {
|
|
319
|
+
return 'bg-slate-200';
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return 'bg-white';
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const optionSizeClass = computed(() => {
|
|
326
|
+
if (props.size == 'xs') {
|
|
327
|
+
return 'text-xs';
|
|
328
|
+
}
|
|
329
|
+
if (props.size == 'sm') {
|
|
330
|
+
return 'text-sm';
|
|
331
|
+
}
|
|
332
|
+
return 'text-sm';
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
const optionIconClass = computed(() => {
|
|
336
|
+
if (props.size == 'xs') {
|
|
337
|
+
return 'w-4 h-4';
|
|
338
|
+
}
|
|
339
|
+
if (props.size == 'sm') {
|
|
340
|
+
return 'w-5 h-5';
|
|
341
|
+
}
|
|
342
|
+
return 'w-5 h-5';
|
|
343
|
+
});
|
|
344
|
+
</script>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<BaseAutocomplete
|
|
3
3
|
ref="input"
|
|
4
4
|
:loading="showLoading && page == 1"
|
|
5
|
+
:loading-bottom="showLoading && page > 1"
|
|
5
6
|
:model-value="modelValue"
|
|
6
7
|
:disabled="disabled"
|
|
7
8
|
:name="name"
|
|
@@ -46,7 +47,7 @@
|
|
|
46
47
|
|
|
47
48
|
<script lang="ts" setup>
|
|
48
49
|
import { config } from '@/index';
|
|
49
|
-
import { debounce } from 'lodash';
|
|
50
|
+
import { debounce, throttle } from 'lodash';
|
|
50
51
|
import { PropType, Ref } from 'vue';
|
|
51
52
|
import { Option } from '@/types';
|
|
52
53
|
import BaseAutocomplete from './BaseAutocomplete.vue';
|
|
@@ -161,12 +162,16 @@ const onClear = () => {
|
|
|
161
162
|
emit('clear');
|
|
162
163
|
};
|
|
163
164
|
|
|
164
|
-
const scrollBottom = () => {
|
|
165
|
+
const scrollBottom = throttle(() => {
|
|
166
|
+
if (fetching.value) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
165
170
|
if (!reachedEnd.value) {
|
|
166
171
|
page.value++;
|
|
167
172
|
search();
|
|
168
173
|
}
|
|
169
|
-
};
|
|
174
|
+
}, 500);
|
|
170
175
|
|
|
171
176
|
function search() {
|
|
172
177
|
if (fetching.value) {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
class="base-date-picker relative"
|
|
4
|
+
:class="{ 'base-date-picker--inline': inline }"
|
|
5
|
+
>
|
|
3
6
|
<div
|
|
4
7
|
class="pointer-events-none absolute top-0 left-0 flex h-10 items-center justify-center pl-2.5"
|
|
5
8
|
>
|
|
@@ -205,3 +208,154 @@ function arrayRotate(arr: any[], reverse = false): any[] {
|
|
|
205
208
|
return arr;
|
|
206
209
|
}
|
|
207
210
|
</script>
|
|
211
|
+
|
|
212
|
+
<style lang="postcss">
|
|
213
|
+
@import 'pikaday/css/pikaday.css';
|
|
214
|
+
|
|
215
|
+
.base-date-picker--inline {
|
|
216
|
+
.pikaday-white.pika-single {
|
|
217
|
+
box-shadow: none;
|
|
218
|
+
border: none;
|
|
219
|
+
padding: 0;
|
|
220
|
+
}
|
|
221
|
+
.pika-lendar {
|
|
222
|
+
margin: 0;
|
|
223
|
+
margin-top: 0.25rem;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.pikaday-white {
|
|
228
|
+
--backgroundColor: #ffffff;
|
|
229
|
+
--textColor: #718096;
|
|
230
|
+
--currentDateTextColor: #3182ce;
|
|
231
|
+
--selectedDateBackgroundColor: #3182ce;
|
|
232
|
+
--selectedDateTextColor: #ffffff;
|
|
233
|
+
|
|
234
|
+
--labelTextColor: #4a5568; /* eg. May 2020 */
|
|
235
|
+
--weekDaysTextColor: #a0aec0; /* eg. Mo Tu We ....*/
|
|
236
|
+
|
|
237
|
+
font-family: inherit;
|
|
238
|
+
background-color: var(--backgroundColor);
|
|
239
|
+
padding: 0.5rem;
|
|
240
|
+
z-index: 60;
|
|
241
|
+
margin: 6px 0 0 0;
|
|
242
|
+
|
|
243
|
+
@apply shadow;
|
|
244
|
+
@apply rounded-lg;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.pikaday-white .pika-title {
|
|
248
|
+
width: 100%;
|
|
249
|
+
text-align: center;
|
|
250
|
+
display: flex;
|
|
251
|
+
justify-content: flex-start;
|
|
252
|
+
margin-bottom: 5px;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* Next/Previous */
|
|
256
|
+
.pikaday-white .pika-prev,
|
|
257
|
+
.pikaday-white .pika-next {
|
|
258
|
+
position: absolute;
|
|
259
|
+
outline: none;
|
|
260
|
+
padding: 0;
|
|
261
|
+
width: 28px;
|
|
262
|
+
height: 28px;
|
|
263
|
+
top: -1px;
|
|
264
|
+
display: inline-block;
|
|
265
|
+
margin-top: 0;
|
|
266
|
+
cursor: pointer;
|
|
267
|
+
/* hide text using text-indent trick, using width value (it's enough) */
|
|
268
|
+
text-indent: -9999px;
|
|
269
|
+
white-space: nowrap;
|
|
270
|
+
overflow: hidden;
|
|
271
|
+
background-color: transparent;
|
|
272
|
+
background-position: center center;
|
|
273
|
+
background-repeat: no-repeat;
|
|
274
|
+
opacity: 0.8;
|
|
275
|
+
}
|
|
276
|
+
.pikaday-white .pika-prev:hover,
|
|
277
|
+
.pikaday-white .pika-next:hover {
|
|
278
|
+
opacity: 1;
|
|
279
|
+
}
|
|
280
|
+
.pikaday-white .pika-prev {
|
|
281
|
+
right: 30px;
|
|
282
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2364748b'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M15 19l-7-7 7-7'%3E%3C/path%3E%3C/svg%3E");
|
|
283
|
+
}
|
|
284
|
+
.pikaday-white .pika-next {
|
|
285
|
+
right: 2px;
|
|
286
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2364748b'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5l7 7-7 7'%3E%3C/path%3E%3C/svg%3E");
|
|
287
|
+
}
|
|
288
|
+
.pika-prev.is-disabled,
|
|
289
|
+
.pika-next.is-disabled {
|
|
290
|
+
cursor: default;
|
|
291
|
+
opacity: 0.2;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.pikaday-white .pika-label {
|
|
295
|
+
@apply mr-1.5 rounded border border-slate-200 py-1 px-2 text-[15px] font-normal leading-none;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/* Show Month & Year select */
|
|
299
|
+
.pikaday-white .pika-label {
|
|
300
|
+
position: relative; /* add this */
|
|
301
|
+
/* some additional code */
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.pikaday-white .pika-select-month,
|
|
305
|
+
.pikaday-white .pika-select-year {
|
|
306
|
+
/*display: none;*/
|
|
307
|
+
width: 100%;
|
|
308
|
+
cursor: pointer;
|
|
309
|
+
position: absolute;
|
|
310
|
+
z-index: 9998;
|
|
311
|
+
margin: 0;
|
|
312
|
+
left: 0;
|
|
313
|
+
top: 0;
|
|
314
|
+
opacity: 0;
|
|
315
|
+
padding: 0px;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.pikaday-white table {
|
|
319
|
+
width: 100%;
|
|
320
|
+
border-collapse: collapse;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.pikaday-white table th {
|
|
324
|
+
width: 2.5em;
|
|
325
|
+
height: 2.5em;
|
|
326
|
+
font-weight: normal;
|
|
327
|
+
color: var(--weekDaysTextColor);
|
|
328
|
+
font-size: 13px;
|
|
329
|
+
text-align: center;
|
|
330
|
+
}
|
|
331
|
+
.pikaday-white table th abbr {
|
|
332
|
+
text-decoration: none;
|
|
333
|
+
}
|
|
334
|
+
.pikaday-white table td {
|
|
335
|
+
padding: 1px;
|
|
336
|
+
}
|
|
337
|
+
.pikaday-white table td button {
|
|
338
|
+
width: 2.5em;
|
|
339
|
+
height: 2.5em;
|
|
340
|
+
text-align: center;
|
|
341
|
+
@apply rounded-full;
|
|
342
|
+
font-size: 13px;
|
|
343
|
+
background-color: var(--backgroundColor);
|
|
344
|
+
}
|
|
345
|
+
.pikaday-white table td button:hover {
|
|
346
|
+
@apply bg-slate-100;
|
|
347
|
+
@apply text-slate-900;
|
|
348
|
+
}
|
|
349
|
+
.pikaday-white table td.is-today button {
|
|
350
|
+
color: var(--currentDateTextColor);
|
|
351
|
+
}
|
|
352
|
+
.pikaday-white table td.is-selected button {
|
|
353
|
+
@apply bg-primary-500;
|
|
354
|
+
@apply text-white;
|
|
355
|
+
@apply font-normal;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.pikaday-white table td button {
|
|
359
|
+
color: var(--textColor);
|
|
360
|
+
}
|
|
361
|
+
</style>
|
|
@@ -2,6 +2,7 @@ import BaseBadge from './BaseBadge.vue';
|
|
|
2
2
|
import BaseAutocomplete from './BaseAutocomplete.vue';
|
|
3
3
|
import BaseModalCenter from './BaseModalCenter.vue';
|
|
4
4
|
import BaseDropdown from './BaseDropdown.vue';
|
|
5
|
+
import BaseDatePicker from './BaseDatePicker.vue';
|
|
5
6
|
import { options } from '../../.storybook/utils';
|
|
6
7
|
|
|
7
8
|
const items = [];
|
|
@@ -151,3 +152,27 @@ export const ModalWithScroll = (args) => ({
|
|
|
151
152
|
</BaseModalCenter>
|
|
152
153
|
`,
|
|
153
154
|
});
|
|
155
|
+
|
|
156
|
+
export const WithDatePicker = (args) => ({
|
|
157
|
+
components: { BaseDropdown, BaseDatePicker },
|
|
158
|
+
setup() {
|
|
159
|
+
const date = ref('2022-01-01');
|
|
160
|
+
return { args, items, date };
|
|
161
|
+
},
|
|
162
|
+
template: `
|
|
163
|
+
<BaseDropdown v-bind="args">
|
|
164
|
+
<template #button>
|
|
165
|
+
<div class="btn btn-primary">Click me</div>
|
|
166
|
+
</template>
|
|
167
|
+
<template #dropdown>
|
|
168
|
+
<div class="bg-white shadow-lg rounded border p-2 w-[260px]">
|
|
169
|
+
<BaseDatePicker v-model="date" inline></BaseDatePicker>
|
|
170
|
+
</div>
|
|
171
|
+
</template>
|
|
172
|
+
</BaseDropdown>
|
|
173
|
+
`,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
WithDatePicker.args = {
|
|
177
|
+
placement: 'bottom-start',
|
|
178
|
+
};
|
|
@@ -96,14 +96,14 @@ const props = defineProps({
|
|
|
96
96
|
|
|
97
97
|
const emit = defineEmits(['update:modelValue']);
|
|
98
98
|
|
|
99
|
-
const modal = useModal(
|
|
100
|
-
computed(() => props.modelValue),
|
|
101
|
-
emit
|
|
102
|
-
);
|
|
103
|
-
|
|
104
99
|
const breakpoints = useBreakpoints();
|
|
105
100
|
|
|
106
101
|
const realMaxWidth = computed((): string => {
|
|
107
102
|
return breakpoints.greater('sm').value ? props.maxWidth : '100%';
|
|
108
103
|
});
|
|
104
|
+
|
|
105
|
+
const modal = useModal(
|
|
106
|
+
computed(() => props.modelValue),
|
|
107
|
+
emit
|
|
108
|
+
);
|
|
109
109
|
</script>
|
|
@@ -3,10 +3,25 @@ import BaseTagAutocomplete from './BaseTagAutocomplete.vue';
|
|
|
3
3
|
import ShowValue from '@/../.storybook/components/ShowValue.vue';
|
|
4
4
|
import BaseAppNotifications from './BaseAppNotifications.vue';
|
|
5
5
|
|
|
6
|
+
const sizes = ['xs', 'sm', 'base'];
|
|
7
|
+
|
|
6
8
|
export default {
|
|
7
9
|
title: 'Form/BaseTagAutocomplete',
|
|
8
10
|
component: BaseTagAutocomplete,
|
|
9
|
-
argTypes: {
|
|
11
|
+
argTypes: {
|
|
12
|
+
size: {
|
|
13
|
+
control: {
|
|
14
|
+
type: 'select',
|
|
15
|
+
options: sizes,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
dropdownShow: {
|
|
19
|
+
control: {
|
|
20
|
+
type: 'select',
|
|
21
|
+
options: ['always', 'focus'],
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
10
25
|
args: {
|
|
11
26
|
labelKey: 'label',
|
|
12
27
|
valueKey: 'value',
|
|
@@ -30,6 +45,17 @@ const Template = (args) => ({
|
|
|
30
45
|
export const Demo = Template.bind({});
|
|
31
46
|
Demo.args = {};
|
|
32
47
|
|
|
48
|
+
export const AlwaysShowDropdown = Template.bind({});
|
|
49
|
+
AlwaysShowDropdown.args = {
|
|
50
|
+
inline: true,
|
|
51
|
+
dropdownShow: 'always',
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const NoFocus = Template.bind({});
|
|
55
|
+
NoFocus.args = {
|
|
56
|
+
visibleFocus: false,
|
|
57
|
+
};
|
|
58
|
+
|
|
33
59
|
export const Disabled = Template.bind({});
|
|
34
60
|
Disabled.args = {
|
|
35
61
|
options: options,
|
|
@@ -42,6 +68,25 @@ Loading.args = {
|
|
|
42
68
|
loading: true,
|
|
43
69
|
};
|
|
44
70
|
|
|
71
|
+
export const Inline = Template.bind({});
|
|
72
|
+
Inline.args = {
|
|
73
|
+
inline: true,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const Sizes = (args) => ({
|
|
77
|
+
components: { BaseTagAutocomplete },
|
|
78
|
+
setup() {
|
|
79
|
+
const value = ref([]);
|
|
80
|
+
return { args, sizes, value };
|
|
81
|
+
},
|
|
82
|
+
template: `
|
|
83
|
+
<div v-for="size in sizes" class="mb-1">
|
|
84
|
+
<p class="text-xs text-slate-600 leading-tight">{{ size }}</p>
|
|
85
|
+
<BaseTagAutocomplete v-model="value" v-bind="args" :size="size"></BaseTagAutocomplete>
|
|
86
|
+
</div>
|
|
87
|
+
`,
|
|
88
|
+
});
|
|
89
|
+
|
|
45
90
|
export const SlotOption = (args) => ({
|
|
46
91
|
components: { BaseTagAutocomplete, ShowValue, BaseAppNotifications },
|
|
47
92
|
setup() {
|