@finema/core 3.22.0 → 3.24.0

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 (130) hide show
  1. package/README.md +22 -2
  2. package/dist/module.d.mts +2 -0
  3. package/dist/module.json +2 -2
  4. package/dist/module.mjs +18 -15
  5. package/dist/runtime/components/App.vue +1 -1
  6. package/dist/runtime/components/DevToolsWindow/index.vue +24 -26
  7. package/dist/runtime/components/Dialog/index.d.vue.ts +1 -4
  8. package/dist/runtime/components/Dialog/index.vue +17 -17
  9. package/dist/runtime/components/Dialog/index.vue.d.ts +1 -4
  10. package/dist/runtime/components/Empty.d.vue.ts +1 -2
  11. package/dist/runtime/components/Empty.vue +19 -11
  12. package/dist/runtime/components/Empty.vue.d.ts +1 -2
  13. package/dist/runtime/components/FlexDeck/index.vue +21 -23
  14. package/dist/runtime/components/Form/FieldWrapper.vue +1 -1
  15. package/dist/runtime/components/Form/Fields.d.vue.ts +1 -1
  16. package/dist/runtime/components/Form/Fields.vue +26 -20
  17. package/dist/runtime/components/Form/Fields.vue.d.ts +1 -1
  18. package/dist/runtime/components/Form/InputCheckbox/index.vue +3 -6
  19. package/dist/runtime/components/Form/InputCheckboxGroup/index.vue +3 -6
  20. package/dist/runtime/components/Form/InputComponent/index.vue +2 -1
  21. package/dist/runtime/components/Form/InputCurrency/index.vue +11 -11
  22. package/dist/runtime/components/Form/InputDateTime/date_time_field.types.d.ts +3 -1
  23. package/dist/runtime/components/Form/InputDateTime/index.vue +12 -17
  24. package/dist/runtime/components/Form/InputDateTimeRange/date_range_time_field.types.d.ts +1 -1
  25. package/dist/runtime/components/Form/InputDateTimeRange/index.vue +28 -20
  26. package/dist/runtime/components/Form/InputMonth/index.vue +14 -12
  27. package/dist/runtime/components/Form/InputMonth/types.d.ts +1 -1
  28. package/dist/runtime/components/Form/InputNumber/index.vue +3 -6
  29. package/dist/runtime/components/Form/InputRadio/index.vue +3 -6
  30. package/dist/runtime/components/Form/InputRating/index.vue +3 -6
  31. package/dist/runtime/components/Form/InputSearch/index.vue +6 -7
  32. package/dist/runtime/components/Form/InputSelect/index.vue +6 -15
  33. package/dist/runtime/components/Form/InputSelectMultiple/index.vue +7 -18
  34. package/dist/runtime/components/Form/InputTags/index.vue +3 -6
  35. package/dist/runtime/components/Form/InputText/index.vue +41 -51
  36. package/dist/runtime/components/Form/InputTextarea/index.vue +3 -6
  37. package/dist/runtime/components/Form/InputTime/index.vue +32 -27
  38. package/dist/runtime/components/Form/InputTime/types.d.ts +1 -1
  39. package/dist/runtime/components/Form/InputToggle/index.vue +3 -5
  40. package/dist/runtime/components/Form/InputUploadDropzone/index.d.vue.ts +1 -4
  41. package/dist/runtime/components/Form/InputUploadDropzone/index.vue +25 -23
  42. package/dist/runtime/components/Form/InputUploadDropzone/index.vue.d.ts +1 -4
  43. package/dist/runtime/components/Form/InputUploadDropzone/types.d.ts +2 -2
  44. package/dist/runtime/components/Form/InputUploadDropzoneAuto/index.d.vue.ts +0 -5
  45. package/dist/runtime/components/Form/InputUploadDropzoneAuto/index.vue +22 -23
  46. package/dist/runtime/components/Form/InputUploadDropzoneAuto/index.vue.d.ts +0 -5
  47. package/dist/runtime/components/Form/InputUploadDropzoneAuto/types.d.ts +3 -3
  48. package/dist/runtime/components/Form/InputUploadDropzoneAutoMultiple/index.d.vue.ts +0 -5
  49. package/dist/runtime/components/Form/InputUploadDropzoneAutoMultiple/index.vue +26 -24
  50. package/dist/runtime/components/Form/InputUploadDropzoneAutoMultiple/index.vue.d.ts +0 -5
  51. package/dist/runtime/components/Form/InputUploadDropzoneAutoMultiple/types.d.ts +3 -3
  52. package/dist/runtime/components/Form/InputUploadImageAuto/index.d.vue.ts +1 -6
  53. package/dist/runtime/components/Form/InputUploadImageAuto/index.vue +22 -23
  54. package/dist/runtime/components/Form/InputUploadImageAuto/index.vue.d.ts +1 -6
  55. package/dist/runtime/components/Form/InputUploadImageAuto/types.d.ts +3 -3
  56. package/dist/runtime/components/Form/InputWYSIWYG/EditorImagePasteExtension.js +7 -6
  57. package/dist/runtime/components/Form/InputWYSIWYG/EditorImageUploadExtension.js +13 -12
  58. package/dist/runtime/components/Form/InputWYSIWYG/EditorImageUploadNode.vue +3 -1
  59. package/dist/runtime/components/Form/InputWYSIWYG/EditorLinkPopover.vue +20 -22
  60. package/dist/runtime/components/Form/InputWYSIWYG/index.vue +140 -118
  61. package/dist/runtime/components/Form/fileState/EmptyState.vue +1 -1
  62. package/dist/runtime/components/Form/fileState/FailedState.vue +4 -2
  63. package/dist/runtime/components/Form/fileState/MultipleFilesState.d.vue.ts +4 -4
  64. package/dist/runtime/components/Form/fileState/MultipleFilesState.vue +5 -5
  65. package/dist/runtime/components/Form/fileState/MultipleFilesState.vue.d.ts +4 -4
  66. package/dist/runtime/components/Form/fileState/SuccessState.d.vue.ts +2 -2
  67. package/dist/runtime/components/Form/fileState/SuccessState.vue +8 -13
  68. package/dist/runtime/components/Form/fileState/SuccessState.vue.d.ts +2 -2
  69. package/dist/runtime/components/Form/fileState/useFilePicker.d.ts +34 -0
  70. package/dist/runtime/components/Form/fileState/useFilePicker.js +48 -0
  71. package/dist/runtime/components/Form/fileState/useFileState.d.ts +7 -9
  72. package/dist/runtime/components/Form/fileState/useFileState.js +32 -120
  73. package/dist/runtime/components/Form/fileState/useUploadImageState.d.ts +7 -8
  74. package/dist/runtime/components/Form/fileState/useUploadImageState.js +27 -301
  75. package/dist/runtime/components/Form/fileState/useUploadState.d.ts +11 -7
  76. package/dist/runtime/components/Form/fileState/useUploadState.js +52 -136
  77. package/dist/runtime/components/Form/fileState/useUploadStateMultiple.d.ts +12 -18
  78. package/dist/runtime/components/Form/fileState/useUploadStateMultiple.js +88 -226
  79. package/dist/runtime/components/Form/types.d.ts +2 -0
  80. package/dist/runtime/components/Image.vue +4 -10
  81. package/dist/runtime/components/Loader.d.vue.ts +2 -1
  82. package/dist/runtime/components/Loader.vue +10 -7
  83. package/dist/runtime/components/Loader.vue.d.ts +2 -1
  84. package/dist/runtime/components/Log/LogItem.vue +1 -3
  85. package/dist/runtime/components/Log/index.vue +1 -3
  86. package/dist/runtime/components/Table/Base.d.vue.ts +15 -1
  87. package/dist/runtime/components/Table/Base.vue +9 -29
  88. package/dist/runtime/components/Table/Base.vue.d.ts +15 -1
  89. package/dist/runtime/components/Table/ColumnText.d.vue.ts +2 -3
  90. package/dist/runtime/components/Table/ColumnText.vue +1 -1
  91. package/dist/runtime/components/Table/ColumnText.vue.d.ts +2 -3
  92. package/dist/runtime/components/Table/Pagination.vue +19 -22
  93. package/dist/runtime/components/Table/index.d.vue.ts +1 -1
  94. package/dist/runtime/components/Table/index.vue +5 -7
  95. package/dist/runtime/components/Table/index.vue.d.ts +1 -1
  96. package/dist/runtime/composables/loaderList.d.ts +2 -1
  97. package/dist/runtime/composables/loaderList.js +3 -4
  98. package/dist/runtime/composables/loaderObject.d.ts +2 -1
  99. package/dist/runtime/composables/loaderObject.js +2 -1
  100. package/dist/runtime/composables/loaderPage.js +223 -350
  101. package/dist/runtime/composables/useConfig.js +4 -3
  102. package/dist/runtime/composables/useDialog.js +3 -1
  103. package/dist/runtime/composables/useForm.d.ts +2 -2
  104. package/dist/runtime/composables/useForm.js +2 -4
  105. package/dist/runtime/composables/useMessages.d.ts +10 -0
  106. package/dist/runtime/composables/useMessages.js +10 -0
  107. package/dist/runtime/composables/useTable.js +2 -14
  108. package/dist/runtime/helpers/apiBaseHelper.js +6 -24
  109. package/dist/runtime/helpers/apiListHelper.js +2 -5
  110. package/dist/runtime/helpers/apiObjectHelper.js +2 -5
  111. package/dist/runtime/helpers/apiPageHelper.d.ts +0 -1
  112. package/dist/runtime/helpers/apiPageHelper.js +19 -38
  113. package/dist/runtime/helpers/componentHelper.js +2 -6
  114. package/dist/runtime/helpers/uploadCore.d.ts +54 -0
  115. package/dist/runtime/helpers/uploadCore.js +81 -0
  116. package/dist/runtime/i18n/valibot.d.ts +5 -68
  117. package/dist/runtime/i18n/valibot.js +33 -29
  118. package/dist/runtime/locale/en.d.ts +2 -0
  119. package/dist/runtime/locale/en.js +42 -0
  120. package/dist/runtime/locale/index.d.ts +7 -0
  121. package/dist/runtime/locale/index.js +5 -0
  122. package/dist/runtime/locale/th.d.ts +43 -0
  123. package/dist/runtime/locale/th.js +42 -0
  124. package/dist/runtime/server/tsconfig.json +1 -1
  125. package/dist/runtime/styles/main.css +1 -1
  126. package/dist/runtime/utils/ParamHelper.js +1 -1
  127. package/dist/runtime/utils/StringHelper.js +1 -4
  128. package/dist/runtime/utils/TimeHelper.js +2 -2
  129. package/dist/runtime/utils/lodash.js +2 -2
  130. package/package.json +57 -36
@@ -27,9 +27,7 @@
27
27
  :style="{ width: `${item.progress}%` }"
28
28
  />
29
29
  </div>
30
- <p class="text-sm text-gray-400">
31
- {{ item.progress }}%
32
- </p>
30
+ <p class="text-sm text-gray-400">{{ item.progress }}%</p>
33
31
  </div>
34
32
  </div>
35
33
  </div>
@@ -49,7 +47,7 @@
49
47
  <h1 class="truncate font-bold">
50
48
  {{ item.file.name }}
51
49
  </h1>
52
- <p class="text-error-500 text-sm">
50
+ <p class="text-sm text-error-500">
53
51
  {{ item.error || uploadFailedLabel }}
54
52
  </p>
55
53
  </div>
@@ -67,7 +65,7 @@
67
65
  v-if="!disabled && !readonly"
68
66
  :name="icons.actionDeleteIcon"
69
67
  :class="theme.actionIconClass()"
70
- title="ลบไฟล์"
68
+ :title="messages.upload.remove"
71
69
  @click="$emit('delete', index)"
72
70
  />
73
71
  </div>
@@ -77,6 +75,7 @@
77
75
  </template>
78
76
 
79
77
  <script setup>
78
+ import { useCoreMessages } from "#core/composables/useMessages";
80
79
  import { useUiIconConfig } from "#core/composables/useConfig";
81
80
  import { UploadState } from "./useUploadStateMultiple";
82
81
  defineEmits(["preview", "download", "delete", "retry"]);
@@ -89,4 +88,5 @@ defineProps({
89
88
  retryLabel: { type: String, required: false }
90
89
  });
91
90
  const icons = useUiIconConfig("uploadFileDropzone");
91
+ const { messages } = useCoreMessages();
92
92
  </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;
@@ -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;
@@ -4,9 +4,7 @@
4
4
  v-if="isImageFromPath(value.path)"
5
5
  :class="theme.onPreviewImgWrapper()"
6
6
  >
7
- <div
8
- class="size-full overflow-hidden"
9
- >
7
+ <div class="size-full overflow-hidden">
10
8
  <img
11
9
  :src="value.url"
12
10
  :class="theme.onPreviewImgClass()"
@@ -44,27 +42,27 @@
44
42
  <Icon
45
43
  :name="icons.actionPreviewIcon"
46
44
  :class="theme.actionIconClass()"
47
- title="ดูตัวอย่าง"
45
+ :title="messages.upload.preview"
48
46
  />
49
47
  </a>
50
48
  <Icon
51
49
  v-if="isImageFromPath(value.path) || isVideoFromPath(value.path)"
52
50
  :name="icons.actionPreviewIcon"
53
51
  :class="theme.actionIconClass()"
54
- title="ดูตัวอย่าง"
52
+ :title="messages.upload.preview"
55
53
  @click="$emit('preview')"
56
54
  />
57
55
  <Icon
58
56
  :name="icons.actionDownloadIcon"
59
57
  :class="theme.actionIconClass()"
60
- title="ดาวน์โหลดไฟล์"
58
+ :title="messages.upload.download"
61
59
  @click="$emit('download')"
62
60
  />
63
61
  <Icon
64
62
  v-if="!disabled && !readonly"
65
63
  :name="icons.actionDeleteIcon"
66
64
  :class="theme.actionIconClass()"
67
- title="ลบไฟล์"
65
+ :title="messages.upload.remove"
68
66
  @click="$emit('delete')"
69
67
  />
70
68
  </div>
@@ -73,12 +71,8 @@
73
71
  </template>
74
72
 
75
73
  <script setup>
76
- import {
77
- isImageFromPath,
78
- isPDFFromPath,
79
- isVideoFromPath,
80
- useFileSize
81
- } from "#core/helpers/componentHelper";
74
+ import { useCoreMessages } from "#core/composables/useMessages";
75
+ import { isImageFromPath, isPDFFromPath, isVideoFromPath, useFileSize } from "#core/helpers/componentHelper";
82
76
  import { useUiIconConfig } from "#core/composables/useConfig";
83
77
  defineEmits(["preview", "download", "delete"]);
84
78
  defineProps({
@@ -92,4 +86,5 @@ const getFileSizeFromValue = (fileValue) => {
92
86
  const allocate = useFileSize(fileValue.size || 0);
93
87
  return allocate.isSelectedFileUseMb.value ? `${allocate.selectedFileSizeMb.value} MB` : `${allocate.selectedFileSizeKb.value} KB`;
94
88
  };
89
+ const { messages } = useCoreMessages();
95
90
  </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 { IFileValue } from '#core/components/Form/types';
4
- export declare enum UploadState {
5
- EMPTY = "empty",
6
- UPLOADING = "uploading",
7
- SUCCESS = "success",
8
- ERROR = "error"
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,50 @@
1
- import { useDropZone, useFileDialog } from "@vueuse/core";
2
- import PreviewModal from "../fileState/PreviewModal.vue";
3
- import { computed, ref, useOverlay } from "#imports";
4
- import { useFileAllocate } from "#core/helpers/componentHelper";
5
- const overlay = useOverlay();
6
- const previewModal = overlay.create(PreviewModal);
7
- export var UploadState = /* @__PURE__ */ ((UploadState2) => {
8
- UploadState2["EMPTY"] = "empty";
9
- UploadState2["UPLOADING"] = "uploading";
10
- UploadState2["SUCCESS"] = "success";
11
- UploadState2["ERROR"] = "error";
12
- return UploadState2;
13
- })(UploadState || {});
14
- export const useFileState = (props, emits, onChange, setErrors, value, acceptedFileTypes, wrapperProps, dropzoneRef) => {
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 { props, emits, onChange, setErrors, value } = context;
8
+ const { messages } = useCoreMessages();
15
9
  const selectedFile = ref();
16
10
  const currentObjectUrl = ref("");
17
- const fileAllocate = useFileAllocate(selectedFile, props);
18
- const validateFile = (file) => {
19
- if (props.accept && fileAllocate.acceptFile.value) {
20
- const acceptedTypes = fileAllocate.acceptFile.value;
21
- const acceptedTypesList = acceptedTypes.split(",").map((type) => type.trim());
22
- const fileExtension = file.name.toLowerCase().split(".").pop();
23
- const isValidFileType = acceptedTypesList.some((acceptedType) => {
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;
11
+ const currentState = computed(() => value.value ? UploadState.SUCCESS : UploadState.EMPTY);
12
+ const isEmpty = computed(() => currentState.value === UploadState.EMPTY);
13
+ const isSuccess = computed(() => currentState.value === UploadState.SUCCESS);
14
+ const cleanup = () => {
15
+ if (!currentObjectUrl.value) return;
16
+ URL.revokeObjectURL(currentObjectUrl.value);
17
+ currentObjectUrl.value = "";
54
18
  };
55
19
  const processFile = (file) => {
56
- if (!validateFile(file)) return;
20
+ const error = validateFile(file, props, messages.value.upload);
21
+ setErrors(error ?? "");
22
+ if (error) return;
57
23
  selectedFile.value = file;
58
24
  emits("change", file);
59
- if (currentObjectUrl.value) {
60
- URL.revokeObjectURL(currentObjectUrl.value);
61
- }
62
- const objectUrl = URL.createObjectURL(file);
63
- currentObjectUrl.value = objectUrl;
25
+ cleanup();
26
+ currentObjectUrl.value = URL.createObjectURL(file);
64
27
  value.value = file;
65
28
  emits("success", file);
66
29
  };
67
- const handleFileDrop = (files) => {
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,
30
+ const picker = useFilePicker(context, {
82
31
  multiple: false,
83
- preventDefaultForUnhandled: false
84
- });
85
- const currentState = computed(() => {
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]);
32
+ canDrop: () => isEmpty.value,
33
+ onFiles: ([file]) => {
34
+ if (file) processFile(file);
101
35
  }
102
36
  });
103
- const handleOpenFile = () => {
104
- if (wrapperProps.value.disabled || wrapperProps.value.readonly) return;
105
- fileDialog.open();
106
- };
107
37
  const handleDeleteFile = () => {
108
- fileDialog.reset();
109
- if (currentObjectUrl.value) {
110
- URL.revokeObjectURL(currentObjectUrl.value);
111
- currentObjectUrl.value = "";
112
- }
38
+ picker.fileDialog.reset();
39
+ cleanup();
113
40
  selectedFile.value = void 0;
114
41
  onChange(void 0);
115
42
  emits("delete");
116
43
  };
117
44
  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
- }
45
+ if (selectedFile.value) processFile(selectedFile.value);
132
46
  };
133
- if (typeof window !== "undefined") {
134
- window.addEventListener("beforeunload", cleanup);
135
- }
47
+ onScopeDispose(cleanup);
136
48
  return {
137
49
  // State
138
50
  currentState,
@@ -141,13 +53,13 @@ export const useFileState = (props, emits, onChange, setErrors, value, acceptedF
141
53
  selectedFile,
142
54
  currentObjectUrl,
143
55
  // Upload utilities
144
- dropzone,
56
+ dropzone: picker.dropzone,
145
57
  // Handlers
146
- handleInputChange,
147
- handleOpenFile,
58
+ handleInputChange: picker.handleInputChange,
59
+ handleOpenFile: picker.handleOpenFile,
148
60
  handleDeleteFile,
149
61
  handleRetryUpload,
150
- handlePreview,
62
+ handlePreview: () => picker.openPreview(value.value),
151
63
  // Cleanup
152
64
  cleanup
153
65
  };
@@ -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 declare enum UploadState {
5
- EMPTY = "empty",
6
- UPLOADING = "uploading",
7
- SUCCESS = "success",
8
- ERROR = "error"
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>;