@finema/core 1.4.97 → 1.4.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/module.json +1 -1
- package/dist/module.mjs +6 -3
- package/dist/runtime/components/Form/InputUploadDropzone/index.vue +9 -12
- package/dist/runtime/components/Form/InputUploadDropzoneAuto/index.vue +14 -33
- package/dist/runtime/components/Form/InputUploadDropzoneAutoMultiple/Item.vue +15 -33
- package/dist/runtime/components/Form/InputUploadDropzoneAutoMultiple/index.vue +25 -9
- package/dist/runtime/components/Form/InputUploadDropzoneImageAutoMultiple/index.vue +24 -8
- package/dist/runtime/components/Form/InputUploadDropzoneImageAutoMultiple/item.vue +9 -28
- package/dist/runtime/components/Form/InputUploadFileClassic/index.vue +2 -9
- package/dist/runtime/components/Form/InputUploadFileClassicAuto/index.vue +18 -53
- package/dist/runtime/components/QRCode.vue +1 -1
- package/dist/runtime/components/Slideover/index.vue +1 -1
- package/dist/runtime/helpers/componentHelper.d.ts +18 -8
- package/dist/runtime/helpers/componentHelper.mjs +47 -8
- package/dist/runtime/utils/ArrayHelper.spec.mjs +1 -5
- package/dist/runtime/utils/FileHelper.spec.d.ts +1 -0
- package/dist/runtime/utils/FileHelper.spec.mjs +14 -0
- package/dist/runtime/utils/ObjectHelper.spec.d.ts +1 -0
- package/dist/runtime/utils/ObjectHelper.spec.mjs +52 -0
- package/dist/runtime/utils/ParamHelper.spec.d.ts +1 -0
- package/dist/runtime/utils/ParamHelper.spec.mjs +78 -0
- package/dist/runtime/utils/StringHelper.spec.d.ts +1 -0
- package/dist/runtime/utils/StringHelper.spec.mjs +76 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineNuxtModule, createResolver, installModule, addPlugin, addComponentsDir, addImportsDir } from '@nuxt/kit';
|
|
2
2
|
|
|
3
3
|
const name = "@finema/core";
|
|
4
|
-
const version = "1.4.
|
|
4
|
+
const version = "1.4.99";
|
|
5
5
|
|
|
6
6
|
const colors = {
|
|
7
7
|
black: "#20243E",
|
|
@@ -18,7 +18,9 @@ const colors = {
|
|
|
18
18
|
DEFAULT: "#9095A6",
|
|
19
19
|
disabled: "#C1C4D0",
|
|
20
20
|
border: "#E2E4EA",
|
|
21
|
-
fill: "#F4F5FA"
|
|
21
|
+
fill: "#F4F5FA",
|
|
22
|
+
50: "#E0E0E0",
|
|
23
|
+
600: "#616161"
|
|
22
24
|
},
|
|
23
25
|
secondary: {
|
|
24
26
|
DEFAULT: "#EE8B36",
|
|
@@ -38,7 +40,8 @@ const colors = {
|
|
|
38
40
|
50: "#EBF6FF",
|
|
39
41
|
100: "#EBF6FF",
|
|
40
42
|
400: "#0D8CEE",
|
|
41
|
-
500: "#0D8CEE"
|
|
43
|
+
500: "#0D8CEE",
|
|
44
|
+
600: "#0D8CEE"
|
|
42
45
|
},
|
|
43
46
|
danger: {
|
|
44
47
|
DEFAULT: "#F25555",
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
<h1 class="truncate font-bold">{{ selectedFile?.name }}</h1>
|
|
55
55
|
<p class="truncate font-light text-gray-400">
|
|
56
56
|
{{
|
|
57
|
-
fileAllocate.isSelectedFileUseMb
|
|
58
|
-
? `${fileAllocate.selectedFileSizeMb} MB`
|
|
59
|
-
: `${fileAllocate.selectedFileSizeKb} KB`
|
|
57
|
+
fileAllocate.isSelectedFileUseMb.value
|
|
58
|
+
? `${fileAllocate.selectedFileSizeMb.value} MB`
|
|
59
|
+
: `${fileAllocate.selectedFileSizeKb.value} KB`
|
|
60
60
|
}}
|
|
61
61
|
</p>
|
|
62
62
|
</div>
|
|
@@ -96,8 +96,8 @@ import {
|
|
|
96
96
|
generateURL,
|
|
97
97
|
checkMaxSize,
|
|
98
98
|
checkFileType,
|
|
99
|
-
getFileAllocate,
|
|
100
99
|
downloadFileFromURL,
|
|
100
|
+
useFileAllocate,
|
|
101
101
|
} from '#core/helpers/componentHelper'
|
|
102
102
|
import FieldWrapper from '#core/components/Form/FieldWrapper.vue'
|
|
103
103
|
import { useFieldHOC } from '#core/composables/useForm'
|
|
@@ -122,11 +122,8 @@ const acceptFile = computed(() =>
|
|
|
122
122
|
typeof props.accept === 'string' ? props.accept : props.accept?.join(',')
|
|
123
123
|
)
|
|
124
124
|
|
|
125
|
-
const fileAllocate = computed(() =>
|
|
126
|
-
getFileAllocate(props.maxSize ?? 0, selectedFile.value?.size ?? 0)
|
|
127
|
-
)
|
|
128
|
-
|
|
129
125
|
const selectedFile = computed(() => value?.value)
|
|
126
|
+
const fileAllocate = useFileAllocate(selectedFile, props)
|
|
130
127
|
|
|
131
128
|
const onDrop = (files: File[] | null) => {
|
|
132
129
|
if (props.isDisabled || files?.length === 0 || !files) return
|
|
@@ -182,16 +179,16 @@ const handleCheckFileCondition = (file: File | undefined): boolean => {
|
|
|
182
179
|
return false
|
|
183
180
|
}
|
|
184
181
|
|
|
185
|
-
const maxSize = checkMaxSize(file, fileAllocate.value
|
|
182
|
+
const maxSize = checkMaxSize(file, fileAllocate.acceptFileSizeKb.value)
|
|
186
183
|
|
|
187
184
|
if (!maxSize) {
|
|
188
|
-
if (fileAllocate.value
|
|
185
|
+
if (fileAllocate.isAcceptFileUseMb.value) {
|
|
189
186
|
setErrors(
|
|
190
|
-
i18next.t('custom:invalid_file_size_mb', { size: fileAllocate.value
|
|
187
|
+
i18next.t('custom:invalid_file_size_mb', { size: fileAllocate.acceptFileSizeMb.value })
|
|
191
188
|
)
|
|
192
189
|
} else {
|
|
193
190
|
setErrors(
|
|
194
|
-
i18next.t('custom:invalid_file_size_kb', { size: fileAllocate.value
|
|
191
|
+
i18next.t('custom:invalid_file_size_kb', { size: fileAllocate.acceptFileSizeKb.value })
|
|
195
192
|
)
|
|
196
193
|
}
|
|
197
194
|
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
<h1 class="truncate font-bold">{{ selectedFile?.name }}</h1>
|
|
51
51
|
<p class="truncate font-light text-gray-400">
|
|
52
52
|
{{
|
|
53
|
-
fileAllocate.isSelectedFileUseMb
|
|
54
|
-
? `${fileAllocate.selectedFileSizeMb} MB`
|
|
55
|
-
: `${fileAllocate.selectedFileSizeKb} KB`
|
|
53
|
+
fileAllocate.isSelectedFileUseMb.value
|
|
54
|
+
? `${fileAllocate.selectedFileSizeMb.value} MB`
|
|
55
|
+
: `${fileAllocate.selectedFileSizeKb.value} KB`
|
|
56
56
|
}}
|
|
57
57
|
- {{ percent }}% {{ uploadingLabel ?? 'กำลังอัพโหลด...' }}
|
|
58
58
|
</p>
|
|
@@ -88,9 +88,9 @@
|
|
|
88
88
|
<h1 class="truncate font-bold">{{ selectedFile?.name }}</h1>
|
|
89
89
|
<p class="truncate text-sm font-light text-gray-400">
|
|
90
90
|
{{
|
|
91
|
-
fileAllocate.isSelectedFileUseMb
|
|
92
|
-
? `${fileAllocate.selectedFileSizeMb} MB`
|
|
93
|
-
: `${fileAllocate.selectedFileSizeKb} KB`
|
|
91
|
+
fileAllocate.isSelectedFileUseMb.value
|
|
92
|
+
? `${fileAllocate.selectedFileSizeMb.value} MB`
|
|
93
|
+
: `${fileAllocate.selectedFileSizeKb.value} KB`
|
|
94
94
|
}}
|
|
95
95
|
</p>
|
|
96
96
|
</div>
|
|
@@ -170,8 +170,9 @@ import {
|
|
|
170
170
|
checkMaxSize,
|
|
171
171
|
checkFileType,
|
|
172
172
|
isImage,
|
|
173
|
-
getFileAllocate,
|
|
174
173
|
downloadFileFromURL,
|
|
174
|
+
useFileAllocate,
|
|
175
|
+
useFileProgress,
|
|
175
176
|
} from '#core/helpers/componentHelper'
|
|
176
177
|
import FieldWrapper from '#core/components/Form/FieldWrapper.vue'
|
|
177
178
|
import { useFieldHOC } from '#core/composables/useForm'
|
|
@@ -215,16 +216,14 @@ const { ui } = useUI('uploadFileDropzone', toRef(props, 'ui'), config)
|
|
|
215
216
|
const fileInputRef = ref<HTMLInputElement>()
|
|
216
217
|
const dropzoneRef = ref<HTMLDivElement>()
|
|
217
218
|
const selectedFile = ref<File | undefined>()
|
|
218
|
-
const percent = ref<number | string>(0)
|
|
219
219
|
const isPreviewOpen = ref<boolean>(false)
|
|
220
220
|
|
|
221
221
|
const acceptFile = computed(() =>
|
|
222
222
|
typeof props.accept === 'string' ? props.accept : props.accept?.join(',')
|
|
223
223
|
)
|
|
224
224
|
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
)
|
|
225
|
+
const { onUploadProgress, onDownloadProgress, percent } = useFileProgress()
|
|
226
|
+
const fileAllocate = useFileAllocate(selectedFile, props)
|
|
228
227
|
|
|
229
228
|
const onDrop = (files: File[] | null) => {
|
|
230
229
|
if (props.isDisabled || files?.length === 0 || !files) return
|
|
@@ -286,16 +285,16 @@ const handleCheckFileCondition = (file: File | undefined): boolean => {
|
|
|
286
285
|
return false
|
|
287
286
|
}
|
|
288
287
|
|
|
289
|
-
const maxSize = checkMaxSize(file, fileAllocate.value
|
|
288
|
+
const maxSize = checkMaxSize(file, fileAllocate.acceptFileSizeKb.value)
|
|
290
289
|
|
|
291
290
|
if (!maxSize) {
|
|
292
|
-
if (fileAllocate.value
|
|
291
|
+
if (fileAllocate.isAcceptFileUseMb.value) {
|
|
293
292
|
setErrors(
|
|
294
|
-
i18next.t('custom:invalid_file_size_mb', { size: fileAllocate.value
|
|
293
|
+
i18next.t('custom:invalid_file_size_mb', { size: fileAllocate.acceptFileSizeMb.value })
|
|
295
294
|
)
|
|
296
295
|
} else {
|
|
297
296
|
setErrors(
|
|
298
|
-
i18next.t('custom:invalid_file_size_kb', { size: fileAllocate.value
|
|
297
|
+
i18next.t('custom:invalid_file_size_kb', { size: fileAllocate.acceptFileSizeKb.value })
|
|
299
298
|
)
|
|
300
299
|
}
|
|
301
300
|
|
|
@@ -320,24 +319,6 @@ const handleRetryUpload = () => {
|
|
|
320
319
|
}
|
|
321
320
|
}
|
|
322
321
|
|
|
323
|
-
const onUploadProgress = (progressEvent: ProgressEvent) => {
|
|
324
|
-
percent.value = StringHelper.withFixed(
|
|
325
|
-
(Math.floor((progressEvent.loaded * 100) / progressEvent.total) || 0) * 0.8
|
|
326
|
-
)
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
const onDownloadProgress = (progressEvent: ProgressEvent) => {
|
|
330
|
-
if (progressEvent.total === 0) {
|
|
331
|
-
percent.value = 100
|
|
332
|
-
|
|
333
|
-
return
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
percent.value = StringHelper.withFixed(
|
|
337
|
-
(Math.floor((progressEvent.loaded * 100) / progressEvent.total) || 0) * 0.2 + 80
|
|
338
|
-
)
|
|
339
|
-
}
|
|
340
|
-
|
|
341
322
|
useWatchTrue(
|
|
342
323
|
() => upload.status.value.isSuccess,
|
|
343
324
|
() => {
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
<h1 class="truncate font-bold">{{ selectedFile?.name }}</h1>
|
|
26
26
|
<p class="truncate font-light text-gray-400">
|
|
27
27
|
{{
|
|
28
|
-
fileAllocate.isSelectedFileUseMb
|
|
29
|
-
? `${fileAllocate.selectedFileSizeMb} MB`
|
|
30
|
-
: `${fileAllocate.selectedFileSizeKb} KB`
|
|
28
|
+
fileAllocate.isSelectedFileUseMb.value
|
|
29
|
+
? `${fileAllocate.selectedFileSizeMb.value} MB`
|
|
30
|
+
: `${fileAllocate.selectedFileSizeKb.value} KB`
|
|
31
31
|
}}
|
|
32
32
|
- {{ percent }}% {{ uploadingLabel }}
|
|
33
33
|
</p>
|
|
@@ -65,9 +65,9 @@
|
|
|
65
65
|
</h1>
|
|
66
66
|
<p class="truncate text-sm font-light text-gray-400">
|
|
67
67
|
{{
|
|
68
|
-
fileAllocate.isSelectedFileUseMb
|
|
69
|
-
? `${fileAllocate.selectedFileSizeMb} MB`
|
|
70
|
-
: `${fileAllocate.selectedFileSizeKb} KB`
|
|
68
|
+
fileAllocate.isSelectedFileUseMb.value
|
|
69
|
+
? `${fileAllocate.selectedFileSizeMb.value} MB`
|
|
70
|
+
: `${fileAllocate.selectedFileSizeKb.value} KB`
|
|
71
71
|
}}
|
|
72
72
|
</p>
|
|
73
73
|
</div>
|
|
@@ -148,9 +148,10 @@ import {
|
|
|
148
148
|
checkFileType,
|
|
149
149
|
checkMaxSize,
|
|
150
150
|
downloadFileFromURL,
|
|
151
|
-
getFileAllocate,
|
|
152
151
|
generateURL,
|
|
153
152
|
isImage,
|
|
153
|
+
useFileAllocate,
|
|
154
|
+
useFileProgress,
|
|
154
155
|
} from '#core/helpers/componentHelper'
|
|
155
156
|
import { type uploadFileDropzone } from '#core/ui.config'
|
|
156
157
|
import type { IUploadDropzoneAutoMultipleProps } from '#core/components/Form/InputUploadDropzoneAutoMultiple/types'
|
|
@@ -159,6 +160,7 @@ import {
|
|
|
159
160
|
type IUploadRequest,
|
|
160
161
|
onMounted,
|
|
161
162
|
ref,
|
|
163
|
+
toRef,
|
|
162
164
|
StringHelper,
|
|
163
165
|
useUploadLoader,
|
|
164
166
|
useWatchTrue,
|
|
@@ -179,7 +181,6 @@ const request: IUploadRequest = {
|
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
const isPreviewOpen = ref<boolean>(false)
|
|
182
|
-
const percent = ref<number | string>(0)
|
|
183
184
|
const upload = useUploadLoader(request)
|
|
184
185
|
const errMsg = ref<string>('')
|
|
185
186
|
|
|
@@ -187,9 +188,8 @@ const acceptFile = computed(() =>
|
|
|
187
188
|
typeof props.accept === 'string' ? props.accept : props.accept?.join(',')
|
|
188
189
|
)
|
|
189
190
|
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
)
|
|
191
|
+
const { onUploadProgress, onDownloadProgress, percent } = useFileProgress()
|
|
192
|
+
const fileAllocate = useFileAllocate(toRef(props.selectedFile), props)
|
|
193
193
|
|
|
194
194
|
const handleDeleteFile = () => {
|
|
195
195
|
emits('delete')
|
|
@@ -206,24 +206,6 @@ const handleDownloadFile = () => {
|
|
|
206
206
|
downloadFileFromURL(URL.createObjectURL(props.selectedFile), props.selectedFile.name)
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
-
const onUploadProgress = (progressEvent: ProgressEvent) => {
|
|
210
|
-
percent.value = StringHelper.withFixed(
|
|
211
|
-
(Math.floor((progressEvent.loaded * 100) / progressEvent.total) || 0) * 0.8
|
|
212
|
-
)
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const onDownloadProgress = (progressEvent: ProgressEvent) => {
|
|
216
|
-
if (progressEvent.total === 0) {
|
|
217
|
-
percent.value = 100
|
|
218
|
-
|
|
219
|
-
return
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
percent.value = StringHelper.withFixed(
|
|
223
|
-
(Math.floor((progressEvent.loaded * 100) / progressEvent.total) || 0) * 0.2 + 80
|
|
224
|
-
)
|
|
225
|
-
}
|
|
226
|
-
|
|
227
209
|
const handleCheckFileCondition = (file: File | undefined): boolean => {
|
|
228
210
|
if (!file) return false
|
|
229
211
|
|
|
@@ -235,16 +217,16 @@ const handleCheckFileCondition = (file: File | undefined): boolean => {
|
|
|
235
217
|
return false
|
|
236
218
|
}
|
|
237
219
|
|
|
238
|
-
const maxSize = checkMaxSize(file, fileAllocate.value
|
|
220
|
+
const maxSize = checkMaxSize(file, fileAllocate.acceptFileSizeKb.value)
|
|
239
221
|
|
|
240
222
|
if (!maxSize) {
|
|
241
|
-
if (fileAllocate.value
|
|
223
|
+
if (fileAllocate.isAcceptFileUseMb.value) {
|
|
242
224
|
errMsg.value = i18next.t('custom:invalid_file_size_mb', {
|
|
243
|
-
size: fileAllocate.value
|
|
225
|
+
size: fileAllocate.acceptFileSizeMb.value,
|
|
244
226
|
})
|
|
245
227
|
} else {
|
|
246
228
|
errMsg.value = i18next.t('custom:invalid_file_size_kb', {
|
|
247
|
-
size: fileAllocate.value
|
|
229
|
+
size: fileAllocate.acceptFileSizeKb.value,
|
|
248
230
|
})
|
|
249
231
|
}
|
|
250
232
|
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
</div>
|
|
38
38
|
<Item
|
|
39
39
|
v-for="(file, index) in selectedFiles"
|
|
40
|
-
:key="file.
|
|
40
|
+
:key="file.key"
|
|
41
41
|
v-bind="$props"
|
|
42
42
|
:ui="ui"
|
|
43
|
-
:selected-file="file"
|
|
43
|
+
:selected-file="file.file"
|
|
44
44
|
@success="handleSuccess(index, $event)"
|
|
45
45
|
@delete="handleDeleteFile(index)"
|
|
46
46
|
@error="handleError(index, $event)"
|
|
@@ -54,12 +54,11 @@ import { useDropZone } from '@vueuse/core'
|
|
|
54
54
|
import { type IUploadDropzoneAutoMultipleProps } from './types'
|
|
55
55
|
import FieldWrapper from '#core/components/Form/FieldWrapper.vue'
|
|
56
56
|
import { useFieldHOC } from '#core/composables/useForm'
|
|
57
|
-
import { _get, computed, ref, toRef, useUI, useUiConfig } from '#imports'
|
|
57
|
+
import { _get, computed, ref, toRef, useUI, useUiConfig, StringHelper } from '#imports'
|
|
58
58
|
import { uploadFileDropzone } from '#core/ui.config'
|
|
59
59
|
import Item from './Item.vue'
|
|
60
60
|
|
|
61
61
|
const config = useUiConfig<typeof uploadFileDropzone>(uploadFileDropzone, 'uploadFileDropzone')
|
|
62
|
-
|
|
63
62
|
const props = withDefaults(defineProps<IUploadDropzoneAutoMultipleProps>(), {
|
|
64
63
|
bodyKey: 'file',
|
|
65
64
|
responseKey: 'url',
|
|
@@ -71,13 +70,12 @@ const props = withDefaults(defineProps<IUploadDropzoneAutoMultipleProps>(), {
|
|
|
71
70
|
})
|
|
72
71
|
|
|
73
72
|
const emits = defineEmits(['change', 'success', 'delete'])
|
|
74
|
-
const selectedFiles = ref<File
|
|
75
|
-
|
|
73
|
+
const selectedFiles = ref<Array<{ file: File; key: string }>>([])
|
|
76
74
|
const { wrapperProps, handleChange: onChange, setErrors, value } = useFieldHOC<File[]>(props)
|
|
77
75
|
|
|
78
76
|
const { ui } = useUI('uploadFileDropzone', toRef(props, 'ui'), config)
|
|
79
77
|
|
|
80
|
-
const fileInputRef = ref<HTMLInputElement>()
|
|
78
|
+
const fileInputRef = ref<HTMLInputElement | null>()
|
|
81
79
|
const dropzoneRef = ref<HTMLDivElement>()
|
|
82
80
|
|
|
83
81
|
const acceptFile = computed(() =>
|
|
@@ -88,7 +86,14 @@ const onDrop = (files: File[] | null) => {
|
|
|
88
86
|
if (props.isDisabled || files?.length === 0 || !files) return
|
|
89
87
|
|
|
90
88
|
for (const file of files) {
|
|
91
|
-
selectedFiles.value = [
|
|
89
|
+
selectedFiles.value = [
|
|
90
|
+
{
|
|
91
|
+
file,
|
|
92
|
+
key: StringHelper.genString(),
|
|
93
|
+
},
|
|
94
|
+
...selectedFiles.value,
|
|
95
|
+
]
|
|
96
|
+
|
|
92
97
|
emits('change', value.value)
|
|
93
98
|
}
|
|
94
99
|
}
|
|
@@ -101,9 +106,20 @@ const handleChange = (e: Event) => {
|
|
|
101
106
|
if (props.isDisabled) return
|
|
102
107
|
|
|
103
108
|
for (const file of (e.target as HTMLInputElement).files ?? []) {
|
|
104
|
-
selectedFiles.value = [
|
|
109
|
+
selectedFiles.value = [
|
|
110
|
+
{
|
|
111
|
+
file,
|
|
112
|
+
key: StringHelper.genString(),
|
|
113
|
+
},
|
|
114
|
+
...selectedFiles.value,
|
|
115
|
+
]
|
|
116
|
+
|
|
105
117
|
emits('change', value.value)
|
|
106
118
|
}
|
|
119
|
+
|
|
120
|
+
fileInputRef.value!.type = 'text'
|
|
121
|
+
fileInputRef.value!.type = 'file'
|
|
122
|
+
fileInputRef.value = null
|
|
107
123
|
}
|
|
108
124
|
|
|
109
125
|
const handleOpenFile = () => {
|
|
@@ -37,15 +37,14 @@
|
|
|
37
37
|
<div v-else :class="ui.imageItemWrapper">
|
|
38
38
|
<Item
|
|
39
39
|
v-for="(file, index) in selectedFiles"
|
|
40
|
-
:key="file.
|
|
40
|
+
:key="file.key"
|
|
41
41
|
v-bind="$props"
|
|
42
42
|
:ui="ui"
|
|
43
|
-
:selected-file="file"
|
|
43
|
+
:selected-file="file.file"
|
|
44
44
|
@success="handleSuccess(index, $event)"
|
|
45
45
|
@delete="handleDeleteFile(index)"
|
|
46
46
|
@error="handleError(index, $event)"
|
|
47
47
|
/>
|
|
48
|
-
|
|
49
48
|
<Item v-bind="$props" is-adding-btn :ui="ui" @add="handleOpenFile" />
|
|
50
49
|
</div>
|
|
51
50
|
</div>
|
|
@@ -59,7 +58,7 @@ import { useDropZone } from '@vueuse/core'
|
|
|
59
58
|
import { type IUploadDropzoneImageAutoMultipleProps } from './types'
|
|
60
59
|
import FieldWrapper from '#core/components/Form/FieldWrapper.vue'
|
|
61
60
|
import { useFieldHOC } from '#core/composables/useForm'
|
|
62
|
-
import { _get, computed, ref, toRef, useUI, useUiConfig } from '#imports'
|
|
61
|
+
import { _get, computed, ref, toRef, StringHelper, useUI, useUiConfig } from '#imports'
|
|
63
62
|
import { uploadFileDropzoneImage } from '#core/ui.config'
|
|
64
63
|
import Item from './item.vue'
|
|
65
64
|
|
|
@@ -78,13 +77,13 @@ const props = withDefaults(defineProps<IUploadDropzoneImageAutoMultipleProps>(),
|
|
|
78
77
|
})
|
|
79
78
|
|
|
80
79
|
const emits = defineEmits(['change', 'success', 'delete'])
|
|
81
|
-
const selectedFiles = ref<File
|
|
80
|
+
const selectedFiles = ref<Array<{ file: File; key: string }>>([])
|
|
82
81
|
|
|
83
82
|
const { wrapperProps, handleChange: onChange, value } = useFieldHOC<File[]>(props)
|
|
84
83
|
|
|
85
84
|
const { ui } = useUI('uploadFileDropzoneImage', toRef(props, 'ui'), config)
|
|
86
85
|
|
|
87
|
-
const fileInputRef = ref<HTMLInputElement>()
|
|
86
|
+
const fileInputRef = ref<HTMLInputElement | null>()
|
|
88
87
|
const dropzoneRef = ref<HTMLDivElement>()
|
|
89
88
|
|
|
90
89
|
const acceptFile = computed(() =>
|
|
@@ -95,7 +94,13 @@ const onDrop = (files: File[] | null) => {
|
|
|
95
94
|
if (props.isDisabled || files?.length === 0 || !files) return
|
|
96
95
|
|
|
97
96
|
for (const file of files) {
|
|
98
|
-
selectedFiles.value = [
|
|
97
|
+
selectedFiles.value = [
|
|
98
|
+
...selectedFiles.value,
|
|
99
|
+
{
|
|
100
|
+
file,
|
|
101
|
+
key: StringHelper.genString(),
|
|
102
|
+
},
|
|
103
|
+
]
|
|
99
104
|
|
|
100
105
|
emits('change', value.value)
|
|
101
106
|
}
|
|
@@ -109,9 +114,20 @@ const handleChange = (e: Event) => {
|
|
|
109
114
|
if (props.isDisabled) return
|
|
110
115
|
|
|
111
116
|
for (const file of (e.target as HTMLInputElement).files ?? []) {
|
|
112
|
-
selectedFiles.value = [
|
|
117
|
+
selectedFiles.value = [
|
|
118
|
+
...selectedFiles.value,
|
|
119
|
+
{
|
|
120
|
+
file,
|
|
121
|
+
key: StringHelper.genString(),
|
|
122
|
+
},
|
|
123
|
+
]
|
|
124
|
+
|
|
113
125
|
emits('change', value.value)
|
|
114
126
|
}
|
|
127
|
+
|
|
128
|
+
fileInputRef.value!.type = 'text'
|
|
129
|
+
fileInputRef.value!.type = 'file'
|
|
130
|
+
fileInputRef.value = null
|
|
115
131
|
}
|
|
116
132
|
|
|
117
133
|
const handleOpenFile = () => {
|
|
@@ -73,7 +73,8 @@ import {
|
|
|
73
73
|
checkFileType,
|
|
74
74
|
checkMaxSize,
|
|
75
75
|
generateURL,
|
|
76
|
-
|
|
76
|
+
useFileAllocate,
|
|
77
|
+
useFileProgress,
|
|
77
78
|
} from '#core/helpers/componentHelper'
|
|
78
79
|
import { type uploadFileDropzoneImage } from '#core/ui.config'
|
|
79
80
|
import type { IUploadDropzoneImageAutoMultipleProps } from '#core/components/Form/InputUploadDropzoneImageAutoMultiple/types'
|
|
@@ -82,7 +83,7 @@ import {
|
|
|
82
83
|
type IUploadRequest,
|
|
83
84
|
onMounted,
|
|
84
85
|
ref,
|
|
85
|
-
|
|
86
|
+
toRef,
|
|
86
87
|
useUploadLoader,
|
|
87
88
|
useWatchTrue,
|
|
88
89
|
} from '#imports'
|
|
@@ -103,7 +104,6 @@ const request: IUploadRequest = {
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
const isPreviewOpen = ref<boolean>(false)
|
|
106
|
-
const percent = ref<number | string>(0)
|
|
107
107
|
const upload = useUploadLoader(request)
|
|
108
108
|
const errMsg = ref<string>('')
|
|
109
109
|
|
|
@@ -111,32 +111,13 @@ const acceptFile = computed(() =>
|
|
|
111
111
|
typeof props.accept === 'string' ? props.accept : props.accept?.join(',')
|
|
112
112
|
)
|
|
113
113
|
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
)
|
|
114
|
+
const { onUploadProgress, onDownloadProgress, percent } = useFileProgress()
|
|
115
|
+
const fileAllocate = useFileAllocate(toRef(props.selectedFile), props)
|
|
117
116
|
|
|
118
117
|
const handleDeleteFile = () => {
|
|
119
118
|
emits('delete')
|
|
120
119
|
}
|
|
121
120
|
|
|
122
|
-
const onUploadProgress = (progressEvent: ProgressEvent) => {
|
|
123
|
-
percent.value = StringHelper.withFixed(
|
|
124
|
-
(Math.floor((progressEvent.loaded * 100) / progressEvent.total) || 0) * 0.8
|
|
125
|
-
)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const onDownloadProgress = (progressEvent: ProgressEvent) => {
|
|
129
|
-
if (progressEvent.total === 0) {
|
|
130
|
-
percent.value = 100
|
|
131
|
-
|
|
132
|
-
return
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
percent.value = StringHelper.withFixed(
|
|
136
|
-
(Math.floor((progressEvent.loaded * 100) / progressEvent.total) || 0) * 0.2 + 80
|
|
137
|
-
)
|
|
138
|
-
}
|
|
139
|
-
|
|
140
121
|
const handleCheckFileCondition = (file: File | undefined): boolean => {
|
|
141
122
|
if (!file) return false
|
|
142
123
|
|
|
@@ -148,16 +129,16 @@ const handleCheckFileCondition = (file: File | undefined): boolean => {
|
|
|
148
129
|
return false
|
|
149
130
|
}
|
|
150
131
|
|
|
151
|
-
const maxSize = checkMaxSize(file, fileAllocate.value
|
|
132
|
+
const maxSize = checkMaxSize(file, fileAllocate.acceptFileSizeKb.value)
|
|
152
133
|
|
|
153
134
|
if (!maxSize) {
|
|
154
|
-
if (fileAllocate.value
|
|
135
|
+
if (fileAllocate.isAcceptFileUseMb.value) {
|
|
155
136
|
errMsg.value = i18next.t('custom:invalid_file_size_mb', {
|
|
156
|
-
size: fileAllocate.value
|
|
137
|
+
size: fileAllocate.acceptFileSizeMb.value,
|
|
157
138
|
})
|
|
158
139
|
} else {
|
|
159
140
|
errMsg.value = i18next.t('custom:invalid_file_size_kb', {
|
|
160
|
-
size: fileAllocate.value
|
|
141
|
+
size: fileAllocate.acceptFileSizeKb.value,
|
|
161
142
|
})
|
|
162
143
|
}
|
|
163
144
|
|
|
@@ -32,6 +32,7 @@ import { useUiConfig } from '#core/composables/useConfig'
|
|
|
32
32
|
import { uploadFileInputClassicAuto } from '#core/ui.config'
|
|
33
33
|
import { computed, ref, toRef, useUI } from '#imports'
|
|
34
34
|
import i18next from 'i18next'
|
|
35
|
+
import { checkMaxSize } from '#core/helpers/componentHelper'
|
|
35
36
|
|
|
36
37
|
const config = useUiConfig<typeof uploadFileInputClassicAuto>(
|
|
37
38
|
uploadFileInputClassicAuto,
|
|
@@ -63,18 +64,10 @@ const handleOpenFile = () => {
|
|
|
63
64
|
|
|
64
65
|
const { ui } = useUI('uploadFileInputClassicAuto', toRef(props, 'ui'), config)
|
|
65
66
|
|
|
66
|
-
const checkMaxSize = (file: File): boolean => {
|
|
67
|
-
if (acceptFileSizeKb.value) {
|
|
68
|
-
return file.size / 1000 <= acceptFileSizeKb.value
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return true
|
|
72
|
-
}
|
|
73
|
-
|
|
74
67
|
const handleCheckFileCondition = (file: File | undefined): boolean => {
|
|
75
68
|
if (!file) return false
|
|
76
69
|
|
|
77
|
-
const maxSize = checkMaxSize(file)
|
|
70
|
+
const maxSize = checkMaxSize(file, props.maxSize)
|
|
78
71
|
|
|
79
72
|
if (!maxSize) {
|
|
80
73
|
if (isAcceptFileUseMb.value) {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
type="file"
|
|
7
7
|
class="hidden"
|
|
8
8
|
:name="name"
|
|
9
|
-
:accept="acceptFile"
|
|
9
|
+
:accept="fileAllocate.acceptFile.value"
|
|
10
10
|
:disabled="isDisabled"
|
|
11
11
|
@change="handleChange"
|
|
12
12
|
/>
|
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
{{ selectedFile?.name ?? placeholder ?? 'ยังไม่ได้เลือกไฟล์' }}
|
|
18
18
|
</p>
|
|
19
19
|
<Badge v-if="selectedFile" size="xs" variant="outline">
|
|
20
|
-
{{
|
|
20
|
+
{{
|
|
21
|
+
fileAllocate.isSelectedFileUseMb.value
|
|
22
|
+
? `${fileAllocate.selectedFileSizeMb.value} MB`
|
|
23
|
+
: `${fileAllocate.selectedFileSizeKb.value} KB`
|
|
24
|
+
}}
|
|
21
25
|
</Badge>
|
|
22
26
|
</div>
|
|
23
27
|
<div v-if="selectedFile">
|
|
@@ -44,22 +48,14 @@
|
|
|
44
48
|
</template>
|
|
45
49
|
|
|
46
50
|
<script lang="tsx" setup>
|
|
47
|
-
import {
|
|
48
|
-
_get,
|
|
49
|
-
computed,
|
|
50
|
-
ref,
|
|
51
|
-
StringHelper,
|
|
52
|
-
toRef,
|
|
53
|
-
useUI,
|
|
54
|
-
useUiConfig,
|
|
55
|
-
useWatchTrue,
|
|
56
|
-
} from '#imports'
|
|
51
|
+
import { _get, ref, StringHelper, toRef, useUI, useUiConfig, useWatchTrue } from '#imports'
|
|
57
52
|
import { type IUploadFileProps } from './types'
|
|
58
53
|
import FieldWrapper from '#core/components/Form/FieldWrapper.vue'
|
|
59
54
|
import { useFieldHOC } from '#core/composables/useForm'
|
|
60
55
|
import { type IUploadRequest, useUploadLoader } from '#core/composables/useUpload'
|
|
61
56
|
import { uploadFileInputClassicAuto } from '#core/ui.config'
|
|
62
57
|
import i18next from 'i18next'
|
|
58
|
+
import { checkMaxSize, useFileAllocate, useFileProgress } from '#core/helpers/componentHelper'
|
|
63
59
|
|
|
64
60
|
const config = useUiConfig<typeof uploadFileInputClassicAuto>(
|
|
65
61
|
uploadFileInputClassicAuto,
|
|
@@ -84,22 +80,9 @@ const upload = useUploadLoader(request)
|
|
|
84
80
|
|
|
85
81
|
const fileInput = ref<HTMLInputElement>()
|
|
86
82
|
const selectedFile = ref<File | undefined>()
|
|
87
|
-
const percent = ref<number>(0)
|
|
88
|
-
|
|
89
|
-
const selectedFileSizeKb = computed(() => ((selectedFile.value?.size || 0) / 1000).toFixed(2))
|
|
90
|
-
const selectedFileSizeMb = computed(() =>
|
|
91
|
-
((selectedFile.value?.size || 0) / 1000 / 1000).toFixed(2)
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
const isSelectedFileUseMb = computed(() => (selectedFile.value?.size || 0) / 1000 > 1024)
|
|
95
|
-
|
|
96
|
-
const acceptFileSizeKb = computed(() => props.maxSize)
|
|
97
|
-
const acceptFileSizeMb = computed(() => ((acceptFileSizeKb.value || 0) / 1024).toFixed(2))
|
|
98
|
-
const isAcceptFileUseMb = computed(() => acceptFileSizeKb.value && acceptFileSizeKb.value > 1024)
|
|
99
|
-
const acceptFile = computed(() =>
|
|
100
|
-
typeof props.accept === 'string' ? props.accept : props.accept?.join(',')
|
|
101
|
-
)
|
|
102
83
|
|
|
84
|
+
const fileAllocate = useFileAllocate(selectedFile, props)
|
|
85
|
+
const { onUploadProgress, onDownloadProgress, percent } = useFileProgress()
|
|
103
86
|
const { ui } = useUI('uploadFileInputClassicAuto', toRef(props, 'ui'), config)
|
|
104
87
|
|
|
105
88
|
const handleOpenFile = () => {
|
|
@@ -122,13 +105,17 @@ const handleChange = (e: Event) => {
|
|
|
122
105
|
const handleCheckFileCondition = (file: File | undefined): boolean => {
|
|
123
106
|
if (!file) return false
|
|
124
107
|
|
|
125
|
-
const maxSize = checkMaxSize(file)
|
|
108
|
+
const maxSize = checkMaxSize(file, fileAllocate.acceptFileSizeKb.value)
|
|
126
109
|
|
|
127
110
|
if (!maxSize) {
|
|
128
|
-
if (isAcceptFileUseMb.value) {
|
|
129
|
-
setErrors(
|
|
111
|
+
if (fileAllocate.isAcceptFileUseMb.value) {
|
|
112
|
+
setErrors(
|
|
113
|
+
i18next.t('custom:invalid_file_size_mb', { size: fileAllocate.acceptFileSizeMb.value })
|
|
114
|
+
)
|
|
130
115
|
} else {
|
|
131
|
-
setErrors(
|
|
116
|
+
setErrors(
|
|
117
|
+
i18next.t('custom:invalid_file_size_kb', { size: fileAllocate.acceptFileSizeKb.value })
|
|
118
|
+
)
|
|
132
119
|
}
|
|
133
120
|
|
|
134
121
|
return false
|
|
@@ -139,28 +126,6 @@ const handleCheckFileCondition = (file: File | undefined): boolean => {
|
|
|
139
126
|
return true
|
|
140
127
|
}
|
|
141
128
|
|
|
142
|
-
const checkMaxSize = (file: File): boolean => {
|
|
143
|
-
if (acceptFileSizeKb.value) {
|
|
144
|
-
return file.size / 1000 <= acceptFileSizeKb.value
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return true
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const onUploadProgress = (progressEvent: ProgressEvent) => {
|
|
151
|
-
percent.value = (Math.floor((progressEvent.loaded * 100) / progressEvent.total) || 0) * 0.8
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const onDownloadProgress = (progressEvent: ProgressEvent) => {
|
|
155
|
-
if (progressEvent.total === 0) {
|
|
156
|
-
percent.value = 100
|
|
157
|
-
|
|
158
|
-
return
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
percent.value = (Math.floor((progressEvent.loaded * 100) / progressEvent.total) || 0) * 0.2 + 80
|
|
162
|
-
}
|
|
163
|
-
|
|
164
129
|
useWatchTrue(
|
|
165
130
|
() => upload.status.value.isSuccess,
|
|
166
131
|
() => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<QrcodeVue :value="value" :level="level" :render-as="renderAs" class="
|
|
2
|
+
<QrcodeVue :value="value" :level="level" :render-as="renderAs" class="size-[350px]" />
|
|
3
3
|
</template>
|
|
4
4
|
<script lang="ts" setup>
|
|
5
5
|
import QrcodeVue from 'qrcode.vue'
|
|
@@ -1,13 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Ref } from 'vue';
|
|
2
|
+
export declare const checkMaxSize: (file: File, acceptFileSize?: number) => boolean;
|
|
2
3
|
export declare const checkFileType: (file: File, acceptFileType: string | string[]) => boolean;
|
|
3
4
|
export declare const generateURL: (file: File) => string;
|
|
4
5
|
export declare const isImage: (file: File) => boolean;
|
|
5
|
-
export declare const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
selectedFileSizeKb:
|
|
10
|
-
selectedFileSizeMb:
|
|
11
|
-
isSelectedFileUseMb:
|
|
6
|
+
export declare const useFileAllocate: (selectedFile: Ref<File | undefined | null>, props: {
|
|
7
|
+
accept?: string | string[];
|
|
8
|
+
maxSize?: number;
|
|
9
|
+
}) => {
|
|
10
|
+
selectedFileSizeKb: any;
|
|
11
|
+
selectedFileSizeMb: any;
|
|
12
|
+
isSelectedFileUseMb: any;
|
|
13
|
+
acceptFileSizeKb: any;
|
|
14
|
+
acceptFileSizeMb: any;
|
|
15
|
+
isAcceptFileUseMb: any;
|
|
16
|
+
acceptFile: any;
|
|
17
|
+
};
|
|
18
|
+
export declare const useFileProgress: () => {
|
|
19
|
+
percent: any;
|
|
20
|
+
onUploadProgress: (progressEvent: ProgressEvent) => void;
|
|
21
|
+
onDownloadProgress: (progressEvent: ProgressEvent) => void;
|
|
12
22
|
};
|
|
13
23
|
export declare const downloadFileFromURL: (url: string, filename?: string) => Promise<void>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { computed, ref, StringHelper } from "#imports";
|
|
2
|
+
export const checkMaxSize = (file, acceptFileSize = 0) => {
|
|
2
3
|
if (acceptFileSize) {
|
|
3
4
|
return file.size / 1e3 <= acceptFileSize;
|
|
4
5
|
}
|
|
@@ -37,14 +38,52 @@ export const generateURL = (file) => {
|
|
|
37
38
|
export const isImage = (file) => {
|
|
38
39
|
return file.type.startsWith("image/");
|
|
39
40
|
};
|
|
40
|
-
export const
|
|
41
|
+
export const useFileAllocate = (selectedFile, props) => {
|
|
42
|
+
const selectedFileSizeKb = computed(() => ((selectedFile.value?.size || 0) / 1e3).toFixed(2));
|
|
43
|
+
const selectedFileSizeMb = computed(
|
|
44
|
+
() => ((selectedFile.value?.size || 0) / 1e3 / 1e3).toFixed(2)
|
|
45
|
+
);
|
|
46
|
+
const isSelectedFileUseMb = computed(() => (selectedFile.value?.size || 0) / 1e3 > 1024);
|
|
47
|
+
const acceptFileSizeKb = computed(() => props.maxSize || 0);
|
|
48
|
+
const acceptFileSizeMb = computed(() => ((acceptFileSizeKb.value || 0) / 1024).toFixed(2));
|
|
49
|
+
const isAcceptFileUseMb = computed(() => acceptFileSizeKb.value && acceptFileSizeKb.value > 1024);
|
|
50
|
+
const acceptFile = computed(
|
|
51
|
+
() => typeof props.accept === "string" ? props.accept : props.accept?.join(",")
|
|
52
|
+
);
|
|
41
53
|
return {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
selectedFileSizeKb,
|
|
55
|
+
selectedFileSizeMb,
|
|
56
|
+
isSelectedFileUseMb,
|
|
57
|
+
acceptFileSizeKb,
|
|
58
|
+
acceptFileSizeMb,
|
|
59
|
+
isAcceptFileUseMb,
|
|
60
|
+
acceptFile
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
export const useFileProgress = () => {
|
|
64
|
+
const percent = ref(0);
|
|
65
|
+
const onUploadProgress = (progressEvent) => {
|
|
66
|
+
percent.value = parseFloat(
|
|
67
|
+
StringHelper.withFixed(
|
|
68
|
+
(Math.floor(progressEvent.loaded * 100 / progressEvent.total) || 0) * 0.8
|
|
69
|
+
)
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
const onDownloadProgress = (progressEvent) => {
|
|
73
|
+
if (progressEvent.total === 0) {
|
|
74
|
+
percent.value = 100;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
percent.value = parseFloat(
|
|
78
|
+
StringHelper.withFixed(
|
|
79
|
+
(Math.floor(progressEvent.loaded * 100 / progressEvent.total) || 0) * 0.2 + 80
|
|
80
|
+
)
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
percent,
|
|
85
|
+
onUploadProgress,
|
|
86
|
+
onDownloadProgress
|
|
48
87
|
};
|
|
49
88
|
};
|
|
50
89
|
export const downloadFileFromURL = async (url, filename) => {
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { describe, test, expect, vi } from "vitest";
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
3
2
|
import { ArrayHelper } from "./ArrayHelper.mjs";
|
|
4
|
-
vi.mock("#build/imports", () => ({
|
|
5
|
-
_get: get
|
|
6
|
-
}));
|
|
7
3
|
describe("ArrayHelper", () => {
|
|
8
4
|
test("toOptions default attr", () => {
|
|
9
5
|
const data = [
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { FileHelper } from "./FileHelper.mjs";
|
|
3
|
+
describe("FileHelper", () => {
|
|
4
|
+
describe("dataURLtoFile", () => {
|
|
5
|
+
it("should convert a data URL to a File", async () => {
|
|
6
|
+
const dataUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==";
|
|
7
|
+
const filename = "test.png";
|
|
8
|
+
const file = await FileHelper.dataURLtoFile(dataUrl, filename);
|
|
9
|
+
expect(file).toBeInstanceOf(File);
|
|
10
|
+
expect(file.name).toBe(filename);
|
|
11
|
+
expect(file.type).toBe("image/png");
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { ObjectHelper } from "./ObjectHelper.mjs";
|
|
3
|
+
describe("ObjectHelper", () => {
|
|
4
|
+
describe("createOption", () => {
|
|
5
|
+
it("should create an option object with the provided value and label", () => {
|
|
6
|
+
const value = "value1";
|
|
7
|
+
const label = "Label 1";
|
|
8
|
+
const option = ObjectHelper.createOption(value, label);
|
|
9
|
+
expect(option).toEqual({ value, label });
|
|
10
|
+
});
|
|
11
|
+
it("should create an option object with an empty label if not provided", () => {
|
|
12
|
+
const value = "value1";
|
|
13
|
+
const option = ObjectHelper.createOption(value);
|
|
14
|
+
expect(option).toEqual({ value, label: "" });
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
describe("toOption", () => {
|
|
18
|
+
it("should create an option object from the provided data", () => {
|
|
19
|
+
const data = { id: 1, name: "Option 1" };
|
|
20
|
+
const option = ObjectHelper.toOption(data);
|
|
21
|
+
expect(option).toEqual({ value: 1, label: "Option 1" });
|
|
22
|
+
});
|
|
23
|
+
it("should use the provided value and label attributes", () => {
|
|
24
|
+
const data = { code: "CODE1", description: "Description 1" };
|
|
25
|
+
const option = ObjectHelper.toOption(data, "code", "description");
|
|
26
|
+
expect(option).toEqual({ value: "CODE1", label: "Description 1" });
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe("toErrorStatus", () => {
|
|
30
|
+
it("should create an error status object from the provided error", () => {
|
|
31
|
+
const error = {
|
|
32
|
+
response: {
|
|
33
|
+
status: 400,
|
|
34
|
+
request: {
|
|
35
|
+
response: JSON.stringify({ code: "INVALID_REQUEST", message: "Invalid request" })
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const status = ObjectHelper.toErrorStatus({}, error);
|
|
40
|
+
expect(status.isError).toBe(true);
|
|
41
|
+
expect(status.isSuccess).toBe(false);
|
|
42
|
+
expect(status.errorData).toEqual({ code: "INVALID_REQUEST", message: "Invalid request" });
|
|
43
|
+
});
|
|
44
|
+
it("should create a network error status if the error response status is missing", () => {
|
|
45
|
+
const error = {};
|
|
46
|
+
const status = ObjectHelper.toErrorStatus({}, error);
|
|
47
|
+
expect(status.isError).toBe(true);
|
|
48
|
+
expect(status.isSuccess).toBe(false);
|
|
49
|
+
expect(status.errorData).toEqual({ code: "NETWORK_ERROR", message: "Network error" });
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { ParamHelper } from "./ParamHelper.mjs";
|
|
3
|
+
describe("ParamHelper", () => {
|
|
4
|
+
describe("getParams", () => {
|
|
5
|
+
it("should merge params from opts and reqOptions", () => {
|
|
6
|
+
const opts = { params: { page: 1, limit: 10 } };
|
|
7
|
+
const reqOptions = { params: { sort: "name" } };
|
|
8
|
+
const result = ParamHelper.getParams(opts, reqOptions);
|
|
9
|
+
expect(result).toEqual({ page: 1, limit: 10, sort: "name" });
|
|
10
|
+
});
|
|
11
|
+
it("should return reqOptions.params if opts.params is not provided", () => {
|
|
12
|
+
const opts = {};
|
|
13
|
+
const reqOptions = { params: { sort: "name" } };
|
|
14
|
+
const result = ParamHelper.getParams(opts, reqOptions);
|
|
15
|
+
expect(result).toEqual({ sort: "name" });
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
describe("getBoolTrue", () => {
|
|
19
|
+
it("should return true for truthy values", () => {
|
|
20
|
+
expect(ParamHelper.getBoolTrue(true)).toBe(true);
|
|
21
|
+
expect(ParamHelper.getBoolTrue(1)).toBe(true);
|
|
22
|
+
expect(ParamHelper.getBoolTrue("true")).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
it("should return false for falsy values", () => {
|
|
25
|
+
expect(ParamHelper.getBoolTrue(false)).toBe(false);
|
|
26
|
+
expect(ParamHelper.getBoolTrue(0)).toBe(false);
|
|
27
|
+
expect(ParamHelper.getBoolTrue("false")).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
it("should return true for null value", () => {
|
|
30
|
+
expect(ParamHelper.getBoolTrue(null)).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe("getBoolFalse", () => {
|
|
34
|
+
it("should return false for falsy values", () => {
|
|
35
|
+
expect(ParamHelper.getBoolFalse(false)).toBe(false);
|
|
36
|
+
expect(ParamHelper.getBoolFalse(0)).toBe(false);
|
|
37
|
+
expect(ParamHelper.getBoolFalse("false")).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
it("should return true for truthy values", () => {
|
|
40
|
+
expect(ParamHelper.getBoolFalse(true)).toBe(true);
|
|
41
|
+
expect(ParamHelper.getBoolFalse(1)).toBe(true);
|
|
42
|
+
expect(ParamHelper.getBoolFalse("true")).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
it("should return false for null value", () => {
|
|
45
|
+
expect(ParamHelper.getBoolFalse(null)).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe("isNotFoundError", () => {
|
|
49
|
+
it('should return true if the error code is "NOT_FOUND"', () => {
|
|
50
|
+
const error = { code: "NOT_FOUND" };
|
|
51
|
+
expect(ParamHelper.isNotFoundError(error)).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
it('should return false if the error code is not "NOT_FOUND"', () => {
|
|
54
|
+
const error = { code: "INTERNAL_SERVER_ERROR" };
|
|
55
|
+
expect(ParamHelper.isNotFoundError(error)).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe("isChangeWithFalse", () => {
|
|
59
|
+
it("should return true if the value changes from true to false", () => {
|
|
60
|
+
expect(ParamHelper.isChangeWithFalse(false, true)).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
it("should return false if the value does not change or changes to true", () => {
|
|
63
|
+
expect(ParamHelper.isChangeWithFalse(true, true)).toBe(false);
|
|
64
|
+
expect(ParamHelper.isChangeWithFalse(false, false)).toBe(false);
|
|
65
|
+
expect(ParamHelper.isChangeWithFalse(true, false)).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
describe("isChangeWithTrue", () => {
|
|
69
|
+
it("should return true if the value changes from false to true", () => {
|
|
70
|
+
expect(ParamHelper.isChangeWithTrue(true, false)).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
it("should return false if the value does not change or changes to false", () => {
|
|
73
|
+
expect(ParamHelper.isChangeWithTrue(false, false)).toBe(false);
|
|
74
|
+
expect(ParamHelper.isChangeWithTrue(true, true)).toBe(false);
|
|
75
|
+
expect(ParamHelper.isChangeWithTrue(false, true)).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { StringHelper } from "./StringHelper.mjs";
|
|
3
|
+
describe("StringHelper", () => {
|
|
4
|
+
describe("genString", () => {
|
|
5
|
+
it("should generate a random string of the specified length", () => {
|
|
6
|
+
const length = 10;
|
|
7
|
+
const result = StringHelper.genString(length);
|
|
8
|
+
expect(result).toHaveLength(length);
|
|
9
|
+
expect(typeof result).toBe("string");
|
|
10
|
+
});
|
|
11
|
+
it("should generate a random string of default length if not specified", () => {
|
|
12
|
+
const result = StringHelper.genString();
|
|
13
|
+
expect(result).toHaveLength(5);
|
|
14
|
+
expect(typeof result).toBe("string");
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
describe("withComma", () => {
|
|
18
|
+
it("should format a number with comma separator", () => {
|
|
19
|
+
expect(StringHelper.withComma(1e3)).toBe("1,000");
|
|
20
|
+
expect(StringHelper.withComma(1234.56)).toBe("1,234.56");
|
|
21
|
+
});
|
|
22
|
+
it('should return "0" for null or undefined values', () => {
|
|
23
|
+
expect(StringHelper.withComma(void 0)).toBe("0");
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
describe("withFixed", () => {
|
|
27
|
+
it("should format a number with comma separator and fixed decimal places", () => {
|
|
28
|
+
expect(StringHelper.withFixed(1e3)).toBe("1,000");
|
|
29
|
+
expect(StringHelper.withFixed(1234.56)).toBe("1,234.56");
|
|
30
|
+
});
|
|
31
|
+
it('should return "0" for null or undefined values', () => {
|
|
32
|
+
expect(StringHelper.withFixed(void 0)).toBe("0");
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe("split", () => {
|
|
36
|
+
it("should split a string into an array based on the specified separator", () => {
|
|
37
|
+
const str = "apple,banana,orange";
|
|
38
|
+
const separator = ",";
|
|
39
|
+
const result = StringHelper.split(str, separator);
|
|
40
|
+
expect(result).toEqual(["apple", "banana", "orange"]);
|
|
41
|
+
});
|
|
42
|
+
it("should return an empty array for null or undefined string", () => {
|
|
43
|
+
expect(StringHelper.split(null, ",")).toEqual([]);
|
|
44
|
+
expect(StringHelper.split(void 0, ",")).toEqual([]);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe("joinURL", () => {
|
|
48
|
+
it("should join the provided URL parts", () => {
|
|
49
|
+
const result = StringHelper.joinURL("https://example.com", "path", "to", "resource");
|
|
50
|
+
expect(result).toBe("https://example.com/path/to/resource");
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe("truncate", () => {
|
|
54
|
+
it("should truncate a string if it exceeds the specified length", () => {
|
|
55
|
+
const str = "This is a long string that needs to be truncated";
|
|
56
|
+
const result = StringHelper.truncate(str, 20);
|
|
57
|
+
expect(result).toBe("This is a long strin...");
|
|
58
|
+
});
|
|
59
|
+
it("should return the original string if it does not exceed the specified length", () => {
|
|
60
|
+
const str = "Short string";
|
|
61
|
+
const result = StringHelper.truncate(str, 20);
|
|
62
|
+
expect(result).toBe("Short string");
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe("getError", () => {
|
|
66
|
+
it("should return the error message from the errorData", () => {
|
|
67
|
+
const errorData = { code: "ERROR_CODE", message: "Error message" };
|
|
68
|
+
const result = StringHelper.getError(errorData);
|
|
69
|
+
expect(result).toBe("Error message");
|
|
70
|
+
});
|
|
71
|
+
it("should return the default error message if the errorData is empty", () => {
|
|
72
|
+
const result = StringHelper.getError({}, "Default error message");
|
|
73
|
+
expect(result).toBe("Default error message");
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|