@finema/core 1.4.205 → 1.4.207

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.
Files changed (64) hide show
  1. package/README.md +60 -60
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +1 -1
  4. package/dist/runtime/components/Alert.vue +48 -48
  5. package/dist/runtime/components/Avatar.vue +27 -27
  6. package/dist/runtime/components/Badge.vue +11 -11
  7. package/dist/runtime/components/Breadcrumb.vue +44 -44
  8. package/dist/runtime/components/Button/Group.vue +37 -37
  9. package/dist/runtime/components/Button/index.vue +75 -75
  10. package/dist/runtime/components/Card.vue +38 -38
  11. package/dist/runtime/components/Core.vue +45 -45
  12. package/dist/runtime/components/Dialog/index.vue +108 -108
  13. package/dist/runtime/components/Dropdown/index.vue +70 -70
  14. package/dist/runtime/components/FlexDeck/Base.vue +152 -152
  15. package/dist/runtime/components/FlexDeck/index.vue +68 -68
  16. package/dist/runtime/components/Form/FieldWrapper.vue +23 -23
  17. package/dist/runtime/components/Form/Fields.vue +230 -230
  18. package/dist/runtime/components/Form/InputCheckbox/index.vue +28 -28
  19. package/dist/runtime/components/Form/InputDateTime/index.vue +61 -61
  20. package/dist/runtime/components/Form/InputDateTimeRange/index.vue +83 -83
  21. package/dist/runtime/components/Form/InputNumber/index.vue +27 -27
  22. package/dist/runtime/components/Form/InputRadio/index.vue +27 -27
  23. package/dist/runtime/components/Form/InputSelect/index.vue +54 -45
  24. package/dist/runtime/components/Form/InputSelect/types.d.ts +1 -0
  25. package/dist/runtime/components/Form/InputSelectMultiple/index.vue +54 -54
  26. package/dist/runtime/components/Form/InputStatic/index.vue +16 -16
  27. package/dist/runtime/components/Form/InputTags/index.vue +141 -141
  28. package/dist/runtime/components/Form/InputText/index.vue +68 -68
  29. package/dist/runtime/components/Form/InputTextarea/index.vue +25 -25
  30. package/dist/runtime/components/Form/InputToggle/index.vue +27 -27
  31. package/dist/runtime/components/Form/InputUploadDropzone/index.vue +206 -206
  32. package/dist/runtime/components/Form/InputUploadDropzoneAuto/index.vue +342 -342
  33. package/dist/runtime/components/Form/InputUploadDropzoneAutoMultiple/ItemUpload.vue +241 -241
  34. package/dist/runtime/components/Form/InputUploadDropzoneAutoMultiple/ItemView.vue +89 -89
  35. package/dist/runtime/components/Form/InputUploadDropzoneAutoMultiple/index.vue +170 -170
  36. package/dist/runtime/components/Form/InputUploadDropzoneImageAutoMultiple/ItemUpload.vue +161 -161
  37. package/dist/runtime/components/Form/InputUploadDropzoneImageAutoMultiple/ItemView.vue +64 -64
  38. package/dist/runtime/components/Form/InputUploadDropzoneImageAutoMultiple/index.vue +178 -178
  39. package/dist/runtime/components/Form/InputUploadFileClassic/index.vue +95 -95
  40. package/dist/runtime/components/Form/InputUploadFileClassicAuto/index.vue +151 -151
  41. package/dist/runtime/components/Form/InputUploadImageAuto/index.vue +219 -219
  42. package/dist/runtime/components/Form/InputWYSIWYG/UploadImageForm.vue +55 -55
  43. package/dist/runtime/components/Form/InputWYSIWYG/index.vue +228 -228
  44. package/dist/runtime/components/Form/index.vue +6 -6
  45. package/dist/runtime/components/Icon.vue +23 -23
  46. package/dist/runtime/components/Image.vue +36 -36
  47. package/dist/runtime/components/Loader.vue +27 -27
  48. package/dist/runtime/components/Modal/index.vue +146 -146
  49. package/dist/runtime/components/QRCode.vue +22 -22
  50. package/dist/runtime/components/SimplePagination.vue +96 -96
  51. package/dist/runtime/components/Slideover/index.vue +110 -110
  52. package/dist/runtime/components/Table/Base.vue +153 -153
  53. package/dist/runtime/components/Table/ColumnDate.vue +16 -16
  54. package/dist/runtime/components/Table/ColumnDateTime.vue +18 -18
  55. package/dist/runtime/components/Table/ColumnImage.vue +15 -15
  56. package/dist/runtime/components/Table/ColumnNumber.vue +14 -14
  57. package/dist/runtime/components/Table/ColumnText.vue +29 -29
  58. package/dist/runtime/components/Table/Simple.vue +69 -69
  59. package/dist/runtime/components/Table/index.vue +65 -65
  60. package/dist/runtime/components/Tabs/index.vue +64 -64
  61. package/dist/runtime/components/TeleportSafe.vue +40 -40
  62. package/package.json +103 -102
  63. package/dist/runtime/components/Form/InputDateTime/index.vue~ +0 -61
  64. package/dist/runtime/ui.config/table.ts~ +0 -48
@@ -1,170 +1,170 @@
1
- <template>
2
- <FieldWrapper v-bind="wrapperProps">
3
- <div class="space-y-3">
4
- <div
5
- ref="dropzoneRef"
6
- :class="[
7
- ui.base,
8
- {
9
- [ui.disabled]: isDisabled,
10
- [ui.background.default]: !isOverDropZone && !isDisabled,
11
- [ui.background.dragover]: isOverDropZone && !isDisabled,
12
- },
13
- ]"
14
- >
15
- <input
16
- ref="fileInputRef"
17
- type="file"
18
- class="hidden"
19
- :name="name"
20
- :accept="acceptFile"
21
- :disabled="isDisabled"
22
- multiple
23
- @change="handleChange"
24
- />
25
- <div :class="[ui.wrapper]">
26
- <div :class="[ui.placeholderWrapper]">
27
- <Icon :name="ui.default.uploadIcon" :class="[ui.labelIcon]" />
28
- <div :class="[ui.labelWrapper]">
29
- <p class="text-primary cursor-pointer" @click="handleOpenFile">
30
- {{ selectFileLabel }}
31
- </p>
32
- <p>{{ selectFileSubLabel }}</p>
33
- </div>
34
- <p v-if="placeholder" :class="[ui.placeholder]">{{ placeholder }}</p>
35
- </div>
36
- </div>
37
- </div>
38
- <ItemView
39
- v-for="(file, index) in value"
40
- :key="file.url"
41
- v-bind="$props"
42
- :ui="ui"
43
- :selected-file="file"
44
- @delete="handleDeleteFileView(index)"
45
- />
46
- <ItemUpload
47
- v-for="(file, index) in selectedFiles"
48
- :key="file.key"
49
- v-bind="$props"
50
- :ui="ui"
51
- :selected-file="file.file"
52
- @success="handleSuccess(index, $event)"
53
- @delete="handleDeleteFile(index)"
54
- @error="handleError(index, $event)"
55
- />
56
- </div>
57
- </FieldWrapper>
58
- </template>
59
-
60
- <script lang="ts" setup>
61
- import { useDropZone } from '@vueuse/core'
62
- import { type IUploadDropzoneAutoMultipleProps } from './types'
63
- import FieldWrapper from '#core/components/Form/FieldWrapper.vue'
64
- import { useFieldHOC } from '#core/composables/useForm'
65
- import { _get, computed, ref, toRef, useUI, useUiConfig, StringHelper, ArrayHelper } from '#imports'
66
- import { uploadFileDropzone } from '#core/ui.config'
67
- import ItemView from './ItemView.vue'
68
- import ItemUpload from './ItemUpload.vue'
69
- import type { IFileValue } from '#core/components/Form/types'
70
-
71
- const config = useUiConfig<typeof uploadFileDropzone>(uploadFileDropzone, 'uploadFileDropzone')
72
- const props = withDefaults(defineProps<IUploadDropzoneAutoMultipleProps>(), {
73
- bodyKey: 'file',
74
- responseURL: 'url',
75
- responsePath: 'path',
76
- selectFileLabel: 'คลิกเพื่อเลือกไฟล์',
77
- selectFileSubLabel: 'หรือ ลากและวางที่นี่',
78
- retryLabel: 'ลองอีกครั้ง',
79
- uploadingLabel: 'กำลังอัพโหลด...',
80
- uploadFailedLabel: 'อัพโหลดล้มเหลว, กรุณาลองอีกครั้ง',
81
- })
82
-
83
- const emits = defineEmits(['change', 'success', 'delete'])
84
- const selectedFiles = ref<Array<{ file: File; key: string }>>([])
85
- const { wrapperProps, handleChange: onChange, setErrors, value } = useFieldHOC<IFileValue[]>(props)
86
-
87
- const { ui } = useUI('uploadFileDropzone', toRef(props, 'ui'), config)
88
-
89
- const fileInputRef = ref<HTMLInputElement | null>()
90
- const dropzoneRef = ref<HTMLDivElement>()
91
-
92
- const acceptFile = computed(() =>
93
- typeof props.accept === 'string' ? props.accept : props.accept?.join(',')
94
- )
95
-
96
- const onDrop = (files: File[] | null) => {
97
- if (props.isDisabled || files?.length === 0 || !files) return
98
-
99
- for (const file of files) {
100
- selectedFiles.value = [
101
- {
102
- file,
103
- key: StringHelper.genString(),
104
- },
105
- ...selectedFiles.value,
106
- ]
107
-
108
- emits('change', value.value)
109
- }
110
- }
111
-
112
- const { isOverDropZone } = useDropZone(dropzoneRef as unknown as HTMLElement, {
113
- onDrop,
114
- })
115
-
116
- const handleChange = (e: Event) => {
117
- if (props.isDisabled) return
118
-
119
- for (const file of (e.target as HTMLInputElement).files ?? []) {
120
- selectedFiles.value = [
121
- {
122
- file,
123
- key: StringHelper.genString(),
124
- },
125
- ...selectedFiles.value,
126
- ]
127
-
128
- emits('change', value.value)
129
- }
130
-
131
- fileInputRef.value?.value && (fileInputRef.value.value = '')
132
- }
133
-
134
- const handleOpenFile = () => {
135
- fileInputRef.value?.click()
136
- }
137
-
138
- const handleDeleteFile = (index: number) => {
139
- const updatedValue = [...selectedFiles.value]
140
-
141
- updatedValue.splice(index, 1)
142
- selectedFiles.value = updatedValue
143
- }
144
-
145
- const handleDeleteFileView = (index: number) => {
146
- const updatedValue = [...value.value]
147
-
148
- updatedValue.splice(index, 1)
149
- value.value = updatedValue
150
- emits('delete')
151
- }
152
-
153
- const handleError = (index: number, error: any) => {}
154
-
155
- const handleSuccess = (index: number, res: any) => {
156
- value.value = [
157
- {
158
- url: _get(res, props.responseURL),
159
- path: _get(res, props.responsePath),
160
- name: res.name,
161
- size: res.size,
162
- },
163
- ...ArrayHelper.toArray(value.value),
164
- ]
165
-
166
- selectedFiles.value.splice(index, 1)
167
- emits('change', value.value)
168
- emits('success', value.value)
169
- }
170
- </script>
1
+ <template>
2
+ <FieldWrapper v-bind="wrapperProps">
3
+ <div class="space-y-3">
4
+ <div
5
+ ref="dropzoneRef"
6
+ :class="[
7
+ ui.base,
8
+ {
9
+ [ui.disabled]: isDisabled,
10
+ [ui.background.default]: !isOverDropZone && !isDisabled,
11
+ [ui.background.dragover]: isOverDropZone && !isDisabled,
12
+ },
13
+ ]"
14
+ >
15
+ <input
16
+ ref="fileInputRef"
17
+ type="file"
18
+ class="hidden"
19
+ :name="name"
20
+ :accept="acceptFile"
21
+ :disabled="isDisabled"
22
+ multiple
23
+ @change="handleChange"
24
+ />
25
+ <div :class="[ui.wrapper]">
26
+ <div :class="[ui.placeholderWrapper]">
27
+ <Icon :name="ui.default.uploadIcon" :class="[ui.labelIcon]" />
28
+ <div :class="[ui.labelWrapper]">
29
+ <p class="text-primary cursor-pointer" @click="handleOpenFile">
30
+ {{ selectFileLabel }}
31
+ </p>
32
+ <p>{{ selectFileSubLabel }}</p>
33
+ </div>
34
+ <p v-if="placeholder" :class="[ui.placeholder]">{{ placeholder }}</p>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ <ItemView
39
+ v-for="(file, index) in value"
40
+ :key="file.url"
41
+ v-bind="$props"
42
+ :ui="ui"
43
+ :selected-file="file"
44
+ @delete="handleDeleteFileView(index)"
45
+ />
46
+ <ItemUpload
47
+ v-for="(file, index) in selectedFiles"
48
+ :key="file.key"
49
+ v-bind="$props"
50
+ :ui="ui"
51
+ :selected-file="file.file"
52
+ @success="handleSuccess(index, $event)"
53
+ @delete="handleDeleteFile(index)"
54
+ @error="handleError(index, $event)"
55
+ />
56
+ </div>
57
+ </FieldWrapper>
58
+ </template>
59
+
60
+ <script lang="ts" setup>
61
+ import { useDropZone } from '@vueuse/core'
62
+ import { type IUploadDropzoneAutoMultipleProps } from './types'
63
+ import FieldWrapper from '#core/components/Form/FieldWrapper.vue'
64
+ import { useFieldHOC } from '#core/composables/useForm'
65
+ import { _get, computed, ref, toRef, useUI, useUiConfig, StringHelper, ArrayHelper } from '#imports'
66
+ import { uploadFileDropzone } from '#core/ui.config'
67
+ import ItemView from './ItemView.vue'
68
+ import ItemUpload from './ItemUpload.vue'
69
+ import type { IFileValue } from '#core/components/Form/types'
70
+
71
+ const config = useUiConfig<typeof uploadFileDropzone>(uploadFileDropzone, 'uploadFileDropzone')
72
+ const props = withDefaults(defineProps<IUploadDropzoneAutoMultipleProps>(), {
73
+ bodyKey: 'file',
74
+ responseURL: 'url',
75
+ responsePath: 'path',
76
+ selectFileLabel: 'คลิกเพื่อเลือกไฟล์',
77
+ selectFileSubLabel: 'หรือ ลากและวางที่นี่',
78
+ retryLabel: 'ลองอีกครั้ง',
79
+ uploadingLabel: 'กำลังอัพโหลด...',
80
+ uploadFailedLabel: 'อัพโหลดล้มเหลว, กรุณาลองอีกครั้ง',
81
+ })
82
+
83
+ const emits = defineEmits(['change', 'success', 'delete'])
84
+ const selectedFiles = ref<Array<{ file: File; key: string }>>([])
85
+ const { wrapperProps, handleChange: onChange, setErrors, value } = useFieldHOC<IFileValue[]>(props)
86
+
87
+ const { ui } = useUI('uploadFileDropzone', toRef(props, 'ui'), config)
88
+
89
+ const fileInputRef = ref<HTMLInputElement | null>()
90
+ const dropzoneRef = ref<HTMLDivElement>()
91
+
92
+ const acceptFile = computed(() =>
93
+ typeof props.accept === 'string' ? props.accept : props.accept?.join(',')
94
+ )
95
+
96
+ const onDrop = (files: File[] | null) => {
97
+ if (props.isDisabled || files?.length === 0 || !files) return
98
+
99
+ for (const file of files) {
100
+ selectedFiles.value = [
101
+ {
102
+ file,
103
+ key: StringHelper.genString(),
104
+ },
105
+ ...selectedFiles.value,
106
+ ]
107
+
108
+ emits('change', value.value)
109
+ }
110
+ }
111
+
112
+ const { isOverDropZone } = useDropZone(dropzoneRef as unknown as HTMLElement, {
113
+ onDrop,
114
+ })
115
+
116
+ const handleChange = (e: Event) => {
117
+ if (props.isDisabled) return
118
+
119
+ for (const file of (e.target as HTMLInputElement).files ?? []) {
120
+ selectedFiles.value = [
121
+ {
122
+ file,
123
+ key: StringHelper.genString(),
124
+ },
125
+ ...selectedFiles.value,
126
+ ]
127
+
128
+ emits('change', value.value)
129
+ }
130
+
131
+ fileInputRef.value?.value && (fileInputRef.value.value = '')
132
+ }
133
+
134
+ const handleOpenFile = () => {
135
+ fileInputRef.value?.click()
136
+ }
137
+
138
+ const handleDeleteFile = (index: number) => {
139
+ const updatedValue = [...selectedFiles.value]
140
+
141
+ updatedValue.splice(index, 1)
142
+ selectedFiles.value = updatedValue
143
+ }
144
+
145
+ const handleDeleteFileView = (index: number) => {
146
+ const updatedValue = [...value.value]
147
+
148
+ updatedValue.splice(index, 1)
149
+ value.value = updatedValue
150
+ emits('delete')
151
+ }
152
+
153
+ const handleError = (index: number, error: any) => {}
154
+
155
+ const handleSuccess = (index: number, res: any) => {
156
+ value.value = [
157
+ {
158
+ url: _get(res, props.responseURL),
159
+ path: _get(res, props.responsePath),
160
+ name: res.name,
161
+ size: res.size,
162
+ },
163
+ ...ArrayHelper.toArray(value.value),
164
+ ]
165
+
166
+ selectedFiles.value.splice(index, 1)
167
+ emits('change', value.value)
168
+ emits('success', value.value)
169
+ }
170
+ </script>
@@ -1,161 +1,161 @@
1
- <template>
2
- <div :class="[ui.imageItem.wrapper]">
3
- <div v-if="isAddingBtn" class="w-full">
4
- <div :class="[ui.action.addingWrapper]" @click="$emit('add')">
5
- <Icon :name="ui.action.addingIcon" :class="[ui.action.addingBtnClass]" />
6
- <p :class="[ui.action.addingTextClass]">{{ uploadAddLabel }}</p>
7
- </div>
8
- </div>
9
-
10
- <!-- Loading State -->
11
- <div v-if="selectedFile && upload.status.value.isLoading" class="w-full">
12
- <div :class="[ui.imageItem.onLoading.wrapper]">
13
- <div :class="[ui.imageItem.onLoading.percentClass]">{{ percent }}%</div>
14
- <UProgress :value="percent" />
15
- </div>
16
- </div>
17
-
18
- <!-- Success State -->
19
- <div v-if="selectedFile && upload.status.value.isSuccess" class="w-full">
20
- <div :class="[ui.imageItem.onPreview.wrapper]">
21
- <img
22
- :class="[ui.imageItem.onPreview.previewImgClass]"
23
- :src="upload.data.value[responseURL!]"
24
- alt="img"
25
- />
26
- <div :class="[ui.imageItem.onPreview.previewActionWrapper]">
27
- <Icon
28
- title="ลบไฟล์"
29
- :name="ui.action.deleteIcon"
30
- :class="[ui.imageItem.onPreview.actionBtnClass]"
31
- @click="handleDeleteFile"
32
- />
33
- </div>
34
- </div>
35
-
36
- <div :class="[ui.imageItem.onPreview.previewTextWrapper]">
37
- <p :class="[ui.imageItem.onPreview.previewText]">{{ selectedFile.name }}</p>
38
- </div>
39
- </div>
40
-
41
- <!-- Failed State -->
42
- <div v-if="selectedFile && upload.status.value.isError" class="w-full">
43
- <div :class="[ui.imageItem.onFailed.wrapper]">
44
- <img
45
- :class="[ui.imageItem.onFailed.failedImgClass]"
46
- :src="generateURL(selectedFile)"
47
- alt="img"
48
- />
49
- <div :class="[ui.imageItem.onFailed.failedActionWrapper]">
50
- <Icon
51
- title="ลบไฟล์"
52
- :name="ui.action.deleteIcon"
53
- :class="[ui.imageItem.onFailed.actionBtnClass]"
54
- @click="handleDeleteFile"
55
- />
56
- </div>
57
- </div>
58
- </div>
59
- </div>
60
- </template>
61
-
62
- <script lang="ts" setup>
63
- import {
64
- checkFileType,
65
- checkMaxSize,
66
- generateURL,
67
- useFileAllocate,
68
- useFileProgress,
69
- } from '#core/helpers/componentHelper'
70
- import { type uploadFileDropzoneImage } from '#core/ui.config'
71
- import type { IUploadDropzoneImageAutoMultipleProps } from '#core/components/Form/InputUploadDropzoneImageAutoMultiple/types'
72
- import { type IUploadRequest, onMounted, ref, toRef, useUploadLoader, useWatchTrue } from '#imports'
73
- import i18next from 'i18next'
74
-
75
- const emits = defineEmits(['success', 'error', 'delete', 'add'])
76
- const props = defineProps<
77
- {
78
- ui: typeof uploadFileDropzoneImage
79
- selectedFile?: File
80
- isAddingBtn?: boolean
81
- } & IUploadDropzoneImageAutoMultipleProps
82
- >()
83
-
84
- const request: IUploadRequest = {
85
- pathURL: props.uploadPathURL,
86
- requestOptions: props.requestOptions,
87
- }
88
-
89
- const upload = useUploadLoader(request)
90
- const errMsg = ref<string>('')
91
-
92
- const { onUploadProgress, onDownloadProgress, percent } = useFileProgress()
93
- const fileAllocate = useFileAllocate(toRef(props.selectedFile), props)
94
-
95
- const handleDeleteFile = () => {
96
- emits('delete')
97
- }
98
-
99
- const handleCheckFileCondition = (file: File | undefined): boolean => {
100
- if (!file) return false
101
-
102
- const fileType = checkFileType(file, fileAllocate.acceptFile.value ?? '')
103
-
104
- if (!fileType) {
105
- errMsg.value = i18next.t('custom:invalid_file_type')
106
-
107
- return false
108
- }
109
-
110
- const maxSize = checkMaxSize(file, fileAllocate.acceptFileSizeKb.value)
111
-
112
- if (!maxSize) {
113
- if (fileAllocate.isAcceptFileUseMb.value) {
114
- errMsg.value = i18next.t('custom:invalid_file_size_mb', {
115
- size: fileAllocate.acceptFileSizeMb.value,
116
- })
117
- } else {
118
- errMsg.value = i18next.t('custom:invalid_file_size_kb', {
119
- size: fileAllocate.acceptFileSizeKb.value,
120
- })
121
- }
122
-
123
- return false
124
- }
125
-
126
- errMsg.value = ''
127
-
128
- return true
129
- }
130
-
131
- onMounted(() => {
132
- if (props.isAddingBtn) return
133
-
134
- const result = handleCheckFileCondition(props.selectedFile)
135
-
136
- if (result) {
137
- const formData = new FormData()
138
-
139
- formData.append(props.bodyKey!, props.selectedFile!)
140
- upload.run(formData, { data: { onUploadProgress, onDownloadProgress } })
141
- }
142
- })
143
-
144
- useWatchTrue(
145
- () => upload.status.value.isSuccess,
146
- () => {
147
- emits('success', {
148
- ...upload.data.value,
149
- name: props.selectedFile?.name,
150
- size: +(props.selectedFile?.size || 0),
151
- })
152
- }
153
- )
154
-
155
- useWatchTrue(
156
- () => upload.status.value.isError,
157
- () => {
158
- emits('error', upload.status.value.errorData)
159
- }
160
- )
161
- </script>
1
+ <template>
2
+ <div :class="[ui.imageItem.wrapper]">
3
+ <div v-if="isAddingBtn" class="w-full">
4
+ <div :class="[ui.action.addingWrapper]" @click="$emit('add')">
5
+ <Icon :name="ui.action.addingIcon" :class="[ui.action.addingBtnClass]" />
6
+ <p :class="[ui.action.addingTextClass]">{{ uploadAddLabel }}</p>
7
+ </div>
8
+ </div>
9
+
10
+ <!-- Loading State -->
11
+ <div v-if="selectedFile && upload.status.value.isLoading" class="w-full">
12
+ <div :class="[ui.imageItem.onLoading.wrapper]">
13
+ <div :class="[ui.imageItem.onLoading.percentClass]">{{ percent }}%</div>
14
+ <UProgress :value="percent" />
15
+ </div>
16
+ </div>
17
+
18
+ <!-- Success State -->
19
+ <div v-if="selectedFile && upload.status.value.isSuccess" class="w-full">
20
+ <div :class="[ui.imageItem.onPreview.wrapper]">
21
+ <img
22
+ :class="[ui.imageItem.onPreview.previewImgClass]"
23
+ :src="upload.data.value[responseURL!]"
24
+ alt="img"
25
+ />
26
+ <div :class="[ui.imageItem.onPreview.previewActionWrapper]">
27
+ <Icon
28
+ title="ลบไฟล์"
29
+ :name="ui.action.deleteIcon"
30
+ :class="[ui.imageItem.onPreview.actionBtnClass]"
31
+ @click="handleDeleteFile"
32
+ />
33
+ </div>
34
+ </div>
35
+
36
+ <div :class="[ui.imageItem.onPreview.previewTextWrapper]">
37
+ <p :class="[ui.imageItem.onPreview.previewText]">{{ selectedFile.name }}</p>
38
+ </div>
39
+ </div>
40
+
41
+ <!-- Failed State -->
42
+ <div v-if="selectedFile && upload.status.value.isError" class="w-full">
43
+ <div :class="[ui.imageItem.onFailed.wrapper]">
44
+ <img
45
+ :class="[ui.imageItem.onFailed.failedImgClass]"
46
+ :src="generateURL(selectedFile)"
47
+ alt="img"
48
+ />
49
+ <div :class="[ui.imageItem.onFailed.failedActionWrapper]">
50
+ <Icon
51
+ title="ลบไฟล์"
52
+ :name="ui.action.deleteIcon"
53
+ :class="[ui.imageItem.onFailed.actionBtnClass]"
54
+ @click="handleDeleteFile"
55
+ />
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ </template>
61
+
62
+ <script lang="ts" setup>
63
+ import {
64
+ checkFileType,
65
+ checkMaxSize,
66
+ generateURL,
67
+ useFileAllocate,
68
+ useFileProgress,
69
+ } from '#core/helpers/componentHelper'
70
+ import { type uploadFileDropzoneImage } from '#core/ui.config'
71
+ import type { IUploadDropzoneImageAutoMultipleProps } from '#core/components/Form/InputUploadDropzoneImageAutoMultiple/types'
72
+ import { type IUploadRequest, onMounted, ref, toRef, useUploadLoader, useWatchTrue } from '#imports'
73
+ import i18next from 'i18next'
74
+
75
+ const emits = defineEmits(['success', 'error', 'delete', 'add'])
76
+ const props = defineProps<
77
+ {
78
+ ui: typeof uploadFileDropzoneImage
79
+ selectedFile?: File
80
+ isAddingBtn?: boolean
81
+ } & IUploadDropzoneImageAutoMultipleProps
82
+ >()
83
+
84
+ const request: IUploadRequest = {
85
+ pathURL: props.uploadPathURL,
86
+ requestOptions: props.requestOptions,
87
+ }
88
+
89
+ const upload = useUploadLoader(request)
90
+ const errMsg = ref<string>('')
91
+
92
+ const { onUploadProgress, onDownloadProgress, percent } = useFileProgress()
93
+ const fileAllocate = useFileAllocate(toRef(props.selectedFile), props)
94
+
95
+ const handleDeleteFile = () => {
96
+ emits('delete')
97
+ }
98
+
99
+ const handleCheckFileCondition = (file: File | undefined): boolean => {
100
+ if (!file) return false
101
+
102
+ const fileType = checkFileType(file, fileAllocate.acceptFile.value ?? '')
103
+
104
+ if (!fileType) {
105
+ errMsg.value = i18next.t('custom:invalid_file_type')
106
+
107
+ return false
108
+ }
109
+
110
+ const maxSize = checkMaxSize(file, fileAllocate.acceptFileSizeKb.value)
111
+
112
+ if (!maxSize) {
113
+ if (fileAllocate.isAcceptFileUseMb.value) {
114
+ errMsg.value = i18next.t('custom:invalid_file_size_mb', {
115
+ size: fileAllocate.acceptFileSizeMb.value,
116
+ })
117
+ } else {
118
+ errMsg.value = i18next.t('custom:invalid_file_size_kb', {
119
+ size: fileAllocate.acceptFileSizeKb.value,
120
+ })
121
+ }
122
+
123
+ return false
124
+ }
125
+
126
+ errMsg.value = ''
127
+
128
+ return true
129
+ }
130
+
131
+ onMounted(() => {
132
+ if (props.isAddingBtn) return
133
+
134
+ const result = handleCheckFileCondition(props.selectedFile)
135
+
136
+ if (result) {
137
+ const formData = new FormData()
138
+
139
+ formData.append(props.bodyKey!, props.selectedFile!)
140
+ upload.run(formData, { data: { onUploadProgress, onDownloadProgress } })
141
+ }
142
+ })
143
+
144
+ useWatchTrue(
145
+ () => upload.status.value.isSuccess,
146
+ () => {
147
+ emits('success', {
148
+ ...upload.data.value,
149
+ name: props.selectedFile?.name,
150
+ size: +(props.selectedFile?.size || 0),
151
+ })
152
+ }
153
+ )
154
+
155
+ useWatchTrue(
156
+ () => upload.status.value.isError,
157
+ () => {
158
+ emits('error', upload.status.value.errorData)
159
+ }
160
+ )
161
+ </script>