sprintify-ui 0.0.11 → 0.0.13
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/README.md +8 -7
- package/dist/sprintify-ui.es.js +5911 -3760
- package/dist/style.css +1 -1
- package/dist/tailwindcss/index.js +1 -2
- package/dist/types/src/components/BaseCharacterCounter.vue.d.ts +143 -0
- package/dist/types/src/components/BaseHasMany.vue.d.ts +277 -0
- package/dist/types/src/components/BaseInput.vue.d.ts +39 -5
- package/dist/types/src/components/BaseLoadingCover.vue.d.ts +72 -0
- package/dist/types/src/components/{BaseMediaLibraryItem.vue.d.ts → BaseMediaItem.vue.d.ts} +26 -4
- package/dist/types/src/components/BaseMediaLibrary.vue.d.ts +23 -15
- package/dist/types/src/components/BaseMediaPreview.vue.d.ts +97 -0
- package/dist/types/src/components/BaseModalCenter.vue.d.ts +8 -8
- package/dist/types/src/components/BaseModalSide.vue.d.ts +8 -8
- package/dist/types/src/components/BasePagination.vue.d.ts +105 -13
- package/dist/types/src/components/BaseSelect.vue.d.ts +130 -26
- package/dist/types/src/components/BaseSideNavigationItem.vue.d.ts +20 -1
- package/dist/types/src/components/BaseSwitch.vue.d.ts +15 -8
- package/dist/types/src/components/BaseTabItem.vue.d.ts +45 -4
- package/dist/types/src/components/BaseTagAutocomplete.vue.d.ts +25 -17
- package/dist/types/src/components/BaseTagAutocompleteFetch.vue.d.ts +37 -21
- package/dist/types/src/components/BaseTextareaAutoresize.vue.d.ts +175 -21
- package/dist/types/src/components/index.d.ts +30 -1
- package/dist/types/src/index.d.ts +4 -0
- package/package.json +1 -1
- package/src/components/BaseAppDialogs.vue +2 -2
- package/src/components/BaseAppNotifications.vue +1 -1
- package/src/components/BaseAutocomplete.vue +16 -18
- package/src/components/BaseBelongsTo.vue +1 -0
- package/src/components/BaseCharacterCounter.stories.js +30 -0
- package/src/components/BaseCharacterCounter.vue +60 -0
- package/src/components/BaseClipboard.vue +1 -1
- package/src/components/BaseDataIterator.stories.js +2 -2
- package/src/components/BaseDataIterator.vue +32 -38
- package/src/components/BaseDataTable.stories.js +2 -2
- package/src/components/BaseFileUploader.vue +4 -0
- package/src/components/BaseHasMany.vue +92 -0
- package/src/components/BaseInput.stories.js +46 -0
- package/src/components/BaseInput.vue +10 -2
- package/src/components/BaseInputLabel.stories.js +31 -0
- package/src/components/BaseInputLabel.vue +1 -1
- package/src/components/BaseLoadingCover.stories.js +55 -0
- package/src/components/BaseLoadingCover.vue +19 -1
- package/src/components/BaseMediaItem.stories.js +41 -0
- package/src/components/BaseMediaItem.vue +71 -0
- package/src/components/BaseMediaLibrary.stories.js +80 -0
- package/src/components/BaseMediaLibrary.vue +67 -68
- package/src/components/BaseMediaPreview.stories.js +72 -0
- package/src/components/BaseMediaPreview.vue +90 -0
- package/src/components/BaseMenu.stories.js +125 -0
- package/src/components/BaseMenu.vue +1 -1
- package/src/components/BaseModalCenter.stories.js +61 -0
- package/src/components/BaseModalCenter.vue +2 -2
- package/src/components/BaseModalSide.stories.js +55 -0
- package/src/components/BaseModalSide.vue +2 -2
- package/src/components/BaseNavbar.stories.js +150 -0
- package/src/components/BaseNavbar.vue +3 -0
- package/src/components/BaseNavbarItem.vue +1 -0
- package/src/components/BaseNavbarItemContent.vue +3 -0
- package/src/components/BasePagination.stories.js +32 -0
- package/src/components/BasePagination.vue +126 -40
- package/src/components/BasePanel.stories.js +56 -0
- package/src/components/BasePassword.stories.js +36 -0
- package/src/components/BasePassword.vue +11 -5
- package/src/components/BaseProcessRing.stories.js +27 -0
- package/src/components/BaseReadMore.stories.js +30 -0
- package/src/components/BaseReadMore.vue +1 -1
- package/src/components/BaseSelect.stories.js +67 -0
- package/src/components/BaseSelect.vue +144 -44
- package/src/components/BaseSideNavigation.stories.js +55 -0
- package/src/components/BaseSideNavigation.vue +7 -2
- package/src/components/BaseSideNavigationItem.vue +21 -5
- package/src/components/BaseSkeleton.stories.js +36 -0
- package/src/components/BaseSwitch.stories.js +101 -0
- package/src/components/BaseSwitch.vue +90 -12
- package/src/components/BaseSystemAlert.stories.js +63 -0
- package/src/components/BaseTabItem.vue +29 -6
- package/src/components/BaseTable.vue +2 -2
- package/src/components/BaseTabs.stories.js +54 -0
- package/src/components/BaseTabs.vue +3 -3
- package/src/components/BaseTagAutocomplete.stories.js +129 -0
- package/src/components/BaseTagAutocomplete.vue +155 -57
- package/src/components/BaseTagAutocompleteFetch.stories.js +130 -0
- package/src/components/BaseTagAutocompleteFetch.vue +36 -25
- package/src/components/BaseTextarea.stories.js +35 -0
- package/src/components/BaseTextarea.vue +1 -1
- package/src/components/BaseTextareaAutoresize.stories.js +49 -0
- package/src/components/BaseTextareaAutoresize.vue +83 -87
- package/src/components/HasMany.stories.js +135 -0
- package/src/components/index.ts +58 -0
- package/src/lang/en.json +2 -1
- package/src/lang/fr.json +2 -1
- package/dist/types/src/components/BasePaginationSimple.vue.d.ts +0 -25
- package/dist/types/src/components/BaseWordCount.vue.d.ts +0 -31
- package/src/components/BaseMediaLibraryItem.vue +0 -92
- package/src/components/BasePaginationSimple.vue +0 -60
- package/src/components/BaseWordCount.vue +0 -36
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
:class="{ 'col-span-1': !mobileLayout, 'col-span-2': mobileLayout }"
|
|
12
12
|
>
|
|
13
13
|
<!-- Header -->
|
|
14
|
-
<div class="mb-4
|
|
14
|
+
<div class="flex mb-4 space-x-2 empty:mb-0">
|
|
15
15
|
<!-- Search bar -->
|
|
16
16
|
<div v-if="searchable" class="grow">
|
|
17
17
|
<div class="relative h-11">
|
|
18
18
|
<div
|
|
19
|
-
class="pointer-events-none absolute top-0 left-0
|
|
19
|
+
class="flex pointer-events-none absolute top-0 left-0 h-full items-center justify-center pl-2.5"
|
|
20
20
|
>
|
|
21
21
|
<BaseIcon
|
|
22
22
|
class="h-6 w-6 text-slate-400"
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
/>
|
|
34
34
|
<div
|
|
35
35
|
v-if="searchQuery"
|
|
36
|
-
class="absolute top-0 right-0
|
|
36
|
+
class="flex absolute top-0 right-0 h-full items-center justify-center p-1"
|
|
37
37
|
>
|
|
38
38
|
<button
|
|
39
39
|
type="button"
|
|
@@ -55,15 +55,15 @@
|
|
|
55
55
|
<!-- Filters (mobile) -->
|
|
56
56
|
<button
|
|
57
57
|
v-if="mobileLayout && hasFilters"
|
|
58
|
-
class="btn
|
|
58
|
+
class="flex btn h-11 items-center justify-center py-1 text-base shadow-sm"
|
|
59
59
|
type="button"
|
|
60
60
|
@click="showFilters = true"
|
|
61
61
|
>
|
|
62
62
|
<BaseIcon
|
|
63
|
-
class="
|
|
63
|
+
class="h-6 w-6 text-slate-500 xs:mr-2"
|
|
64
64
|
icon="heroicons:adjustments-horizontal-solid"
|
|
65
65
|
/>
|
|
66
|
-
<span>{{ $t('sui.filters') }}</span>
|
|
66
|
+
<span class="hidden xs:block">{{ $t('sui.filters') }}</span>
|
|
67
67
|
</button>
|
|
68
68
|
|
|
69
69
|
<!-- Menu -->
|
|
@@ -99,38 +99,32 @@
|
|
|
99
99
|
|
|
100
100
|
<!-- Pagination -->
|
|
101
101
|
|
|
102
|
+
<div class="mt-5">
|
|
103
|
+
<p class="text-center text-xs text-slate-400 sm:text-right">
|
|
104
|
+
{{
|
|
105
|
+
(paginationMetadata.current_page - 1) *
|
|
106
|
+
paginationMetadata.per_page +
|
|
107
|
+
1
|
|
108
|
+
}}
|
|
109
|
+
-
|
|
110
|
+
{{
|
|
111
|
+
$t('sui.pagination_detail', {
|
|
112
|
+
page: Math.min(
|
|
113
|
+
paginationMetadata.current_page * paginationMetadata.per_page,
|
|
114
|
+
paginationMetadata.total
|
|
115
|
+
),
|
|
116
|
+
total: paginationMetadata.total,
|
|
117
|
+
})
|
|
118
|
+
}}
|
|
119
|
+
</p>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
102
122
|
<div class="mt-4">
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
:model-value="page"
|
|
109
|
-
:last-page="lastPage"
|
|
110
|
-
@model-value:update="onPageChange"
|
|
111
|
-
/>
|
|
112
|
-
<div>
|
|
113
|
-
<p class="text-sm text-slate-600">
|
|
114
|
-
{{
|
|
115
|
-
(paginationMetadata.current_page - 1) *
|
|
116
|
-
paginationMetadata.per_page +
|
|
117
|
-
1
|
|
118
|
-
}}
|
|
119
|
-
-
|
|
120
|
-
{{
|
|
121
|
-
$t('sui.pagination_detail', {
|
|
122
|
-
page: Math.min(
|
|
123
|
-
paginationMetadata.current_page *
|
|
124
|
-
paginationMetadata.per_page,
|
|
125
|
-
paginationMetadata.total
|
|
126
|
-
),
|
|
127
|
-
total: paginationMetadata.total,
|
|
128
|
-
})
|
|
129
|
-
}}
|
|
130
|
-
</p>
|
|
131
|
-
</div>
|
|
132
|
-
</div>
|
|
133
|
-
</div>
|
|
123
|
+
<BasePagination
|
|
124
|
+
:model-value="page"
|
|
125
|
+
:last-page="lastPage"
|
|
126
|
+
@update:model-value="onPageChange"
|
|
127
|
+
/>
|
|
134
128
|
</div>
|
|
135
129
|
</div>
|
|
136
130
|
|
|
@@ -216,7 +210,7 @@ import {
|
|
|
216
210
|
import BaseMenu from './BaseMenu.vue';
|
|
217
211
|
import BaseCard from './BaseCard.vue';
|
|
218
212
|
import BaseCardRow from './BaseCardRow.vue';
|
|
219
|
-
import
|
|
213
|
+
import BasePagination from './BasePagination.vue';
|
|
220
214
|
import BaseModalSide from './BaseModalSide.vue';
|
|
221
215
|
import { config } from '@/index';
|
|
222
216
|
import { useMutationObserver, useResizeObserver } from '@vueuse/core';
|
|
@@ -87,9 +87,9 @@ const template = `
|
|
|
87
87
|
<BaseSelect
|
|
88
88
|
:model-value="query.type ?? null"
|
|
89
89
|
class="w-full rounded border-slate-300"
|
|
90
|
+
placeholder="-"
|
|
90
91
|
@update:model-value="updateQueryValue('type', $event)"
|
|
91
92
|
>
|
|
92
|
-
<option value="">-</option>
|
|
93
93
|
<option value="video">
|
|
94
94
|
Video
|
|
95
95
|
</option>
|
|
@@ -105,9 +105,9 @@ const template = `
|
|
|
105
105
|
<BaseSelect
|
|
106
106
|
:model-value="query.access_level ?? null"
|
|
107
107
|
class="w-full rounded border-slate-300"
|
|
108
|
+
placeholder="-"
|
|
108
109
|
@update:model-value="updateQueryValue('access_level', $event)"
|
|
109
110
|
>
|
|
110
|
-
<option value="">-</option>
|
|
111
111
|
<option value="public">
|
|
112
112
|
Public
|
|
113
113
|
</option>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<BaseTagAutocompleteFetch
|
|
3
|
+
:model-value="models"
|
|
4
|
+
:url="url"
|
|
5
|
+
:disabled="disabled"
|
|
6
|
+
:placeholder="placeholder"
|
|
7
|
+
:required="required"
|
|
8
|
+
:value-key="foreignKey"
|
|
9
|
+
:label-key="field"
|
|
10
|
+
:input-class="inputClass"
|
|
11
|
+
:query-key="queryKey"
|
|
12
|
+
:max="max"
|
|
13
|
+
@update:model-value="onUpdate"
|
|
14
|
+
>
|
|
15
|
+
<template #option="optionProps">
|
|
16
|
+
<slot name="option" v-bind="optionProps" />
|
|
17
|
+
</template>
|
|
18
|
+
<template #empty="emptyProps">
|
|
19
|
+
<slot name="empty" v-bind="emptyProps"></slot>
|
|
20
|
+
</template>
|
|
21
|
+
<template #footer="footerProps">
|
|
22
|
+
<slot name="footer" v-bind="footerProps"></slot>
|
|
23
|
+
</template>
|
|
24
|
+
</BaseTagAutocompleteFetch>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<script lang="ts" setup>
|
|
28
|
+
import { PropType } from 'vue';
|
|
29
|
+
import { Option } from '@/types/types';
|
|
30
|
+
import BaseTagAutocompleteFetch from './BaseTagAutocompleteFetch.vue';
|
|
31
|
+
|
|
32
|
+
const props = defineProps({
|
|
33
|
+
modelValue: {
|
|
34
|
+
required: true,
|
|
35
|
+
type: Array as PropType<Option[]>,
|
|
36
|
+
},
|
|
37
|
+
url: {
|
|
38
|
+
required: true,
|
|
39
|
+
type: String,
|
|
40
|
+
},
|
|
41
|
+
foreignKey: {
|
|
42
|
+
default: 'id',
|
|
43
|
+
type: String,
|
|
44
|
+
},
|
|
45
|
+
field: {
|
|
46
|
+
required: true,
|
|
47
|
+
type: String,
|
|
48
|
+
},
|
|
49
|
+
required: {
|
|
50
|
+
default: false,
|
|
51
|
+
type: Boolean,
|
|
52
|
+
},
|
|
53
|
+
disabled: {
|
|
54
|
+
default: false,
|
|
55
|
+
type: Boolean,
|
|
56
|
+
},
|
|
57
|
+
placeholder: {
|
|
58
|
+
default: undefined,
|
|
59
|
+
type: String,
|
|
60
|
+
},
|
|
61
|
+
inputClass: {
|
|
62
|
+
default: undefined,
|
|
63
|
+
type: String,
|
|
64
|
+
},
|
|
65
|
+
max: {
|
|
66
|
+
default: undefined,
|
|
67
|
+
type: Number,
|
|
68
|
+
},
|
|
69
|
+
queryKey: {
|
|
70
|
+
default: 'search',
|
|
71
|
+
type: String,
|
|
72
|
+
},
|
|
73
|
+
currentModels: {
|
|
74
|
+
default() {
|
|
75
|
+
return [];
|
|
76
|
+
},
|
|
77
|
+
type: Array as PropType<Option[]>,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const emit = defineEmits(['update:modelValue']);
|
|
82
|
+
|
|
83
|
+
const models = ref(props.currentModels);
|
|
84
|
+
|
|
85
|
+
function onUpdate(newModels: Option[]) {
|
|
86
|
+
models.value = newModels;
|
|
87
|
+
emit(
|
|
88
|
+
'update:modelValue',
|
|
89
|
+
newModels.map((m) => m[props.foreignKey])
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
</script>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import BaseInput from './BaseInput.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Form/BaseInput',
|
|
5
|
+
component: BaseInput,
|
|
6
|
+
args: {
|
|
7
|
+
required: true,
|
|
8
|
+
type: 'text',
|
|
9
|
+
name: 'name',
|
|
10
|
+
class: 'w-full',
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const Template = (args) => ({
|
|
15
|
+
components: {
|
|
16
|
+
BaseInput,
|
|
17
|
+
},
|
|
18
|
+
setup() {
|
|
19
|
+
const value = ref('');
|
|
20
|
+
return { args, value };
|
|
21
|
+
},
|
|
22
|
+
template: `
|
|
23
|
+
<form @submit.prevent="" class="border-none">
|
|
24
|
+
<BaseInput v-model="value" v-bind="args"></BaseInput>
|
|
25
|
+
</form>
|
|
26
|
+
`,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const Demo = Template.bind({});
|
|
30
|
+
Demo.args = {
|
|
31
|
+
placeholder: 'Enter your name',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const Number = Template.bind({});
|
|
35
|
+
Number.args = {
|
|
36
|
+
type: 'number',
|
|
37
|
+
step: 0.1,
|
|
38
|
+
placeholder: 'Enter a number',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const Disabled = Template.bind({});
|
|
42
|
+
Disabled.args = {
|
|
43
|
+
modelValue: 'Disabled input!',
|
|
44
|
+
disabled: true,
|
|
45
|
+
placeholder: 'Enter your name',
|
|
46
|
+
};
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
:value="modelValue"
|
|
5
5
|
:type="type"
|
|
6
6
|
:name="name"
|
|
7
|
+
:step="step"
|
|
7
8
|
:disabled="disabled"
|
|
8
9
|
:placeholder="placeholder"
|
|
9
10
|
:required="required"
|
|
10
|
-
class="rounded"
|
|
11
|
+
class="rounded border-slate-300 disabled:cursor-not-allowed disabled:text-slate-300"
|
|
11
12
|
:autocomplete="autocomplete ? 'on' : 'off'"
|
|
12
13
|
@keydown.enter="onEnter"
|
|
13
14
|
@input="$emit('update:modelValue', transformInputValue($event))"
|
|
@@ -20,17 +21,24 @@ import { PropType } from 'vue';
|
|
|
20
21
|
|
|
21
22
|
const props = defineProps({
|
|
22
23
|
modelValue: {
|
|
23
|
-
|
|
24
|
+
default: '',
|
|
24
25
|
type: [String, Number, null] as PropType<string | number | null>,
|
|
25
26
|
},
|
|
26
27
|
type: {
|
|
27
28
|
type: String,
|
|
28
29
|
default: 'text',
|
|
29
30
|
},
|
|
31
|
+
step: {
|
|
32
|
+
default: undefined,
|
|
33
|
+
type: Number,
|
|
34
|
+
},
|
|
30
35
|
autocomplete: {
|
|
31
36
|
default: true,
|
|
32
37
|
type: Boolean,
|
|
33
38
|
},
|
|
39
|
+
/**
|
|
40
|
+
* Prevents submit when pressing 'Enter' while the input is focused.
|
|
41
|
+
*/
|
|
34
42
|
preventSubmit: {
|
|
35
43
|
default: false,
|
|
36
44
|
type: Boolean,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import BaseInputLabel from './BaseInputLabel.vue';
|
|
2
|
+
import BaseInput from './BaseInput.vue';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Form/BaseInputLabel',
|
|
6
|
+
component: BaseInputLabel,
|
|
7
|
+
args: {
|
|
8
|
+
required: true,
|
|
9
|
+
label: 'First Name',
|
|
10
|
+
class: 'text-slate-600 block text-sm',
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const Template = (args) => ({
|
|
15
|
+
components: {
|
|
16
|
+
BaseInputLabel,
|
|
17
|
+
BaseInput,
|
|
18
|
+
},
|
|
19
|
+
setup() {
|
|
20
|
+
return { args };
|
|
21
|
+
},
|
|
22
|
+
template: `
|
|
23
|
+
<form @submit.prevent="">
|
|
24
|
+
<BaseInputLabel v-bind="args"></BaseInputLabel>
|
|
25
|
+
<BaseInput required name="name" placeholder="Enter your first name"></BaseInput>
|
|
26
|
+
</form>
|
|
27
|
+
`,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export const Demo = Template.bind({});
|
|
31
|
+
Demo.args = {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import BaseLoadingCover from './BaseLoadingCover.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Layout/BaseLoadingCover',
|
|
5
|
+
component: BaseLoadingCover,
|
|
6
|
+
args: {
|
|
7
|
+
delay: 0,
|
|
8
|
+
modelValue: true,
|
|
9
|
+
size: 'sm',
|
|
10
|
+
},
|
|
11
|
+
argTypes: {
|
|
12
|
+
size: {
|
|
13
|
+
control: { type: 'select' },
|
|
14
|
+
options: ['sm', 'lg'],
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const Template = (args) => ({
|
|
20
|
+
components: {
|
|
21
|
+
BaseLoadingCover,
|
|
22
|
+
},
|
|
23
|
+
setup() {
|
|
24
|
+
return { args };
|
|
25
|
+
},
|
|
26
|
+
template: `
|
|
27
|
+
<div class="relative bg-slate-100 p-6 py-10">
|
|
28
|
+
<p class="text-center text-slate-700">Non dolor consectetur et occaecat.</p>
|
|
29
|
+
<BaseLoadingCover v-bind="args" />
|
|
30
|
+
</div>
|
|
31
|
+
`,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export const Demo = Template.bind({});
|
|
35
|
+
Demo.args = {};
|
|
36
|
+
|
|
37
|
+
export const SmallSpinner = Template.bind({});
|
|
38
|
+
SmallSpinner.args = {
|
|
39
|
+
size: 'sm',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const LargeSpinner = Template.bind({});
|
|
43
|
+
LargeSpinner.args = {
|
|
44
|
+
size: 'lg',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const SpinnerCustomClass = Template.bind({});
|
|
48
|
+
SpinnerCustomClass.args = {
|
|
49
|
+
iconClass: 'text-red-500 w-10 h-10',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const BackdropCustomClass = Template.bind({});
|
|
53
|
+
BackdropCustomClass.args = {
|
|
54
|
+
backdropClass: 'bg-slate-100 opacity-50',
|
|
55
|
+
};
|
|
@@ -25,28 +25,46 @@ import BaseSpinnerSmall from '../svg/BaseSpinnerSmall.vue';
|
|
|
25
25
|
import BaseSpinnerLarge from '../svg/BaseSpinnerLarge.vue';
|
|
26
26
|
|
|
27
27
|
const props = defineProps({
|
|
28
|
+
/**
|
|
29
|
+
* Show/Hide the loading cover
|
|
30
|
+
*/
|
|
28
31
|
modelValue: {
|
|
29
32
|
default: true,
|
|
30
33
|
type: Boolean,
|
|
31
34
|
},
|
|
35
|
+
/**
|
|
36
|
+
* Classes added to the backdrop
|
|
37
|
+
*/
|
|
32
38
|
backdropClass: {
|
|
33
39
|
default: 'bg-white',
|
|
34
40
|
type: String,
|
|
35
41
|
},
|
|
42
|
+
/**
|
|
43
|
+
* Classes added to the spinner icon
|
|
44
|
+
*/
|
|
36
45
|
iconClass: {
|
|
37
46
|
default: 'text-blue-500 w-10 h-10',
|
|
38
47
|
type: String,
|
|
39
48
|
},
|
|
49
|
+
/**
|
|
50
|
+
* Change the type of spinner. 'sm' for a small spinner. 'lg' for a large spinner.
|
|
51
|
+
*/
|
|
40
52
|
size: {
|
|
41
53
|
default: 'sm',
|
|
42
54
|
type: String as PropType<'sm' | 'lg'>,
|
|
43
55
|
},
|
|
56
|
+
/**
|
|
57
|
+
* Transition duration class.
|
|
58
|
+
*/
|
|
44
59
|
duration: {
|
|
45
60
|
default: 'duration-300',
|
|
46
61
|
type: String,
|
|
47
62
|
},
|
|
63
|
+
/**
|
|
64
|
+
* Delay before showing the loading cover. This delay is used to avoid unnecessary showing the spinner for a fraction of second when the loading time is too short.
|
|
65
|
+
*/
|
|
48
66
|
delay: {
|
|
49
|
-
default:
|
|
67
|
+
default: 0,
|
|
50
68
|
type: Number,
|
|
51
69
|
},
|
|
52
70
|
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import BaseMediaItem from './BaseMediaItem.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Components/BaseMediaItem',
|
|
5
|
+
component: BaseMediaItem,
|
|
6
|
+
args: {},
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const Template = (args) => ({
|
|
10
|
+
components: {
|
|
11
|
+
BaseMediaItem,
|
|
12
|
+
},
|
|
13
|
+
setup() {
|
|
14
|
+
return { args };
|
|
15
|
+
},
|
|
16
|
+
template: `
|
|
17
|
+
<BaseMediaItem v-bind="args" />
|
|
18
|
+
`,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const Demo = Template.bind({});
|
|
22
|
+
Demo.args = {
|
|
23
|
+
media: {
|
|
24
|
+
id: 'xxxxxxxxxxx',
|
|
25
|
+
file_name: 'picture.jpg',
|
|
26
|
+
url: 'https://images.unsplash.com/photo-1670139018938-af8bf748a1bc?auto=format&fit=crop&w=800&h=800&q=80',
|
|
27
|
+
mime_type: 'image',
|
|
28
|
+
size: 430 * 1024,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const PDF = Template.bind({});
|
|
33
|
+
PDF.args = {
|
|
34
|
+
media: {
|
|
35
|
+
id: 'xxxxxxxxxxx',
|
|
36
|
+
file_name: 'document.pdf',
|
|
37
|
+
url: 'https://images.unsplash.com/photo-1670139018938-af8bf748a1bc?auto=format&fit=crop&w=800&h=800&q=80',
|
|
38
|
+
mime_type: 'application/pdf',
|
|
39
|
+
size: 430 * 1024,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="rounded border border-slate-300 bg-white shadow">
|
|
3
|
+
<div class="relative flex">
|
|
4
|
+
<div class="shrink-0">
|
|
5
|
+
<BaseMediaPreview class="h-14 w-14 rounded-l" :media="media" />
|
|
6
|
+
</div>
|
|
7
|
+
<component
|
|
8
|
+
:is="url ? 'a' : 'p'"
|
|
9
|
+
:href="url"
|
|
10
|
+
target="_blank"
|
|
11
|
+
class="grow overflow-hidden px-3 pt-3"
|
|
12
|
+
>
|
|
13
|
+
<div class="text-left leading-tight">
|
|
14
|
+
<p class="grow truncate text-sm font-medium">
|
|
15
|
+
{{ name }}
|
|
16
|
+
</p>
|
|
17
|
+
<p class="shrink-0 text-[10px] font-medium text-slate-400">
|
|
18
|
+
{{ size }}
|
|
19
|
+
</p>
|
|
20
|
+
</div>
|
|
21
|
+
</component>
|
|
22
|
+
<div v-if="showRemove" class="shrink-0 p-2">
|
|
23
|
+
<button
|
|
24
|
+
type="button"
|
|
25
|
+
class="rounded-full bg-slate-400 p-1 text-white hover:bg-slate-500"
|
|
26
|
+
@click="$emit('delete')"
|
|
27
|
+
>
|
|
28
|
+
<BaseIcon icon="heroicons:x-mark-20-solid" class="h-4 w-4"></BaseIcon>
|
|
29
|
+
</button>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script lang="ts" setup>
|
|
36
|
+
import { Media } from '@/types/Media';
|
|
37
|
+
import { UploadedFile } from '@/types/UploadedFile';
|
|
38
|
+
import { PropType } from 'vue';
|
|
39
|
+
import { fileSizeFormat } from '@/utils';
|
|
40
|
+
import BaseMediaPreview from './BaseMediaPreview.vue';
|
|
41
|
+
import { BaseIcon } from '.';
|
|
42
|
+
|
|
43
|
+
defineEmits(['delete']);
|
|
44
|
+
|
|
45
|
+
const props = defineProps({
|
|
46
|
+
media: {
|
|
47
|
+
required: true,
|
|
48
|
+
type: Object as PropType<Media | UploadedFile>,
|
|
49
|
+
},
|
|
50
|
+
showRemove: {
|
|
51
|
+
default: true,
|
|
52
|
+
type: Boolean,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const name = computed(() => {
|
|
57
|
+
return props.media.file_name;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const size = computed(() => {
|
|
61
|
+
return fileSizeFormat(props.media.size);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const url = computed(() => {
|
|
65
|
+
if ('url' in props.media) {
|
|
66
|
+
return props.media.url;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return null;
|
|
70
|
+
});
|
|
71
|
+
</script>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import BaseApp from './BaseApp.vue';
|
|
2
|
+
import BaseMediaLibrary from './BaseMediaLibrary.vue';
|
|
3
|
+
|
|
4
|
+
const mediaModel = {
|
|
5
|
+
id: 'xxxxx',
|
|
6
|
+
file_name: 'picture0-1-2dfjjje-23refg-45t.jpg',
|
|
7
|
+
mime_type: 'image/jpg',
|
|
8
|
+
url: 'https://images.unsplash.com/photo-1670139018938-af8bf748a1bc?auto=format&fit=crop&w=1200&h=800&q=80',
|
|
9
|
+
size: 430 * 1024,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
title: 'Form/BaseMediaLibrary',
|
|
14
|
+
component: BaseMediaLibrary,
|
|
15
|
+
args: {
|
|
16
|
+
name: 'media',
|
|
17
|
+
max: 2,
|
|
18
|
+
min: 2,
|
|
19
|
+
acceptedExtensions: ['jpg', 'png'],
|
|
20
|
+
maxSize: 500 * 1024,
|
|
21
|
+
currentMedia: [
|
|
22
|
+
mediaModel,
|
|
23
|
+
{
|
|
24
|
+
id: '1',
|
|
25
|
+
url: '',
|
|
26
|
+
mime_type: 'application/pdf',
|
|
27
|
+
file_name: 'document.pdf',
|
|
28
|
+
size: 40012,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: '2',
|
|
32
|
+
url: '',
|
|
33
|
+
mime_type: 'application/excel',
|
|
34
|
+
file_name: 'finance-2022.xlsx',
|
|
35
|
+
size: 5461,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: '3',
|
|
39
|
+
url: '',
|
|
40
|
+
mime_type: 'image/png',
|
|
41
|
+
file_name: '34345-1.png',
|
|
42
|
+
size: 40012,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: '4',
|
|
46
|
+
url: '',
|
|
47
|
+
mime_type: 'audio/mp3',
|
|
48
|
+
file_name: 'test.mp3',
|
|
49
|
+
size: 792834,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const Template = (args) => ({
|
|
56
|
+
components: {
|
|
57
|
+
BaseApp,
|
|
58
|
+
BaseMediaLibrary,
|
|
59
|
+
},
|
|
60
|
+
setup() {
|
|
61
|
+
return { args };
|
|
62
|
+
},
|
|
63
|
+
template: `
|
|
64
|
+
<BaseMediaLibrary v-bind="args" />
|
|
65
|
+
<BaseApp></BaseApp>
|
|
66
|
+
`,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export const Demo = Template.bind({});
|
|
70
|
+
Demo.args = {};
|
|
71
|
+
|
|
72
|
+
export const Disabled = Template.bind({});
|
|
73
|
+
Disabled.args = {
|
|
74
|
+
disabled: true,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const Errors = Template.bind({});
|
|
78
|
+
Errors.args = {
|
|
79
|
+
errors: ['Whoops, you forgot the name of your mother!'],
|
|
80
|
+
};
|