@leancodepl/image-uploader 9.7.2 → 9.7.4
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.
- package/CHANGELOG.md +142 -0
- package/LICENSE +201 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3903 -0
- package/dist/index.umd.cjs +86 -0
- package/{src → dist}/lib/UploadImages/Cropper.d.ts +3 -2
- package/dist/lib/UploadImages/Cropper.d.ts.map +1 -0
- package/{src → dist}/lib/UploadImages/CropperEditor.d.ts +2 -1
- package/dist/lib/UploadImages/CropperEditor.d.ts.map +1 -0
- package/{src → dist}/lib/UploadImages/File.d.ts +3 -2
- package/dist/lib/UploadImages/File.d.ts.map +1 -0
- package/{src → dist}/lib/UploadImages/Files.d.ts +3 -2
- package/dist/lib/UploadImages/Files.d.ts.map +1 -0
- package/{src → dist}/lib/UploadImages/Provider.d.ts +3 -2
- package/dist/lib/UploadImages/Provider.d.ts.map +1 -0
- package/{src → dist}/lib/UploadImages/Root.d.ts +3 -2
- package/dist/lib/UploadImages/Root.d.ts.map +1 -0
- package/{src → dist}/lib/UploadImages/Zone.d.ts +3 -2
- package/dist/lib/UploadImages/Zone.d.ts.map +1 -0
- package/dist/lib/UploadImages/index.d.ts +21 -0
- package/dist/lib/UploadImages/index.d.ts.map +1 -0
- package/{src → dist}/lib/_hooks/useCropper.d.ts +9 -8
- package/dist/lib/_hooks/useCropper.d.ts.map +1 -0
- package/{src → dist}/lib/_hooks/useUploadImages.d.ts +19 -18
- package/dist/lib/_hooks/useUploadImages.d.ts.map +1 -0
- package/{src → dist}/lib/_utils/errors.d.ts +2 -1
- package/dist/lib/_utils/errors.d.ts.map +1 -0
- package/{src → dist}/lib/_utils/getImagePreviewData.d.ts +1 -0
- package/dist/lib/_utils/getImagePreviewData.d.ts.map +1 -0
- package/{src → dist}/lib/_utils/isExactFile.d.ts +1 -0
- package/dist/lib/_utils/isExactFile.d.ts.map +1 -0
- package/{src → dist}/lib/_utils/tryUploadWithUploadParams.d.ts +2 -1
- package/dist/lib/_utils/tryUploadWithUploadParams.d.ts.map +1 -0
- package/{src → dist}/lib/config.d.ts +2 -1
- package/dist/lib/config.d.ts.map +1 -0
- package/{src → dist}/lib/types.d.ts +2 -1
- package/dist/lib/types.d.ts.map +1 -0
- package/package.json +20 -6
- package/index.d.ts +0 -1
- package/index.esm.js +0 -651
- package/src/index.d.ts +0 -5
- package/src/lib/UploadImages/index.d.ts +0 -20
package/index.esm.js
DELETED
|
@@ -1,651 +0,0 @@
|
|
|
1
|
-
import { ErrorCode as ErrorCode$1, useDropzone } from 'react-dropzone';
|
|
2
|
-
import { useContext, createContext, useCallback, useState, useEffect } from 'react';
|
|
3
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
|
-
import EasyCrop from 'react-easy-crop';
|
|
5
|
-
import { v4 } from 'uuid';
|
|
6
|
-
import { useSyncState } from '@leancodepl/utils';
|
|
7
|
-
|
|
8
|
-
var ErrorCode;
|
|
9
|
-
(function(ErrorCode) {
|
|
10
|
-
ErrorCode[ErrorCode["FileTooLarge"] = ErrorCode$1.FileTooLarge] = "FileTooLarge";
|
|
11
|
-
ErrorCode[ErrorCode["FileTooSmall"] = ErrorCode$1.FileTooSmall] = "FileTooSmall";
|
|
12
|
-
ErrorCode[ErrorCode["FileInvalidType"] = ErrorCode$1.FileInvalidType] = "FileInvalidType";
|
|
13
|
-
ErrorCode[ErrorCode["TooManyFiles"] = ErrorCode$1.TooManyFiles] = "TooManyFiles";
|
|
14
|
-
ErrorCode["Unknown"] = "unknown";
|
|
15
|
-
})(ErrorCode || (ErrorCode = {}));
|
|
16
|
-
function getFlatErrorCodes(fileRejections) {
|
|
17
|
-
return fileRejections.flatMap((fileRejection)=>fileRejection.errors.map((error)=>error.code));
|
|
18
|
-
}
|
|
19
|
-
function getErrorCode(errorCodes) {
|
|
20
|
-
if (errorCodes.includes(ErrorCode$1.FileTooLarge)) {
|
|
21
|
-
return ErrorCode.FileTooLarge;
|
|
22
|
-
}
|
|
23
|
-
if (errorCodes.includes(ErrorCode$1.FileTooSmall)) {
|
|
24
|
-
return ErrorCode.FileTooSmall;
|
|
25
|
-
}
|
|
26
|
-
if (errorCodes.includes(ErrorCode$1.FileInvalidType)) {
|
|
27
|
-
return ErrorCode.FileInvalidType;
|
|
28
|
-
}
|
|
29
|
-
if (errorCodes.includes(ErrorCode$1.TooManyFiles)) {
|
|
30
|
-
return ErrorCode.TooManyFiles;
|
|
31
|
-
}
|
|
32
|
-
return "unknown";
|
|
33
|
-
}
|
|
34
|
-
function mapFileRejectionsToErrorCode(fileRejections) {
|
|
35
|
-
return getErrorCode(getFlatErrorCodes(fileRejections));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function _extends() {
|
|
39
|
-
_extends = Object.assign || function assign(target) {
|
|
40
|
-
for(var i = 1; i < arguments.length; i++){
|
|
41
|
-
var source = arguments[i];
|
|
42
|
-
for(var key in source)if (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key];
|
|
43
|
-
}
|
|
44
|
-
return target;
|
|
45
|
-
};
|
|
46
|
-
return _extends.apply(this, arguments);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Uploads an image file using provided upload parameters.
|
|
51
|
-
*
|
|
52
|
-
* Handles file upload by calling the getUploadParams function to retrieve upload configuration,
|
|
53
|
-
* then performs a fetch request to upload the file. Returns early if the image is already uploaded.
|
|
54
|
-
*
|
|
55
|
-
* @param image - Image file with ID or already uploaded image with URL
|
|
56
|
-
* @param getUploadParams - Function that returns upload parameters (URI, method, headers) for the image
|
|
57
|
-
* @returns Promise resolving to uploaded image with URL
|
|
58
|
-
* @throws {Error} When upload params are not defined, image is not defined, or upload fails
|
|
59
|
-
*
|
|
60
|
-
* @example
|
|
61
|
-
* ```typescript
|
|
62
|
-
* import { tryUploadWithUploadParams } from "@leancodepl/image-uploader";
|
|
63
|
-
*
|
|
64
|
-
* const uploadedImage = await tryUploadWithUploadParams(image, async (img) => ({
|
|
65
|
-
* uri: "https://api.example.com/upload",
|
|
66
|
-
* method: "POST",
|
|
67
|
-
* requiredHeaders: { "Content-Type": "image/jpeg" }
|
|
68
|
-
* }));
|
|
69
|
-
* ```
|
|
70
|
-
*/ async function tryUploadWithUploadParams(image, getUploadParams) {
|
|
71
|
-
if ("url" in image) {
|
|
72
|
-
return image;
|
|
73
|
-
}
|
|
74
|
-
if (!image.originalFile) {
|
|
75
|
-
throw new Error("Image is not defined");
|
|
76
|
-
}
|
|
77
|
-
try {
|
|
78
|
-
const uploadParams = await getUploadParams(image);
|
|
79
|
-
if (!uploadParams) {
|
|
80
|
-
throw new Error("Upload params are not defined");
|
|
81
|
-
}
|
|
82
|
-
const { uri, method, requiredHeaders } = uploadParams;
|
|
83
|
-
const response = await fetch(uri, {
|
|
84
|
-
method,
|
|
85
|
-
headers: requiredHeaders,
|
|
86
|
-
body: image.originalFile
|
|
87
|
-
});
|
|
88
|
-
if (!response.ok) {
|
|
89
|
-
throw new Error("Failed to upload image");
|
|
90
|
-
}
|
|
91
|
-
return _extends({}, image, {
|
|
92
|
-
url: uri
|
|
93
|
-
});
|
|
94
|
-
} catch (e) {
|
|
95
|
-
throw new Error("Failed to upload image");
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const defaultAccept = {
|
|
100
|
-
"image/*": []
|
|
101
|
-
};
|
|
102
|
-
const defaultCrop = {
|
|
103
|
-
x: 0,
|
|
104
|
-
y: 0
|
|
105
|
-
};
|
|
106
|
-
const defaultZoom = 1;
|
|
107
|
-
const defaultRotation = 0;
|
|
108
|
-
|
|
109
|
-
const UploadImagesContext = /*#__PURE__*/ createContext(undefined);
|
|
110
|
-
function UploadImagesProvider({ children, uploader }) {
|
|
111
|
-
return /*#__PURE__*/ jsx(UploadImagesContext.Provider, {
|
|
112
|
-
value: uploader,
|
|
113
|
-
children: children
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
function useUploadImagesContext() {
|
|
117
|
-
const context = useContext(UploadImagesContext);
|
|
118
|
-
if (!context) {
|
|
119
|
-
throw new Error("All UploadImages components must be wrapped in an UploadImages.Root component");
|
|
120
|
-
}
|
|
121
|
-
return context;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Image cropper component with editing controls.
|
|
126
|
-
*
|
|
127
|
-
* Provides image cropping functionality with zoom, rotation, and crop area controls.
|
|
128
|
-
* Processes cropped images and integrates with the upload state management.
|
|
129
|
-
*
|
|
130
|
-
* @param children - Content or render function receiving cropper controls
|
|
131
|
-
* @returns JSX element with cropper editing interface
|
|
132
|
-
* @throws {Error} When cropper config is not defined in context
|
|
133
|
-
*
|
|
134
|
-
* @example
|
|
135
|
-
* ```typescript
|
|
136
|
-
* import { UploadImages } from "@leancodepl/image-uploader";
|
|
137
|
-
*
|
|
138
|
-
* <UploadImages.Cropper>
|
|
139
|
-
* {({ zoom, rotation, setZoom, setRotation, accept, close }) => (
|
|
140
|
-
* <div>
|
|
141
|
-
* <UploadImages.CropperEditor />
|
|
142
|
-
* <input
|
|
143
|
-
* type="range"
|
|
144
|
-
* value={zoom}
|
|
145
|
-
* onChange={(e) => setZoom(Number(e.target.value))}
|
|
146
|
-
* />
|
|
147
|
-
* <button onClick={accept}>Accept</button>
|
|
148
|
-
* <button onClick={close}>Cancel</button>
|
|
149
|
-
* </div>
|
|
150
|
-
* )}
|
|
151
|
-
* </UploadImages.Cropper>
|
|
152
|
-
* ```
|
|
153
|
-
*/ function UploadImagesCropper({ children }) {
|
|
154
|
-
const { cropper } = useUploadImagesContext();
|
|
155
|
-
if (!cropper) {
|
|
156
|
-
throw new Error("Cropper config is not defined");
|
|
157
|
-
}
|
|
158
|
-
const { file, editorImage, acceptImage, closeImage, config, cropArea, zoom, rotation, setCropArea, setCrop, setZoom, setRotation } = cropper;
|
|
159
|
-
const close = useCallback(()=>{
|
|
160
|
-
closeImage();
|
|
161
|
-
setCrop(defaultCrop);
|
|
162
|
-
setZoom(defaultZoom);
|
|
163
|
-
setRotation(defaultRotation);
|
|
164
|
-
setCropArea(undefined);
|
|
165
|
-
}, [
|
|
166
|
-
closeImage,
|
|
167
|
-
setCrop,
|
|
168
|
-
setCropArea,
|
|
169
|
-
setRotation,
|
|
170
|
-
setZoom
|
|
171
|
-
]);
|
|
172
|
-
const accept = useCallback(()=>{
|
|
173
|
-
if (!editorImage) return;
|
|
174
|
-
const canvas = document.createElement("canvas");
|
|
175
|
-
const canvas2 = document.createElement("canvas");
|
|
176
|
-
const ctx = canvas.getContext("2d");
|
|
177
|
-
const ctx2 = canvas2.getContext("2d");
|
|
178
|
-
const img = new Image();
|
|
179
|
-
img.onload = function() {
|
|
180
|
-
const { naturalWidth: imgWidth, naturalHeight: imgHeight } = img;
|
|
181
|
-
const angle = rotation * (Math.PI / 180);
|
|
182
|
-
const sine = Math.abs(Math.sin(angle));
|
|
183
|
-
const cosine = Math.abs(Math.cos(angle));
|
|
184
|
-
const squareWidth = imgWidth * cosine + imgHeight * sine;
|
|
185
|
-
const squareHeight = imgHeight * cosine + imgWidth * sine;
|
|
186
|
-
canvas.width = squareWidth;
|
|
187
|
-
canvas.height = squareHeight;
|
|
188
|
-
ctx.fillStyle = "#fff";
|
|
189
|
-
ctx.fillRect(0, 0, squareWidth, squareHeight);
|
|
190
|
-
const squareHalfWidth = squareWidth / 2;
|
|
191
|
-
const squareHalfHeight = squareHeight / 2;
|
|
192
|
-
ctx.translate(squareHalfWidth, squareHalfHeight);
|
|
193
|
-
ctx.rotate(angle);
|
|
194
|
-
ctx.translate(-squareHalfWidth, -squareHalfHeight);
|
|
195
|
-
const imgX = (squareWidth - imgWidth) / 2;
|
|
196
|
-
const imgY = (squareHeight - imgHeight) / 2;
|
|
197
|
-
ctx.drawImage(img, 0, 0, imgWidth, imgHeight, imgX, imgY, imgWidth, imgHeight);
|
|
198
|
-
var _cropArea_width, _config_maxWidth;
|
|
199
|
-
canvas2.width = Math.min(((_cropArea_width = cropArea == null ? void 0 : cropArea.width) != null ? _cropArea_width : 100) / 100 * squareWidth, (_config_maxWidth = config.maxWidth) != null ? _config_maxWidth : Number.POSITIVE_INFINITY);
|
|
200
|
-
var _cropArea_height, _config_maxHeight;
|
|
201
|
-
canvas2.height = Math.min(((_cropArea_height = cropArea == null ? void 0 : cropArea.height) != null ? _cropArea_height : 100) / 100 * squareHeight, (_config_maxHeight = config.maxHeight) != null ? _config_maxHeight : Number.POSITIVE_INFINITY);
|
|
202
|
-
var _cropArea_x, _cropArea_y, _cropArea_width1, _cropArea_height1;
|
|
203
|
-
ctx2.drawImage(canvas, ((_cropArea_x = cropArea == null ? void 0 : cropArea.x) != null ? _cropArea_x : 0) / 100 * squareWidth, ((_cropArea_y = cropArea == null ? void 0 : cropArea.y) != null ? _cropArea_y : 0) / 100 * squareHeight, ((_cropArea_width1 = cropArea == null ? void 0 : cropArea.width) != null ? _cropArea_width1 : 100) / 100 * squareWidth, ((_cropArea_height1 = cropArea == null ? void 0 : cropArea.height) != null ? _cropArea_height1 : 100) / 100 * squareHeight, 0, 0, canvas2.width, canvas2.height);
|
|
204
|
-
canvas2.toBlob((blob)=>{
|
|
205
|
-
if (!file) {
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
var _file_originalFile_name, _file_originalFile_type;
|
|
209
|
-
const newFile = new File([
|
|
210
|
-
blob
|
|
211
|
-
], (_file_originalFile_name = file.originalFile.name) != null ? _file_originalFile_name : "image.png", {
|
|
212
|
-
type: (_file_originalFile_type = file.originalFile.type) != null ? _file_originalFile_type : "image/png"
|
|
213
|
-
});
|
|
214
|
-
const uploadFile = {
|
|
215
|
-
originalFile: newFile,
|
|
216
|
-
id: file.id
|
|
217
|
-
};
|
|
218
|
-
acceptImage(uploadFile);
|
|
219
|
-
});
|
|
220
|
-
close();
|
|
221
|
-
};
|
|
222
|
-
img.src = editorImage;
|
|
223
|
-
}, [
|
|
224
|
-
close,
|
|
225
|
-
config,
|
|
226
|
-
cropArea,
|
|
227
|
-
file,
|
|
228
|
-
editorImage,
|
|
229
|
-
rotation,
|
|
230
|
-
acceptImage
|
|
231
|
-
]);
|
|
232
|
-
return typeof children === "function" ? children({
|
|
233
|
-
zoom,
|
|
234
|
-
rotation,
|
|
235
|
-
setZoom,
|
|
236
|
-
setRotation,
|
|
237
|
-
accept,
|
|
238
|
-
close
|
|
239
|
-
}) : children;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function _object_without_properties_loose(source, excluded) {
|
|
243
|
-
if (source == null) return {};
|
|
244
|
-
var target = {};
|
|
245
|
-
var sourceKeys = Object.keys(source);
|
|
246
|
-
var key, i;
|
|
247
|
-
for(i = 0; i < sourceKeys.length; i++){
|
|
248
|
-
key = sourceKeys[i];
|
|
249
|
-
if (excluded.indexOf(key) >= 0) continue;
|
|
250
|
-
target[key] = source[key];
|
|
251
|
-
}
|
|
252
|
-
return target;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Visual editor component for image cropping.
|
|
257
|
-
*
|
|
258
|
-
* Renders the interactive crop editor interface using `"react-easy-crop"`.
|
|
259
|
-
* Provides visual feedback for crop area, zoom, and rotation adjustments.
|
|
260
|
-
*
|
|
261
|
-
* @param style - CSS style object, merged with default positioning styles
|
|
262
|
-
* @param props - Additional HTML div attributes
|
|
263
|
-
* @returns JSX element with interactive crop editor
|
|
264
|
-
* @throws {Error} When cropper config is not defined in context
|
|
265
|
-
*
|
|
266
|
-
* @example
|
|
267
|
-
* ```typescript
|
|
268
|
-
* import { UploadImages } from "@leancodepl/image-uploader";
|
|
269
|
-
*
|
|
270
|
-
* <UploadImages.Cropper>
|
|
271
|
-
* {({ zoom, setZoom, rotation, setRotation, accept, close }) => (
|
|
272
|
-
* <div>
|
|
273
|
-
* <UploadImages.CropperEditor />
|
|
274
|
-
* <div>
|
|
275
|
-
* <label>Zoom: <input type="range" value={zoom} onChange={(e) => setZoom(Number(e.target.value))} /></label>
|
|
276
|
-
* <button onClick={accept}>Accept</button>
|
|
277
|
-
* <button onClick={close}>Cancel</button>
|
|
278
|
-
* </div>
|
|
279
|
-
* </div>
|
|
280
|
-
* )}
|
|
281
|
-
* </UploadImages.Cropper>
|
|
282
|
-
* ```
|
|
283
|
-
*/ function UploadImagesCropperEditor(_param) {
|
|
284
|
-
var { style } = _param, props = _object_without_properties_loose(_param, [
|
|
285
|
-
"style"
|
|
286
|
-
]);
|
|
287
|
-
const { cropper } = useUploadImagesContext();
|
|
288
|
-
if (!cropper) {
|
|
289
|
-
throw new Error("Cropper config is not defined");
|
|
290
|
-
}
|
|
291
|
-
const { editorImage, config, zoom, rotation, crop, setCropArea, setCrop, setZoom, setRotation } = cropper;
|
|
292
|
-
return /*#__PURE__*/ jsx("div", _extends({}, props, {
|
|
293
|
-
style: _extends({
|
|
294
|
-
position: "relative",
|
|
295
|
-
width: "100%",
|
|
296
|
-
height: "300px"
|
|
297
|
-
}, style),
|
|
298
|
-
children: /*#__PURE__*/ jsx(EasyCrop, {
|
|
299
|
-
aspect: config.aspect,
|
|
300
|
-
crop: crop,
|
|
301
|
-
image: editorImage,
|
|
302
|
-
rotation: rotation,
|
|
303
|
-
zoom: zoom,
|
|
304
|
-
onCropChange: setCrop,
|
|
305
|
-
onCropComplete: setCropArea,
|
|
306
|
-
onRotationChange: setRotation,
|
|
307
|
-
onZoomChange: setZoom
|
|
308
|
-
})
|
|
309
|
-
}));
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
async function getImagePreviewData(file) {
|
|
313
|
-
return file ? await getBase64(file) : "";
|
|
314
|
-
}
|
|
315
|
-
const getBase64 = (file)=>new Promise((resolve, reject)=>{
|
|
316
|
-
const reader = new FileReader();
|
|
317
|
-
reader.readAsDataURL(file);
|
|
318
|
-
reader.onload = ()=>resolve(String(reader.result));
|
|
319
|
-
reader.onerror = (error)=>reject(error);
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Individual file item component with preview and removal functionality.
|
|
324
|
-
*
|
|
325
|
-
* Displays a single uploaded file with preview image generation and removal capability.
|
|
326
|
-
* Automatically generates image preview data and provides remove function to children.
|
|
327
|
-
*
|
|
328
|
-
* @param children - Content or render function receiving file, preview, and remove function
|
|
329
|
-
* @param file - File object with ID to display
|
|
330
|
-
* @param props - Additional HTML div attributes
|
|
331
|
-
* @returns JSX element for individual file with preview and controls
|
|
332
|
-
*
|
|
333
|
-
* @example
|
|
334
|
-
* ```typescript
|
|
335
|
-
* import { UploadImages } from "@leancodepl/image-uploader";
|
|
336
|
-
*
|
|
337
|
-
* <UploadImages.File file={fileWithId}>
|
|
338
|
-
* {({ file, preview, remove }) => (
|
|
339
|
-
* <div>
|
|
340
|
-
* <img src={preview} alt={file.originalFile.name} />
|
|
341
|
-
* <p>{file.originalFile.name}</p>
|
|
342
|
-
* <button onClick={remove}>Remove</button>
|
|
343
|
-
* </div>
|
|
344
|
-
* )}
|
|
345
|
-
* </UploadImages.File>
|
|
346
|
-
* ```
|
|
347
|
-
*/ function UploadImagesFileItem(_param) {
|
|
348
|
-
var { children, file } = _param, props = _object_without_properties_loose(_param, [
|
|
349
|
-
"children",
|
|
350
|
-
"file"
|
|
351
|
-
]);
|
|
352
|
-
const { removeFile } = useUploadImagesContext();
|
|
353
|
-
const [previewData, setPreviewData] = useState();
|
|
354
|
-
useEffect(()=>{
|
|
355
|
-
const loadPreview = async ()=>{
|
|
356
|
-
const preview = await getImagePreviewData(file.originalFile);
|
|
357
|
-
setPreviewData(preview);
|
|
358
|
-
};
|
|
359
|
-
loadPreview();
|
|
360
|
-
}, [
|
|
361
|
-
file.originalFile
|
|
362
|
-
]);
|
|
363
|
-
return /*#__PURE__*/ jsx("div", _extends({}, props, {
|
|
364
|
-
children: typeof children === "function" ? children({
|
|
365
|
-
file,
|
|
366
|
-
preview: previewData,
|
|
367
|
-
remove: ()=>removeFile(file.id)
|
|
368
|
-
}) : children
|
|
369
|
-
}));
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Container component for displaying uploaded files.
|
|
374
|
-
*
|
|
375
|
-
* Renders a container for uploaded files and provides current file list to children.
|
|
376
|
-
* Used to display and manage the collection of uploaded images.
|
|
377
|
-
*
|
|
378
|
-
* @param children - Content or render function receiving files array
|
|
379
|
-
* @param props - Additional HTML div attributes
|
|
380
|
-
* @returns JSX element containing uploaded files
|
|
381
|
-
*
|
|
382
|
-
* @example
|
|
383
|
-
* ```typescript
|
|
384
|
-
* import { UploadImages } from "@leancodepl/image-uploader";
|
|
385
|
-
*
|
|
386
|
-
* <UploadImages.Files>
|
|
387
|
-
* {({ files }) => (
|
|
388
|
-
* <div>
|
|
389
|
-
* {files?.map(file => (
|
|
390
|
-
* <UploadImages.File key={file.id} file={file}>
|
|
391
|
-
* {({ preview, remove }) => (
|
|
392
|
-
* <div>
|
|
393
|
-
* <img src={preview} alt="Preview" />
|
|
394
|
-
* <button onClick={remove}>Remove</button>
|
|
395
|
-
* </div>
|
|
396
|
-
* )}
|
|
397
|
-
* </UploadImages.File>
|
|
398
|
-
* ))}
|
|
399
|
-
* </div>
|
|
400
|
-
* )}
|
|
401
|
-
* </UploadImages.Files>
|
|
402
|
-
* ```
|
|
403
|
-
*/ function UploadImagesFiles(_param) {
|
|
404
|
-
var { children } = _param, props = _object_without_properties_loose(_param, [
|
|
405
|
-
"children"
|
|
406
|
-
]);
|
|
407
|
-
const { value } = useUploadImagesContext();
|
|
408
|
-
return /*#__PURE__*/ jsx("div", _extends({}, props, {
|
|
409
|
-
children: typeof children === "function" ? children({
|
|
410
|
-
files: value
|
|
411
|
-
}) : children
|
|
412
|
-
}));
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Root wrapper component for image upload functionality.
|
|
417
|
-
*
|
|
418
|
-
* Provides upload context to all child UploadImages components and renders a div container.
|
|
419
|
-
* Must wrap all other UploadImages components to provide shared state.
|
|
420
|
-
*
|
|
421
|
-
* @param uploader - Upload state and functions from `useUploadImages` hook
|
|
422
|
-
* @param props - Additional HTML div attributes
|
|
423
|
-
* @returns JSX element wrapping children with upload context
|
|
424
|
-
*
|
|
425
|
-
* @example
|
|
426
|
-
* ```typescript
|
|
427
|
-
* import { UploadImages, useUploadImages } from "@leancodepl/image-uploader";
|
|
428
|
-
*
|
|
429
|
-
* const uploader = useUploadImages({ value: files, onChange: setFiles });
|
|
430
|
-
*
|
|
431
|
-
* <UploadImages.Root uploader={uploader}>
|
|
432
|
-
* <UploadImages.Zone>Drop files here</UploadImages.Zone>
|
|
433
|
-
* <UploadImages.Files />
|
|
434
|
-
* </UploadImages.Root>
|
|
435
|
-
* ```
|
|
436
|
-
*/ function UploadImagesRoot(_param) {
|
|
437
|
-
var { uploader } = _param, props = _object_without_properties_loose(_param, [
|
|
438
|
-
"uploader"
|
|
439
|
-
]);
|
|
440
|
-
return /*#__PURE__*/ jsx(UploadImagesProvider, {
|
|
441
|
-
uploader: uploader,
|
|
442
|
-
children: /*#__PURE__*/ jsx("div", _extends({}, props))
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* Drag-and-drop zone component for file uploads.
|
|
448
|
-
*
|
|
449
|
-
* Creates an interactive drop zone with file input capabilities. Supports both click-to-select
|
|
450
|
-
* and drag-and-drop file uploads. Provides drag state to children for visual feedback.
|
|
451
|
-
*
|
|
452
|
-
* @param children - Content or render function receiving drag state props
|
|
453
|
-
* @param props - Additional HTML div attributes
|
|
454
|
-
* @returns JSX element with drag-and-drop upload functionality
|
|
455
|
-
*
|
|
456
|
-
* @example
|
|
457
|
-
* ```typescript
|
|
458
|
-
* import { UploadImages } from "@leancodepl/image-uploader";
|
|
459
|
-
*
|
|
460
|
-
* <UploadImages.Zone>
|
|
461
|
-
* {({ isDragActive, isFocused }) => (
|
|
462
|
-
* <div className={isDragActive ? "drag-active" : ""}>
|
|
463
|
-
* {isDragActive ? "Drop files here" : "Click or drag files to upload"}
|
|
464
|
-
* </div>
|
|
465
|
-
* )}
|
|
466
|
-
* </UploadImages.Zone>
|
|
467
|
-
* ```
|
|
468
|
-
*/ function UploadImagesZone(_param) {
|
|
469
|
-
var { children } = _param, props = _object_without_properties_loose(_param, [
|
|
470
|
-
"children"
|
|
471
|
-
]);
|
|
472
|
-
const { dropzone: { getRootProps, getInputProps, isDragActive, isFocused, isFileDialogActive } } = useUploadImagesContext();
|
|
473
|
-
return /*#__PURE__*/ jsxs("div", _extends({}, getRootProps(), props, {
|
|
474
|
-
children: [
|
|
475
|
-
/*#__PURE__*/ jsx("input", _extends({}, getInputProps())),
|
|
476
|
-
typeof children === "function" ? children({
|
|
477
|
-
isDragActive,
|
|
478
|
-
isFocused,
|
|
479
|
-
isFileDialogActive
|
|
480
|
-
}) : children
|
|
481
|
-
]
|
|
482
|
-
}));
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
const UploadImages = {
|
|
486
|
-
Root: UploadImagesRoot,
|
|
487
|
-
Zone: UploadImagesZone,
|
|
488
|
-
Files: UploadImagesFiles,
|
|
489
|
-
File: UploadImagesFileItem,
|
|
490
|
-
Cropper: UploadImagesCropper,
|
|
491
|
-
CropperEditor: UploadImagesCropperEditor
|
|
492
|
-
};
|
|
493
|
-
|
|
494
|
-
function isExactFile(a, b) {
|
|
495
|
-
return a.name === b.name && a.size === b.size && a.lastModified === b.lastModified;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
function useCropper({ value, onChange }) {
|
|
499
|
-
const [fileQueue, setFileQueue] = useState([]);
|
|
500
|
-
const [editorImage, setEditorImage] = useState();
|
|
501
|
-
const [cropArea, setCropArea] = useState();
|
|
502
|
-
const [crop, setCrop] = useState(defaultCrop);
|
|
503
|
-
const [zoom, setZoom] = useState(defaultZoom);
|
|
504
|
-
const [rotation, setRotation] = useState(defaultRotation);
|
|
505
|
-
const file = fileQueue.at(0);
|
|
506
|
-
const isOpen = !!file;
|
|
507
|
-
useSyncState(file, (newCropperFile)=>{
|
|
508
|
-
setEditorImage(undefined);
|
|
509
|
-
if (!newCropperFile) {
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
const reader = new FileReader();
|
|
513
|
-
reader.addEventListener("load", ()=>{
|
|
514
|
-
if (typeof reader.result === "string") {
|
|
515
|
-
setEditorImage(reader.result);
|
|
516
|
-
}
|
|
517
|
-
});
|
|
518
|
-
reader.readAsDataURL(newCropperFile.originalFile);
|
|
519
|
-
});
|
|
520
|
-
const closeImage = useCallback(()=>{
|
|
521
|
-
setFileQueue(fileQueue.filter(({ id })=>id !== (file == null ? void 0 : file.id)));
|
|
522
|
-
}, [
|
|
523
|
-
fileQueue,
|
|
524
|
-
file == null ? void 0 : file.id
|
|
525
|
-
]);
|
|
526
|
-
const acceptImage = useCallback((file)=>onChange == null ? void 0 : onChange([
|
|
527
|
-
...value != null ? value : [],
|
|
528
|
-
file
|
|
529
|
-
]), [
|
|
530
|
-
onChange,
|
|
531
|
-
value
|
|
532
|
-
]);
|
|
533
|
-
return {
|
|
534
|
-
fileQueue,
|
|
535
|
-
file,
|
|
536
|
-
editorImage,
|
|
537
|
-
cropArea,
|
|
538
|
-
crop,
|
|
539
|
-
zoom,
|
|
540
|
-
rotation,
|
|
541
|
-
isOpen,
|
|
542
|
-
setFileQueue,
|
|
543
|
-
setEditorImage,
|
|
544
|
-
setCropArea,
|
|
545
|
-
setCrop,
|
|
546
|
-
setZoom,
|
|
547
|
-
setRotation,
|
|
548
|
-
closeImage,
|
|
549
|
-
acceptImage
|
|
550
|
-
};
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
/**
|
|
554
|
-
* Manages image upload state and provides drag-and-drop functionality.
|
|
555
|
-
*
|
|
556
|
-
* Creates a complete image upload solution with file validation, drag-and-drop support,
|
|
557
|
-
* optional cropping, and duplicate detection. Returns upload state and control functions.
|
|
558
|
-
*
|
|
559
|
-
* @param value - Current array of uploaded files with IDs
|
|
560
|
-
* @param accept - File types to accept (defaults to image types)
|
|
561
|
-
* @param cropper - Optional cropper configuration for image editing
|
|
562
|
-
* @param onError - Callback for handling upload errors
|
|
563
|
-
* @param onChange - Callback when file list changes
|
|
564
|
-
* @returns Upload state and control functions including dropzone props
|
|
565
|
-
*
|
|
566
|
-
* @example
|
|
567
|
-
* ```typescript
|
|
568
|
-
* import { useUploadImages } from "@leancodepl/image-uploader";
|
|
569
|
-
*
|
|
570
|
-
* const uploader = useUploadImages({
|
|
571
|
-
* value: files,
|
|
572
|
-
* onChange: setFiles,
|
|
573
|
-
* onError: (error) => console.error("Upload error:", error),
|
|
574
|
-
* accept: { "image/*": [".jpg", ".png"] }
|
|
575
|
-
* });
|
|
576
|
-
* ```
|
|
577
|
-
*/ function useUploadImages({ value, accept = defaultAccept, cropper: cropperConfig, onError, onChange }) {
|
|
578
|
-
const _useCropper = useCropper({
|
|
579
|
-
value,
|
|
580
|
-
onChange
|
|
581
|
-
}), { setFileQueue: setCropperFileQueue } = _useCropper, cropperProps = _object_without_properties_loose(_useCropper, [
|
|
582
|
-
"setFileQueue"
|
|
583
|
-
]);
|
|
584
|
-
const handleNewFiles = useCallback((newFiles)=>{
|
|
585
|
-
if (cropperConfig) {
|
|
586
|
-
setCropperFileQueue(newFiles);
|
|
587
|
-
} else {
|
|
588
|
-
onChange == null ? void 0 : onChange([
|
|
589
|
-
...value != null ? value : [],
|
|
590
|
-
...newFiles
|
|
591
|
-
]);
|
|
592
|
-
}
|
|
593
|
-
}, [
|
|
594
|
-
cropperConfig,
|
|
595
|
-
onChange,
|
|
596
|
-
value,
|
|
597
|
-
setCropperFileQueue
|
|
598
|
-
]);
|
|
599
|
-
const addFiles = useCallback((newFiles)=>{
|
|
600
|
-
const uniqueNewFiles = newFiles.filter((newFile)=>!(value == null ? void 0 : value.some((existingFile)=>isExactFile(existingFile.originalFile, newFile.originalFile))));
|
|
601
|
-
handleNewFiles(uniqueNewFiles);
|
|
602
|
-
}, [
|
|
603
|
-
handleNewFiles,
|
|
604
|
-
value
|
|
605
|
-
]);
|
|
606
|
-
const removeFile = useCallback((id)=>{
|
|
607
|
-
var _value_filter;
|
|
608
|
-
onChange == null ? void 0 : onChange((_value_filter = value == null ? void 0 : value.filter((f)=>f.id !== id)) != null ? _value_filter : []);
|
|
609
|
-
}, [
|
|
610
|
-
value,
|
|
611
|
-
onChange
|
|
612
|
-
]);
|
|
613
|
-
const clearFiles = useCallback(()=>{
|
|
614
|
-
onChange == null ? void 0 : onChange([]);
|
|
615
|
-
}, [
|
|
616
|
-
onChange
|
|
617
|
-
]);
|
|
618
|
-
const handleDrop = useCallback((acceptedFiles)=>{
|
|
619
|
-
addFiles(acceptedFiles.map((file)=>({
|
|
620
|
-
originalFile: file,
|
|
621
|
-
id: v4()
|
|
622
|
-
})));
|
|
623
|
-
}, [
|
|
624
|
-
addFiles
|
|
625
|
-
]);
|
|
626
|
-
const handleDropRejected = useCallback((fileRejections)=>{
|
|
627
|
-
onError == null ? void 0 : onError(mapFileRejectionsToErrorCode(fileRejections));
|
|
628
|
-
}, [
|
|
629
|
-
onError
|
|
630
|
-
]);
|
|
631
|
-
const dropzone = useDropzone({
|
|
632
|
-
onDrop: handleDrop,
|
|
633
|
-
onDropRejected: handleDropRejected,
|
|
634
|
-
accept
|
|
635
|
-
});
|
|
636
|
-
const uploader = {
|
|
637
|
-
value,
|
|
638
|
-
dropzone,
|
|
639
|
-
addFiles,
|
|
640
|
-
removeFile,
|
|
641
|
-
clearFiles,
|
|
642
|
-
cropper: cropperConfig && _extends({
|
|
643
|
-
config: cropperConfig
|
|
644
|
-
}, cropperProps)
|
|
645
|
-
};
|
|
646
|
-
return _extends({}, uploader, {
|
|
647
|
-
uploader
|
|
648
|
-
});
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
export { ErrorCode, UploadImages, tryUploadWithUploadParams, useUploadImages };
|
package/src/index.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
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";
|