@anu3ev/fabric-image-editor 0.10.2 → 0.10.3
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 +49 -3
- package/dist/assets/{worker-CA0Zz9Rg.js → worker-Ds8wxpzF.js} +1 -1
- package/dist/main.js +2131 -1963
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,6 +26,8 @@ A modern, powerful browser-based image editor built with [FabricJS](https://fabr
|
|
|
26
26
|
- **Font Loader** - FontManager handles Google Fonts + custom sources with automatic `@font-face` registration
|
|
27
27
|
- **Configurable Toolbar** - Dynamic toolbar with context-sensitive actions
|
|
28
28
|
- **Clipboard Integration** - Native copy/paste support with system clipboard
|
|
29
|
+
- **Deletion Guards** - Application-defined rules can prevent user deletion for protected objects
|
|
30
|
+
- **Clone Preparation** - Applications can sanitize object clones before copy, paste, duplicate, or cut
|
|
29
31
|
|
|
30
32
|
### Developer Features
|
|
31
33
|
- **TypeScript Support** - Full type definitions included
|
|
@@ -72,6 +74,44 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|
|
72
74
|
})
|
|
73
75
|
```
|
|
74
76
|
|
|
77
|
+
### Deletion Guards and Clone Preparation
|
|
78
|
+
|
|
79
|
+
The editor does not know application-specific object roles. If an application needs to protect a domain object from user deletion, pass a rule through `canDeleteObject`.
|
|
80
|
+
|
|
81
|
+
```javascript
|
|
82
|
+
const MAIN_IMAGE_HANDLE = 'main-image'
|
|
83
|
+
|
|
84
|
+
const editor = await initEditor('editor', {
|
|
85
|
+
canDeleteObject: (object) => {
|
|
86
|
+
return object.customData?.handle !== MAIN_IMAGE_HANDLE
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The rule is checked by shared delete operations, so keyboard deletion, toolbar deletion, direct `DeletionManager` calls, and `cut()` use the same result. Locked objects are still skipped by the editor itself. If a technical operation must delete a protected object, call deletion with `ignoreDeleteGuard: true`.
|
|
92
|
+
|
|
93
|
+
When deletion is skipped by the guard, the editor fires `editor:objects-delete-skipped`. The event is only a notification for UI code; it does not cancel deletion, because the decision was already made by `canDeleteObject`.
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
editor.canvas.on('editor:objects-delete-skipped', ({ skippedObjects }) => {
|
|
97
|
+
console.log('Some objects were not deleted:', skippedObjects)
|
|
98
|
+
})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Use `prepareObjectClone` when copied objects must not keep a domain marker from the source object. The callback receives only clones. The editor calls it for the root clone and for nested objects inside groups or active selections.
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
const editor = await initEditor('editor', {
|
|
105
|
+
prepareObjectClone: (object) => {
|
|
106
|
+
if (object.customData?.handle !== MAIN_IMAGE_HANDLE) return
|
|
107
|
+
|
|
108
|
+
delete object.customData.handle
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
This hook is used by `copy()`, `cut()`, `copyPaste()`, and `paste()`. Before the hook is called, the editor detaches `customData` on the clone so the callback does not accidentally mutate the original object.
|
|
114
|
+
|
|
75
115
|
### Working with Images
|
|
76
116
|
|
|
77
117
|
```javascript
|
|
@@ -398,9 +438,9 @@ The editor follows a modular architecture with specialized managers:
|
|
|
398
438
|
|
|
399
439
|
### Utility Managers
|
|
400
440
|
- **`SelectionManager`** - Object selection and multi-selection handling
|
|
401
|
-
- **`ClipboardManager`** - Copy/paste
|
|
441
|
+
- **`ClipboardManager`** - Copy/paste, cut, duplicate, clone preparation, and system clipboard integration
|
|
402
442
|
- **`GroupingManager`** - Object grouping and ungrouping operations
|
|
403
|
-
- **`DeletionManager`** - Object deletion
|
|
443
|
+
- **`DeletionManager`** - Object deletion, delete guards, skipped-delete events, and group handling
|
|
404
444
|
- **`ShapeManager`** - Preset-based shape groups with inner text, layout, scaling, and style controls
|
|
405
445
|
- **`ObjectLockManager`** - Object locking and unlocking functionality
|
|
406
446
|
- **`SnappingManager`** - Alignment guides and equal-spacing snaps while moving objects
|
|
@@ -451,7 +491,13 @@ initEditor(containerId, options): Promise<ImageEditor>
|
|
|
451
491
|
acceptContentTypes: ['image/png', 'image/jpeg', 'image/svg+xml'],
|
|
452
492
|
|
|
453
493
|
// Callback when ready
|
|
454
|
-
_onReadyCallback: (editor) => console.log('Ready!')
|
|
494
|
+
_onReadyCallback: (editor) => console.log('Ready!'),
|
|
495
|
+
|
|
496
|
+
// Optional user-delete rule. Return false to skip this object.
|
|
497
|
+
canDeleteObject: (object) => true,
|
|
498
|
+
|
|
499
|
+
// Optional clone preparation for copy, cut, duplicate, and paste.
|
|
500
|
+
prepareObjectClone: (object) => {}
|
|
455
501
|
}
|
|
456
502
|
```
|
|
457
503
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
(function(){self.onmessage=async e=>{let{action:t,payload:n,requestId:r}=e.data;try{switch(t){case`resizeImage`:{let{dataURL:e,maxWidth:i,maxHeight:a,minWidth:o,minHeight:s,contentType:c,quality:l,sizeType:u}=n,d=await createImageBitmap(await(await fetch(e)).blob()),{width:f,height:p}=d,m=Math.min(i/f,a/p);u===`min`&&(m=Math.max(o/f,s/p)),f=Math.floor(f*m),p=Math.floor(p*m);let h=new OffscreenCanvas(f,p),g=h.getContext(`2d`);if(!g)throw Error(`Failed to get 2D context from OffscreenCanvas`);g.drawImage(d,0,0,f,p);let _=await h.convertToBlob({type:c,quality:l});self.postMessage({requestId:r,action:t,success:!0,data:_});break}case`toDataURL`:{let{bitmap:e,contentType:i,quality:a,returnBlob:o}=n,{width:s,height:c}=e,l=new OffscreenCanvas(e.width,e.height),u=l.getContext(`2d`);if(!u)throw Error(`Failed to get 2D context from OffscreenCanvas`);u.drawImage(e,0,0,s,c);let d=await l.convertToBlob({type:i,quality:a});if(o){self.postMessage({requestId:r,action:t,success:!0,data:d});break}let f=await new Promise(e=>{let t=new FileReader;t.onload=()=>e(t.result),t.readAsDataURL(d)});self.postMessage({requestId:r,action:t,success:!0,data:f});break}default:throw Error(`Unknown action ${t}`)}}catch(e){self.postMessage({requestId:r,action:t,success:!1,error:e.message})}}})();
|
|
2
|
-
//# sourceMappingURL=worker-
|
|
2
|
+
//# sourceMappingURL=worker-Ds8wxpzF.js.map
|