From 741d3bf33067164ae7b5bb86de48a5a9d0912ece Mon Sep 17 00:00:00 2001
From: Enchu
Date: Sun, 2 Nov 2025 10:22:38 +0300
Subject: [PATCH 1/5] fix(50): adding an error
---
packages/pkg.fileuploader/FileUploader.tsx | 8 ++++++++
packages/pkg.fileuploader/types.ts | 1 +
2 files changed, 9 insertions(+)
diff --git a/packages/pkg.fileuploader/FileUploader.tsx b/packages/pkg.fileuploader/FileUploader.tsx
index 51f275b2..71765b27 100644
--- a/packages/pkg.fileuploader/FileUploader.tsx
+++ b/packages/pkg.fileuploader/FileUploader.tsx
@@ -64,6 +64,7 @@ export const FileUploader = ({
disabled,
isWarning,
onChange,
+ isError,
limit = 3,
bytesSizeLimit = DEFAULT_SIZE_LIMIT,
children,
@@ -91,10 +92,17 @@ export const FileUploader = ({
const handleFilesChange = (files?: FileList | null) => {
setError('');
+ isError = undefined;
if (!files || files.length == 0) return;
const fileList = [...files];
if (fileList.length > limit || !validateSize(fileList, bytesSizeLimit)) {
+ if (isError) {
+ isError = `Можно отправить не более ${limit} ${plural(
+ pluralFiles,
+ limit,
+ )} общим объёмом до ${formatedSizeLimit}`;
+ }
return setError(
`Можно отправить не более ${limit} ${plural(
pluralFiles,
diff --git a/packages/pkg.fileuploader/types.ts b/packages/pkg.fileuploader/types.ts
index 61ac448c..245ed25c 100644
--- a/packages/pkg.fileuploader/types.ts
+++ b/packages/pkg.fileuploader/types.ts
@@ -11,6 +11,7 @@ export type FileUploaderProps = {
withLargeError?: boolean;
size?: SizeType;
limit?: number;
+ isError?: string;
isWarning?: boolean;
descriptionText?: string;
onChange: (files: File[]) => void;
From a1d5d0e38e889f60a0464e5694296d86bcc1652f Mon Sep 17 00:00:00 2001
From: Enchu
Date: Sun, 2 Nov 2025 10:27:25 +0300
Subject: [PATCH 2/5] feat(50): version increase
---
packages/pkg.fileuploader/package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/pkg.fileuploader/package.json b/packages/pkg.fileuploader/package.json
index 16a1c167..51014659 100644
--- a/packages/pkg.fileuploader/package.json
+++ b/packages/pkg.fileuploader/package.json
@@ -1,6 +1,6 @@
{
"name": "@xipkg/fileuploader",
- "version": "2.0.12",
+ "version": "2.0.13",
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
From ee8f2803b8f15d272aa4577badba10312c50d61a Mon Sep 17 00:00:00 2001
From: Enchu
Date: Wed, 5 Nov 2025 19:19:28 +0300
Subject: [PATCH 3/5] fex(50): adding error handling
---
packages/pkg.fileuploader/FileUploader.tsx | 79 ++++++++++++++++++----
packages/pkg.fileuploader/types.ts | 2 +-
2 files changed, 66 insertions(+), 15 deletions(-)
diff --git a/packages/pkg.fileuploader/FileUploader.tsx b/packages/pkg.fileuploader/FileUploader.tsx
index 71765b27..1034c0ee 100644
--- a/packages/pkg.fileuploader/FileUploader.tsx
+++ b/packages/pkg.fileuploader/FileUploader.tsx
@@ -56,6 +56,13 @@ const DEFAULT_SIZE_LIMIT = 6 * 1024 * 1024; // 6 MB
const pluralFiles = ['файла', 'файлов', 'файлов'];
+const readFile = (file: File) =>
+ new Promise((resolve) => {
+ const reader = new FileReader();
+ reader.addEventListener('load', () => resolve(reader.result), false);
+ reader.readAsDataURL(file);
+ });
+
export const FileUploader = ({
withError = true,
withLargeError = true,
@@ -64,7 +71,7 @@ export const FileUploader = ({
disabled,
isWarning,
onChange,
- isError,
+ isError = () => {},
limit = 3,
bytesSizeLimit = DEFAULT_SIZE_LIMIT,
children,
@@ -88,27 +95,67 @@ export const FileUploader = ({
stopDefaultEvents(e);
setIsDragOver(false);
handleFilesChange(e.dataTransfer.files);
+ handleFileChange(e.dataTransfer.files[0]);
+ };
+
+ const handleFileChange = async (file: File) => {
+ setError('');
+ isError(undefined);
+
+ if (!file) return;
+
+ if (file.size === 0) {
+ const errorMessage = 'Файл пустой. Пожалуйста, выберите другой файл';
+ setError(errorMessage);
+ isError(errorMessage);
+ return;
+ }
+
+ if (file.size > bytesSizeLimit) {
+ const errorMessage = `Фото слишком большое. Выберите фото до 1 Мб`;
+ setError(errorMessage);
+ isError(errorMessage);
+ return;
+ }
+
+ if (!file.type.startsWith('image/')) {
+ const errorMessage = 'Пожалуйста, загрузите изображение';
+ setError(errorMessage);
+ isError(errorMessage);
+ return;
+ }
+
+ const allowedTypes = ['image/png', 'image/jpeg'];
+ if (!allowedTypes.includes(file.type)) {
+ const errorMessage = 'Этот формат не поддерживается. Загрузите фото в PNG или JPEG';
+ setError(errorMessage);
+ isError(errorMessage);
+ return;
+ }
+
+ if (validateBeforeUpload) {
+ const validationError = validateBeforeUpload([file]);
+ if (validationError) {
+ setError(validationError);
+ isError(validationError);
+ return;
+ }
+ }
+
+ return await readFile(file);
};
const handleFilesChange = (files?: FileList | null) => {
setError('');
- isError = undefined;
if (!files || files.length == 0) return;
const fileList = [...files];
if (fileList.length > limit || !validateSize(fileList, bytesSizeLimit)) {
- if (isError) {
- isError = `Можно отправить не более ${limit} ${plural(
- pluralFiles,
- limit,
- )} общим объёмом до ${formatedSizeLimit}`;
- }
- return setError(
- `Можно отправить не более ${limit} ${plural(
- pluralFiles,
- limit,
- )} общим объёмом до ${formatedSizeLimit}`,
- );
+ const errorMessage = `Можно отправить не более ${limit} ${plural(
+ pluralFiles,
+ limit,
+ )} общим объёмом до ${formatedSizeLimit}`;
+ return setError(errorMessage);
}
if (validateBeforeUpload && validateBeforeUpload(fileList)) {
@@ -120,6 +167,10 @@ export const FileUploader = ({
};
const handleChange = (e: ChangeEvent) => {
+ if (e.target.files && e.target.files[0]) {
+ handleFileChange(e.target.files[0]);
+ }
+
handleFilesChange(e.target.files);
};
diff --git a/packages/pkg.fileuploader/types.ts b/packages/pkg.fileuploader/types.ts
index 245ed25c..82c55086 100644
--- a/packages/pkg.fileuploader/types.ts
+++ b/packages/pkg.fileuploader/types.ts
@@ -11,7 +11,7 @@ export type FileUploaderProps = {
withLargeError?: boolean;
size?: SizeType;
limit?: number;
- isError?: string;
+ isError?: (error: string | undefined) => void;
isWarning?: boolean;
descriptionText?: string;
onChange: (files: File[]) => void;
From faf4f3da4b90f29c087472013cf5a7b251aa0508 Mon Sep 17 00:00:00 2001
From: Enchu
Date: Tue, 11 Nov 2025 10:20:36 +0300
Subject: [PATCH 4/5] fix(50): change types, change errors
---
packages/pkg.fileuploader/FileUploader.tsx | 149 ++++++++++-----------
packages/pkg.fileuploader/types.ts | 6 +-
2 files changed, 74 insertions(+), 81 deletions(-)
diff --git a/packages/pkg.fileuploader/FileUploader.tsx b/packages/pkg.fileuploader/FileUploader.tsx
index 1034c0ee..505c610e 100644
--- a/packages/pkg.fileuploader/FileUploader.tsx
+++ b/packages/pkg.fileuploader/FileUploader.tsx
@@ -20,9 +20,6 @@ const containerStyles = cva(
isDragOver: {
true: 'shadow-[0px_0px_0px_4px_var(--xi-brand-80)] outline-offset-4 outline-4 outline-brand-20 outline border-transparent dark:shadow-[0px_0px_0px_4px_var(--xi-brand-60)] dark:outline-brand-40',
},
- isError: {
- true: 'ring-2 ring-red-60 dark:ring-red-40 !border-transparent',
- },
isWarning: {
true: 'ring-2 ring-orange-80 dark:ring-orange-40 !border-transparent',
},
@@ -56,32 +53,23 @@ const DEFAULT_SIZE_LIMIT = 6 * 1024 * 1024; // 6 MB
const pluralFiles = ['файла', 'файлов', 'файлов'];
-const readFile = (file: File) =>
- new Promise((resolve) => {
- const reader = new FileReader();
- reader.addEventListener('load', () => resolve(reader.result), false);
- reader.readAsDataURL(file);
- });
-
export const FileUploader = ({
- withError = true,
- withLargeError = true,
size = 'medium',
descriptionText,
disabled,
isWarning,
onChange,
- isError = () => {},
+ onError,
+ enableErrorHandling = true,
limit = 3,
bytesSizeLimit = DEFAULT_SIZE_LIMIT,
children,
- multiple,
validateBeforeUpload,
fileTypesHint,
+ acceptedFileTypes,
...inputProps
}: FileUploaderProps & DefaultInputPropsT) => {
const [isDragOver, setIsDragOver] = useState(false);
- const [error, setError] = useState('');
const inputRef = useRef(null);
const dragDeph = useRef(0);
@@ -91,75 +79,88 @@ export const FileUploader = ({
const isLarge = size === 'large';
const formatedSizeLimit = formatBytesSize(bytesSizeLimit);
- const handleDrop = (e: DragEvent) => {
- stopDefaultEvents(e);
- setIsDragOver(false);
- handleFilesChange(e.dataTransfer.files);
- handleFileChange(e.dataTransfer.files[0]);
- };
-
- const handleFileChange = async (file: File) => {
- setError('');
- isError(undefined);
-
- if (!file) return;
-
- if (file.size === 0) {
- const errorMessage = 'Файл пустой. Пожалуйста, выберите другой файл';
- setError(errorMessage);
- isError(errorMessage);
- return;
- }
-
- if (file.size > bytesSizeLimit) {
- const errorMessage = `Фото слишком большое. Выберите фото до 1 Мб`;
- setError(errorMessage);
- isError(errorMessage);
- return;
+ const handleError = (fileList: File[]): boolean => {
+ if (!enableErrorHandling || !onError) {
+ return false;
}
- if (!file.type.startsWith('image/')) {
- const errorMessage = 'Пожалуйста, загрузите изображение';
- setError(errorMessage);
- isError(errorMessage);
- return;
+ if (fileList.length > limit) {
+ onError(`Можно отправить не более ${limit} ${plural(
+ pluralFiles,
+ limit,
+ )} общим объёмом до ${formatedSizeLimit}`);
+ return true;
}
- const allowedTypes = ['image/png', 'image/jpeg'];
- if (!allowedTypes.includes(file.type)) {
- const errorMessage = 'Этот формат не поддерживается. Загрузите фото в PNG или JPEG';
- setError(errorMessage);
- isError(errorMessage);
- return;
+ if (acceptedFileTypes && acceptedFileTypes.length > 0) {
+ const invalidTypeFile = fileList.find((file) => {
+ const fileType = file.type.toLowerCase();
+ const fileName = file.name.toLowerCase();
+
+ return !acceptedFileTypes.some((acceptedType) => {
+ const normalizedAcceptedType = acceptedType.toLowerCase().trim();
+
+ if (fileType === normalizedAcceptedType) {
+ return true;
+ }
+
+ if (normalizedAcceptedType.endsWith('/') && fileType.startsWith(normalizedAcceptedType)) {
+ return true;
+ }
+
+ if (normalizedAcceptedType.startsWith('.')) {
+ return fileName.endsWith(normalizedAcceptedType);
+ }
+
+ if (fileName.endsWith(`.${normalizedAcceptedType}`)) {
+ return true;
+ }
+
+ return false;
+ });
+ });
+
+ if (invalidTypeFile) {
+ const displayTypes = acceptedFileTypes
+ .map((type) => {
+ if (type.startsWith('.')) {
+ return type.toUpperCase();
+ }
+ if (type.includes('/')) {
+ return type.split('/')[1]?.toUpperCase() || type.toUpperCase();
+ }
+ return type.toUpperCase();
+ })
+ .join(', ');
+
+ onError(`Неподдерживаемый формат файла. Разрешены: ${displayTypes}`);
+ return true;
+ }
}
if (validateBeforeUpload) {
- const validationError = validateBeforeUpload([file]);
+ const validationError = validateBeforeUpload(fileList);
if (validationError) {
- setError(validationError);
- isError(validationError);
- return;
+ onError(validationError);
+ return true;
}
}
- return await readFile(file);
+ return false;
+ };
+
+ const handleDrop = (e: DragEvent) => {
+ stopDefaultEvents(e);
+ setIsDragOver(false);
+ handleFilesChange(e.dataTransfer.files);
};
const handleFilesChange = (files?: FileList | null) => {
- setError('');
if (!files || files.length == 0) return;
const fileList = [...files];
- if (fileList.length > limit || !validateSize(fileList, bytesSizeLimit)) {
- const errorMessage = `Можно отправить не более ${limit} ${plural(
- pluralFiles,
- limit,
- )} общим объёмом до ${formatedSizeLimit}`;
- return setError(errorMessage);
- }
-
- if (validateBeforeUpload && validateBeforeUpload(fileList)) {
- return setError(validateBeforeUpload(fileList));
+ if (handleError(fileList)) {
+ return;
}
onChange(fileList);
@@ -167,19 +168,15 @@ export const FileUploader = ({
};
const handleChange = (e: ChangeEvent) => {
- if (e.target.files && e.target.files[0]) {
- handleFileChange(e.target.files[0]);
- }
-
handleFilesChange(e.target.files);
};
- const handleDragEnter = (e: React.DragEvent) => {
+ const handleDragEnter = (_e: React.DragEvent) => {
dragDeph.current++;
setIsDragOver(true);
};
- const handleDragLeave = (e: React.DragEvent) => {
+ const handleDragLeave = (_e: React.DragEvent) => {
dragDeph.current--;
if (dragDeph.current === 0) {
setIsDragOver(false);
@@ -211,7 +208,6 @@ export const FileUploader = ({
className={containerStyles({
isDisabled: disabled,
isDragOver,
- isError: !!error,
isWarning,
size,
})}
@@ -234,7 +230,7 @@ export const FileUploader = ({
)}
- {isLarge && fileTypesHint && withLargeError && (
+ {isLarge && fileTypesHint && (
{descriptionText ||
`${fileTypesHint.map((el) => el.toUpperCase()).join(', ')} до ${formatedSizeLimit}`}
@@ -245,9 +241,6 @@ export const FileUploader = ({
)}
- {error && withError && (
-
{error}
- )}
);
};
diff --git a/packages/pkg.fileuploader/types.ts b/packages/pkg.fileuploader/types.ts
index 82c55086..b801c7cb 100644
--- a/packages/pkg.fileuploader/types.ts
+++ b/packages/pkg.fileuploader/types.ts
@@ -7,11 +7,10 @@ export type DefaultInputPropsT = Omit<
>;
export type FileUploaderProps = {
- withError?: boolean;
- withLargeError?: boolean;
size?: SizeType;
limit?: number;
- isError?: (error: string | undefined) => void;
+ onError?: (error: string) => void;
+ enableErrorHandling?: boolean;
isWarning?: boolean;
descriptionText?: string;
onChange: (files: File[]) => void;
@@ -19,6 +18,7 @@ export type FileUploaderProps = {
bytesSizeLimit?: number;
children?: React.ReactNode;
fileTypesHint?: string[];
+ acceptedFileTypes?: string[];
};
export type FileProps = {
From 3c2f3e2720d1484108f331f09ee5b083d62c826f Mon Sep 17 00:00:00 2001
From: Enchu
Date: Sun, 16 Nov 2025 10:23:57 +0300
Subject: [PATCH 5/5] feat(50): update package
---
package-lock.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package-lock.json b/package-lock.json
index 82dbeee1..02163d10 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22994,7 +22994,7 @@
},
"packages/pkg.fileuploader": {
"name": "@xipkg/fileuploader",
- "version": "2.0.12",
+ "version": "2.0.13",
"license": "MIT",
"dependencies": {
"@xipkg/icons": "^2.5.2",