@bensitu/image-editor 1.5.1 → 1.5.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.
package/README.md CHANGED
@@ -19,9 +19,10 @@ ImageEditor offers:
19
19
  - Merge, download, base64 export, and `File` export helpers
20
20
  - Optional DOM/UI binding for common editor controls
21
21
  - Large-image downsampling to reduce browser memory pressure
22
+ - Configurable history bounds for large image sessions
22
23
  - Centralized error and warning callbacks
23
24
 
24
- **Note:** This library requires **fabric.js v5.x** to be loaded before creating or initializing the editor.
25
+ **Note:** This library uses **fabric.js v5.x**. Bundler and CommonJS entries load Fabric through the peer dependency. Browser global usage needs `window.fabric` available by the time `init()` runs; constructing the editor before Fabric is available is tolerated as long as Fabric is registered before initialization.
25
26
 
26
27
  ## Demo
27
28
 
@@ -61,6 +62,14 @@ import ImageEditor, {
61
62
  } from "@bensitu/image-editor";
62
63
  ```
63
64
 
65
+ ### CommonJS usage
66
+
67
+ ```javascript
68
+ const ImageEditor = require("@bensitu/image-editor");
69
+
70
+ const editor = new ImageEditor();
71
+ ```
72
+
64
73
  ### Browser global usage
65
74
 
66
75
  Include fabric.js first, then ImageEditor:
@@ -173,12 +182,14 @@ const editor = new ImageEditor({
173
182
 
174
183
  editor.init({
175
184
  canvas: "fabricCanvas",
185
+ canvasContainer: null,
176
186
  imagePlaceholder: "imagePlaceholder",
177
187
  scalePercentageInput: "scalePercentageInput",
178
188
  rotateLeftButton: "rotateLeftButton",
179
189
  rotateRightButton: "rotateRightButton",
180
190
  rotateLeftDegreesInput: "rotateLeftDegreesInput",
181
191
  rotateRightDegreesInput: "rotateRightDegreesInput",
192
+ createMaskButton: null,
182
193
  removeSelectedMaskButton: "removeSelectedMaskButton",
183
194
  removeAllMasksButton: "removeAllMasksButton",
184
195
  mergeMasksButton: "mergeMasksButton",
@@ -187,10 +198,14 @@ editor.init({
187
198
  enterCropModeButton: "enterCropModeButton",
188
199
  applyCropButton: "applyCropButton",
189
200
  cancelCropButton: "cancelCropButton",
190
- resetImageTransformButton: "resetImageTransformButton"
201
+ resetImageTransformButton: "resetImageTransformButton",
202
+ imageInput: null,
203
+ uploadArea: null
191
204
  });
192
205
  ```
193
206
 
207
+ The demo binds `createMaskButton`, `imageInput`, and `uploadArea` itself, so it passes `null` for those built-in bindings. Regular integrations can omit those keys to use the default IDs, or pass `null` to disable any optional binding.
208
+
194
209
 
195
210
  ### Loading an Image Manually
196
211
 
@@ -233,6 +248,8 @@ When creating the editor instance, pass an options object to override defaults.
233
248
  | `downsampleMimeType` | `null` | Optional output MIME type for downsampled images. Supported values include `jpeg`, `jpg`, `png`, `webp`, `image/jpeg`, `image/png`, and `image/webp`. |
234
249
  | `imageLoadTimeoutMs` | `30000` | Timeout for image decode/load operations. |
235
250
  | `exportMultiplier` | `1` | Default scale multiplier for export. |
251
+ | `maxExportPixels` | `50000000` | Maximum output pixel count allowed per export after applying the multiplier. |
252
+ | `maxHistorySize` | `50` | Maximum undo/redo history entries to retain. Large base64 source images can make each history snapshot expensive. |
236
253
  | `exportImageAreaByDefault` | `true` | Export only the image area by default instead of the full canvas. |
237
254
  | `defaultMaskWidth` | `50` | Default mask width in pixels. |
238
255
  | `defaultMaskHeight` | `80` | Default mask height in pixels. |
@@ -242,19 +259,28 @@ When creating the editor instance, pass an options object to override defaults.
242
259
  | `maskName` | `mask` | Prefix for mask names and labels. |
243
260
  | `groupSelection` | `false` | Whether Fabric can select multiple masks as an active selection. |
244
261
  | `label.getText` | `(mask) => mask.maskName` | Callback for custom label text. The second argument is the mask's stable zero-based creation index (`mask.maskId - 1`). |
245
- | `showPlaceholder` | `true` | Show a placeholder when no image is loaded. |
262
+ | `label.create` | `undefined` | Optional callback that returns a custom Fabric label object for the selected mask. Invalid or throwing callbacks fall back to the default label. |
263
+ | `label.textOptions` | see defaults | Fabric text options merged into the default label when `label.create` is not used or falls back. |
264
+ | `showPlaceholder` | `true` | Show a placeholder when no image is loaded. When `false`, internal load, rollback, and status paths keep the placeholder hidden. |
246
265
  | `initialImageBase64` | `null` | Base64 data URL to auto-load during initialization. |
247
266
  | `defaultDownloadFileName` | `edited_image.jpg` | Default file name for downloads. |
267
+ | `crop.minWidth` | `100` | Minimum crop rectangle width, clamped to the current image bounds. |
268
+ | `crop.minHeight` | `100` | Minimum crop rectangle height, clamped to the current image bounds. |
269
+ | `crop.padding` | `10` | Initial inset from the image bounds when entering crop mode. |
270
+ | `crop.hideMasksDuringCrop` | `true` | Hide editable masks while crop mode is active. |
248
271
  | `crop.preserveMasksAfterCrop` | `false` | Keep masks that intersect the crop area, shifted into the cropped canvas. Merge masks first if they should be baked into the image pixels. |
249
- | `onImageLoaded` | `null` | Callback invoked after an image finishes loading. |
272
+ | `crop.allowRotationOfCropRect` | `false` | Reserved for future rotated crop support. In v1.x, crop rectangles stay axis-aligned; setting this to `true` reports a warning and rotation remains disabled. |
273
+ | `onImageLoaded` | `null` | Callback invoked after an image load is committed. Callback errors are reported as warnings and do not roll back the loaded image. |
250
274
  | `onError` | `null` | Callback invoked for recoverable internal errors. |
251
275
  | `onWarning` | `null` | Callback invoked for recoverable internal warnings. |
252
276
 
253
- `expandCanvasToImage`, `fitImageToCanvas`, and `coverImageToCanvas` are mutually exclusive layout modes. If more than one is enabled, the editor reports a warning and uses the first active mode in its existing load order.
277
+ `expandCanvasToImage`, `fitImageToCanvas`, and `coverImageToCanvas` are mutually exclusive layout modes. If more than one is enabled, the editor reports a warning and uses this precedence: `fitImageToCanvas`, then `coverImageToCanvas`, then `expandCanvasToImage`.
278
+
279
+ Numeric runtime options are normalized during construction. Non-finite or invalid dimensions, scale limits, export limits, crop sizes, label offsets, and history sizes fall back to safe defaults. `animationDuration: 0` and `downsampleQuality: 0` remain valid.
254
280
 
255
281
  ## DOM Binding Keys
256
282
 
257
- `init(idMap)` binds editor behavior to DOM elements by ID. All keys are optional.
283
+ `init(idMap)` binds editor behavior to DOM elements by ID. All keys are optional when the default IDs are present. Optional bindings can also be set to `null` to disable the built-in listener for that element.
258
284
 
259
285
  | Key | Description |
260
286
  | --------------------------- | -------------------------------------------------------------- |
@@ -314,11 +340,12 @@ The following DOM binding keys remain supported in `1.x` for compatibility, but
314
340
  | Method | Returns | Description |
315
341
  | --------------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------- |
316
342
  | `init(idMap)` | `void` | Bind the editor to DOM elements. Pass IDs in an object. |
343
+ | `dispose()` | `void` | Cleans up the canvas, listeners, crop state, animations, and captured DOM visibility/disabled/pointer-events state. |
317
344
  | `loadImage(imageBase64, options)` | `Promise<void>` | Load an image from a base64 data URL. Resolves after the Fabric image is on the canvas. |
318
345
  | `isImageLoaded()` | `boolean` | Return whether a valid image is loaded on the canvas. |
319
- | `isBusy()` | `boolean` | Return whether the editor is loading, animating, cropping, or running another compound operation. |
320
- | `scaleImage(factor)` | `Promise<void>` | Scale the image to the given factor relative to the base scale. |
321
- | `rotateImage(degrees)` | `Promise<void>` | Rotate the image to the given angle in degrees. |
346
+ | `isBusy()` | `boolean` | Return whether the editor is loading, animating, cropping, applying crop, or running another compound operation. |
347
+ | `scaleImage(factor)` | `Promise<void>` | Scale the image to the given finite factor relative to the base scale. Non-finite values are ignored. |
348
+ | `rotateImage(degrees)` | `Promise<void>` | Rotate the image to the given finite angle in degrees. Non-finite values are ignored. |
322
349
  | `resetImageTransform()` | `Promise<void>` | Reset scale to `1` and rotation to `0`. |
323
350
  | `undo()` | `Promise<void>` | Undo the last state change. Resolves after the canvas state is restored. |
324
351
  | `redo()` | `Promise<void>` | Redo the next state change. Resolves after the canvas state is restored. |
@@ -326,8 +353,8 @@ The following DOM binding keys remain supported in `1.x` for compatibility, but
326
353
  | `removeSelectedMask()` | `void` | Remove the currently selected mask or selected masks. |
327
354
  | `removeAllMasks(options)` | `void` | Remove all masks from the canvas. |
328
355
  | `enterCropMode()` | `void` | Create a resizable/movable selection rectangle on top of the image. |
329
- | `cancelCrop()` | `void` | Cancel crop mode and remove the temporary selection rectangle. |
330
- | `applyCrop()` | `Promise<void>` | Apply the current crop rectangle to the canvas. |
356
+ | `cancelCrop()` | `void` | Cancel crop mode and remove the temporary selection rectangle. No-ops while `applyCrop()` is already running. |
357
+ | `applyCrop()` | `Promise<void>` | Apply the current valid crop rectangle to the canvas. Invalid crop regions warn and keep crop mode active. |
331
358
  | `mergeMasks()` | `Promise<void>` | Merge masks into the base image on the canvas. |
332
359
  | `downloadImage(fileName)` | `void` | Download the edited image as a file. |
333
360
  | `exportImageBase64(options)` | `Promise<string>` | Export an image data URL. `fileType` can be `jpeg`, `jpg`, `png`, `webp`, or a supported image MIME type. |
@@ -348,6 +375,8 @@ Deprecated method aliases are still available for compatibility and will be remo
348
375
 
349
376
  `createMask(config)` accepts an optional configuration object.
350
377
 
378
+ Invalid mask configuration, such as non-finite numeric values, non-positive dimensions/radii, duplicate or zero-area polygon points, malformed polygon points, throwing custom generators, or custom generators that do not return a Fabric object, is rejected with a warning and returns `null` without mutating the canvas or history.
379
+
351
380
  | Option | Description |
352
381
  | ------------------ | ------------------------------------------------------------------------------------------ |
353
382
  | `shape` | Mask shape. Built-in values include `rect`, `circle`, `ellipse`, and `polygon`. |
@@ -365,6 +394,10 @@ Deprecated method aliases are still available for compatibility and will be remo
365
394
  | `fabricGenerator` | Factory callback for creating a custom Fabric object. |
366
395
  | `onCreate` | Callback invoked after the mask is added to the canvas. |
367
396
 
397
+ `fabricGenerator` runs before the mask is committed. If it throws or returns an invalid object, `createMask()` reports a warning and returns `null`. `onCreate` runs after the mask and history entry are committed; if it throws, the mask remains on the canvas and the error is reported as a warning.
398
+
399
+ Label callbacks are also isolated. If `label.create` throws or returns an invalid object, the editor uses the default Fabric text label. If `label.getText` throws, the editor uses `mask.maskName`.
400
+
368
401
  Example:
369
402
 
370
403
  ```javascript
@@ -385,6 +418,10 @@ By default, applying crop removes unmerged masks.
385
418
 
386
419
  This is intentional: an unmerged mask is still an editable overlay object, not part of the image pixels. When `crop.preserveMasksAfterCrop` is `false`, applying crop discards unmerged masks instead of trying to keep or partially crop those overlay objects.
387
420
 
421
+ `applyCrop()` owns a short crop-apply lock while it exports the selected region and reloads the cropped image. Calls to `cancelCrop()` or `enterCropMode()` during that pending apply are ignored and reported as warnings so crop state cannot be mutated mid-apply. The crop region is validated before export; invalid or out-of-bounds regions do not silently produce a 1x1 image.
422
+
423
+ While crop mode is active, the built-in DOM binding disables editor operation controls but keeps the canvas, canvas container, and placeholder interaction available so the crop rectangle and scrollable viewport remain usable. The apply/cancel crop buttons stay enabled.
424
+
388
425
  Choose the workflow based on the result you want:
389
426
 
390
427
  - **Crop first, then add masks** when masks should be placed only on the final cropped image.
@@ -449,6 +486,24 @@ try {
449
486
  }
450
487
  ```
451
488
 
489
+ Exports are limited by `maxExportPixels`. Increase that option only when the host page can tolerate the memory cost of larger canvas exports.
490
+
491
+ ```javascript
492
+ const editor = new ImageEditor({
493
+ maxExportPixels: 80000000,
494
+ });
495
+ ```
496
+
497
+ JPEG exports cannot preserve transparency. Transparent, zero-alpha, empty, or invalid `backgroundColor` values are flattened against white; valid opaque CSS colors are preserved.
498
+
499
+ Undo/redo history is limited by `maxHistorySize`. The default is `50`; lower it for workflows that load large base64 images and perform many edits.
500
+
501
+ ```javascript
502
+ const editor = new ImageEditor({
503
+ maxHistorySize: 20,
504
+ });
505
+ ```
506
+
452
507
  ## Error Handling
453
508
 
454
509
  Some ImageEditor API methods may throw synchronously or reject their returned Promise when the operation cannot be completed.
@@ -499,13 +554,13 @@ function showEditorMessage(message) {
499
554
 
500
555
  Common failure cases include:
501
556
 
502
- - fabric.js is not loaded before creating or initializing the editor.
557
+ - fabric.js is not available when `init()` or another canvas operation needs it.
503
558
  - The configured canvas element cannot be found.
504
- - The image data URL is invalid, unsupported, too large, or times out while loading.
559
+ - The image data URL or selected file is invalid, unsupported, too large, or times out while loading.
505
560
  - Another operation is already running, such as image loading, animation, crop, undo, redo, merge, or export.
506
561
  - The editor has been disposed before the operation completes.
507
- - The browser cannot create a canvas export because of platform, memory, or security restrictions.
508
- - A custom `fabricGenerator`, label callback, or external event handler throws an error.
562
+ - The requested export exceeds `maxExportPixels`, uses an invalid multiplier, or the browser cannot create a canvas export because of platform, memory, or security restrictions.
563
+ - A custom callback such as `onImageLoaded`, `onCreate`, `fabricGenerator`, `label.create`, or `label.getText` throws. These are isolated where possible and reported through `onWarning` without rolling back already committed successful work.
509
564
 
510
565
  ## Example Workflow
511
566
 
@@ -564,7 +619,7 @@ IE11 and old mobile Safari are not supported by the distributed build. If you ne
564
619
 
565
620
  ## Dependencies
566
621
 
567
- - **fabric.js v5.x** — Must be loaded before ImageEditor.
622
+ - **fabric.js v5.x** — Required peer dependency. Browser global usage needs `window.fabric` available before `init()`.
568
623
 
569
624
  ## License
570
625