@bensitu/image-editor 1.5.1 → 2.0.0
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 +370 -466
- package/dist/cjs/index.cjs +5422 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/esm/animation/animation-queue.js +67 -0
- package/dist/esm/animation/animation-queue.js.map +1 -0
- package/dist/esm/core/callback-reporter.js +23 -0
- package/dist/esm/core/callback-reporter.js.map +1 -0
- package/dist/esm/core/default-options.js +322 -0
- package/dist/esm/core/default-options.js.map +1 -0
- package/dist/esm/core/errors.js +156 -0
- package/dist/esm/core/errors.js.map +1 -0
- package/dist/esm/core/operation-guard.js +129 -0
- package/dist/esm/core/operation-guard.js.map +1 -0
- package/dist/esm/core/public-types.js +4 -0
- package/dist/esm/core/public-types.js.map +1 -0
- package/dist/esm/core/state-serializer.js +251 -0
- package/dist/esm/core/state-serializer.js.map +1 -0
- package/dist/esm/crop/crop-controller.js +403 -0
- package/dist/esm/crop/crop-controller.js.map +1 -0
- package/dist/esm/export/export-format.js +53 -0
- package/dist/esm/export/export-format.js.map +1 -0
- package/dist/esm/export/export-service.js +596 -0
- package/dist/esm/export/export-service.js.map +1 -0
- package/dist/esm/fabric/fabric-adapter.js +37 -0
- package/dist/esm/fabric/fabric-adapter.js.map +1 -0
- package/dist/esm/fabric/fabric-animation.js +37 -0
- package/dist/esm/fabric/fabric-animation.js.map +1 -0
- package/dist/esm/history/command.js +2 -0
- package/dist/esm/history/command.js.map +1 -0
- package/dist/esm/history/history-manager.js +103 -0
- package/dist/esm/history/history-manager.js.map +1 -0
- package/dist/esm/image/image-loader.js +245 -0
- package/dist/esm/image/image-loader.js.map +1 -0
- package/dist/esm/image/image-resampler.js +55 -0
- package/dist/esm/image/image-resampler.js.map +1 -0
- package/dist/esm/image/layout-manager.js +224 -0
- package/dist/esm/image/layout-manager.js.map +1 -0
- package/dist/esm/image/transform-controller.js +132 -0
- package/dist/esm/image/transform-controller.js.map +1 -0
- package/dist/esm/image-editor.js +1740 -0
- package/dist/esm/image-editor.js.map +1 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/mask/mask-factory.js +332 -0
- package/dist/esm/mask/mask-factory.js.map +1 -0
- package/dist/esm/mask/mask-label-manager.js +120 -0
- package/dist/esm/mask/mask-label-manager.js.map +1 -0
- package/dist/esm/mask/mask-list.js +47 -0
- package/dist/esm/mask/mask-list.js.map +1 -0
- package/dist/esm/mask/mask-style.js +182 -0
- package/dist/esm/mask/mask-style.js.map +1 -0
- package/dist/esm/ui/dom-bindings.js +60 -0
- package/dist/esm/ui/dom-bindings.js.map +1 -0
- package/dist/esm/ui/ui-state.js +25 -0
- package/dist/esm/ui/ui-state.js.map +1 -0
- package/dist/esm/ui/visibility-state.js +11 -0
- package/dist/esm/ui/visibility-state.js.map +1 -0
- package/dist/esm/utils/canvas-region.js +100 -0
- package/dist/esm/utils/canvas-region.js.map +1 -0
- package/dist/esm/utils/dom.js +6 -0
- package/dist/esm/utils/dom.js.map +1 -0
- package/dist/esm/utils/file.js +53 -0
- package/dist/esm/utils/file.js.map +1 -0
- package/dist/esm/utils/number.js +24 -0
- package/dist/esm/utils/number.js.map +1 -0
- package/dist/esm/utils/timeout.js +17 -0
- package/dist/esm/utils/timeout.js.map +1 -0
- package/dist/types/animation/animation-queue.d.ts +111 -0
- package/dist/types/animation/animation-queue.d.ts.map +1 -0
- package/dist/types/core/callback-reporter.d.ts +125 -0
- package/dist/types/core/callback-reporter.d.ts.map +1 -0
- package/dist/types/core/default-options.d.ts +56 -0
- package/dist/types/core/default-options.d.ts.map +1 -0
- package/dist/types/core/errors.d.ts +142 -0
- package/dist/types/core/errors.d.ts.map +1 -0
- package/dist/types/core/operation-guard.d.ts +192 -0
- package/dist/types/core/operation-guard.d.ts.map +1 -0
- package/dist/types/core/public-types.d.ts +678 -0
- package/dist/types/core/public-types.d.ts.map +1 -0
- package/dist/types/core/state-serializer.d.ts +301 -0
- package/dist/types/core/state-serializer.d.ts.map +1 -0
- package/dist/types/crop/crop-controller.d.ts +407 -0
- package/dist/types/crop/crop-controller.d.ts.map +1 -0
- package/dist/types/export/export-format.d.ts +136 -0
- package/dist/types/export/export-format.d.ts.map +1 -0
- package/dist/types/export/export-service.d.ts +333 -0
- package/dist/types/export/export-service.d.ts.map +1 -0
- package/dist/types/fabric/fabric-adapter.d.ts +74 -0
- package/dist/types/fabric/fabric-adapter.d.ts.map +1 -0
- package/dist/types/fabric/fabric-animation.d.ts +141 -0
- package/dist/types/fabric/fabric-animation.d.ts.map +1 -0
- package/dist/types/history/command.d.ts +16 -0
- package/dist/types/history/command.d.ts.map +1 -0
- package/dist/types/history/history-manager.d.ts +129 -0
- package/dist/types/history/history-manager.d.ts.map +1 -0
- package/dist/types/image/image-loader.d.ts +265 -0
- package/dist/types/image/image-loader.d.ts.map +1 -0
- package/dist/types/image/image-resampler.d.ts +139 -0
- package/dist/types/image/image-resampler.d.ts.map +1 -0
- package/dist/types/image/layout-manager.d.ts +255 -0
- package/dist/types/image/layout-manager.d.ts.map +1 -0
- package/dist/types/image/transform-controller.d.ts +287 -0
- package/dist/types/image/transform-controller.d.ts.map +1 -0
- package/dist/types/image-editor.d.ts +650 -0
- package/dist/types/image-editor.d.ts.map +1 -0
- package/dist/types/index.d.cts +31 -0
- package/dist/types/index.d.cts.map +1 -0
- package/dist/types/index.d.ts +31 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/mask/mask-factory.d.ts +209 -0
- package/dist/types/mask/mask-factory.d.ts.map +1 -0
- package/dist/types/mask/mask-label-manager.d.ts +171 -0
- package/dist/types/mask/mask-label-manager.d.ts.map +1 -0
- package/dist/types/mask/mask-list.d.ts +144 -0
- package/dist/types/mask/mask-list.d.ts.map +1 -0
- package/dist/types/mask/mask-style.d.ts +338 -0
- package/dist/types/mask/mask-style.d.ts.map +1 -0
- package/dist/types/ui/dom-bindings.d.ts +103 -0
- package/dist/types/ui/dom-bindings.d.ts.map +1 -0
- package/dist/types/ui/ui-state.d.ts +112 -0
- package/dist/types/ui/ui-state.d.ts.map +1 -0
- package/dist/types/ui/visibility-state.d.ts +77 -0
- package/dist/types/ui/visibility-state.d.ts.map +1 -0
- package/dist/types/utils/canvas-region.d.ts +177 -0
- package/dist/types/utils/canvas-region.d.ts.map +1 -0
- package/dist/types/utils/dom.d.ts +26 -0
- package/dist/types/utils/dom.d.ts.map +1 -0
- package/dist/types/utils/file.d.ts +80 -0
- package/dist/types/utils/file.d.ts.map +1 -0
- package/dist/types/utils/number.d.ts +132 -0
- package/dist/types/utils/number.d.ts.map +1 -0
- package/dist/types/utils/timeout.d.ts +84 -0
- package/dist/types/utils/timeout.d.ts.map +1 -0
- package/dist/umd/image-editor.umd.js +2 -0
- package/dist/umd/image-editor.umd.js.map +1 -0
- package/package.json +72 -66
- package/dist/image-editor.cjs +0 -4185
- package/dist/image-editor.cjs.map +0 -7
- package/dist/image-editor.esm.js +0 -4154
- package/dist/image-editor.esm.js.map +0 -7
- package/dist/image-editor.esm.min.js +0 -9
- package/dist/image-editor.esm.min.js.map +0 -7
- package/dist/image-editor.esm.min.mjs +0 -9
- package/dist/image-editor.esm.min.mjs.map +0 -7
- package/dist/image-editor.esm.mjs +0 -4154
- package/dist/image-editor.esm.mjs.map +0 -7
- package/dist/image-editor.js +0 -4151
- package/dist/image-editor.js.map +0 -7
- package/dist/image-editor.min.js +0 -9
- package/dist/image-editor.min.js.map +0 -7
- package/image-editor.d.ts +0 -269
- package/src/browser.js +0 -11
- package/src/esm.js +0 -9
- package/src/image-editor.js +0 -4775
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,eAAe,WAAW,CAAC;AAE3B,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAItD,YAAY,EAER,kBAAkB,EAClB,eAAe,EACf,UAAU,EAEV,WAAW,EACX,UAAU,EACV,kBAAkB,EAElB,gBAAgB,EAChB,qBAAqB,EAErB,UAAU,EACV,UAAU,EACV,eAAe,EACf,kBAAkB,EAElB,aAAa,EACb,aAAa,EACb,qBAAqB,EAErB,UAAU,EACV,mBAAmB,EACnB,sBAAsB,EAEtB,SAAS,EACT,gBAAgB,EAChB,oBAAoB,EACpB,0BAA0B,EAC1B,oBAAoB,EAEpB,YAAY,EAEZ,YAAY,GACf,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical public API barrel for the image-editor library.
|
|
3
|
+
*
|
|
4
|
+
* The package surface consists of:
|
|
5
|
+
* - `ImageEditor` (default and named export) — the only public class.
|
|
6
|
+
* - `isMaskObject` — runtime type guard for mask objects.
|
|
7
|
+
* - The documented public types listed below.
|
|
8
|
+
*
|
|
9
|
+
* Internal helpers (animation queue, command, history manager, controllers,
|
|
10
|
+
* services, managers, and utility modules) are intentionally not re-exported;
|
|
11
|
+
* they are implementation details and may change without notice.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import * as fabric from 'fabric';
|
|
16
|
+
* import { ImageEditor } from '@bensitu/image-editor';
|
|
17
|
+
* import type {
|
|
18
|
+
* ImageEditorOptions,
|
|
19
|
+
* MaskConfig,
|
|
20
|
+
* MaskObject,
|
|
21
|
+
* } from '@bensitu/image-editor';
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @module
|
|
25
|
+
*/
|
|
26
|
+
import { ImageEditor } from './image-editor.js';
|
|
27
|
+
export { ImageEditor };
|
|
28
|
+
export default ImageEditor;
|
|
29
|
+
export { isMaskObject } from './core/public-types.js';
|
|
30
|
+
export type { ImageEditorOptions, ResolvedOptions, LayoutMode, LabelConfig, CropConfig, CropExportFileType, LoadImageOptions, RemoveAllMasksOptions, MaskConfig, MaskObject, MaskNumericProp, ResolvedMaskConfig, ImageMimeType, ImageFileType, NormalizedImageFormat, ExportArea, Base64ExportOptions, ImageFileExportOptions, ImageInfo, ImageEditorState, ImageEditorSelection, ImageEditorCallbackContext, ImageEditorOperation, ElementIdMap, FabricModule, } from './core/public-types.js';
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,eAAe,WAAW,CAAC;AAE3B,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAItD,YAAY,EAER,kBAAkB,EAClB,eAAe,EACf,UAAU,EAEV,WAAW,EACX,UAAU,EACV,kBAAkB,EAElB,gBAAgB,EAChB,qBAAqB,EAErB,UAAU,EACV,UAAU,EACV,eAAe,EACf,kBAAkB,EAElB,aAAa,EACb,aAAa,EACb,qBAAqB,EAErB,UAAU,EACV,mBAAmB,EACnB,sBAAsB,EAEtB,SAAS,EACT,gBAAgB,EAChB,oBAAoB,EACpB,0BAA0B,EAC1B,oBAAoB,EAEpB,YAAY,EAEZ,YAAY,GACf,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Function-based mask creation entry point used by the
|
|
3
|
+
* `ImageEditor` orchestrator.
|
|
4
|
+
*
|
|
5
|
+
* ## Owned contracts
|
|
6
|
+
*
|
|
7
|
+
* - On a successful `createMask`, the editor SHALL
|
|
8
|
+
* increment `maskCounter` and assign the result to `mask.maskId`. Counter
|
|
9
|
+
* bookkeeping flows through `context.getMaskCounter` / `context.setMaskCounter` so
|
|
10
|
+
* the orchestrator retains ownership of the field across loadImage and
|
|
11
|
+
* loadFromState (which reset / restore the counter).
|
|
12
|
+
* - Together with `core/state-serializer.ts`, mask IDs
|
|
13
|
+
* stay unique across mixed `createMask` / `mergeMasks` / `undo` / `redo`
|
|
14
|
+
* sequences because the counter is monotonic.
|
|
15
|
+
* - `createMask` is the public mask-creation entry point.
|
|
16
|
+
* - For `'rect' | 'circle' | 'ellipse' | 'polygon'`
|
|
17
|
+
* the corresponding Fabric shape is built with explicit
|
|
18
|
+
* `originX: 'left'`, `originY: 'top'`, plus the resolved color, opacity,
|
|
19
|
+
* angle, and the user's `styles` block.
|
|
20
|
+
* - When `config.fabricGenerator` is supplied it is
|
|
21
|
+
* called with `(resolvedConfig, canvas, options)` and its return value is
|
|
22
|
+
* used verbatim as the mask object.
|
|
23
|
+
* - Post-create order is fixed: add to canvas →
|
|
24
|
+
* update list DOM → `setActiveObject` (when `selectable !== false`) →
|
|
25
|
+
* `saveState` → `config.onCreate(mask, canvas)`.
|
|
26
|
+
* - `config.onCreate` is invoked exactly once,
|
|
27
|
+
* strictly after `saveState` has run.
|
|
28
|
+
* - Falsy values supplied via `config.styles` (`0`,
|
|
29
|
+
* `false`, `null`, `''`, `NaN`) are applied verbatim. The factory does
|
|
30
|
+
* NOT use `??` to default stroke / strokeWidth / strokeDashArray when the
|
|
31
|
+
* key is explicitly present on `styles`.
|
|
32
|
+
* - `hasControls`, `selectable`, `transparentCorners`,
|
|
33
|
+
* `strokeUniform` use the `'foo' in config ? … : default` pattern so that
|
|
34
|
+
* an explicit `false` is preserved.
|
|
35
|
+
* - When a polygon mask is built, its visible
|
|
36
|
+
* bounding-box top-left SHALL equal the resolved `(left, top)`. Fabric
|
|
37
|
+
* v7's `Polygon` constructor positions the object so the polygon's
|
|
38
|
+
* `pathOffset` is centered on `(left, top)`, which means the bounding
|
|
39
|
+
* rect generally does NOT land at `(left, top)`. The factory therefore
|
|
40
|
+
* constructs the polygon without `left`/`top`, reads the resulting
|
|
41
|
+
* bounding rect, and shifts the object by the delta so the rendered
|
|
42
|
+
* bounding box top-left matches the requested coordinate.
|
|
43
|
+
* - Polygon points may be supplied as `{ x, y }`
|
|
44
|
+
* objects or `[x, y]` tuples; both forms are normalized via
|
|
45
|
+
* `coercePoint` from `utils/number.ts` before reaching Fabric.
|
|
46
|
+
*
|
|
47
|
+
* - `removeAllMasks(options?)` accepts a
|
|
48
|
+
* `RemoveAllMasksOptions` argument with `saveHistory` defaulting to
|
|
49
|
+
* `true`, pushing a single history entry for the batch.
|
|
50
|
+
* - `removeAllMasks({ saveHistory: false})` removes
|
|
51
|
+
* masks without pushing a history entry (used by merge/crop pipelines).
|
|
52
|
+
* - `removeAllMasks` is operation-guard-rejected
|
|
53
|
+
* while `isAnimating` is `true`. The guard lives on the orchestrator;
|
|
54
|
+
* this module is only invoked after the guard has cleared.
|
|
55
|
+
* - `removeAllMasks` clears `lastMask` to `null`,
|
|
56
|
+
* so subsequent `createMask` calls cannot auto-place relative to a
|
|
57
|
+
* removed reference and `maskId` uniqueness is preserved across mixed
|
|
58
|
+
* `createMask` / `removeAllMasks` / `undo` / `redo` sequences.
|
|
59
|
+
*
|
|
60
|
+
* ## Out of scope (handled by sibling tasks)
|
|
61
|
+
*
|
|
62
|
+
* - Mask label creation/synchronization — see `mask/mask-label-manager.ts`.
|
|
63
|
+
*
|
|
64
|
+
* ## Design notes
|
|
65
|
+
*
|
|
66
|
+
* - The orchestrator owns the editor-level state (`maskCounter`,
|
|
67
|
+
* `lastMask`, the canvas, `saveState`, `updateMaskList`). The factory
|
|
68
|
+
* reads/writes those slots through getter/setter callbacks supplied in
|
|
69
|
+
* {@link CreateMaskContext} so this module is independent of the
|
|
70
|
+
* `ImageEditor` class shape.
|
|
71
|
+
* - `expandCanvasIfNeeded` is optional. The orchestrator may supply it to
|
|
72
|
+
* route through `setCanvasSizePx` (which forces a synchronous reflow on
|
|
73
|
+
* the scroll container, see `image-editor.ts`). When absent, the factory
|
|
74
|
+
* falls back to the public Fabric API `canvas.setDimensions`.
|
|
75
|
+
*
|
|
76
|
+
* @module
|
|
77
|
+
*/
|
|
78
|
+
import type * as FabricNS from 'fabric';
|
|
79
|
+
import type { FabricModule, MaskConfig, MaskObject, RemoveAllMasksOptions, ResolvedOptions } from '../core/public-types.js';
|
|
80
|
+
/**
|
|
81
|
+
* State and orchestration callbacks the mask factory needs from the
|
|
82
|
+
* `ImageEditor` orchestrator.
|
|
83
|
+
*
|
|
84
|
+
* The factory does NOT own any of these slots — it only reads and updates
|
|
85
|
+
* them through the supplied accessors so ownership of `maskCounter`,
|
|
86
|
+
* `lastMask`, history snapshots, and the mask list DOM stays on the editor.
|
|
87
|
+
*/
|
|
88
|
+
export interface CreateMaskContext {
|
|
89
|
+
/** Injected Fabric.js v7 module used to construct the shape. */
|
|
90
|
+
fabric: FabricModule;
|
|
91
|
+
/** The live Fabric canvas the mask is added to. */
|
|
92
|
+
canvas: FabricNS.Canvas;
|
|
93
|
+
/** Fully resolved editor options (defaults already merged). */
|
|
94
|
+
options: ResolvedOptions;
|
|
95
|
+
/** Last mask reference, used for the auto-place-to-right behavior. */
|
|
96
|
+
getLastMask(): MaskObject | null;
|
|
97
|
+
setLastMask(mask: MaskObject | null): void;
|
|
98
|
+
/** Mask counter — owned by the orchestrator. */
|
|
99
|
+
getMaskCounter(): number;
|
|
100
|
+
setMaskCounter(n: number): void;
|
|
101
|
+
/** Re-render the mask list DOM (UI ownership lives in `mask/mask-list.ts`). */
|
|
102
|
+
updateMaskList(): void;
|
|
103
|
+
/** Save canvas state to history. */
|
|
104
|
+
saveCanvasState(): void;
|
|
105
|
+
/**
|
|
106
|
+
* Optional canvas resize hook used when `options.expandCanvasToImage` is
|
|
107
|
+
* `true` and the placed mask would extend past the current canvas size.
|
|
108
|
+
* If omitted, the factory calls `canvas.setDimensions` directly. The
|
|
109
|
+
* orchestrator typically passes `setCanvasSizePx` here so the scroll
|
|
110
|
+
* container reflows synchronously with the new canvas size.
|
|
111
|
+
*/
|
|
112
|
+
expandCanvasIfNeeded?: (width: number, height: number) => void;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Create a mask via the resolved {@link MaskConfig} and add it to the
|
|
116
|
+
* canvas.
|
|
117
|
+
*
|
|
118
|
+
* Creation steps:
|
|
119
|
+
*
|
|
120
|
+
* 1. Resolve the config: apply defaults, then resolve placement
|
|
121
|
+
* (`left`/`top`) and dimensions (`width`/`height`/`rx`/`ry`/`radius`)
|
|
122
|
+
* via {@link resolveNumeric} so percentages and factory functions
|
|
123
|
+
* collapse to pixel numbers before Fabric shape construction.
|
|
124
|
+
* 2. Optionally expand the canvas if the placement would overflow.
|
|
125
|
+
* 3. Build the Fabric shape — switch on `config.shape`, or call
|
|
126
|
+
* `config.fabricGenerator` if provided.
|
|
127
|
+
* 4. Apply common mask properties. Falsy flags (`hasControls`,
|
|
128
|
+
* `selectable`, `transparentCorners`, `strokeUniform`) use the
|
|
129
|
+
* `'foo' in config ? … : default` pattern so an explicit `false` is
|
|
130
|
+
* preserved. Stroke / strokeWidth /
|
|
131
|
+
* strokeDashArray pulled out of `styles` use the same `in` check so
|
|
132
|
+
* `null` and `0` are preserved verbatim.
|
|
133
|
+
* 5. Increment `maskCounter` and assign `maskId`, `maskName`,
|
|
134
|
+
* `originalAlpha`.
|
|
135
|
+
* 6. Post-create order: add to canvas → `updateMaskList` →
|
|
136
|
+
* `setActiveObject` (when `selectable !== false`) → `saveCanvasState`
|
|
137
|
+
* → `config.onCreate(mask, canvas)`.
|
|
138
|
+
*
|
|
139
|
+
* @param context - Orchestration context — see {@link CreateMaskContext}.
|
|
140
|
+
* @param config - User-supplied mask configuration.
|
|
141
|
+
* @returns The created mask object, or `null` if the canvas is unset.
|
|
142
|
+
*/
|
|
143
|
+
export declare function createMask(context: CreateMaskContext, config?: MaskConfig): MaskObject | null;
|
|
144
|
+
/**
|
|
145
|
+
* Orchestration callbacks needed by {@link removeSelectedMask} and
|
|
146
|
+
* {@link removeAllMasks}. The helpers do NOT own any of these slots — they
|
|
147
|
+
* read and update them through the supplied accessors so ownership of the
|
|
148
|
+
* canvas, mask label DOM, mask list DOM, history, and `lastMask` stays on
|
|
149
|
+
* the editor.
|
|
150
|
+
*/
|
|
151
|
+
export interface RemoveMaskContext {
|
|
152
|
+
/** The live Fabric canvas the mask(s) are removed from. */
|
|
153
|
+
canvas: FabricNS.Canvas;
|
|
154
|
+
/**
|
|
155
|
+
* Remove the label overlay associated with `mask` (if any). The
|
|
156
|
+
* orchestrator typically delegates to `mask/mask-label-manager.ts`.
|
|
157
|
+
*/
|
|
158
|
+
removeLabelForMask(mask: MaskObject): void;
|
|
159
|
+
/** Re-render the mask list DOM (UI ownership in `mask/mask-list.ts`). */
|
|
160
|
+
updateMaskList(): void;
|
|
161
|
+
/** Push a single history entry for the removal batch. */
|
|
162
|
+
saveCanvasState(): void;
|
|
163
|
+
/**
|
|
164
|
+
* Reset the orchestrator's `lastMask` reference. Called with `null`
|
|
165
|
+
* when every mask is removed so the next `createMask` does not
|
|
166
|
+
* auto-place relative to a removed mask.
|
|
167
|
+
*/
|
|
168
|
+
setLastMask(mask: MaskObject | null): void;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Remove the currently selected mask (if it is a {@link MaskObject}).
|
|
172
|
+
*
|
|
173
|
+
* Steps:
|
|
174
|
+
*
|
|
175
|
+
* 1. Read the active object from the canvas. No-op if missing or not a mask.
|
|
176
|
+
* 2. Remove the mask's label overlay via {@link RemoveMaskContext.removeLabelForMask}.
|
|
177
|
+
* 3. Remove the mask object from the canvas and clear the active selection.
|
|
178
|
+
* 4. Re-render the mask list DOM and the canvas.
|
|
179
|
+
* 5. Push a single history entry via {@link RemoveMaskContext.saveCanvasState}.
|
|
180
|
+
*
|
|
181
|
+
* @param context - Orchestration context — see {@link RemoveMaskContext}.
|
|
182
|
+
*/
|
|
183
|
+
export declare function removeSelectedMask(context: RemoveMaskContext): void;
|
|
184
|
+
/**
|
|
185
|
+
* Remove all masks (and their label overlays) from the canvas.
|
|
186
|
+
*
|
|
187
|
+
* When `options.saveHistory` is `false`, the helper does NOT push a history
|
|
188
|
+
* entry — used by the internal `mergeMasks` and `applyCrop` pipelines, which
|
|
189
|
+
* already record one enclosing history entry for the operation. The default
|
|
190
|
+
* (and the public-facing call from `ImageEditor.removeAllMasks`) is
|
|
191
|
+
* `saveHistory: true`, which pushes a single entry for the batch.
|
|
192
|
+
*
|
|
193
|
+
* Steps:
|
|
194
|
+
*
|
|
195
|
+
* 1. Collect every {@link MaskObject} on the canvas. No-op if none.
|
|
196
|
+
* 2. For each mask: remove its label overlay, then remove the mask object
|
|
197
|
+
* from the canvas.
|
|
198
|
+
* 3. Clear the active selection.
|
|
199
|
+
* 4. Reset `lastMask` to `null` so the next `createMask` does not
|
|
200
|
+
* auto-place relative to a removed reference.
|
|
201
|
+
* 5. Re-render the mask list DOM and the canvas.
|
|
202
|
+
* 6. Conditionally push a history entry depending on
|
|
203
|
+
* `options.saveHistory`.
|
|
204
|
+
*
|
|
205
|
+
* @param context - Orchestration context — see {@link RemoveMaskContext}.
|
|
206
|
+
* @param options - Bulk-removal options. Defaults to `{ saveHistory: true}`.
|
|
207
|
+
*/
|
|
208
|
+
export declare function removeAllMasks(context: RemoveMaskContext, options?: RemoveAllMasksOptions): void;
|
|
209
|
+
//# sourceMappingURL=mask-factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mask-factory.d.ts","sourceRoot":"","sources":["../../../src/mask/mask-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4EG;AAEH,OAAO,KAAK,KAAK,QAAQ,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EACR,YAAY,EACZ,UAAU,EACV,UAAU,EACV,qBAAqB,EAErB,eAAe,EAClB,MAAM,yBAAyB,CAAC;AAcjC;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAC9B,gEAAgE;IAChE,MAAM,EAAE,YAAY,CAAC;IACrB,mDAAmD;IACnD,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC;IACxB,+DAA+D;IAC/D,OAAO,EAAE,eAAe,CAAC;IACzB,sEAAsE;IACtE,WAAW,IAAI,UAAU,GAAG,IAAI,CAAC;IACjC,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,IAAI,CAAC;IAC3C,gDAAgD;IAChD,cAAc,IAAI,MAAM,CAAC;IACzB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,+EAA+E;IAC/E,cAAc,IAAI,IAAI,CAAC;IACvB,oCAAoC;IACpC,eAAe,IAAI,IAAI,CAAC;IACxB;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAClE;AA+GD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,EAAE,MAAM,GAAE,UAAe,GAAG,UAAU,GAAG,IAAI,CAsSjG;AA6BD;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAC9B,2DAA2D;IAC3D,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC;IACxB;;;OAGG;IACH,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IAC3C,yEAAyE;IACzE,cAAc,IAAI,IAAI,CAAC;IACvB,yDAAyD;IACzD,eAAe,IAAI,IAAI,CAAC;IACxB;;;;OAIG;IACH,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,IAAI,CAAC;CAC9C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAYnE;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,cAAc,CAC1B,OAAO,EAAE,iBAAiB,EAC1B,OAAO,GAAE,qBAA0B,GACpC,IAAI,CAqBN"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-mask label overlay creation, positioning, show/hide, and
|
|
3
|
+
* removal. Owns the legacy `createLabelForMask`, `syncMaskLabel`,
|
|
4
|
+
* `showLabelForMask`, `hideAllMaskLabels`, and
|
|
5
|
+
* `removeLabelForMask` logic that was inlined on the editor
|
|
6
|
+
* in legacy and is now extracted into pure(ish) helpers that take a
|
|
7
|
+
* {@link MaskLabelManagerContext}.
|
|
8
|
+
*
|
|
9
|
+
* ## Owned contracts
|
|
10
|
+
*
|
|
11
|
+
* - Label text is computed via
|
|
12
|
+
* `options.label.getText(mask, mask.maskId - 1)`. The index argument is
|
|
13
|
+
* the stable creation index (`maskId - 1`), NOT the live canvas list
|
|
14
|
+
* position. legacy passed `this.maskCounter` here, which drifted whenever
|
|
15
|
+
* masks were added or removed; the current contract pins the index to the
|
|
16
|
+
* mask's own identity so labels stay consistent across
|
|
17
|
+
* `createMask` / `removeSelectedMask` / `removeAllMasks` / `undo`/`redo`.
|
|
18
|
+
*
|
|
19
|
+
* - Every label-overlay text object SHALL be
|
|
20
|
+
* constructed with `originX: 'left'`, `originY: 'top'`, since the label
|
|
21
|
+
* is positioned by its top-left corner. The defaults injected by this
|
|
22
|
+
* module set those origins explicitly, and {@link syncMaskLabel}
|
|
23
|
+
* re-asserts them on every sync so an externally-mutated label still
|
|
24
|
+
* honors the contract.
|
|
25
|
+
*
|
|
26
|
+
* - **Pretty_Printer filter** — Every label object is
|
|
27
|
+
* tagged with `maskLabel = true` so `core/state-serializer.ts` can
|
|
28
|
+
* exclude it from history snapshots. Labels are session-only and never
|
|
29
|
+
* persisted.
|
|
30
|
+
*
|
|
31
|
+
* ## Out of scope (handled by sibling modules)
|
|
32
|
+
*
|
|
33
|
+
* - Mask creation, falsy-style preservation, polygon placement — see
|
|
34
|
+
* `mask/mask-factory.ts`.
|
|
35
|
+
* - Mask list DOM rendering — see `mask/mask-list.ts`.
|
|
36
|
+
* - Hover/selection appearance — see `mask/mask-style.ts`.
|
|
37
|
+
*
|
|
38
|
+
* ## Implementation notes
|
|
39
|
+
*
|
|
40
|
+
* - The orchestrator (`src/image-editor.ts`) owns the canvas reference,
|
|
41
|
+
* resolved options, and Fabric module. The helpers in this module
|
|
42
|
+
* receive those slots through a {@link MaskLabelManagerContext} so the
|
|
43
|
+
* module is independent of the `ImageEditor` class shape and can be unit
|
|
44
|
+
* tested in isolation against a stub Fabric environment.
|
|
45
|
+
* - When `options.label.create` is supplied, the user owns the full label
|
|
46
|
+
* build (it may even return `null` to opt out for a particular mask).
|
|
47
|
+
* When the factory returns `null` or the option is absent, this module
|
|
48
|
+
* falls back to a default Fabric text constructed from the deep-merged
|
|
49
|
+
* `options.label.textOptions`. The default already carries
|
|
50
|
+
* `originX: 'left'`/`originY: 'top'` via `core/default-options.ts`, but
|
|
51
|
+
* we set them explicitly here as well so the contract holds even when a
|
|
52
|
+
* caller bypasses `resolveOptions` (e.g. in unit tests).
|
|
53
|
+
*
|
|
54
|
+
* @module
|
|
55
|
+
*/
|
|
56
|
+
import type * as FabricNS from 'fabric';
|
|
57
|
+
import type { FabricModule, MaskObject, ResolvedOptions } from '../core/public-types.js';
|
|
58
|
+
/**
|
|
59
|
+
* State the label helpers read from the `ImageEditor` orchestrator.
|
|
60
|
+
*
|
|
61
|
+
* The module does NOT own any of these slots — it only reads them so
|
|
62
|
+
* ownership of the canvas, Fabric module, and resolved options stays on the
|
|
63
|
+
* orchestrator (where legacy left them).
|
|
64
|
+
*/
|
|
65
|
+
export interface MaskLabelManagerContext {
|
|
66
|
+
/** Injected Fabric.js v7 module used to construct the label text. */
|
|
67
|
+
fabric: FabricModule;
|
|
68
|
+
/** The live Fabric canvas the label overlay is added to. */
|
|
69
|
+
canvas: FabricNS.Canvas;
|
|
70
|
+
/** Fully resolved editor options (defaults already merged). */
|
|
71
|
+
options: ResolvedOptions;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Remove the label overlay associated with `mask` (if any).
|
|
75
|
+
*
|
|
76
|
+
* No-op when the canvas is unset or the mask has no `labelObject`. Each
|
|
77
|
+
* removal step is wrapped in `try/catch` so a stale Fabric reference does
|
|
78
|
+
* not break callers that iterate every mask (e.g. {@link hideAllMaskLabels}
|
|
79
|
+
* or `removeAllMasks`).
|
|
80
|
+
*
|
|
81
|
+
* Steps:
|
|
82
|
+
*
|
|
83
|
+
* 1. If `mask.labelObject` is present and still on the canvas, remove it.
|
|
84
|
+
* 2. Delete `mask.labelObject` so subsequent {@link showLabelForMask} calls
|
|
85
|
+
* rebuild it instead of reusing a stale reference.
|
|
86
|
+
*
|
|
87
|
+
* @param context - Orchestration context — see {@link MaskLabelManagerContext}.
|
|
88
|
+
* @param mask - The mask whose label overlay should be removed.
|
|
89
|
+
*/
|
|
90
|
+
export declare function removeLabelForMask(context: MaskLabelManagerContext, mask: MaskObject): void;
|
|
91
|
+
/**
|
|
92
|
+
* Create (or recreate) the label overlay for `mask`.
|
|
93
|
+
*
|
|
94
|
+
* No-op when the canvas is unset or `options.maskLabelOnSelect` is `false`.
|
|
95
|
+
*
|
|
96
|
+
* Steps:
|
|
97
|
+
*
|
|
98
|
+
* 1. Remove any existing label so a stale reference is never reused.
|
|
99
|
+
* 2. If `options.label.create` is a function, call it with
|
|
100
|
+
* `(mask, fabric)` and use its return value when truthy. The factory
|
|
101
|
+
* may return `null` to opt out, in which case the default builder runs.
|
|
102
|
+
* 3. Otherwise (or when the factory returned `null`), compute label text
|
|
103
|
+
* via `options.label.getText(mask, mask.maskId - 1)` so the index is
|
|
104
|
+
* the stable creation index rather than the live list position. Build
|
|
105
|
+
* a Fabric text using `options.label.textOptions` with `originX: 'left'`
|
|
106
|
+
* and `originY: 'top'` re-asserted explicitly.
|
|
107
|
+
* 4. Tag the resulting object with `maskLabel = true` so the state
|
|
108
|
+
* serializer filters it out of history snapshots.
|
|
109
|
+
* 5. Attach the label to the mask, add it to the canvas, bring it to the
|
|
110
|
+
* front, and run an initial {@link syncMaskLabel} to position it.
|
|
111
|
+
*
|
|
112
|
+
* @param context - Orchestration context — see {@link MaskLabelManagerContext}.
|
|
113
|
+
* @param mask - The mask the label overlay is being created for.
|
|
114
|
+
*/
|
|
115
|
+
export declare function createLabelForMask(context: MaskLabelManagerContext, mask: MaskObject): void;
|
|
116
|
+
/**
|
|
117
|
+
* Re-position the label overlay to track its mask's current bounding box,
|
|
118
|
+
* angle, and visibility.
|
|
119
|
+
*
|
|
120
|
+
* No-op when the canvas is unset, `options.maskLabelOnSelect` is `false`,
|
|
121
|
+
* or the mask has no `labelObject`. Designed to be called from Fabric event
|
|
122
|
+
* handlers (`object:moving`, `object:scaling`, `object:rotating`,
|
|
123
|
+
* `object:modified`) without checking those guards at every call site.
|
|
124
|
+
*
|
|
125
|
+
* Geometry (matches legacy):
|
|
126
|
+
*
|
|
127
|
+
* - The label's top-left is placed `options.maskLabelOffset` pixels from
|
|
128
|
+
* the mask's top-left corner, along the vector from the top-left to the
|
|
129
|
+
* center of the rotated bounding box. This keeps the offset visually
|
|
130
|
+
* consistent regardless of mask rotation.
|
|
131
|
+
* - The label inherits the mask's `angle` so it rotates with the mask.
|
|
132
|
+
* - `originX: 'left'` and `originY: 'top'` are re-asserted on every sync
|
|
133
|
+
* to.
|
|
134
|
+
*
|
|
135
|
+
* @param context - Orchestration context — see {@link MaskLabelManagerContext}.
|
|
136
|
+
* @param mask - The mask whose label should be repositioned.
|
|
137
|
+
*/
|
|
138
|
+
export declare function syncMaskLabel(context: MaskLabelManagerContext, mask: MaskObject): void;
|
|
139
|
+
/**
|
|
140
|
+
* Ensure the label for `mask` is present and visible.
|
|
141
|
+
*
|
|
142
|
+
* No-op when `options.maskLabelOnSelect` is `false`. Creates a fresh label
|
|
143
|
+
* via {@link createLabelForMask} when the mask has none, otherwise toggles
|
|
144
|
+
* `visible` to `true` and re-syncs the position. Used by the orchestrator's
|
|
145
|
+
* selection handler when a single mask becomes the active object.
|
|
146
|
+
*
|
|
147
|
+
* @param context - Orchestration context — see {@link MaskLabelManagerContext}.
|
|
148
|
+
* @param mask - The mask whose label should be shown.
|
|
149
|
+
*/
|
|
150
|
+
export declare function showLabelForMask(context: MaskLabelManagerContext, mask: MaskObject): void;
|
|
151
|
+
/**
|
|
152
|
+
* Remove every label overlay currently on the canvas and detach the
|
|
153
|
+
* `labelObject` reference from every mask.
|
|
154
|
+
*
|
|
155
|
+
* Called by the orchestrator before serialization (`saveState`) and
|
|
156
|
+
* after `loadFromJSON`-driven restores so that label objects — which
|
|
157
|
+
* Fabric DOES serialize unless filtered — never leak into history.
|
|
158
|
+
*
|
|
159
|
+
* Steps:
|
|
160
|
+
*
|
|
161
|
+
* 1. Remove every object flagged with `maskLabel === true` from the
|
|
162
|
+
* canvas. Each removal is wrapped in `try/catch` so a stale Fabric
|
|
163
|
+
* reference does not break the loop.
|
|
164
|
+
* 2. Delete `labelObject` from every mask object so a subsequent
|
|
165
|
+
* {@link showLabelForMask} rebuilds the label rather than reusing a
|
|
166
|
+
* detached reference.
|
|
167
|
+
*
|
|
168
|
+
* @param context - Orchestration context — see {@link MaskLabelManagerContext}.
|
|
169
|
+
*/
|
|
170
|
+
export declare function hideAllMaskLabels(context: MaskLabelManagerContext): void;
|
|
171
|
+
//# sourceMappingURL=mask-label-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mask-label-manager.d.ts","sourceRoot":"","sources":["../../../src/mask/mask-label-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,KAAK,KAAK,QAAQ,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAIzF;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB;IACpC,qEAAqE;IACrE,MAAM,EAAE,YAAY,CAAC;IACrB,4DAA4D;IAC5D,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC;IACxB,+DAA+D;IAC/D,OAAO,EAAE,eAAe,CAAC;CAC5B;AAUD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,uBAAuB,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAc3F;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,uBAAuB,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAgE3F;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,uBAAuB,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CA2BtF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CASzF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAuBxE"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mask list DOM rendering — owns the legacy `updateMaskList` and
|
|
3
|
+
* `_handleMaskListClick` logic that was inlined on the editor in
|
|
4
|
+
* legacy and is now extracted into pure(ish) helpers that take a
|
|
5
|
+
* {@link MaskListContext}.
|
|
6
|
+
*
|
|
7
|
+
* ## Owned contracts
|
|
8
|
+
*
|
|
9
|
+
* - When the mask list DOM container is bound,
|
|
10
|
+
* {@link renderMaskList} renders exactly one `<li>` per mask currently on
|
|
11
|
+
* the canvas, in canvas object order. The list is rebuilt from scratch on
|
|
12
|
+
* every call so it stays in sync with `canvas.getObjects` after creates,
|
|
13
|
+
* removals, undo/redo, and JSON restores.
|
|
14
|
+
*
|
|
15
|
+
* - Every `<li>` carries a `data-mask-id` attribute
|
|
16
|
+
* (via `dataset.maskId`) equal to the mask's `maskId`. Clients keying off
|
|
17
|
+
* the list (the click handler below, the test suite under
|
|
18
|
+
* `tests/properties/mask-list-dom.test.mjs`) MUST use this attribute and
|
|
19
|
+
* never the visible label text — the label may be rewritten by the
|
|
20
|
+
* integrator via `options.label.getText`, so it is NOT a stable key.
|
|
21
|
+
*
|
|
22
|
+
* - Clicking a list item selects the mask whose
|
|
23
|
+
* `maskId` equals the clicked item's `data-mask-id`, regardless of where
|
|
24
|
+
* the item currently sits in the list. The click handler does NOT use the
|
|
25
|
+
* item's array index, so reordering the canvas (e.g. via Fabric layering)
|
|
26
|
+
* does not break selection.
|
|
27
|
+
*
|
|
28
|
+
* ## Out of scope (handled by sibling modules)
|
|
29
|
+
*
|
|
30
|
+
* - Label text generation and `mask.maskId - 1` index — see
|
|
31
|
+
* `mask/mask-label-manager.ts`.
|
|
32
|
+
* - Mask creation, removal, and the post-create `updateMaskList` call — see
|
|
33
|
+
* `mask/mask-factory.ts`. The factory
|
|
34
|
+
* invokes the orchestrator's `updateMaskList` callback, which delegates
|
|
35
|
+
* here.
|
|
36
|
+
* - Hover/selection appearance on the canvas — see `mask/mask-style.ts`.
|
|
37
|
+
*
|
|
38
|
+
* ## Implementation notes
|
|
39
|
+
*
|
|
40
|
+
* - The orchestrator (`src/image-editor.ts`) owns the canvas reference, the
|
|
41
|
+
* resolved `elements` table, and the selection callback. The helpers in
|
|
42
|
+
* this module receive those slots through a {@link MaskListContext} so the
|
|
43
|
+
* module is independent of the `ImageEditor` class shape and can be unit
|
|
44
|
+
* tested in isolation against a stub Fabric environment plus a JSDOM
|
|
45
|
+
* container.
|
|
46
|
+
* - The DOM contract — `<li class="list-group-item mask-item">` with a
|
|
47
|
+
* `dataset.maskId` — matches legacy verbatim so existing CSS, theme overrides,
|
|
48
|
+
* and integrator selectors continue to work unchanged.
|
|
49
|
+
* - Each render replaces the container's `innerHTML`. That is intentional:
|
|
50
|
+
* it guarantees the DOM mirrors `canvas.getObjects` exactly, and the
|
|
51
|
+
* Fabric event handlers (`object:added`, `object:removed`, `selection:*`)
|
|
52
|
+
* already fire often enough that an incremental diff is not justified.
|
|
53
|
+
* `innerHTML = ''` also detaches every prior `onclick` handler we attached
|
|
54
|
+
* below, so there is no need to track listeners separately.
|
|
55
|
+
*
|
|
56
|
+
* @module
|
|
57
|
+
*/
|
|
58
|
+
import type * as FabricNS from 'fabric';
|
|
59
|
+
import type { MaskObject } from '../core/public-types.js';
|
|
60
|
+
/**
|
|
61
|
+
* State the mask-list helpers read from the `ImageEditor` orchestrator.
|
|
62
|
+
*
|
|
63
|
+
* The module does NOT own any of these slots — it only reads them so
|
|
64
|
+
* ownership of the canvas, the resolved DOM element ID map, and the
|
|
65
|
+
* selection-changed pipeline stays on the orchestrator (where legacy left
|
|
66
|
+
* them).
|
|
67
|
+
*/
|
|
68
|
+
export interface MaskListContext {
|
|
69
|
+
/**
|
|
70
|
+
* The live Fabric canvas the list mirrors. May be `null` after
|
|
71
|
+
* `dispose` or before `init` has run; the helpers no-op in that
|
|
72
|
+
* case.
|
|
73
|
+
*/
|
|
74
|
+
canvas: FabricNS.Canvas | null;
|
|
75
|
+
/**
|
|
76
|
+
* Returns the resolved DOM element ID for the mask list container, or a
|
|
77
|
+
* falsy value when the integrator omitted `maskList` from the `idMap`.
|
|
78
|
+
* The orchestrator's `elements.maskList` slot fills this role.
|
|
79
|
+
*/
|
|
80
|
+
getListElementId(): string | null | undefined;
|
|
81
|
+
/**
|
|
82
|
+
* Invoked by the click handler after `setActiveObject(mask)` has run,
|
|
83
|
+
* so the orchestrator can drive its selection-changed pipeline (label
|
|
84
|
+
* overlay, hover/selection styling, list highlight) the same way it
|
|
85
|
+
* does for canvas-originated selections. Mirrors legacy's
|
|
86
|
+
* `_handleSelectionChanged([mask])` call from `_handleMaskListClick`.
|
|
87
|
+
*/
|
|
88
|
+
onMaskSelected(mask: MaskObject): void;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Re-render the mask list DOM from `canvas.getObjects`.
|
|
92
|
+
*
|
|
93
|
+
* No-op when the canvas is unset, the integrator did not supply a
|
|
94
|
+
* `maskList` element ID, or the configured element does not exist in the
|
|
95
|
+
* document. Tolerating partial DOM is intentional — the editor supports
|
|
96
|
+
* being driven from code without any sidebar UI (the editor's
|
|
97
|
+
* tolerated-bindings contract).
|
|
98
|
+
*
|
|
99
|
+
* Steps:
|
|
100
|
+
*
|
|
101
|
+
* 1. Resolve the list element via `context.getListElementId`. Bail out if
|
|
102
|
+
* missing.
|
|
103
|
+
* 2. Clear the container with `innerHTML = ''`. This also detaches every
|
|
104
|
+
* `onclick` handler attached on the previous render, so there is no
|
|
105
|
+
* listener bookkeeping to track separately.
|
|
106
|
+
* 3. For each {@link MaskObject} returned by `canvas.getObjects` (in
|
|
107
|
+
* canvas object order), build a fresh `<li>`:
|
|
108
|
+
* - class `list-group-item mask-item` (matches legacy verbatim so
|
|
109
|
+
* existing CSS keeps working);
|
|
110
|
+
* - `textContent` set to `mask.maskName` (the label-text contract
|
|
111
|
+
* owned by `mask/mask-label-manager.ts`; this list shows the same
|
|
112
|
+
* identifier);
|
|
113
|
+
* - `dataset.maskId` set to `String(mask.maskId)` so the click
|
|
114
|
+
* handler and tests can key off a stable identifier;
|
|
115
|
+
* - an `onclick` handler that looks up the mask by `maskId` —
|
|
116
|
+
* regardless of the item's current position in the list — calls
|
|
117
|
+
* `setActiveObject` on the canvas, and
|
|
118
|
+
* forwards to `context.onMaskSelected(mask)` so the orchestrator's
|
|
119
|
+
* selection-changed pipeline runs.
|
|
120
|
+
*
|
|
121
|
+
* @param context - Orchestration context — see {@link MaskListContext}.
|
|
122
|
+
*/
|
|
123
|
+
export declare function renderMaskList(context: MaskListContext): void;
|
|
124
|
+
/**
|
|
125
|
+
* Toggle the `active` CSS class on every `<li class="mask-item">` so the
|
|
126
|
+
* one whose `data-mask-id` matches `selectedMask.maskId` is highlighted.
|
|
127
|
+
*
|
|
128
|
+
* Matches legacy's `updateMaskListSelection` behavior except that selection is
|
|
129
|
+
* keyed off `data-mask-id` instead of the list-item text content. legacy used
|
|
130
|
+
* the visible name (`textContent === selectedMask.maskName`), which broke
|
|
131
|
+
* when an integrator overrode `options.label.getText` to render anything
|
|
132
|
+
* other than `maskName`. Keying off `data-mask-id` keeps the highlight in
|
|
133
|
+
* lock-step with the stable `data-mask-id` identifier and tolerates any
|
|
134
|
+
* label-text customization.
|
|
135
|
+
*
|
|
136
|
+
* No-op when the integrator did not supply a `maskList` element ID or the
|
|
137
|
+
* configured element does not exist in the document.
|
|
138
|
+
*
|
|
139
|
+
* @param context - Orchestration context — see {@link MaskListContext}.
|
|
140
|
+
* @param selectedMask - The currently selected mask, or `null` to clear the
|
|
141
|
+
* highlight.
|
|
142
|
+
*/
|
|
143
|
+
export declare function updateMaskListSelection(context: MaskListContext, selectedMask: MaskObject | null): void;
|
|
144
|
+
//# sourceMappingURL=mask-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mask-list.d.ts","sourceRoot":"","sources":["../../../src/mask/mask-list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAEH,OAAO,KAAK,KAAK,QAAQ,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAG1D;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC5B;;;;OAIG;IACH,MAAM,EAAE,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC;IAC/B;;;;OAIG;IACH,gBAAgB,IAAI,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC9C;;;;;;OAMG;IACH,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CA0C7D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CACnC,OAAO,EAAE,eAAe,EACxB,YAAY,EAAE,UAAU,GAAG,IAAI,GAChC,IAAI,CAWN"}
|