@leancodepl/image-uploader 9.6.2

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.
@@ -0,0 +1,49 @@
1
+ import { ReactNode } from "react";
2
+ import { UploadImagesCropperEditorChildProps } from "../types";
3
+ /**
4
+ * Props for the image cropper component.
5
+ */
6
+ export type UploadImagesCropperProps = {
7
+ children: ((props: UploadImagesCropperEditorChildProps) => ReactNode) | ReactNode;
8
+ };
9
+ /**
10
+ * Image cropper component with editing controls.
11
+ *
12
+ * Provides image cropping functionality with zoom, rotation, and crop area controls.
13
+ * Processes cropped images and integrates with the upload state management.
14
+ *
15
+ * @param children - Content or render function receiving cropper controls
16
+ * @returns JSX element with cropper editing interface
17
+ * @throws {Error} When cropper config is not defined in context
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { UploadImages } from "@leancodepl/image-uploader";
22
+ *
23
+ * <UploadImages.Cropper>
24
+ * {({ zoom, rotation, setZoom, setRotation, accept, close }) => (
25
+ * <div>
26
+ * <UploadImages.CropperEditor />
27
+ * <input
28
+ * type="range"
29
+ * value={zoom}
30
+ * onChange={(e) => setZoom(Number(e.target.value))}
31
+ * />
32
+ * <button onClick={accept}>Accept</button>
33
+ * <button onClick={close}>Cancel</button>
34
+ * </div>
35
+ * )}
36
+ * </UploadImages.Cropper>
37
+ * ```
38
+ */
39
+ export declare function UploadImagesCropper({ children }: UploadImagesCropperProps): ReactNode;
40
+ /**
41
+ * Configuration for image cropper functionality.
42
+ */
43
+ export type CropperConfig = {
44
+ aspect: number;
45
+ maxWidth?: number;
46
+ maxHeight?: number;
47
+ withRotation?: boolean;
48
+ withZoom?: boolean;
49
+ };
@@ -0,0 +1,35 @@
1
+ import { HTMLAttributes } from "react";
2
+ /**
3
+ * Props for the cropper editor visual component.
4
+ */
5
+ export type UploadImagesCropperEditorProps = Omit<HTMLAttributes<HTMLDivElement>, "children">;
6
+ /**
7
+ * Visual editor component for image cropping.
8
+ *
9
+ * Renders the interactive crop editor interface using `"react-easy-crop"`.
10
+ * Provides visual feedback for crop area, zoom, and rotation adjustments.
11
+ *
12
+ * @param style - CSS style object, merged with default positioning styles
13
+ * @param props - Additional HTML div attributes
14
+ * @returns JSX element with interactive crop editor
15
+ * @throws {Error} When cropper config is not defined in context
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { UploadImages } from "@leancodepl/image-uploader";
20
+ *
21
+ * <UploadImages.Cropper>
22
+ * {({ zoom, setZoom, rotation, setRotation, accept, close }) => (
23
+ * <div>
24
+ * <UploadImages.CropperEditor />
25
+ * <div>
26
+ * <label>Zoom: <input type="range" value={zoom} onChange={(e) => setZoom(Number(e.target.value))} /></label>
27
+ * <button onClick={accept}>Accept</button>
28
+ * <button onClick={close}>Cancel</button>
29
+ * </div>
30
+ * </div>
31
+ * )}
32
+ * </UploadImages.Cropper>
33
+ * ```
34
+ */
35
+ export declare function UploadImagesCropperEditor({ style, ...props }: UploadImagesCropperEditorProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,36 @@
1
+ import { HTMLAttributes, ReactNode } from "react";
2
+ import { FileWithId, UploadFileItemChildProps } from "../types";
3
+ /**
4
+ * Props for individual file item component.
5
+ */
6
+ export type UploadImagesFileItemProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
7
+ children: ((props: UploadFileItemChildProps) => ReactNode) | ReactNode;
8
+ file: FileWithId;
9
+ };
10
+ /**
11
+ * Individual file item component with preview and removal functionality.
12
+ *
13
+ * Displays a single uploaded file with preview image generation and removal capability.
14
+ * Automatically generates image preview data and provides remove function to children.
15
+ *
16
+ * @param children - Content or render function receiving file, preview, and remove function
17
+ * @param file - File object with ID to display
18
+ * @param props - Additional HTML div attributes
19
+ * @returns JSX element for individual file with preview and controls
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * import { UploadImages } from "@leancodepl/image-uploader";
24
+ *
25
+ * <UploadImages.File file={fileWithId}>
26
+ * {({ file, preview, remove }) => (
27
+ * <div>
28
+ * <img src={preview} alt={file.originalFile.name} />
29
+ * <p>{file.originalFile.name}</p>
30
+ * <button onClick={remove}>Remove</button>
31
+ * </div>
32
+ * )}
33
+ * </UploadImages.File>
34
+ * ```
35
+ */
36
+ export declare function UploadImagesFileItem({ children, file, ...props }: UploadImagesFileItemProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,41 @@
1
+ import { HTMLAttributes, ReactNode } from "react";
2
+ import { UploadFilesChildProps } from "../types";
3
+ /**
4
+ * Props for the UploadImages files container component.
5
+ */
6
+ export type UploadImagesFilesProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
7
+ children: ((props: UploadFilesChildProps) => ReactNode) | ReactNode;
8
+ };
9
+ /**
10
+ * Container component for displaying uploaded files.
11
+ *
12
+ * Renders a container for uploaded files and provides current file list to children.
13
+ * Used to display and manage the collection of uploaded images.
14
+ *
15
+ * @param children - Content or render function receiving files array
16
+ * @param props - Additional HTML div attributes
17
+ * @returns JSX element containing uploaded files
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { UploadImages } from "@leancodepl/image-uploader";
22
+ *
23
+ * <UploadImages.Files>
24
+ * {({ files }) => (
25
+ * <div>
26
+ * {files?.map(file => (
27
+ * <UploadImages.File key={file.id} file={file}>
28
+ * {({ preview, remove }) => (
29
+ * <div>
30
+ * <img src={preview} alt="Preview" />
31
+ * <button onClick={remove}>Remove</button>
32
+ * </div>
33
+ * )}
34
+ * </UploadImages.File>
35
+ * ))}
36
+ * </div>
37
+ * )}
38
+ * </UploadImages.Files>
39
+ * ```
40
+ */
41
+ export declare function UploadImagesFiles({ children, ...props }: UploadImagesFilesProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import { ReactNode } from "react";
2
+ import { useUploadImages } from "../_hooks/useUploadImages";
3
+ type UploadImagesContextValue = ReturnType<typeof useUploadImages>;
4
+ export interface UploadImagesProviderProps {
5
+ children: ReactNode;
6
+ uploader: UploadImagesContextValue;
7
+ }
8
+ export declare function UploadImagesProvider({ children, uploader }: UploadImagesProviderProps): import("react/jsx-runtime").JSX.Element;
9
+ export declare function useUploadImagesContext(): UploadImagesContextValue;
10
+ export {};
@@ -0,0 +1,31 @@
1
+ import { HTMLAttributes } from "react";
2
+ import { useUploadImages } from "../_hooks/useUploadImages";
3
+ /**
4
+ * Props for the UploadImages root component.
5
+ */
6
+ export type UploadImagesRootProps = HTMLAttributes<HTMLDivElement> & {
7
+ uploader: ReturnType<typeof useUploadImages>;
8
+ };
9
+ /**
10
+ * Root wrapper component for image upload functionality.
11
+ *
12
+ * Provides upload context to all child UploadImages components and renders a div container.
13
+ * Must wrap all other UploadImages components to provide shared state.
14
+ *
15
+ * @param uploader - Upload state and functions from `useUploadImages` hook
16
+ * @param props - Additional HTML div attributes
17
+ * @returns JSX element wrapping children with upload context
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { UploadImages, useUploadImages } from "@leancodepl/image-uploader";
22
+ *
23
+ * const uploader = useUploadImages({ value: files, onChange: setFiles });
24
+ *
25
+ * <UploadImages.Root uploader={uploader}>
26
+ * <UploadImages.Zone>Drop files here</UploadImages.Zone>
27
+ * <UploadImages.Files />
28
+ * </UploadImages.Root>
29
+ * ```
30
+ */
31
+ export declare function UploadImagesRoot({ uploader, ...props }: UploadImagesRootProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,32 @@
1
+ import { HTMLAttributes, ReactNode } from "react";
2
+ import { UploadZoneChildProps } from "../types";
3
+ /**
4
+ * Props for the UploadImages zone component.
5
+ */
6
+ export type UploadImagesZoneProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
7
+ children: ((props: UploadZoneChildProps) => ReactNode) | ReactNode;
8
+ };
9
+ /**
10
+ * Drag-and-drop zone component for file uploads.
11
+ *
12
+ * Creates an interactive drop zone with file input capabilities. Supports both click-to-select
13
+ * and drag-and-drop file uploads. Provides drag state to children for visual feedback.
14
+ *
15
+ * @param children - Content or render function receiving drag state props
16
+ * @param props - Additional HTML div attributes
17
+ * @returns JSX element with drag-and-drop upload functionality
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { UploadImages } from "@leancodepl/image-uploader";
22
+ *
23
+ * <UploadImages.Zone>
24
+ * {({ isDragActive, isFocused }) => (
25
+ * <div className={isDragActive ? "drag-active" : ""}>
26
+ * {isDragActive ? "Drop files here" : "Click or drag files to upload"}
27
+ * </div>
28
+ * )}
29
+ * </UploadImages.Zone>
30
+ * ```
31
+ */
32
+ export declare function UploadImagesZone({ children, ...props }: UploadImagesZoneProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { UploadImagesCropper } from "./Cropper";
2
+ import { UploadImagesCropperEditor } from "./CropperEditor";
3
+ import { UploadImagesFileItem } from "./File";
4
+ import { UploadImagesFiles } from "./Files";
5
+ import { UploadImagesRoot } from "./Root";
6
+ import { UploadImagesZone } from "./Zone";
7
+ export declare const UploadImages: {
8
+ Root: typeof UploadImagesRoot;
9
+ Zone: typeof UploadImagesZone;
10
+ Files: typeof UploadImagesFiles;
11
+ File: typeof UploadImagesFileItem;
12
+ Cropper: typeof UploadImagesCropper;
13
+ CropperEditor: typeof UploadImagesCropperEditor;
14
+ };
15
+ export type { UploadImagesFileItemProps } from "./File";
16
+ export type { UploadImagesFilesProps } from "./Files";
17
+ export type { UploadImagesRootProps } from "./Root";
18
+ export type { UploadImagesZoneProps } from "./Zone";
19
+ export type { UploadImagesCropperProps } from "./Cropper";
20
+ export type { UploadImagesCropperEditorProps } from "./CropperEditor";
@@ -0,0 +1,30 @@
1
+ import { Area } from "react-easy-crop";
2
+ import { FileWithId } from "../types";
3
+ export type UseCropperProps = {
4
+ value?: FileWithId[];
5
+ onChange?: (files: FileWithId[]) => void;
6
+ };
7
+ export declare function useCropper({ value, onChange }: UseCropperProps): {
8
+ fileQueue: FileWithId[];
9
+ file: FileWithId | undefined;
10
+ editorImage: string | undefined;
11
+ cropArea: Area | undefined;
12
+ crop: {
13
+ x: number;
14
+ y: number;
15
+ };
16
+ zoom: number;
17
+ rotation: number;
18
+ isOpen: boolean;
19
+ setFileQueue: import("react").Dispatch<import("react").SetStateAction<FileWithId[]>>;
20
+ setEditorImage: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
21
+ setCropArea: import("react").Dispatch<import("react").SetStateAction<Area | undefined>>;
22
+ setCrop: import("react").Dispatch<import("react").SetStateAction<{
23
+ x: number;
24
+ y: number;
25
+ }>>;
26
+ setZoom: import("react").Dispatch<import("react").SetStateAction<number>>;
27
+ setRotation: import("react").Dispatch<import("react").SetStateAction<number>>;
28
+ closeImage: () => void;
29
+ acceptImage: (file: FileWithId) => void | undefined;
30
+ };
@@ -0,0 +1,101 @@
1
+ import { Accept } from "react-dropzone";
2
+ import { ErrorCode } from "../_utils/errors";
3
+ import { FileWithId } from "../types";
4
+ import { CropperConfig } from "../UploadImages/Cropper";
5
+ /**
6
+ * Configuration for the image upload hook.
7
+ */
8
+ export type UseUploadImagesProps = {
9
+ value?: FileWithId[];
10
+ accept?: Accept;
11
+ cropper?: CropperConfig;
12
+ onError?: (errorCode: ErrorCode) => void;
13
+ onChange?: (files: FileWithId[]) => void;
14
+ };
15
+ /**
16
+ * Manages image upload state and provides drag-and-drop functionality.
17
+ *
18
+ * Creates a complete image upload solution with file validation, drag-and-drop support,
19
+ * optional cropping, and duplicate detection. Returns upload state and control functions.
20
+ *
21
+ * @param value - Current array of uploaded files with IDs
22
+ * @param accept - File types to accept (defaults to image types)
23
+ * @param cropper - Optional cropper configuration for image editing
24
+ * @param onError - Callback for handling upload errors
25
+ * @param onChange - Callback when file list changes
26
+ * @returns Upload state and control functions including dropzone props
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * import { useUploadImages } from "@leancodepl/image-uploader";
31
+ *
32
+ * const uploader = useUploadImages({
33
+ * value: files,
34
+ * onChange: setFiles,
35
+ * onError: (error) => console.error("Upload error:", error),
36
+ * accept: { "image/*": [".jpg", ".png"] }
37
+ * });
38
+ * ```
39
+ */
40
+ export declare function useUploadImages({ value, accept, cropper: cropperConfig, onError, onChange, }: UseUploadImagesProps): {
41
+ uploader: {
42
+ value: FileWithId[] | undefined;
43
+ dropzone: import("react-dropzone").DropzoneState;
44
+ addFiles: (newFiles: FileWithId[]) => void;
45
+ removeFile: (id: string) => void;
46
+ clearFiles: () => void;
47
+ cropper: {
48
+ fileQueue: FileWithId[];
49
+ file: FileWithId | undefined;
50
+ editorImage: string | undefined;
51
+ cropArea: import("react-easy-crop").Area | undefined;
52
+ crop: {
53
+ x: number;
54
+ y: number;
55
+ };
56
+ zoom: number;
57
+ rotation: number;
58
+ isOpen: boolean;
59
+ setEditorImage: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
60
+ setCropArea: import("react").Dispatch<import("react").SetStateAction<import("react-easy-crop").Area | undefined>>;
61
+ setCrop: import("react").Dispatch<import("react").SetStateAction<{
62
+ x: number;
63
+ y: number;
64
+ }>>;
65
+ setZoom: import("react").Dispatch<import("react").SetStateAction<number>>;
66
+ setRotation: import("react").Dispatch<import("react").SetStateAction<number>>;
67
+ closeImage: () => void;
68
+ acceptImage: (file: FileWithId) => void | undefined;
69
+ config: CropperConfig;
70
+ } | undefined;
71
+ };
72
+ value: FileWithId[] | undefined;
73
+ dropzone: import("react-dropzone").DropzoneState;
74
+ addFiles: (newFiles: FileWithId[]) => void;
75
+ removeFile: (id: string) => void;
76
+ clearFiles: () => void;
77
+ cropper: {
78
+ fileQueue: FileWithId[];
79
+ file: FileWithId | undefined;
80
+ editorImage: string | undefined;
81
+ cropArea: import("react-easy-crop").Area | undefined;
82
+ crop: {
83
+ x: number;
84
+ y: number;
85
+ };
86
+ zoom: number;
87
+ rotation: number;
88
+ isOpen: boolean;
89
+ setEditorImage: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
90
+ setCropArea: import("react").Dispatch<import("react").SetStateAction<import("react-easy-crop").Area | undefined>>;
91
+ setCrop: import("react").Dispatch<import("react").SetStateAction<{
92
+ x: number;
93
+ y: number;
94
+ }>>;
95
+ setZoom: import("react").Dispatch<import("react").SetStateAction<number>>;
96
+ setRotation: import("react").Dispatch<import("react").SetStateAction<number>>;
97
+ closeImage: () => void;
98
+ acceptImage: (file: FileWithId) => void | undefined;
99
+ config: CropperConfig;
100
+ } | undefined;
101
+ };
@@ -0,0 +1,23 @@
1
+ import { FileRejection } from "react-dropzone";
2
+ /**
3
+ * Error codes for file upload validation failures.
4
+ *
5
+ * Wraps `"react-dropzone"` error codes with an additional Unknown state.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { ErrorCode } from "@leancodepl/image-uploader";
10
+ *
11
+ * if (errorCode === ErrorCode.FileTooLarge) {
12
+ * console.log("File exceeds maximum size limit");
13
+ * }
14
+ * ```
15
+ */
16
+ export declare enum ErrorCode {
17
+ FileTooLarge = "file-too-large",
18
+ FileTooSmall = "file-too-small",
19
+ FileInvalidType = "file-invalid-type",
20
+ TooManyFiles = "too-many-files",
21
+ Unknown = "unknown"
22
+ }
23
+ export declare function mapFileRejectionsToErrorCode(fileRejections: FileRejection[]): ErrorCode;
@@ -0,0 +1 @@
1
+ export declare function getImagePreviewData(file: File): Promise<string>;
@@ -0,0 +1 @@
1
+ export declare function isExactFile(a: File, b: File): boolean;
@@ -0,0 +1,24 @@
1
+ import { FileWithId, UploadedFileWithId, UploadParams } from "../types";
2
+ /**
3
+ * Uploads an image file using provided upload parameters.
4
+ *
5
+ * Handles file upload by calling the getUploadParams function to retrieve upload configuration,
6
+ * then performs a fetch request to upload the file. Returns early if the image is already uploaded.
7
+ *
8
+ * @param image - Image file with ID or already uploaded image with URL
9
+ * @param getUploadParams - Function that returns upload parameters (URI, method, headers) for the image
10
+ * @returns Promise resolving to uploaded image with URL
11
+ * @throws {Error} When upload params are not defined, image is not defined, or upload fails
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { tryUploadWithUploadParams } from "@leancodepl/image-uploader";
16
+ *
17
+ * const uploadedImage = await tryUploadWithUploadParams(image, async (img) => ({
18
+ * uri: "https://api.example.com/upload",
19
+ * method: "POST",
20
+ * requiredHeaders: { "Content-Type": "image/jpeg" }
21
+ * }));
22
+ * ```
23
+ */
24
+ export declare function tryUploadWithUploadParams(image: FileWithId | UploadedFileWithId, getUploadParams: (image: FileWithId) => Promise<UploadParams | null | undefined>): Promise<UploadedFileWithId>;
@@ -0,0 +1,8 @@
1
+ import { Accept } from "react-dropzone";
2
+ export declare const defaultAccept: Accept;
3
+ export declare const defaultCrop: {
4
+ x: number;
5
+ y: number;
6
+ };
7
+ export declare const defaultZoom = 1;
8
+ export declare const defaultRotation = 0;
@@ -0,0 +1,34 @@
1
+ export type { Accept, FileRejection } from "react-dropzone";
2
+ export type FileWithId = {
3
+ originalFile: File;
4
+ id: string;
5
+ };
6
+ export type UploadedFileWithId = FileWithId & {
7
+ url: string;
8
+ };
9
+ export type UploadParams = {
10
+ uri: string;
11
+ method: string;
12
+ requiredHeaders: Record<string, string>;
13
+ };
14
+ export type UploadFilesChildProps = {
15
+ files?: FileWithId[];
16
+ };
17
+ export type UploadFileItemChildProps = {
18
+ file: FileWithId;
19
+ preview?: string;
20
+ remove: () => void;
21
+ };
22
+ export type UploadZoneChildProps = {
23
+ isDragActive: boolean;
24
+ isFileDialogActive: boolean;
25
+ isFocused: boolean;
26
+ };
27
+ export type UploadImagesCropperEditorChildProps = {
28
+ zoom: number;
29
+ rotation: number;
30
+ setZoom: (zoom: number) => void;
31
+ setRotation: (rotation: number) => void;
32
+ accept: () => void;
33
+ close: () => void;
34
+ };