revamp template

This commit is contained in:
fro1991
2025-02-01 16:44:51 +07:00
parent c432df568a
commit 276a289580
1212 changed files with 112762 additions and 0 deletions

View File

@ -0,0 +1,181 @@
/* eslint-disable no-unused-vars */
import { ChangeEvent, DragEvent, FC, useCallback, useRef, useState } from 'react';
import { getAcceptTypeString, getListFiles, openFileDialog } from './utils';
import React from 'react';
interface IImageInputFile {
dataURL?: string;
file?: File;
[key: string]: any;
}
type TImageInputFiles = IImageInputFile[];
interface IImageInputProps {
value: TImageInputFiles;
onChange: (value: TImageInputFiles, addUpdatedIndex?: number[]) => void;
children?: (props: IImageInputExport) => React.ReactNode;
multiple?: boolean;
maxNumber?: number;
acceptType?: string[];
dataURLKey?: string;
inputProps?: React.HTMLProps<HTMLInputElement>;
}
interface IImageInputExport {
fileList: TImageInputFiles;
onImageUpload: () => void;
onImageRemoveAll: () => void;
onImageUpdate: (index: number) => void;
onImageRemove: (index: number) => void;
isDragging: boolean;
dragProps: {
onDrop: (e: any) => void;
onDragEnter: (e: any) => void;
onDragLeave: (e: any) => void;
onDragOver: (e: any) => void;
onDragStart: (e: any) => void;
};
}
export const DEFAULT_NULL_INDEX = -1;
export const DEFAULT_DATA_URL_KEY = 'dataURL';
const ImageInput: FC<IImageInputProps> = ({
value,
acceptType,
inputProps,
multiple,
children,
onChange
}) => {
const inValue = value || [];
const inputRef = useRef<HTMLInputElement>(null);
const [keyUpdate, setKeyUpdate] = useState<number>(DEFAULT_NULL_INDEX);
const [isDragging, setIsDragging] = useState<boolean>(false);
const onImageRemoveAll = useCallback((): void => {
onChange?.([]);
}, [onChange]);
const handleClickInput = useCallback(() => {
openFileDialog(inputRef);
}, [inputRef]);
const onImageUpload = useCallback((): void => {
handleClickInput();
}, [handleClickInput]);
const onInputChange = async (e: ChangeEvent<HTMLInputElement>): Promise<void> => {
await handleChange(e.target.files);
if (inputRef.current) inputRef.current.value = '';
};
const handleChange = async (files: FileList | null) => {
if (!files) return;
const fileList = await getListFiles(files, DEFAULT_DATA_URL_KEY);
if (!fileList.length) return;
let updatedFileList: TImageInputFiles;
const updatedIndexes: number[] = [];
if (keyUpdate > DEFAULT_NULL_INDEX) {
const [firstFile] = fileList;
updatedFileList = [...inValue];
updatedFileList[keyUpdate] = firstFile;
updatedIndexes.push(keyUpdate);
} else if (multiple) {
updatedFileList = [...inValue, ...fileList];
for (let i = inValue.length; i < updatedFileList.length; i += 1) {
updatedIndexes.push(i);
}
} else {
updatedFileList = [fileList[0]];
updatedIndexes.push(0);
}
onChange?.(updatedFileList, updatedIndexes);
};
const onImageRemove = (index: number | number[]): void => {
const updatedList = [...inValue];
if (Array.isArray(index)) {
index.forEach((i) => {
updatedList.splice(i, 1);
});
} else {
updatedList.splice(index, 1);
}
onChange?.(updatedList);
};
const onImageUpdate = (index: number): void => {
setKeyUpdate(index);
handleClickInput();
};
const handleDrag = (e: DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
};
const handleDragIn = (e: DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
if (e.dataTransfer.items && e.dataTransfer.items.length > 0) {
setIsDragging(true);
}
};
const handleDragOut = (e: DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(false);
};
const handleDrop = (e: DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(false);
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
handleChange(e.dataTransfer.files);
}
};
const handleDragStart = (e: DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
e.dataTransfer.clearData();
};
return (
<>
<input
ref={inputRef}
type="file"
accept={getAcceptTypeString(acceptType)}
multiple={multiple}
onChange={(e) => {
onInputChange(e);
}}
style={{ display: 'none' }}
{...inputProps}
/>
{children?.({
fileList: inValue,
onImageUpload,
onImageRemove,
onImageUpdate,
onImageRemoveAll,
dragProps: {
onDrop: handleDrop,
onDragEnter: handleDragIn,
onDragLeave: handleDragOut,
onDragOver: handleDrag,
onDragStart: handleDragStart
},
isDragging
})}
</>
);
};
export { ImageInput, type IImageInputProps, type TImageInputFiles, type IImageInputFile };

View File

@ -0,0 +1,2 @@
export * from './ImageInput';
export * from './utils';

View File

@ -0,0 +1,52 @@
import { MutableRefObject } from 'react';
import { TImageInputFiles } from './';
export const openFileDialog = (inputRef: MutableRefObject<HTMLInputElement | null>): void => {
if (!inputRef.current) {
return;
}
inputRef.current.click();
};
export const getAcceptTypeString = (acceptType?: string[], allowNonImageType?: boolean) => {
if (acceptType?.length) return acceptType.map((item) => `.${item}`).join(', ');
if (allowNonImageType) return '';
return 'image/*';
};
export const getBase64 = async (file: File): Promise<string> => {
const reader = new FileReader();
return await new Promise((resolve) => {
reader.addEventListener('load', () => {
resolve(String(reader.result));
});
reader.readAsDataURL(file);
});
};
export const getImage = async (file: File): Promise<HTMLImageElement> => {
const image = new Image();
return await new Promise((resolve) => {
image.addEventListener('load', () => {
resolve(image);
});
image.src = URL.createObjectURL(file);
});
};
export const getListFiles = async (
files: FileList,
dataURLKey: string
): Promise<TImageInputFiles> => {
const promiseFiles: Array<Promise<string>> = [];
for (let i = 0; i < files.length; i += 1) {
promiseFiles.push(getBase64(files[i]));
}
return await Promise.all(promiseFiles).then((fileListBase64: string[]) => {
const fileList: TImageInputFiles = fileListBase64.map((base64, index) => ({
[dataURLKey]: base64,
file: files[index]
}));
return fileList;
});
};