@finema/core 3.22.0 → 3.23.2
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/module.d.mts +2 -0
- package/dist/module.json +2 -2
- package/dist/module.mjs +16 -2
- package/dist/runtime/components/App.vue +1 -1
- package/dist/runtime/components/DevToolsWindow/index.vue +1 -1
- package/dist/runtime/components/Dialog/index.d.vue.ts +1 -4
- package/dist/runtime/components/Dialog/index.vue +9 -10
- package/dist/runtime/components/Dialog/index.vue.d.ts +1 -4
- package/dist/runtime/components/Empty.d.vue.ts +0 -1
- package/dist/runtime/components/Empty.vue +6 -2
- package/dist/runtime/components/Empty.vue.d.ts +0 -1
- package/dist/runtime/components/FlexDeck/index.vue +86 -81
- package/dist/runtime/components/Form/FieldWrapper.vue +14 -14
- package/dist/runtime/components/Form/Fields.vue +10 -10
- package/dist/runtime/components/Form/InputCheckbox/index.vue +20 -19
- package/dist/runtime/components/Form/InputCheckboxGroup/index.vue +2 -1
- package/dist/runtime/components/Form/InputComponent/index.vue +2 -1
- package/dist/runtime/components/Form/InputCurrency/index.vue +51 -50
- package/dist/runtime/components/Form/InputDateTime/date_time_field.types.d.ts +1 -1
- package/dist/runtime/components/Form/InputDateTime/index.vue +69 -64
- package/dist/runtime/components/Form/InputDateTimeRange/date_range_time_field.types.d.ts +1 -1
- package/dist/runtime/components/Form/InputDateTimeRange/index.vue +62 -57
- package/dist/runtime/components/Form/InputMonth/index.vue +10 -5
- package/dist/runtime/components/Form/InputMonth/types.d.ts +1 -1
- package/dist/runtime/components/Form/InputNumber/index.vue +22 -21
- package/dist/runtime/components/Form/InputRadio/index.d.vue.ts +1 -1
- package/dist/runtime/components/Form/InputRadio/index.vue +2 -1
- package/dist/runtime/components/Form/InputRadio/index.vue.d.ts +1 -1
- package/dist/runtime/components/Form/InputRating/index.vue +2 -1
- package/dist/runtime/components/Form/InputSearch/index.vue +7 -2
- package/dist/runtime/components/Form/InputSelect/index.vue +112 -111
- package/dist/runtime/components/Form/InputSelectMultiple/index.vue +120 -119
- package/dist/runtime/components/Form/InputTags/index.vue +56 -55
- package/dist/runtime/components/Form/InputText/index.vue +88 -87
- package/dist/runtime/components/Form/InputTextarea/index.vue +20 -19
- package/dist/runtime/components/Form/InputTime/index.vue +45 -40
- package/dist/runtime/components/Form/InputTime/types.d.ts +1 -1
- package/dist/runtime/components/Form/InputToggle/index.vue +19 -18
- package/dist/runtime/components/Form/InputUploadDropzone/index.d.vue.ts +1 -4
- package/dist/runtime/components/Form/InputUploadDropzone/index.vue +40 -35
- package/dist/runtime/components/Form/InputUploadDropzone/index.vue.d.ts +1 -4
- package/dist/runtime/components/Form/InputUploadDropzone/types.d.ts +2 -2
- package/dist/runtime/components/Form/InputUploadDropzoneAuto/index.d.vue.ts +0 -5
- package/dist/runtime/components/Form/InputUploadDropzoneAuto/index.vue +63 -58
- package/dist/runtime/components/Form/InputUploadDropzoneAuto/index.vue.d.ts +0 -5
- package/dist/runtime/components/Form/InputUploadDropzoneAuto/types.d.ts +3 -3
- package/dist/runtime/components/Form/InputUploadDropzoneAutoMultiple/index.d.vue.ts +0 -5
- package/dist/runtime/components/Form/InputUploadDropzoneAutoMultiple/index.vue +69 -60
- package/dist/runtime/components/Form/InputUploadDropzoneAutoMultiple/index.vue.d.ts +0 -5
- package/dist/runtime/components/Form/InputUploadDropzoneAutoMultiple/types.d.ts +3 -3
- package/dist/runtime/components/Form/InputUploadImageAuto/index.d.vue.ts +1 -6
- package/dist/runtime/components/Form/InputUploadImageAuto/index.vue +63 -58
- package/dist/runtime/components/Form/InputUploadImageAuto/index.vue.d.ts +1 -6
- package/dist/runtime/components/Form/InputUploadImageAuto/types.d.ts +3 -3
- package/dist/runtime/components/Form/InputWYSIWYG/EditorImageUploadNode.vue +18 -18
- package/dist/runtime/components/Form/InputWYSIWYG/EditorLinkPopover.vue +65 -65
- package/dist/runtime/components/Form/InputWYSIWYG/README.md +96 -96
- package/dist/runtime/components/Form/InputWYSIWYG/index.vue +43 -42
- package/dist/runtime/components/Form/fileState/EmptyState.vue +21 -21
- package/dist/runtime/components/Form/fileState/FailedState.vue +37 -33
- package/dist/runtime/components/Form/fileState/LoadingState.vue +24 -24
- package/dist/runtime/components/Form/fileState/MultipleFilesState.d.vue.ts +4 -4
- package/dist/runtime/components/Form/fileState/MultipleFilesState.vue +79 -75
- package/dist/runtime/components/Form/fileState/MultipleFilesState.vue.d.ts +4 -4
- package/dist/runtime/components/Form/fileState/PreviewModal.vue +23 -23
- package/dist/runtime/components/Form/fileState/SuccessState.d.vue.ts +2 -2
- package/dist/runtime/components/Form/fileState/SuccessState.vue +8 -4
- package/dist/runtime/components/Form/fileState/SuccessState.vue.d.ts +2 -2
- package/dist/runtime/components/Form/fileState/useFilePicker.d.ts +34 -0
- package/dist/runtime/components/Form/fileState/useFilePicker.js +48 -0
- package/dist/runtime/components/Form/fileState/useFileState.d.ts +7 -9
- package/dist/runtime/components/Form/fileState/useFileState.js +40 -120
- package/dist/runtime/components/Form/fileState/useUploadImageState.d.ts +7 -8
- package/dist/runtime/components/Form/fileState/useUploadImageState.js +28 -300
- package/dist/runtime/components/Form/fileState/useUploadState.d.ts +11 -7
- package/dist/runtime/components/Form/fileState/useUploadState.js +58 -131
- package/dist/runtime/components/Form/fileState/useUploadStateMultiple.d.ts +12 -18
- package/dist/runtime/components/Form/fileState/useUploadStateMultiple.js +89 -222
- package/dist/runtime/components/Form/index.vue +5 -5
- package/dist/runtime/components/Form/types.d.ts +2 -0
- package/dist/runtime/components/Image.vue +28 -28
- package/dist/runtime/components/Loader.d.vue.ts +1 -0
- package/dist/runtime/components/Loader.vue.d.ts +1 -0
- package/dist/runtime/components/Table/Base.d.vue.ts +15 -1
- package/dist/runtime/components/Table/Base.vue +118 -118
- package/dist/runtime/components/Table/Base.vue.d.ts +15 -1
- package/dist/runtime/components/Table/ColumnDate.vue +1 -1
- package/dist/runtime/components/Table/ColumnDateTime.vue +1 -1
- package/dist/runtime/components/Table/ColumnImage.vue +4 -4
- package/dist/runtime/components/Table/ColumnText.d.vue.ts +2 -3
- package/dist/runtime/components/Table/ColumnText.vue +11 -11
- package/dist/runtime/components/Table/ColumnText.vue.d.ts +2 -3
- package/dist/runtime/components/Table/Pagination.vue +70 -67
- package/dist/runtime/components/Table/Simple.vue +19 -19
- package/dist/runtime/components/Table/index.d.vue.ts +1 -1
- package/dist/runtime/components/Table/index.vue +37 -33
- package/dist/runtime/components/Table/index.vue.d.ts +1 -1
- package/dist/runtime/composables/loaderList.d.ts +2 -1
- package/dist/runtime/composables/loaderList.js +2 -1
- package/dist/runtime/composables/loaderObject.d.ts +2 -1
- package/dist/runtime/composables/loaderObject.js +2 -1
- package/dist/runtime/composables/loaderPage.js +194 -344
- package/dist/runtime/composables/useConfig.js +4 -3
- package/dist/runtime/composables/useDialog.js +5 -1
- package/dist/runtime/composables/useForm.d.ts +1 -1
- package/dist/runtime/composables/useForm.js +1 -1
- package/dist/runtime/composables/useMessages.d.ts +10 -0
- package/dist/runtime/composables/useMessages.js +10 -0
- package/dist/runtime/helpers/apiPageHelper.d.ts +0 -1
- package/dist/runtime/helpers/uploadCore.d.ts +54 -0
- package/dist/runtime/helpers/uploadCore.js +84 -0
- package/dist/runtime/i18n/valibot.d.ts +5 -68
- package/dist/runtime/i18n/valibot.js +33 -29
- package/dist/runtime/locale/en.d.ts +2 -0
- package/dist/runtime/locale/en.js +42 -0
- package/dist/runtime/locale/index.d.ts +7 -0
- package/dist/runtime/locale/index.js +8 -0
- package/dist/runtime/locale/th.d.ts +43 -0
- package/dist/runtime/locale/th.js +42 -0
- package/dist/runtime/server/tsconfig.json +3 -3
- package/dist/runtime/utils/ParamHelper.js +1 -1
- package/dist/runtime/utils/StringHelper.js +1 -4
- package/dist/runtime/utils/TimeHelper.js +2 -2
- package/dist/runtime/utils/lodash.js +2 -2
- package/package.json +20 -12
|
@@ -1,82 +1,83 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div :class="theme.multipleFilesWrapper()">
|
|
3
|
-
<div
|
|
4
|
-
v-for="(item, index) in fileItems"
|
|
5
|
-
:key="index"
|
|
6
|
-
:class="theme.fileItemWrapper()"
|
|
7
|
-
>
|
|
8
|
-
<!-- Uploading State -->
|
|
9
|
-
<div
|
|
10
|
-
v-if="item.state === UploadState.UPLOADING"
|
|
11
|
-
:class="theme.uploadingItemWrapper()"
|
|
12
|
-
>
|
|
13
|
-
<div :class="theme.uploadingIconWrapper()">
|
|
14
|
-
<Icon
|
|
15
|
-
:name="icons.filePreviewIcon"
|
|
16
|
-
:class="theme.uploadingIconClass()"
|
|
17
|
-
/>
|
|
18
|
-
</div>
|
|
19
|
-
<div :class="theme.uploadingTextWrapper()">
|
|
20
|
-
<h1 class="truncate font-bold">
|
|
21
|
-
{{ item.file.name }}
|
|
22
|
-
</h1>
|
|
23
|
-
<div class="flex items-center gap-2">
|
|
24
|
-
<div :class="theme.progressBarWrapper()">
|
|
25
|
-
<div
|
|
26
|
-
:class="theme.progressBarFill()"
|
|
27
|
-
:style="{ width: `${item.progress}%` }"
|
|
28
|
-
/>
|
|
29
|
-
</div>
|
|
30
|
-
<p class="text-sm text-gray-400">
|
|
31
|
-
{{ item.progress }}%
|
|
32
|
-
</p>
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
|
|
37
|
-
<!-- Error State -->
|
|
38
|
-
<div
|
|
39
|
-
v-else-if="item.state === UploadState.ERROR"
|
|
40
|
-
:class="theme.errorItemWrapper()"
|
|
41
|
-
>
|
|
42
|
-
<div :class="theme.errorIconWrapper()">
|
|
43
|
-
<Icon
|
|
44
|
-
:name="icons.errorIcon"
|
|
45
|
-
:class="theme.errorIconClass()"
|
|
46
|
-
/>
|
|
47
|
-
</div>
|
|
48
|
-
<div :class="theme.errorTextWrapper()">
|
|
49
|
-
<h1 class="truncate font-bold">
|
|
50
|
-
{{ item.file.name }}
|
|
51
|
-
</h1>
|
|
52
|
-
<p class="text-error-500 text-sm">
|
|
53
|
-
{{ item.error || uploadFailedLabel }}
|
|
54
|
-
</p>
|
|
55
|
-
</div>
|
|
56
|
-
<div :class="theme.errorActionWrapper()">
|
|
57
|
-
<Button
|
|
58
|
-
variant="link"
|
|
59
|
-
:icon="icons.actionRetryIcon"
|
|
60
|
-
:class="theme.actionRetryBtnClass()"
|
|
61
|
-
color="primary"
|
|
62
|
-
@click="$emit('retry', index)"
|
|
63
|
-
>
|
|
64
|
-
{{ retryLabel }}
|
|
65
|
-
</Button>
|
|
66
|
-
<Icon
|
|
67
|
-
v-if="!disabled && !readonly"
|
|
68
|
-
:name="icons.actionDeleteIcon"
|
|
69
|
-
:class="theme.actionIconClass()"
|
|
70
|
-
title="
|
|
71
|
-
@click="$emit('delete', index)"
|
|
72
|
-
/>
|
|
73
|
-
</div>
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
76
|
-
</div>
|
|
2
|
+
<div :class="theme.multipleFilesWrapper()">
|
|
3
|
+
<div
|
|
4
|
+
v-for="(item, index) in fileItems"
|
|
5
|
+
:key="index"
|
|
6
|
+
:class="theme.fileItemWrapper()"
|
|
7
|
+
>
|
|
8
|
+
<!-- Uploading State -->
|
|
9
|
+
<div
|
|
10
|
+
v-if="item.state === UploadState.UPLOADING"
|
|
11
|
+
:class="theme.uploadingItemWrapper()"
|
|
12
|
+
>
|
|
13
|
+
<div :class="theme.uploadingIconWrapper()">
|
|
14
|
+
<Icon
|
|
15
|
+
:name="icons.filePreviewIcon"
|
|
16
|
+
:class="theme.uploadingIconClass()"
|
|
17
|
+
/>
|
|
18
|
+
</div>
|
|
19
|
+
<div :class="theme.uploadingTextWrapper()">
|
|
20
|
+
<h1 class="truncate font-bold">
|
|
21
|
+
{{ item.file.name }}
|
|
22
|
+
</h1>
|
|
23
|
+
<div class="flex items-center gap-2">
|
|
24
|
+
<div :class="theme.progressBarWrapper()">
|
|
25
|
+
<div
|
|
26
|
+
:class="theme.progressBarFill()"
|
|
27
|
+
:style="{ width: `${item.progress}%` }"
|
|
28
|
+
/>
|
|
29
|
+
</div>
|
|
30
|
+
<p class="text-sm text-gray-400">
|
|
31
|
+
{{ item.progress }}%
|
|
32
|
+
</p>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<!-- Error State -->
|
|
38
|
+
<div
|
|
39
|
+
v-else-if="item.state === UploadState.ERROR"
|
|
40
|
+
:class="theme.errorItemWrapper()"
|
|
41
|
+
>
|
|
42
|
+
<div :class="theme.errorIconWrapper()">
|
|
43
|
+
<Icon
|
|
44
|
+
:name="icons.errorIcon"
|
|
45
|
+
:class="theme.errorIconClass()"
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
48
|
+
<div :class="theme.errorTextWrapper()">
|
|
49
|
+
<h1 class="truncate font-bold">
|
|
50
|
+
{{ item.file.name }}
|
|
51
|
+
</h1>
|
|
52
|
+
<p class="text-error-500 text-sm">
|
|
53
|
+
{{ item.error || uploadFailedLabel }}
|
|
54
|
+
</p>
|
|
55
|
+
</div>
|
|
56
|
+
<div :class="theme.errorActionWrapper()">
|
|
57
|
+
<Button
|
|
58
|
+
variant="link"
|
|
59
|
+
:icon="icons.actionRetryIcon"
|
|
60
|
+
:class="theme.actionRetryBtnClass()"
|
|
61
|
+
color="primary"
|
|
62
|
+
@click="$emit('retry', index)"
|
|
63
|
+
>
|
|
64
|
+
{{ retryLabel }}
|
|
65
|
+
</Button>
|
|
66
|
+
<Icon
|
|
67
|
+
v-if="!disabled && !readonly"
|
|
68
|
+
:name="icons.actionDeleteIcon"
|
|
69
|
+
:class="theme.actionIconClass()"
|
|
70
|
+
:title="messages.upload.remove"
|
|
71
|
+
@click="$emit('delete', index)"
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
77
|
</template>
|
|
78
78
|
|
|
79
79
|
<script setup>
|
|
80
|
+
import { useCoreMessages } from "#core/composables/useMessages";
|
|
80
81
|
import { useUiIconConfig } from "#core/composables/useConfig";
|
|
81
82
|
import { UploadState } from "./useUploadStateMultiple";
|
|
82
83
|
defineEmits(["preview", "download", "delete", "retry"]);
|
|
@@ -89,4 +90,7 @@ defineProps({
|
|
|
89
90
|
retryLabel: { type: String, required: false }
|
|
90
91
|
});
|
|
91
92
|
const icons = useUiIconConfig("uploadFileDropzone");
|
|
93
|
+
const {
|
|
94
|
+
messages
|
|
95
|
+
} = useCoreMessages();
|
|
92
96
|
</script>
|
|
@@ -17,14 +17,14 @@ interface Props {
|
|
|
17
17
|
}
|
|
18
18
|
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
19
19
|
delete: (index: number) => any;
|
|
20
|
-
download: (value: IFileValue) => any;
|
|
21
|
-
preview: (value: IFileValue) => any;
|
|
22
20
|
retry: (index: number) => any;
|
|
21
|
+
preview: (value: IFileValue) => any;
|
|
22
|
+
download: (value: IFileValue) => any;
|
|
23
23
|
}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
|
|
24
24
|
onDelete?: ((index: number) => any) | undefined;
|
|
25
|
-
onDownload?: ((value: IFileValue) => any) | undefined;
|
|
26
|
-
onPreview?: ((value: IFileValue) => any) | undefined;
|
|
27
25
|
onRetry?: ((index: number) => any) | undefined;
|
|
26
|
+
onPreview?: ((value: IFileValue) => any) | undefined;
|
|
27
|
+
onDownload?: ((value: IFileValue) => any) | undefined;
|
|
28
28
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
29
29
|
declare const _default: typeof __VLS_export;
|
|
30
30
|
export default _default;
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Modal
|
|
3
|
-
:close="{ onClick: () => emits('close', false) }"
|
|
4
|
-
:dismissible="false"
|
|
5
|
-
:title="value?.name"
|
|
2
|
+
<Modal
|
|
3
|
+
:close="{ onClick: () => emits('close', false) }"
|
|
4
|
+
:dismissible="false"
|
|
5
|
+
:title="value?.name"
|
|
6
6
|
:ui="{
|
|
7
7
|
content: 'max-w-3xl'
|
|
8
|
-
}"
|
|
9
|
-
>
|
|
10
|
-
<template #body>
|
|
11
|
-
<div class="flex justify-center">
|
|
12
|
-
<img
|
|
13
|
-
v-if="value && isImageFromPath(value.path)"
|
|
14
|
-
:src="value.url"
|
|
15
|
-
alt="img-preview"
|
|
16
|
-
class="max-h-96 max-w-full rounded-lg"
|
|
17
|
-
/>
|
|
18
|
-
<video
|
|
19
|
-
v-else-if="value && isVideoFromPath(value.path)"
|
|
20
|
-
:src="value.url"
|
|
21
|
-
controls
|
|
22
|
-
class="max-h-96 max-w-full"
|
|
23
|
-
/>
|
|
24
|
-
</div>
|
|
25
|
-
</template>
|
|
26
|
-
</Modal>
|
|
8
|
+
}"
|
|
9
|
+
>
|
|
10
|
+
<template #body>
|
|
11
|
+
<div class="flex justify-center">
|
|
12
|
+
<img
|
|
13
|
+
v-if="value && isImageFromPath(value.path)"
|
|
14
|
+
:src="value.url"
|
|
15
|
+
alt="img-preview"
|
|
16
|
+
class="max-h-96 max-w-full rounded-lg"
|
|
17
|
+
/>
|
|
18
|
+
<video
|
|
19
|
+
v-else-if="value && isVideoFromPath(value.path)"
|
|
20
|
+
:src="value.url"
|
|
21
|
+
controls
|
|
22
|
+
class="max-h-96 max-w-full"
|
|
23
|
+
/>
|
|
24
|
+
</div>
|
|
25
|
+
</template>
|
|
26
|
+
</Modal>
|
|
27
27
|
</template>
|
|
28
28
|
|
|
29
29
|
<script setup>
|
|
@@ -7,12 +7,12 @@ interface Props {
|
|
|
7
7
|
}
|
|
8
8
|
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
9
9
|
delete: () => any;
|
|
10
|
-
download: () => any;
|
|
11
10
|
preview: () => any;
|
|
11
|
+
download: () => any;
|
|
12
12
|
}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
|
|
13
13
|
onDelete?: (() => any) | undefined;
|
|
14
|
-
onDownload?: (() => any) | undefined;
|
|
15
14
|
onPreview?: (() => any) | undefined;
|
|
15
|
+
onDownload?: (() => any) | undefined;
|
|
16
16
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
17
17
|
declare const _default: typeof __VLS_export;
|
|
18
18
|
export default _default;
|
|
@@ -44,27 +44,27 @@
|
|
|
44
44
|
<Icon
|
|
45
45
|
:name="icons.actionPreviewIcon"
|
|
46
46
|
:class="theme.actionIconClass()"
|
|
47
|
-
title="
|
|
47
|
+
:title="messages.upload.preview"
|
|
48
48
|
/>
|
|
49
49
|
</a>
|
|
50
50
|
<Icon
|
|
51
51
|
v-if="isImageFromPath(value.path) || isVideoFromPath(value.path)"
|
|
52
52
|
:name="icons.actionPreviewIcon"
|
|
53
53
|
:class="theme.actionIconClass()"
|
|
54
|
-
title="
|
|
54
|
+
:title="messages.upload.preview"
|
|
55
55
|
@click="$emit('preview')"
|
|
56
56
|
/>
|
|
57
57
|
<Icon
|
|
58
58
|
:name="icons.actionDownloadIcon"
|
|
59
59
|
:class="theme.actionIconClass()"
|
|
60
|
-
title="
|
|
60
|
+
:title="messages.upload.download"
|
|
61
61
|
@click="$emit('download')"
|
|
62
62
|
/>
|
|
63
63
|
<Icon
|
|
64
64
|
v-if="!disabled && !readonly"
|
|
65
65
|
:name="icons.actionDeleteIcon"
|
|
66
66
|
:class="theme.actionIconClass()"
|
|
67
|
-
title="
|
|
67
|
+
:title="messages.upload.remove"
|
|
68
68
|
@click="$emit('delete')"
|
|
69
69
|
/>
|
|
70
70
|
</div>
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
</template>
|
|
74
74
|
|
|
75
75
|
<script setup>
|
|
76
|
+
import { useCoreMessages } from "#core/composables/useMessages";
|
|
76
77
|
import {
|
|
77
78
|
isImageFromPath,
|
|
78
79
|
isPDFFromPath,
|
|
@@ -92,4 +93,7 @@ const getFileSizeFromValue = (fileValue) => {
|
|
|
92
93
|
const allocate = useFileSize(fileValue.size || 0);
|
|
93
94
|
return allocate.isSelectedFileUseMb.value ? `${allocate.selectedFileSizeMb.value} MB` : `${allocate.selectedFileSizeKb.value} KB`;
|
|
94
95
|
};
|
|
96
|
+
const {
|
|
97
|
+
messages
|
|
98
|
+
} = useCoreMessages();
|
|
95
99
|
</script>
|
|
@@ -7,12 +7,12 @@ interface Props {
|
|
|
7
7
|
}
|
|
8
8
|
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
9
9
|
delete: () => any;
|
|
10
|
-
download: () => any;
|
|
11
10
|
preview: () => any;
|
|
11
|
+
download: () => any;
|
|
12
12
|
}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
|
|
13
13
|
onDelete?: (() => any) | undefined;
|
|
14
|
-
onDownload?: (() => any) | undefined;
|
|
15
14
|
onPreview?: (() => any) | undefined;
|
|
15
|
+
onDownload?: (() => any) | undefined;
|
|
16
16
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
17
17
|
declare const _default: typeof __VLS_export;
|
|
18
18
|
export default _default;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ComputedRef, Ref, TemplateRef } from 'vue';
|
|
2
|
+
import type { IFieldProps, IFileValue } from '#core/components/Form/types';
|
|
3
|
+
/**
|
|
4
|
+
* What an upload field hands to its state composable
|
|
5
|
+
*/
|
|
6
|
+
export interface IUploadFieldContext<Props, Value> {
|
|
7
|
+
props: Props;
|
|
8
|
+
emits: (event: any, ...args: any[]) => void;
|
|
9
|
+
onChange: (value: Value | undefined) => void;
|
|
10
|
+
setErrors: (error: string) => void;
|
|
11
|
+
value: Ref<any>;
|
|
12
|
+
acceptedFileTypes: ComputedRef<string | undefined>;
|
|
13
|
+
wrapperProps: ComputedRef<IFieldProps>;
|
|
14
|
+
dropzoneRef: TemplateRef<HTMLDivElement | undefined>;
|
|
15
|
+
}
|
|
16
|
+
interface IFilePickerOptions {
|
|
17
|
+
multiple: boolean;
|
|
18
|
+
defaultAccept?: string;
|
|
19
|
+
/** Dropped files are ignored while this is false (e.g. a single-file field that already has a file) */
|
|
20
|
+
canDrop?: () => boolean;
|
|
21
|
+
onFiles: (files: File[]) => void | Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The part every upload field shares: file dialog, dropzone, preview, and the disabled/readonly guard
|
|
25
|
+
*/
|
|
26
|
+
export declare const useFilePicker: (context: Pick<IUploadFieldContext<unknown, unknown>, "acceptedFileTypes" | "wrapperProps" | "dropzoneRef">, options: IFilePickerOptions) => {
|
|
27
|
+
fileDialog: import("@vueuse/core").UseFileDialogReturn;
|
|
28
|
+
dropzone: import("@vueuse/core").UseDropZoneReturn;
|
|
29
|
+
isLocked: () => boolean;
|
|
30
|
+
handleInputChange: (event: Event) => void;
|
|
31
|
+
handleOpenFile: () => void;
|
|
32
|
+
openPreview: (fileValue: IFileValue | File) => void;
|
|
33
|
+
};
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { useDropZone, useFileDialog } from "@vueuse/core";
|
|
2
|
+
import PreviewModal from "./PreviewModal.vue";
|
|
3
|
+
import { useOverlay } from "#imports";
|
|
4
|
+
export const useFilePicker = (context, options) => {
|
|
5
|
+
const previewModal = useOverlay().create(PreviewModal);
|
|
6
|
+
const isLocked = () => !!(context.wrapperProps.value.disabled || context.wrapperProps.value.readonly);
|
|
7
|
+
const pick = (files) => options.onFiles(options.multiple ? files : files.slice(0, 1));
|
|
8
|
+
const fileDialog = useFileDialog({
|
|
9
|
+
accept: context.acceptedFileTypes.value || options.defaultAccept || "",
|
|
10
|
+
directory: false,
|
|
11
|
+
multiple: options.multiple
|
|
12
|
+
});
|
|
13
|
+
const dropzone = useDropZone(context.dropzoneRef, {
|
|
14
|
+
onDrop: (files) => {
|
|
15
|
+
if (isLocked() || !files?.length || options.canDrop?.() === false) return;
|
|
16
|
+
pick(files);
|
|
17
|
+
},
|
|
18
|
+
multiple: options.multiple,
|
|
19
|
+
preventDefaultForUnhandled: false
|
|
20
|
+
});
|
|
21
|
+
fileDialog.onChange(async (files) => {
|
|
22
|
+
if (!files?.length) return;
|
|
23
|
+
await pick(Array.from(files));
|
|
24
|
+
if (options.multiple) fileDialog.reset();
|
|
25
|
+
});
|
|
26
|
+
const handleInputChange = (event) => {
|
|
27
|
+
if (isLocked()) return;
|
|
28
|
+
const files = event.target.files;
|
|
29
|
+
if (files?.length) pick(Array.from(files));
|
|
30
|
+
};
|
|
31
|
+
const handleOpenFile = () => {
|
|
32
|
+
if (isLocked()) return;
|
|
33
|
+
fileDialog.open();
|
|
34
|
+
};
|
|
35
|
+
const openPreview = (fileValue) => {
|
|
36
|
+
previewModal.open({
|
|
37
|
+
value: fileValue
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
return {
|
|
41
|
+
fileDialog,
|
|
42
|
+
dropzone,
|
|
43
|
+
isLocked,
|
|
44
|
+
handleInputChange,
|
|
45
|
+
handleOpenFile,
|
|
46
|
+
openPreview
|
|
47
|
+
};
|
|
48
|
+
};
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import type { TemplateRef } from 'vue';
|
|
2
1
|
import type { IUploadDropzoneProps } from '../InputUploadDropzone/types.js';
|
|
3
|
-
import type
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export declare const useFileState: (props: IUploadDropzoneProps, emits: any, onChange: (value: IFileValue | undefined) => void, setErrors: (error: string) => void, value: any, acceptedFileTypes: any, wrapperProps: any, dropzoneRef: TemplateRef<HTMLDivElement | undefined>) => {
|
|
2
|
+
import { type IUploadFieldContext } from './useFilePicker.js';
|
|
3
|
+
import { UploadState } from '#core/helpers/uploadCore';
|
|
4
|
+
export { UploadState };
|
|
5
|
+
/**
|
|
6
|
+
* Keeps the picked `File` itself as the field value (nothing is uploaded)
|
|
7
|
+
*/
|
|
8
|
+
export declare const useFileState: (context: IUploadFieldContext<IUploadDropzoneProps, File>) => {
|
|
11
9
|
currentState: import("vue").ComputedRef<UploadState.EMPTY | UploadState.SUCCESS>;
|
|
12
10
|
isEmpty: import("vue").ComputedRef<boolean>;
|
|
13
11
|
isSuccess: import("vue").ComputedRef<boolean>;
|
|
@@ -1,138 +1,58 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
1
|
+
import { useFilePicker } from "./useFilePicker.js";
|
|
2
|
+
import { UploadState, validateFile } from "#core/helpers/uploadCore";
|
|
3
|
+
import { useCoreMessages } from "#core/composables/useMessages";
|
|
4
|
+
import { computed, onScopeDispose, ref } from "#imports";
|
|
5
|
+
export { UploadState };
|
|
6
|
+
export const useFileState = (context) => {
|
|
7
|
+
const {
|
|
8
|
+
props,
|
|
9
|
+
emits,
|
|
10
|
+
onChange,
|
|
11
|
+
setErrors,
|
|
12
|
+
value
|
|
13
|
+
} = context;
|
|
14
|
+
const {
|
|
15
|
+
messages
|
|
16
|
+
} = useCoreMessages();
|
|
15
17
|
const selectedFile = ref();
|
|
16
18
|
const currentObjectUrl = ref("");
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (acceptedType.startsWith(".")) {
|
|
25
|
-
const extension = acceptedType.slice(1).toLowerCase();
|
|
26
|
-
return fileExtension === extension;
|
|
27
|
-
} else if (!acceptedType.includes("/") && !acceptedType.includes("*")) {
|
|
28
|
-
return fileExtension === acceptedType.toLowerCase();
|
|
29
|
-
}
|
|
30
|
-
if (acceptedType.endsWith("/*")) {
|
|
31
|
-
const baseType = acceptedType.slice(0, -2);
|
|
32
|
-
return file.type.startsWith(baseType + "/");
|
|
33
|
-
}
|
|
34
|
-
return file.type === acceptedType;
|
|
35
|
-
});
|
|
36
|
-
if (!isValidFileType) {
|
|
37
|
-
setErrors("\u0E1B\u0E23\u0E30\u0E40\u0E20\u0E17\u0E44\u0E1F\u0E25\u0E4C\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07 (\u0E23\u0E2D\u0E07\u0E23\u0E31\u0E1A\u0E40\u0E09\u0E1E\u0E32\u0E30 " + acceptedTypesList.join(", ") + ")");
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
if (props.maxSize) {
|
|
42
|
-
const maxSizeBytes = (fileAllocate.acceptFileSizeKb.value || 0) * 1024;
|
|
43
|
-
if (file.size > maxSizeBytes) {
|
|
44
|
-
if (fileAllocate.isAcceptFileUseMb.value) {
|
|
45
|
-
setErrors(`\u0E02\u0E19\u0E32\u0E14\u0E44\u0E1F\u0E25\u0E4C\u0E15\u0E49\u0E2D\u0E07\u0E44\u0E21\u0E48\u0E40\u0E01\u0E34\u0E19 ${fileAllocate.acceptFileSizeMb.value} MB`);
|
|
46
|
-
} else {
|
|
47
|
-
setErrors(`\u0E02\u0E19\u0E32\u0E14\u0E44\u0E1F\u0E25\u0E4C\u0E15\u0E49\u0E2D\u0E07\u0E44\u0E21\u0E48\u0E40\u0E01\u0E34\u0E19 ${fileAllocate.acceptFileSizeKb.value} KB`);
|
|
48
|
-
}
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
setErrors("");
|
|
53
|
-
return true;
|
|
19
|
+
const currentState = computed(() => value.value ? UploadState.SUCCESS : UploadState.EMPTY);
|
|
20
|
+
const isEmpty = computed(() => currentState.value === UploadState.EMPTY);
|
|
21
|
+
const isSuccess = computed(() => currentState.value === UploadState.SUCCESS);
|
|
22
|
+
const cleanup = () => {
|
|
23
|
+
if (!currentObjectUrl.value) return;
|
|
24
|
+
URL.revokeObjectURL(currentObjectUrl.value);
|
|
25
|
+
currentObjectUrl.value = "";
|
|
54
26
|
};
|
|
55
27
|
const processFile = (file) => {
|
|
56
|
-
|
|
28
|
+
const error = validateFile(file, props, messages.value.upload);
|
|
29
|
+
setErrors(error ?? "");
|
|
30
|
+
if (error) return;
|
|
57
31
|
selectedFile.value = file;
|
|
58
32
|
emits("change", file);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
const objectUrl = URL.createObjectURL(file);
|
|
63
|
-
currentObjectUrl.value = objectUrl;
|
|
33
|
+
cleanup();
|
|
34
|
+
currentObjectUrl.value = URL.createObjectURL(file);
|
|
64
35
|
value.value = file;
|
|
65
36
|
emits("success", file);
|
|
66
37
|
};
|
|
67
|
-
const
|
|
68
|
-
if (wrapperProps.value.disabled || wrapperProps.value.readonly || !files?.length || !isEmpty.value) return;
|
|
69
|
-
const file = files[0];
|
|
70
|
-
if (file) {
|
|
71
|
-
processFile(file);
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
const fileDialog = useFileDialog({
|
|
75
|
-
accept: acceptedFileTypes.value || "",
|
|
76
|
-
directory: false,
|
|
77
|
-
multiple: false
|
|
78
|
-
});
|
|
79
|
-
const dropzone = useDropZone(dropzoneRef, {
|
|
80
|
-
onDrop: handleFileDrop,
|
|
81
|
-
// dataTypes: typeof props.accept === 'string' ? [props.accept] : props.accept,
|
|
38
|
+
const picker = useFilePicker(context, {
|
|
82
39
|
multiple: false,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (value.value) return "success" /* SUCCESS */;
|
|
87
|
-
return "empty" /* EMPTY */;
|
|
88
|
-
});
|
|
89
|
-
const isEmpty = computed(() => currentState.value === "empty" /* EMPTY */);
|
|
90
|
-
const isSuccess = computed(() => currentState.value === "success" /* SUCCESS */);
|
|
91
|
-
const handleInputChange = (event) => {
|
|
92
|
-
if (wrapperProps.value.disabled || wrapperProps.value.readonly) return;
|
|
93
|
-
const file = event.target.files?.[0];
|
|
94
|
-
if (file) {
|
|
95
|
-
processFile(file);
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
fileDialog.onChange((files) => {
|
|
99
|
-
if (files?.length) {
|
|
100
|
-
processFile(files[0]);
|
|
40
|
+
canDrop: () => isEmpty.value,
|
|
41
|
+
onFiles: ([file]) => {
|
|
42
|
+
if (file) processFile(file);
|
|
101
43
|
}
|
|
102
44
|
});
|
|
103
|
-
const handleOpenFile = () => {
|
|
104
|
-
if (wrapperProps.value.disabled || wrapperProps.value.readonly) return;
|
|
105
|
-
fileDialog.open();
|
|
106
|
-
};
|
|
107
45
|
const handleDeleteFile = () => {
|
|
108
|
-
fileDialog.reset();
|
|
109
|
-
|
|
110
|
-
URL.revokeObjectURL(currentObjectUrl.value);
|
|
111
|
-
currentObjectUrl.value = "";
|
|
112
|
-
}
|
|
46
|
+
picker.fileDialog.reset();
|
|
47
|
+
cleanup();
|
|
113
48
|
selectedFile.value = void 0;
|
|
114
49
|
onChange(void 0);
|
|
115
50
|
emits("delete");
|
|
116
51
|
};
|
|
117
52
|
const handleRetryUpload = () => {
|
|
118
|
-
if (selectedFile.value)
|
|
119
|
-
processFile(selectedFile.value);
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
const handlePreview = () => {
|
|
123
|
-
previewModal.open({
|
|
124
|
-
value: value.value
|
|
125
|
-
});
|
|
126
|
-
};
|
|
127
|
-
const cleanup = () => {
|
|
128
|
-
if (currentObjectUrl.value) {
|
|
129
|
-
URL.revokeObjectURL(currentObjectUrl.value);
|
|
130
|
-
currentObjectUrl.value = "";
|
|
131
|
-
}
|
|
53
|
+
if (selectedFile.value) processFile(selectedFile.value);
|
|
132
54
|
};
|
|
133
|
-
|
|
134
|
-
window.addEventListener("beforeunload", cleanup);
|
|
135
|
-
}
|
|
55
|
+
onScopeDispose(cleanup);
|
|
136
56
|
return {
|
|
137
57
|
// State
|
|
138
58
|
currentState,
|
|
@@ -141,13 +61,13 @@ export const useFileState = (props, emits, onChange, setErrors, value, acceptedF
|
|
|
141
61
|
selectedFile,
|
|
142
62
|
currentObjectUrl,
|
|
143
63
|
// Upload utilities
|
|
144
|
-
dropzone,
|
|
64
|
+
dropzone: picker.dropzone,
|
|
145
65
|
// Handlers
|
|
146
|
-
handleInputChange,
|
|
147
|
-
handleOpenFile,
|
|
66
|
+
handleInputChange: picker.handleInputChange,
|
|
67
|
+
handleOpenFile: picker.handleOpenFile,
|
|
148
68
|
handleDeleteFile,
|
|
149
69
|
handleRetryUpload,
|
|
150
|
-
handlePreview,
|
|
70
|
+
handlePreview: () => picker.openPreview(value.value),
|
|
151
71
|
// Cleanup
|
|
152
72
|
cleanup
|
|
153
73
|
};
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import type { TemplateRef } from 'vue';
|
|
2
1
|
import type { IUploadImageAutoProps } from '../InputUploadImageAuto/types.js';
|
|
2
|
+
import type { IUploadFieldContext } from './useFilePicker.js';
|
|
3
|
+
import { UploadState } from './useUploadState.js';
|
|
3
4
|
import type { IFileValue } from '#core/components/Form/types';
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
export declare const useUploadImageState: (props: IUploadImageAutoProps, emits: any, onChange: (value: IFileValue | undefined) => void, setErrors: (error: string) => void, value: any, acceptedFileTypes: any, wrapperProps: any, dropzoneRef: TemplateRef<HTMLDivElement | undefined>) => {
|
|
5
|
+
export { UploadState };
|
|
6
|
+
/**
|
|
7
|
+
* `useUploadState` for images: defaults to `image/*` and checks `props.dimensions` before uploading
|
|
8
|
+
*/
|
|
9
|
+
export declare const useUploadImageState: (context: IUploadFieldContext<IUploadImageAutoProps, IFileValue>) => {
|
|
11
10
|
currentState: import("vue").ComputedRef<UploadState>;
|
|
12
11
|
isEmpty: import("vue").ComputedRef<boolean>;
|
|
13
12
|
isUploading: import("vue").ComputedRef<boolean>;
|