@pooder/core 0.0.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/CHANGELOG.md +7 -0
- package/dist/index.d.mts +155 -0
- package/dist/index.d.ts +155 -0
- package/dist/index.js +437 -0
- package/dist/index.mjs +396 -0
- package/package.json +25 -0
- package/src/canvas.ts +3 -0
- package/src/command.ts +61 -0
- package/src/editor.ts +171 -0
- package/src/event.ts +48 -0
- package/src/extension.ts +189 -0
- package/src/index.ts +9 -0
- package/src/layer.ts +13 -0
- package/src/obj.ts +7 -0
- package/src/types.ts +102 -0
- package/tsconfig.json +13 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
// src/event.ts
|
|
2
|
+
var EventBus = class {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.events = /* @__PURE__ */ new Map();
|
|
5
|
+
}
|
|
6
|
+
on(event, handler, priority = 0) {
|
|
7
|
+
if (this.events.has(event)) {
|
|
8
|
+
const listeners = this.events.get(event);
|
|
9
|
+
listeners.push({ handler, priority });
|
|
10
|
+
listeners.sort((a, b) => b.priority - a.priority);
|
|
11
|
+
} else {
|
|
12
|
+
this.events.set(event, [{ handler, priority }]);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
off(event, handler) {
|
|
16
|
+
const listeners = this.events.get(event);
|
|
17
|
+
if (!listeners) return;
|
|
18
|
+
const index = listeners.findIndex((l) => l.handler === handler);
|
|
19
|
+
listeners.splice(index, 1);
|
|
20
|
+
}
|
|
21
|
+
emit(event, ...args) {
|
|
22
|
+
const listeners = this.events.get(event);
|
|
23
|
+
if (!listeners) return;
|
|
24
|
+
for (const { handler } of listeners) {
|
|
25
|
+
const result = handler(...args);
|
|
26
|
+
if (result === false) break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
clear() {
|
|
30
|
+
this.events.clear();
|
|
31
|
+
}
|
|
32
|
+
count(event) {
|
|
33
|
+
var _a, _b;
|
|
34
|
+
return (_b = (_a = this.events.get(event)) == null ? void 0 : _a.length) != null ? _b : 0;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// src/command.ts
|
|
39
|
+
var DefaultCommandManager = class {
|
|
40
|
+
constructor(editor) {
|
|
41
|
+
this.editor = editor;
|
|
42
|
+
}
|
|
43
|
+
register(name, command) {
|
|
44
|
+
if (this.editor.commands.has(name)) {
|
|
45
|
+
console.warn(`Command "${name}" already exists. It will be overwritten.`);
|
|
46
|
+
}
|
|
47
|
+
this.editor.commands.set(name, command);
|
|
48
|
+
}
|
|
49
|
+
unregister(name) {
|
|
50
|
+
this.editor.commands.delete(name);
|
|
51
|
+
}
|
|
52
|
+
execute(name, ...args) {
|
|
53
|
+
const command = this.editor.commands.get(name);
|
|
54
|
+
if (!command) {
|
|
55
|
+
console.warn(`Command "${name}" not found`);
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
return command.execute(...args);
|
|
60
|
+
} catch (e) {
|
|
61
|
+
console.error(`Error executing command "${name}":`, e);
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
get(name) {
|
|
66
|
+
return this.editor.commands.get(name);
|
|
67
|
+
}
|
|
68
|
+
has(name) {
|
|
69
|
+
return this.editor.commands.has(name);
|
|
70
|
+
}
|
|
71
|
+
count() {
|
|
72
|
+
return this.editor.commands.size;
|
|
73
|
+
}
|
|
74
|
+
list() {
|
|
75
|
+
return Array.from(this.editor.commands.keys());
|
|
76
|
+
}
|
|
77
|
+
clear() {
|
|
78
|
+
this.editor.commands.clear();
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// src/extension.ts
|
|
83
|
+
var DefaultExtensionManager = class {
|
|
84
|
+
constructor(editor) {
|
|
85
|
+
this.mounted = false;
|
|
86
|
+
this.editor = editor;
|
|
87
|
+
}
|
|
88
|
+
_registerCommands(extension) {
|
|
89
|
+
if (extension.commands) {
|
|
90
|
+
Object.entries(extension.commands).forEach(([name, command]) => {
|
|
91
|
+
const commandName = `${extension.name}.${name}`;
|
|
92
|
+
this.editor.registerCommand(commandName, command);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
_unregisterCommands(extension) {
|
|
97
|
+
if (extension.commands) {
|
|
98
|
+
Object.keys(extension.commands).forEach((name) => {
|
|
99
|
+
const commandName = `${extension.name}.${name}`;
|
|
100
|
+
this.editor.unregisterCommand(commandName);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
register(extension) {
|
|
105
|
+
var _a, _b;
|
|
106
|
+
if (this.editor.extensions.has(extension.name)) {
|
|
107
|
+
console.warn(`Plugin "${extension.name}" already registered. It will be overwritten.`);
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
if (extension.enabled === void 0) {
|
|
111
|
+
extension.enabled = true;
|
|
112
|
+
}
|
|
113
|
+
this.editor.extensions.set(extension.name, extension);
|
|
114
|
+
(_a = extension.onCreate) == null ? void 0 : _a.call(extension, this.editor);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error(`Error in onCreate hook for plugin "${extension.name}":`, error);
|
|
117
|
+
}
|
|
118
|
+
if (extension.enabled) {
|
|
119
|
+
this._registerCommands(extension);
|
|
120
|
+
if (this.mounted) {
|
|
121
|
+
try {
|
|
122
|
+
(_b = extension.onMount) == null ? void 0 : _b.call(extension, this.editor);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error(`Error in onMount hook for plugin "${extension.name}":`, error);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
console.log(`Plugin "${extension.name}" registered successfully`);
|
|
129
|
+
}
|
|
130
|
+
unregister(name) {
|
|
131
|
+
var _a, _b;
|
|
132
|
+
const extension = this.editor.extensions.get(name);
|
|
133
|
+
if (!extension) {
|
|
134
|
+
console.warn(`Plugin "${name}" not found.`);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (this.mounted && extension.enabled) {
|
|
138
|
+
try {
|
|
139
|
+
(_a = extension.onUnmount) == null ? void 0 : _a.call(extension, this.editor);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error(`Error in onUnmount hook for plugin "${name}":`, error);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
(_b = extension.onDestroy) == null ? void 0 : _b.call(extension, this.editor);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error(`Error in onDestroy hook for plugin "${name}":`, error);
|
|
148
|
+
}
|
|
149
|
+
this._unregisterCommands(extension);
|
|
150
|
+
this.editor.extensions.delete(name);
|
|
151
|
+
console.log(`Plugin "${name}" unregistered`);
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
enable(name) {
|
|
155
|
+
var _a;
|
|
156
|
+
const extension = this.get(name);
|
|
157
|
+
if (!extension) {
|
|
158
|
+
console.warn(`Plugin "${name}" not found.`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (extension.enabled) return;
|
|
162
|
+
extension.enabled = true;
|
|
163
|
+
this._registerCommands(extension);
|
|
164
|
+
if (this.mounted) {
|
|
165
|
+
try {
|
|
166
|
+
(_a = extension.onMount) == null ? void 0 : _a.call(extension, this.editor);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error(`Error in onMount hook for plugin "${name}":`, error);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
disable(name) {
|
|
173
|
+
var _a;
|
|
174
|
+
const extension = this.get(name);
|
|
175
|
+
if (!extension) {
|
|
176
|
+
console.warn(`Plugin "${name}" not found.`);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (!extension.enabled) return;
|
|
180
|
+
extension.enabled = false;
|
|
181
|
+
this._unregisterCommands(extension);
|
|
182
|
+
if (this.mounted) {
|
|
183
|
+
try {
|
|
184
|
+
(_a = extension.onUnmount) == null ? void 0 : _a.call(extension, this.editor);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.error(`Error in onUnmount hook for plugin "${name}":`, error);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
get(name) {
|
|
191
|
+
return this.editor.extensions.get(name);
|
|
192
|
+
}
|
|
193
|
+
has(name) {
|
|
194
|
+
return this.editor.extensions.has(name);
|
|
195
|
+
}
|
|
196
|
+
count() {
|
|
197
|
+
return this.editor.extensions.size;
|
|
198
|
+
}
|
|
199
|
+
list() {
|
|
200
|
+
return Array.from(this.editor.extensions.values());
|
|
201
|
+
}
|
|
202
|
+
mount() {
|
|
203
|
+
if (this.mounted) return;
|
|
204
|
+
this.editor.extensions.forEach((extension) => {
|
|
205
|
+
var _a;
|
|
206
|
+
if (extension.enabled) {
|
|
207
|
+
try {
|
|
208
|
+
(_a = extension.onMount) == null ? void 0 : _a.call(extension, this.editor);
|
|
209
|
+
} catch (e) {
|
|
210
|
+
console.error(`Error in onMount hook for plugin "${extension.name}":`, e);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
this.mounted = true;
|
|
215
|
+
}
|
|
216
|
+
update() {
|
|
217
|
+
const state = this.editor.getState();
|
|
218
|
+
this.editor.extensions.forEach((extension) => {
|
|
219
|
+
var _a;
|
|
220
|
+
if (extension.enabled) {
|
|
221
|
+
try {
|
|
222
|
+
(_a = extension.onUpdate) == null ? void 0 : _a.call(extension, this.editor, state);
|
|
223
|
+
} catch (e) {
|
|
224
|
+
console.error(`Error in onUpdate hook for plugin "${extension.name}":`, e);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
destroy() {
|
|
230
|
+
const extensionNames = Array.from(this.editor.extensions.keys());
|
|
231
|
+
extensionNames.forEach((name) => this.unregister(name));
|
|
232
|
+
this.mounted = false;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
// src/canvas.ts
|
|
237
|
+
import { Canvas } from "fabric";
|
|
238
|
+
|
|
239
|
+
// src/editor.ts
|
|
240
|
+
var PooderEditor = class {
|
|
241
|
+
constructor(el, options = {}) {
|
|
242
|
+
this.extensions = /* @__PURE__ */ new Map();
|
|
243
|
+
this.commands = /* @__PURE__ */ new Map();
|
|
244
|
+
this.destroyed = false;
|
|
245
|
+
this.state = {
|
|
246
|
+
width: options.width || 800,
|
|
247
|
+
height: options.height || 600
|
|
248
|
+
};
|
|
249
|
+
this.canvas = new Canvas(el, { width: this.state.width, height: this.state.height });
|
|
250
|
+
this.eventBus = new EventBus();
|
|
251
|
+
this.commandManager = new DefaultCommandManager(this);
|
|
252
|
+
this.extensionManager = new DefaultExtensionManager(this);
|
|
253
|
+
if (options.extensions && options.extensions.length > 0) {
|
|
254
|
+
options.extensions.forEach(this.extensionManager.register);
|
|
255
|
+
}
|
|
256
|
+
this.extensionManager.mount();
|
|
257
|
+
console.log("Editor initialized with", this.extensionManager.count(), "plugins");
|
|
258
|
+
}
|
|
259
|
+
use(extension) {
|
|
260
|
+
if (this.destroyed) {
|
|
261
|
+
throw new Error("Cannot register plugin: Editor is destroyed");
|
|
262
|
+
}
|
|
263
|
+
this.extensionManager.register(extension);
|
|
264
|
+
this.emit("update", this.state);
|
|
265
|
+
}
|
|
266
|
+
unuse(name) {
|
|
267
|
+
return this.extensionManager.unregister(name);
|
|
268
|
+
}
|
|
269
|
+
getExtension(name) {
|
|
270
|
+
return this.extensionManager.get(name);
|
|
271
|
+
}
|
|
272
|
+
getExtensions() {
|
|
273
|
+
return this.extensionManager.list();
|
|
274
|
+
}
|
|
275
|
+
enableExtension(name) {
|
|
276
|
+
this.extensionManager.enable(name);
|
|
277
|
+
this.emit("update", this.state);
|
|
278
|
+
}
|
|
279
|
+
disableExtension(name) {
|
|
280
|
+
this.extensionManager.disable(name);
|
|
281
|
+
this.emit("update", this.state);
|
|
282
|
+
}
|
|
283
|
+
registerCommand(name, command) {
|
|
284
|
+
this.commandManager.register(name, command);
|
|
285
|
+
}
|
|
286
|
+
unregisterCommand(name) {
|
|
287
|
+
this.commandManager.unregister(name);
|
|
288
|
+
}
|
|
289
|
+
executeCommand(name, ...args) {
|
|
290
|
+
if (this.destroyed) {
|
|
291
|
+
console.warn("Cannot execute command: Editor is destroyed");
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
this.emit("beforeCommand", name, ...args);
|
|
295
|
+
const result = this.commandManager.execute(name, ...args);
|
|
296
|
+
this.emit("afterCommand", name, args, result);
|
|
297
|
+
return result;
|
|
298
|
+
}
|
|
299
|
+
on(event, handler, priority) {
|
|
300
|
+
this.eventBus.on(event, handler, priority);
|
|
301
|
+
}
|
|
302
|
+
off(event, handler) {
|
|
303
|
+
this.eventBus.off(event, handler);
|
|
304
|
+
}
|
|
305
|
+
emit(event, ...args) {
|
|
306
|
+
this.eventBus.emit(event, ...args);
|
|
307
|
+
}
|
|
308
|
+
getObjects() {
|
|
309
|
+
if (this.destroyed) {
|
|
310
|
+
throw new Error("Cannot get objects: Editor is destroyed");
|
|
311
|
+
}
|
|
312
|
+
return this.canvas.getObjects();
|
|
313
|
+
}
|
|
314
|
+
getObject(id, layerId) {
|
|
315
|
+
var _a;
|
|
316
|
+
let objs;
|
|
317
|
+
if (layerId) {
|
|
318
|
+
objs = (_a = this.getLayer(layerId)) == null ? void 0 : _a.getObjects();
|
|
319
|
+
} else {
|
|
320
|
+
objs = this.getObjects();
|
|
321
|
+
}
|
|
322
|
+
return objs == null ? void 0 : objs.find((obj) => {
|
|
323
|
+
var _a2;
|
|
324
|
+
return ((_a2 = obj == null ? void 0 : obj.data) == null ? void 0 : _a2.id) === id;
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
getLayers() {
|
|
328
|
+
return this.getObjects().filter((obj) => obj.type === "group");
|
|
329
|
+
}
|
|
330
|
+
getLayer(id) {
|
|
331
|
+
return this.getLayers().find((obj) => {
|
|
332
|
+
var _a;
|
|
333
|
+
return ((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id) === id;
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
updateState(updater) {
|
|
337
|
+
if (this.destroyed) {
|
|
338
|
+
console.warn("Cannot update state: Editor is destroyed");
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
this.state = updater(this.state);
|
|
342
|
+
this.extensionManager.update();
|
|
343
|
+
this.emit("update", this.state);
|
|
344
|
+
}
|
|
345
|
+
toJSON() {
|
|
346
|
+
return {
|
|
347
|
+
width: this.state.width,
|
|
348
|
+
height: this.state.height,
|
|
349
|
+
metadata: this.state.metadata
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
async loadFromJSON(json) {
|
|
353
|
+
}
|
|
354
|
+
getState() {
|
|
355
|
+
return { ...this.state };
|
|
356
|
+
}
|
|
357
|
+
destroy() {
|
|
358
|
+
if (this.destroyed) return;
|
|
359
|
+
this.emit("beforeDestroy");
|
|
360
|
+
this.canvas.dispose();
|
|
361
|
+
this.extensionManager.destroy();
|
|
362
|
+
this.eventBus.clear();
|
|
363
|
+
this.commandManager.clear();
|
|
364
|
+
this.destroyed = true;
|
|
365
|
+
console.log("Editor destroyed");
|
|
366
|
+
}
|
|
367
|
+
isDestroyed() {
|
|
368
|
+
return this.destroyed;
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
// src/layer.ts
|
|
373
|
+
import { Group } from "fabric";
|
|
374
|
+
|
|
375
|
+
// src/obj.ts
|
|
376
|
+
import { FabricObject } from "fabric";
|
|
377
|
+
|
|
378
|
+
// src/index.ts
|
|
379
|
+
import { FabricImage, Ellipse, Rect, Circle, Line, Text, Group as Group2, Path, Point, Pattern, filters } from "fabric";
|
|
380
|
+
export {
|
|
381
|
+
Circle,
|
|
382
|
+
Ellipse,
|
|
383
|
+
Group2 as Group,
|
|
384
|
+
FabricImage as Image,
|
|
385
|
+
Line,
|
|
386
|
+
Path,
|
|
387
|
+
Pattern,
|
|
388
|
+
Point,
|
|
389
|
+
Canvas as PooderCanvas,
|
|
390
|
+
PooderEditor,
|
|
391
|
+
Group as PooderLayer,
|
|
392
|
+
FabricObject as PooderObject,
|
|
393
|
+
Rect,
|
|
394
|
+
Text,
|
|
395
|
+
filters
|
|
396
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pooder/core",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Core logic for Pooder editor",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"keywords": [],
|
|
9
|
+
"author": "",
|
|
10
|
+
"license": "ISC",
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"tsup": "^8.0.0",
|
|
16
|
+
"typescript": "^5.0.0"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"fabric": "^7.0.0"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
23
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch"
|
|
24
|
+
}
|
|
25
|
+
}
|
package/src/canvas.ts
ADDED
package/src/command.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {Command, Editor} from "./types";
|
|
2
|
+
|
|
3
|
+
export { CommandManager, DefaultCommandManager, CommandMap }
|
|
4
|
+
|
|
5
|
+
interface CommandManager{
|
|
6
|
+
register(name:string,command:Command):void
|
|
7
|
+
unregister(name:string):void
|
|
8
|
+
execute(name:string,...args:any[]):boolean
|
|
9
|
+
get(name:string):Command | undefined
|
|
10
|
+
has(name:string):boolean
|
|
11
|
+
count():number
|
|
12
|
+
list():string[]
|
|
13
|
+
clear():void
|
|
14
|
+
}
|
|
15
|
+
class CommandMap extends Map<string, Command> {}
|
|
16
|
+
class DefaultCommandManager implements CommandManager{
|
|
17
|
+
private editor: Editor
|
|
18
|
+
constructor(editor: Editor) {
|
|
19
|
+
this.editor = editor
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
register(name:string,command:Command){
|
|
23
|
+
if(this.editor.commands.has(name)){
|
|
24
|
+
console.warn(`Command "${name}" already exists. It will be overwritten.`)
|
|
25
|
+
}
|
|
26
|
+
this.editor.commands.set(name,command)
|
|
27
|
+
}
|
|
28
|
+
unregister(name:string){
|
|
29
|
+
this.editor.commands.delete(name)
|
|
30
|
+
}
|
|
31
|
+
execute(name:string,...args:any[]){
|
|
32
|
+
const command=this.editor.commands.get(name)
|
|
33
|
+
if(!command){
|
|
34
|
+
console.warn(`Command "${name}" not found`)
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try{
|
|
39
|
+
return command.execute(...args)
|
|
40
|
+
}catch ( e){
|
|
41
|
+
console.error(`Error executing command "${name}":`, e)
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get(name:string){
|
|
47
|
+
return this.editor.commands.get(name)
|
|
48
|
+
}
|
|
49
|
+
has(name:string){
|
|
50
|
+
return this.editor.commands.has(name)
|
|
51
|
+
}
|
|
52
|
+
count(): number {
|
|
53
|
+
return this.editor.commands.size
|
|
54
|
+
}
|
|
55
|
+
list(){
|
|
56
|
+
return Array.from(this.editor.commands.keys())
|
|
57
|
+
}
|
|
58
|
+
clear(){
|
|
59
|
+
this.editor.commands.clear()
|
|
60
|
+
}
|
|
61
|
+
}
|
package/src/editor.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import {Command, Editor, EditorState, EventHandler, Extension} from "./types";
|
|
2
|
+
import {EventBus} from "./event";
|
|
3
|
+
import {CommandManager, CommandMap, DefaultCommandManager} from "./command";
|
|
4
|
+
import {DefaultExtensionManager, ExtensionManager, ExtensionMap} from "./extension";
|
|
5
|
+
import {PooderCanvas} from "./canvas";
|
|
6
|
+
import {PooderObject} from "./obj";
|
|
7
|
+
import {PooderLayer} from "./layer";
|
|
8
|
+
|
|
9
|
+
export class PooderEditor implements Editor {
|
|
10
|
+
public state: EditorState;
|
|
11
|
+
public canvas: PooderCanvas;
|
|
12
|
+
public extensions: ExtensionMap = new Map()
|
|
13
|
+
public commands: CommandMap = new Map()
|
|
14
|
+
|
|
15
|
+
private eventBus: EventBus;
|
|
16
|
+
private commandManager: CommandManager;
|
|
17
|
+
private extensionManager: ExtensionManager;
|
|
18
|
+
|
|
19
|
+
private destroyed: boolean = false;
|
|
20
|
+
|
|
21
|
+
constructor(el:HTMLCanvasElement, options:{
|
|
22
|
+
width?: number,
|
|
23
|
+
height?: number,
|
|
24
|
+
extensions?: Extension[],
|
|
25
|
+
}={}) {
|
|
26
|
+
this.state = {
|
|
27
|
+
width: options.width || 800,
|
|
28
|
+
height: options.height || 600,
|
|
29
|
+
};
|
|
30
|
+
this.canvas = new PooderCanvas(el, {width: this.state.width, height: this.state.height});
|
|
31
|
+
this.eventBus=new EventBus()
|
|
32
|
+
this.commandManager = new DefaultCommandManager(this);
|
|
33
|
+
|
|
34
|
+
this.extensionManager = new DefaultExtensionManager(this);
|
|
35
|
+
if(options.extensions && options.extensions.length>0){
|
|
36
|
+
options.extensions.forEach(this.extensionManager.register)
|
|
37
|
+
}
|
|
38
|
+
this.extensionManager.mount();
|
|
39
|
+
|
|
40
|
+
console.log('Editor initialized with', this.extensionManager.count(), 'plugins');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
use(extension: Extension){
|
|
44
|
+
if(this.destroyed){
|
|
45
|
+
throw new Error('Cannot register plugin: Editor is destroyed');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this.extensionManager.register(extension)
|
|
49
|
+
|
|
50
|
+
this.emit('update', this.state);
|
|
51
|
+
}
|
|
52
|
+
unuse(name: string) {
|
|
53
|
+
return this.extensionManager.unregister(name)
|
|
54
|
+
}
|
|
55
|
+
getExtension(name: string): Extension | undefined {
|
|
56
|
+
return this.extensionManager.get(name)
|
|
57
|
+
}
|
|
58
|
+
getExtensions():Extension[]{
|
|
59
|
+
return this.extensionManager.list()
|
|
60
|
+
}
|
|
61
|
+
enableExtension(name: string) {
|
|
62
|
+
this.extensionManager.enable(name);
|
|
63
|
+
this.emit('update', this.state);
|
|
64
|
+
}
|
|
65
|
+
disableExtension(name: string) {
|
|
66
|
+
this.extensionManager.disable(name);
|
|
67
|
+
this.emit('update', this.state);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
registerCommand(name: string, command: Command) {
|
|
71
|
+
this.commandManager.register(name, command)
|
|
72
|
+
}
|
|
73
|
+
unregisterCommand(name: string) {
|
|
74
|
+
this.commandManager.unregister(name)
|
|
75
|
+
}
|
|
76
|
+
executeCommand(name: string, ...args:any[]) {
|
|
77
|
+
if(this.destroyed){
|
|
78
|
+
console.warn('Cannot execute command: Editor is destroyed');
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.emit('beforeCommand', name, ...args)
|
|
83
|
+
|
|
84
|
+
const result = this.commandManager.execute(name, ...args)
|
|
85
|
+
|
|
86
|
+
this.emit('afterCommand', name, args,result)
|
|
87
|
+
|
|
88
|
+
return result
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
on(event:string, handler:EventHandler,priority?:number):void{
|
|
93
|
+
this.eventBus.on(event, handler, priority)
|
|
94
|
+
}
|
|
95
|
+
off(event:string, handler:EventHandler):void{
|
|
96
|
+
this.eventBus.off(event, handler)
|
|
97
|
+
}
|
|
98
|
+
emit(event:string, ...args:any[]):void{
|
|
99
|
+
this.eventBus.emit(event, ...args)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
getObjects():PooderObject[] {
|
|
103
|
+
if(this.destroyed){
|
|
104
|
+
throw new Error('Cannot get objects: Editor is destroyed');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return this.canvas.getObjects()
|
|
108
|
+
}
|
|
109
|
+
getObject(id:string,layerId?:string):PooderObject | undefined {
|
|
110
|
+
let objs;
|
|
111
|
+
if(layerId){
|
|
112
|
+
objs=this.getLayer(layerId)?.getObjects()
|
|
113
|
+
}else {
|
|
114
|
+
objs=this.getObjects()
|
|
115
|
+
}
|
|
116
|
+
return objs?.find(obj => obj?.data?.id === id)
|
|
117
|
+
}
|
|
118
|
+
getLayers():PooderLayer[] {
|
|
119
|
+
return this.getObjects().filter(obj => obj.type==="group") as PooderLayer[]
|
|
120
|
+
}
|
|
121
|
+
getLayer(id:string):PooderLayer | undefined {
|
|
122
|
+
return this.getLayers().find(obj => obj?.data?.id === id)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
updateState(updater:(state:EditorState)=>EditorState) {
|
|
126
|
+
if(this.destroyed){
|
|
127
|
+
console.warn('Cannot update state: Editor is destroyed');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
this.state = updater(this.state)
|
|
132
|
+
|
|
133
|
+
this.extensionManager.update()
|
|
134
|
+
this.emit('update', this.state)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
toJSON() {
|
|
138
|
+
return {
|
|
139
|
+
width: this.state.width,
|
|
140
|
+
height: this.state.height,
|
|
141
|
+
metadata: this.state.metadata,
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async loadFromJSON(json: any) {}
|
|
146
|
+
getState(): EditorState {
|
|
147
|
+
return { ...this.state }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
destroy():void{
|
|
151
|
+
if(this.destroyed)return
|
|
152
|
+
|
|
153
|
+
this.emit('beforeDestroy')
|
|
154
|
+
|
|
155
|
+
this.canvas.dispose()
|
|
156
|
+
|
|
157
|
+
this.extensionManager.destroy()
|
|
158
|
+
|
|
159
|
+
this.eventBus.clear()
|
|
160
|
+
|
|
161
|
+
this.commandManager.clear()
|
|
162
|
+
|
|
163
|
+
this.destroyed=true
|
|
164
|
+
|
|
165
|
+
console.log('Editor destroyed');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
isDestroyed():boolean{
|
|
169
|
+
return this.destroyed
|
|
170
|
+
}
|
|
171
|
+
}
|
package/src/event.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {EventHandler} from "./types";
|
|
2
|
+
|
|
3
|
+
export { EventBus, EventListener }
|
|
4
|
+
|
|
5
|
+
interface EventListener {
|
|
6
|
+
handler:EventHandler;
|
|
7
|
+
priority:number;
|
|
8
|
+
}
|
|
9
|
+
type EventListenersMap = Map<string, EventListener[]>
|
|
10
|
+
class EventBus{
|
|
11
|
+
private events: EventListenersMap = new Map()
|
|
12
|
+
|
|
13
|
+
on(event:string,handler:EventHandler,priority:number=0){
|
|
14
|
+
if(this.events.has(event)){
|
|
15
|
+
const listeners=this.events.get(event)
|
|
16
|
+
listeners!.push({handler, priority})
|
|
17
|
+
listeners!.sort((a,b)=>b.priority-a.priority)
|
|
18
|
+
}else {
|
|
19
|
+
this.events.set(event,[{handler, priority}])
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
off(event:string,handler:EventHandler){
|
|
24
|
+
const listeners=this.events.get(event)
|
|
25
|
+
if(!listeners)return
|
|
26
|
+
|
|
27
|
+
const index=listeners.findIndex(l=>l.handler===handler)
|
|
28
|
+
listeners.splice(index,1)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
emit(event:string,...args:any[]){
|
|
32
|
+
const listeners=this.events.get(event)
|
|
33
|
+
if(!listeners)return
|
|
34
|
+
|
|
35
|
+
for(const {handler} of listeners){
|
|
36
|
+
const result=handler(...args)
|
|
37
|
+
if(result===false)break
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
clear(){
|
|
42
|
+
this.events.clear()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
count(event:string){
|
|
46
|
+
return this.events.get(event)?.length ?? 0
|
|
47
|
+
}
|
|
48
|
+
}
|