kritzel-react 0.1.30 → 0.1.32
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/dist/kritzel-react/lib/components/stencil-generated/components.js +122 -0
- package/dist/kritzel-react/lib/index.js +2 -0
- package/dist/kritzel-stencil/src/classes/core/core.class.js +797 -0
- package/dist/kritzel-stencil/src/classes/core/reviver.class.js +97 -0
- package/dist/kritzel-stencil/src/classes/core/store.class.js +153 -0
- package/dist/kritzel-stencil/src/classes/core/viewport.class.js +311 -0
- package/dist/kritzel-stencil/src/classes/core/workspace.class.js +34 -0
- package/dist/kritzel-stencil/src/classes/handlers/base.handler.js +6 -0
- package/dist/kritzel-stencil/src/classes/handlers/context-menu.handler.js +63 -0
- package/dist/kritzel-stencil/src/classes/handlers/hover.handler.js +18 -0
- package/dist/kritzel-stencil/src/classes/handlers/key.handler.js +76 -0
- package/dist/kritzel-stencil/src/classes/handlers/line-handle.handler.js +382 -0
- package/dist/kritzel-stencil/src/classes/handlers/move.handler.js +213 -0
- package/dist/kritzel-stencil/src/classes/handlers/resize.handler.js +205 -0
- package/dist/kritzel-stencil/src/classes/handlers/rotation.handler.js +117 -0
- package/dist/kritzel-stencil/src/classes/handlers/selection.handler.js +313 -0
- package/dist/kritzel-stencil/src/classes/managers/anchor.manager.js +1056 -0
- package/dist/kritzel-stencil/src/classes/managers/cursor.manager.js +117 -0
- package/dist/kritzel-stencil/src/classes/managers/theme.manager.js +103 -0
- package/dist/kritzel-stencil/src/classes/objects/base-object.class.js +249 -0
- package/dist/kritzel-stencil/src/classes/objects/custom-element.class.js +60 -0
- package/dist/kritzel-stencil/src/classes/objects/group.class.js +407 -0
- package/dist/kritzel-stencil/src/classes/objects/image.class.js +55 -0
- package/dist/kritzel-stencil/src/classes/objects/line.class.js +608 -0
- package/dist/kritzel-stencil/src/classes/objects/path.class.js +401 -0
- package/dist/kritzel-stencil/src/classes/objects/selection-box.class.js +21 -0
- package/dist/kritzel-stencil/src/classes/objects/selection-group.class.js +409 -0
- package/dist/kritzel-stencil/src/classes/objects/shape.class.js +412 -0
- package/dist/kritzel-stencil/src/classes/objects/text.class.js +292 -0
- package/dist/kritzel-stencil/src/classes/providers/broadcast-sync-provider.class.js +101 -0
- package/dist/kritzel-stencil/src/classes/providers/hocuspocus-sync-provider.class.js +241 -0
- package/dist/kritzel-stencil/src/classes/providers/indexeddb-sync-provider.class.js +43 -0
- package/dist/kritzel-stencil/src/classes/providers/websocket-sync-provider.class.js +98 -0
- package/dist/kritzel-stencil/src/classes/registries/icon-registry.class.js +66 -0
- package/dist/kritzel-stencil/src/classes/registries/tool.registry.js +21 -0
- package/dist/kritzel-stencil/src/classes/structures/app-state-map.structure.js +212 -0
- package/dist/kritzel-stencil/src/classes/structures/object-map.structure.js +414 -0
- package/dist/kritzel-stencil/src/classes/structures/quadtree.structure.js +151 -0
- package/dist/kritzel-stencil/src/classes/tools/base-tool.class.js +36 -0
- package/dist/kritzel-stencil/src/classes/tools/brush-tool.class.js +161 -0
- package/dist/kritzel-stencil/src/classes/tools/eraser-tool.class.js +85 -0
- package/dist/kritzel-stencil/src/classes/tools/image-tool.class.js +83 -0
- package/dist/kritzel-stencil/src/classes/tools/line-tool.class.js +187 -0
- package/dist/kritzel-stencil/src/classes/tools/selection-tool.class.js +429 -0
- package/dist/kritzel-stencil/src/classes/tools/shape-tool.class.js +196 -0
- package/dist/kritzel-stencil/src/classes/tools/text-tool.class.js +100 -0
- package/dist/kritzel-stencil/src/components/core/kritzel-engine/kritzel-engine.js +1343 -0
- package/dist/kritzel-stencil/src/components/shared/kritzel-brush-style/kritzel-brush-style.js +46 -0
- package/dist/kritzel-stencil/src/components/shared/kritzel-dropdown/kritzel-dropdown.js +312 -0
- package/dist/kritzel-stencil/src/components/shared/kritzel-font-family/kritzel-font-family.js +60 -0
- package/dist/kritzel-stencil/src/components/shared/kritzel-line-endings/kritzel-line-endings.js +105 -0
- package/dist/kritzel-stencil/src/components/shared/kritzel-shape-fill/kritzel-shape-fill.js +53 -0
- package/dist/kritzel-stencil/src/components/ui/kritzel-context-menu/kritzel-context-menu.js +137 -0
- package/dist/kritzel-stencil/src/configs/default-brush-tool.config.js +9 -0
- package/dist/kritzel-stencil/src/configs/default-engine-config.js +63 -0
- package/dist/kritzel-stencil/src/configs/default-line-tool.config.js +9 -0
- package/dist/kritzel-stencil/src/configs/default-sync.config.js +9 -0
- package/dist/kritzel-stencil/src/configs/default-text-tool.config.js +7 -0
- package/dist/kritzel-stencil/src/constants/color-palette.constants.js +37 -0
- package/dist/kritzel-stencil/src/constants/engine.constants.js +2 -0
- package/dist/kritzel-stencil/src/enums/event-button.enum.js +6 -0
- package/dist/kritzel-stencil/src/enums/handle-type.enum.js +7 -0
- package/dist/kritzel-stencil/src/enums/shape-type.enum.js +6 -0
- package/dist/kritzel-stencil/src/helpers/class.helper.js +5 -0
- package/dist/kritzel-stencil/src/helpers/color.helper.js +106 -0
- package/dist/kritzel-stencil/src/helpers/cursor.helper.js +57 -0
- package/dist/kritzel-stencil/src/helpers/devices.helper.js +28 -0
- package/dist/kritzel-stencil/src/helpers/event.helper.js +58 -0
- package/dist/kritzel-stencil/src/helpers/geometry.helper.js +149 -0
- package/dist/kritzel-stencil/src/helpers/keyboard.helper.js +51 -0
- package/dist/kritzel-stencil/src/helpers/math.helper.js +5 -0
- package/dist/kritzel-stencil/src/helpers/object.helper.js +11 -0
- package/dist/kritzel-stencil/src/helpers/theme.helper.js +69 -0
- package/dist/kritzel-stencil/src/index.js +41 -0
- package/dist/kritzel-stencil/src/interfaces/anchor.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/arrow-head.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/bounding-box.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/clonable.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/context-menu-item.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/debug-info.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/dialog.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/displayable-shortcut.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/engine-state.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/line-options.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/master-detail.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/menu-item.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/object.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/path-options.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/point.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/polygon.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/serializable.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/settings.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/shortcut.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/sync-config.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/sync-provider.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/theme.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/tool-config.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/tool.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/toolbar-control.interface.js +1 -0
- package/dist/kritzel-stencil/src/interfaces/undo-state.interface.js +1 -0
- package/dist/kritzel-stencil/src/themes/dark-theme.js +198 -0
- package/dist/kritzel-stencil/src/themes/light-theme.js +199 -0
- package/dist/kritzel-stencil/src/types/shortcut.type.js +1 -0
- package/dist/kritzel-stencil/src/types/state.types.js +1 -0
- package/dist/types/kritzel-react/lib/components/stencil-generated/components.d.ts +74 -0
- package/dist/types/kritzel-react/lib/index.d.ts +2 -0
- package/dist/types/kritzel-stencil/src/classes/core/core.class.d.ts +101 -0
- package/dist/types/kritzel-stencil/src/classes/core/reviver.class.d.ts +6 -0
- package/dist/types/kritzel-stencil/src/classes/core/store.class.d.ts +53 -0
- package/dist/types/kritzel-stencil/src/classes/core/viewport.class.d.ts +48 -0
- package/dist/types/kritzel-stencil/src/classes/core/workspace.class.d.ts +24 -0
- package/dist/types/kritzel-stencil/src/classes/handlers/base.handler.d.ts +5 -0
- package/dist/types/kritzel-stencil/src/classes/handlers/context-menu.handler.d.ts +8 -0
- package/dist/types/kritzel-stencil/src/classes/handlers/hover.handler.d.ts +6 -0
- package/dist/types/kritzel-stencil/src/classes/handlers/key.handler.d.ts +11 -0
- package/dist/types/kritzel-stencil/src/classes/handlers/line-handle.handler.d.ts +34 -0
- package/dist/types/kritzel-stencil/src/classes/handlers/move.handler.d.ts +29 -0
- package/dist/types/kritzel-stencil/src/classes/handlers/resize.handler.d.ts +24 -0
- package/dist/types/kritzel-stencil/src/classes/handlers/rotation.handler.d.ts +12 -0
- package/dist/types/kritzel-stencil/src/classes/handlers/selection.handler.d.ts +27 -0
- package/dist/types/kritzel-stencil/src/classes/managers/anchor.manager.d.ts +180 -0
- package/dist/types/kritzel-stencil/src/classes/managers/cursor.manager.d.ts +43 -0
- package/dist/types/kritzel-stencil/src/classes/managers/theme.manager.d.ts +56 -0
- package/dist/types/kritzel-stencil/src/classes/objects/base-object.class.d.ts +76 -0
- package/dist/types/kritzel-stencil/src/classes/objects/custom-element.class.d.ts +26 -0
- package/dist/types/kritzel-stencil/src/classes/objects/group.class.d.ts +97 -0
- package/dist/types/kritzel-stencil/src/classes/objects/image.class.d.ts +17 -0
- package/dist/types/kritzel-stencil/src/classes/objects/line.class.d.ts +101 -0
- package/dist/types/kritzel-stencil/src/classes/objects/path.class.d.ts +62 -0
- package/dist/types/kritzel-stencil/src/classes/objects/selection-box.class.d.ts +6 -0
- package/dist/types/kritzel-stencil/src/classes/objects/selection-group.class.d.ts +67 -0
- package/dist/types/kritzel-stencil/src/classes/objects/shape.class.d.ts +124 -0
- package/dist/types/kritzel-stencil/src/classes/objects/text.class.d.ts +56 -0
- package/dist/types/kritzel-stencil/src/classes/providers/broadcast-sync-provider.class.d.ts +18 -0
- package/dist/types/kritzel-stencil/src/classes/providers/hocuspocus-sync-provider.class.d.ts +120 -0
- package/dist/types/kritzel-stencil/src/classes/providers/indexeddb-sync-provider.class.d.ts +22 -0
- package/dist/types/kritzel-stencil/src/classes/providers/websocket-sync-provider.class.d.ts +52 -0
- package/dist/types/kritzel-stencil/src/classes/registries/icon-registry.class.d.ts +9 -0
- package/dist/types/kritzel-stencil/src/classes/registries/tool.registry.d.ts +8 -0
- package/dist/types/kritzel-stencil/src/classes/structures/app-state-map.structure.d.ts +31 -0
- package/dist/types/kritzel-stencil/src/classes/structures/object-map.structure.d.ts +63 -0
- package/dist/types/kritzel-stencil/src/classes/structures/quadtree.structure.d.ts +36 -0
- package/dist/types/kritzel-stencil/src/classes/tools/base-tool.class.d.ts +20 -0
- package/dist/types/kritzel-stencil/src/classes/tools/brush-tool.class.d.ts +14 -0
- package/dist/types/kritzel-stencil/src/classes/tools/eraser-tool.class.d.ts +9 -0
- package/dist/types/kritzel-stencil/src/classes/tools/image-tool.class.d.ts +15 -0
- package/dist/types/kritzel-stencil/src/classes/tools/line-tool.class.d.ts +19 -0
- package/dist/types/kritzel-stencil/src/classes/tools/selection-tool.class.d.ts +54 -0
- package/dist/types/kritzel-stencil/src/classes/tools/shape-tool.class.d.ts +39 -0
- package/dist/types/kritzel-stencil/src/classes/tools/text-tool.class.d.ts +13 -0
- package/dist/types/kritzel-stencil/src/components/core/kritzel-engine/kritzel-engine.d.ts +111 -0
- package/dist/types/kritzel-stencil/src/components/shared/kritzel-brush-style/kritzel-brush-style.d.ts +11 -0
- package/dist/types/kritzel-stencil/src/components/shared/kritzel-dropdown/kritzel-dropdown.d.ts +46 -0
- package/dist/types/kritzel-stencil/src/components/shared/kritzel-font-family/kritzel-font-family.d.ts +13 -0
- package/dist/types/kritzel-stencil/src/components/shared/kritzel-line-endings/kritzel-line-endings.d.ts +21 -0
- package/dist/types/kritzel-stencil/src/components/shared/kritzel-shape-fill/kritzel-shape-fill.d.ts +10 -0
- package/dist/types/kritzel-stencil/src/components/ui/kritzel-context-menu/kritzel-context-menu.d.ts +21 -0
- package/dist/types/kritzel-stencil/src/configs/default-brush-tool.config.d.ts +2 -0
- package/dist/types/kritzel-stencil/src/configs/default-engine-config.d.ts +2 -0
- package/dist/types/kritzel-stencil/src/configs/default-line-tool.config.d.ts +2 -0
- package/dist/types/kritzel-stencil/src/configs/default-sync.config.d.ts +5 -0
- package/dist/types/kritzel-stencil/src/configs/default-text-tool.config.d.ts +2 -0
- package/dist/types/kritzel-stencil/src/constants/color-palette.constants.d.ts +29 -0
- package/dist/types/kritzel-stencil/src/constants/engine.constants.d.ts +2 -0
- package/dist/types/kritzel-stencil/src/enums/event-button.enum.d.ts +5 -0
- package/dist/types/kritzel-stencil/src/enums/handle-type.enum.d.ts +6 -0
- package/dist/types/kritzel-stencil/src/enums/shape-type.enum.d.ts +5 -0
- package/dist/types/kritzel-stencil/src/helpers/class.helper.d.ts +3 -0
- package/dist/types/kritzel-stencil/src/helpers/color.helper.d.ts +33 -0
- package/dist/types/kritzel-stencil/src/helpers/cursor.helper.d.ts +22 -0
- package/dist/types/kritzel-stencil/src/helpers/devices.helper.d.ts +8 -0
- package/dist/types/kritzel-stencil/src/helpers/event.helper.d.ts +6 -0
- package/dist/types/kritzel-stencil/src/helpers/geometry.helper.d.ts +38 -0
- package/dist/types/kritzel-stencil/src/helpers/keyboard.helper.d.ts +6 -0
- package/dist/types/kritzel-stencil/src/helpers/math.helper.d.ts +3 -0
- package/dist/types/kritzel-stencil/src/helpers/object.helper.d.ts +4 -0
- package/dist/types/kritzel-stencil/src/helpers/theme.helper.d.ts +41 -0
- package/dist/types/kritzel-stencil/src/index.d.ts +42 -0
- package/dist/types/kritzel-stencil/src/interfaces/anchor.interface.d.ts +137 -0
- package/dist/types/kritzel-stencil/src/interfaces/arrow-head.interface.d.ts +27 -0
- package/dist/types/kritzel-stencil/src/interfaces/bounding-box.interface.d.ts +8 -0
- package/dist/types/kritzel-stencil/src/interfaces/clonable.interface.d.ts +3 -0
- package/dist/types/kritzel-stencil/src/interfaces/context-menu-item.interface.d.ts +17 -0
- package/dist/types/kritzel-stencil/src/interfaces/debug-info.interface.d.ts +4 -0
- package/dist/types/kritzel-stencil/src/interfaces/dialog.interface.d.ts +4 -0
- package/dist/types/kritzel-stencil/src/interfaces/displayable-shortcut.interface.d.ts +5 -0
- package/dist/types/kritzel-stencil/src/interfaces/engine-state.interface.d.ts +73 -0
- package/dist/types/kritzel-stencil/src/interfaces/line-options.interface.d.ts +23 -0
- package/dist/types/kritzel-stencil/src/interfaces/master-detail.interface.d.ts +14 -0
- package/dist/types/kritzel-stencil/src/interfaces/menu-item.interface.d.ts +24 -0
- package/dist/types/kritzel-stencil/src/interfaces/object.interface.d.ts +53 -0
- package/dist/types/kritzel-stencil/src/interfaces/path-options.interface.d.ts +11 -0
- package/dist/types/kritzel-stencil/src/interfaces/point.interface.d.ts +4 -0
- package/dist/types/kritzel-stencil/src/interfaces/polygon.interface.d.ts +7 -0
- package/dist/types/kritzel-stencil/src/interfaces/serializable.interface.d.ts +5 -0
- package/dist/types/kritzel-stencil/src/interfaces/settings.interface.d.ts +11 -0
- package/dist/types/kritzel-stencil/src/interfaces/shortcut.interface.d.ts +10 -0
- package/dist/types/kritzel-stencil/src/interfaces/sync-config.interface.d.ts +22 -0
- package/dist/types/kritzel-stencil/src/interfaces/sync-provider.interface.d.ts +29 -0
- package/dist/types/kritzel-stencil/src/interfaces/theme.interface.d.ts +330 -0
- package/dist/types/kritzel-stencil/src/interfaces/tool-config.interface.d.ts +26 -0
- package/dist/types/kritzel-stencil/src/interfaces/tool.interface.d.ts +7 -0
- package/dist/types/kritzel-stencil/src/interfaces/toolbar-control.interface.d.ts +58 -0
- package/dist/types/kritzel-stencil/src/interfaces/undo-state.interface.d.ts +6 -0
- package/dist/types/kritzel-stencil/src/themes/dark-theme.d.ts +5 -0
- package/dist/types/kritzel-stencil/src/themes/light-theme.d.ts +5 -0
- package/dist/types/kritzel-stencil/src/types/shortcut.type.d.ts +1 -0
- package/dist/types/kritzel-stencil/src/types/state.types.d.ts +3 -0
- package/package.json +2 -2
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { KritzelImage } from "../objects/image.class";
|
|
2
|
+
import { KritzelLine } from "../objects/line.class";
|
|
3
|
+
import { KritzelPath } from "../objects/path.class";
|
|
4
|
+
import { KritzelShape } from "../objects/shape.class";
|
|
5
|
+
import { KritzelSelectionGroup } from "../objects/selection-group.class";
|
|
6
|
+
import { KritzelGroup } from "../objects/group.class";
|
|
7
|
+
import { KritzelText } from "../objects/text.class";
|
|
8
|
+
import { KritzelBrushTool } from "../tools/brush-tool.class";
|
|
9
|
+
import { KritzelEraserTool } from "../tools/eraser-tool.class";
|
|
10
|
+
import { KritzelImageTool } from "../tools/image-tool.class";
|
|
11
|
+
import { KritzelLineTool } from "../tools/line-tool.class";
|
|
12
|
+
import { KritzelSelectionTool } from "../tools/selection-tool.class";
|
|
13
|
+
import { KritzelTextTool } from "../tools/text-tool.class";
|
|
14
|
+
import { KritzelShapeTool } from "../tools/shape-tool.class";
|
|
15
|
+
import { KritzelCustomElement } from "../objects/custom-element.class";
|
|
16
|
+
import { KritzelWorkspace } from "../..";
|
|
17
|
+
export class KritzelReviver {
|
|
18
|
+
constructor(core) {
|
|
19
|
+
this._core = core;
|
|
20
|
+
}
|
|
21
|
+
revive(obj) {
|
|
22
|
+
if (obj && typeof obj === 'object') {
|
|
23
|
+
if (obj.__class__) {
|
|
24
|
+
let revivedObj;
|
|
25
|
+
switch (obj.__class__) {
|
|
26
|
+
case 'KritzelPath':
|
|
27
|
+
revivedObj = KritzelPath.create(this._core).deserialize(obj);
|
|
28
|
+
break;
|
|
29
|
+
case 'KritzelLine':
|
|
30
|
+
revivedObj = KritzelLine.create(this._core).deserialize(obj);
|
|
31
|
+
break;
|
|
32
|
+
case 'KritzelText':
|
|
33
|
+
revivedObj = KritzelText.create(this._core, obj.fontSize, obj.fontFamily).deserialize(obj);
|
|
34
|
+
break;
|
|
35
|
+
case 'KritzelShape':
|
|
36
|
+
revivedObj = KritzelShape.create(this._core, {
|
|
37
|
+
shapeType: obj.shapeType,
|
|
38
|
+
fillColor: obj.fillColor,
|
|
39
|
+
strokeColor: obj.strokeColor,
|
|
40
|
+
strokeWidth: obj.strokeWidth,
|
|
41
|
+
fontSize: obj.fontSize,
|
|
42
|
+
fontFamily: obj.fontFamily,
|
|
43
|
+
fontColor: obj.fontColor,
|
|
44
|
+
}).deserialize(obj);
|
|
45
|
+
break;
|
|
46
|
+
case 'KritzelImage':
|
|
47
|
+
revivedObj = KritzelImage.create(this._core).deserialize(obj);
|
|
48
|
+
break;
|
|
49
|
+
case 'KritzelCustomElement':
|
|
50
|
+
revivedObj = KritzelCustomElement.create(this._core).deserialize(obj);
|
|
51
|
+
break;
|
|
52
|
+
case 'KritzelSelectionGroup':
|
|
53
|
+
revivedObj = KritzelSelectionGroup.create(this._core).deserialize(obj);
|
|
54
|
+
break;
|
|
55
|
+
case 'KritzelGroup':
|
|
56
|
+
revivedObj = KritzelGroup.create(this._core).deserialize(obj);
|
|
57
|
+
break;
|
|
58
|
+
case 'KritzelWorkspace':
|
|
59
|
+
revivedObj = KritzelWorkspace.create(this._core, obj).deserialize(obj);
|
|
60
|
+
break;
|
|
61
|
+
case 'KritzelBrushTool':
|
|
62
|
+
revivedObj = new KritzelBrushTool(this._core);
|
|
63
|
+
break;
|
|
64
|
+
case 'KritzelEraserTool':
|
|
65
|
+
revivedObj = new KritzelEraserTool(this._core);
|
|
66
|
+
break;
|
|
67
|
+
case 'KritzelImageTool':
|
|
68
|
+
revivedObj = new KritzelImageTool(this._core);
|
|
69
|
+
break;
|
|
70
|
+
case 'KritzelSelectionTool':
|
|
71
|
+
revivedObj = new KritzelSelectionTool(this._core);
|
|
72
|
+
break;
|
|
73
|
+
case 'KritzelTextTool':
|
|
74
|
+
revivedObj = new KritzelTextTool(this._core);
|
|
75
|
+
break;
|
|
76
|
+
case 'KritzelLineTool':
|
|
77
|
+
revivedObj = new KritzelLineTool(this._core);
|
|
78
|
+
break;
|
|
79
|
+
case 'KritzelShapeTool':
|
|
80
|
+
revivedObj = new KritzelShapeTool(this._core);
|
|
81
|
+
break;
|
|
82
|
+
default:
|
|
83
|
+
revivedObj = obj;
|
|
84
|
+
}
|
|
85
|
+
return revivedObj;
|
|
86
|
+
}
|
|
87
|
+
const newObj = Array.isArray(obj) ? [] : {};
|
|
88
|
+
for (const key in obj) {
|
|
89
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
90
|
+
newObj[key] = this.revive(obj[key]);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return newObj;
|
|
94
|
+
}
|
|
95
|
+
return obj;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { KritzelSelectionGroup } from '../objects/selection-group.class';
|
|
2
|
+
import { KritzelSelectionBox } from '../objects/selection-box.class';
|
|
3
|
+
import { KritzelText } from '../objects/text.class';
|
|
4
|
+
import { KritzelShape } from '../objects/shape.class';
|
|
5
|
+
import { KritzelPath } from '../objects/path.class';
|
|
6
|
+
import { KritzelLine } from '../objects/line.class';
|
|
7
|
+
import { KritzelObjectMap } from '../structures/object-map.structure';
|
|
8
|
+
export class KritzelStore {
|
|
9
|
+
get state() {
|
|
10
|
+
return this._state;
|
|
11
|
+
}
|
|
12
|
+
get currentZIndex() {
|
|
13
|
+
return Math.max(0, ...this._state.objects.filter(o => !(o instanceof KritzelSelectionGroup) && !(o instanceof KritzelSelectionBox)).map(o => o.zIndex)) + 1;
|
|
14
|
+
}
|
|
15
|
+
get totalObjectCount() {
|
|
16
|
+
return this._state.objects.totalCount;
|
|
17
|
+
}
|
|
18
|
+
get allObjects() {
|
|
19
|
+
return this._state.objects.allObjects();
|
|
20
|
+
}
|
|
21
|
+
get objectsInViewport() {
|
|
22
|
+
const viewportBounds = {
|
|
23
|
+
x: -this._state.translateX / this._state.scale,
|
|
24
|
+
y: -this._state.translateY / this._state.scale,
|
|
25
|
+
z: this._state.scale,
|
|
26
|
+
width: this._state.viewportWidth / this._state.scale,
|
|
27
|
+
height: this._state.viewportHeight / this._state.scale,
|
|
28
|
+
depth: 100,
|
|
29
|
+
};
|
|
30
|
+
return this._state.objects
|
|
31
|
+
.query(viewportBounds)
|
|
32
|
+
.filter(obj => !(obj instanceof KritzelSelectionGroup) && !(obj instanceof KritzelSelectionBox))
|
|
33
|
+
.sort((a, b) => a.zIndex - b.zIndex);
|
|
34
|
+
}
|
|
35
|
+
get objectsToRender() {
|
|
36
|
+
const objects = this.objectsInViewport;
|
|
37
|
+
// Add selection objects explicitly since they are filtered out of objectsInViewport
|
|
38
|
+
const selectionGroup = this.selectionGroup;
|
|
39
|
+
if (selectionGroup) {
|
|
40
|
+
objects.push(selectionGroup);
|
|
41
|
+
}
|
|
42
|
+
const selectionBox = this.selectionBox;
|
|
43
|
+
if (selectionBox) {
|
|
44
|
+
objects.push(selectionBox);
|
|
45
|
+
}
|
|
46
|
+
// Sort combined list by zIndex to ensure correct rendering order
|
|
47
|
+
// This is fast since objectsInViewport is already small compared to allObjects
|
|
48
|
+
return objects.sort((a, b) => a.zIndex - b.zIndex);
|
|
49
|
+
}
|
|
50
|
+
get allNonSelectionObjects() {
|
|
51
|
+
return this._state.objects.allObjects().filter(o => !(o instanceof KritzelSelectionGroup) && !(o instanceof KritzelSelectionBox));
|
|
52
|
+
}
|
|
53
|
+
get selectedObjects() {
|
|
54
|
+
return this.allObjects.filter(o => !(o instanceof KritzelSelectionGroup)).filter(o => o.isSelected);
|
|
55
|
+
}
|
|
56
|
+
get selectionBox() {
|
|
57
|
+
if (this._selectionBoxCacheValid) {
|
|
58
|
+
return this._cachedSelectionBox;
|
|
59
|
+
}
|
|
60
|
+
const selectionBoxes = this._state.objects.filter(o => o instanceof KritzelSelectionBox);
|
|
61
|
+
this._cachedSelectionBox = selectionBoxes.length > 0 ? selectionBoxes[0] : null;
|
|
62
|
+
this._selectionBoxCacheValid = true;
|
|
63
|
+
return this._cachedSelectionBox;
|
|
64
|
+
}
|
|
65
|
+
get selectionGroup() {
|
|
66
|
+
if (this._selectionGroupCacheValid) {
|
|
67
|
+
return this._cachedSelectionGroup;
|
|
68
|
+
}
|
|
69
|
+
const selectionGroups = this._state.objects.filter(o => o instanceof KritzelSelectionGroup);
|
|
70
|
+
this._cachedSelectionGroup = selectionGroups.length > 0 ? selectionGroups[0] : null;
|
|
71
|
+
this._selectionGroupCacheValid = true;
|
|
72
|
+
return this._cachedSelectionGroup;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Invalidate cached selection objects. Call this when selection objects are inserted or removed.
|
|
76
|
+
*/
|
|
77
|
+
invalidateSelectionCache() {
|
|
78
|
+
this._selectionBoxCacheValid = false;
|
|
79
|
+
this._selectionGroupCacheValid = false;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Directly set the cached selection box reference for O(1) access.
|
|
83
|
+
* Use this when creating the selection box to avoid a filter lookup.
|
|
84
|
+
*/
|
|
85
|
+
setSelectionBox(box) {
|
|
86
|
+
this._cachedSelectionBox = box;
|
|
87
|
+
this._selectionBoxCacheValid = true;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Directly set the cached selection group reference for O(1) access.
|
|
91
|
+
* Use this when creating the selection group to avoid a filter lookup.
|
|
92
|
+
*/
|
|
93
|
+
setSelectionGroup(group) {
|
|
94
|
+
this._cachedSelectionGroup = group;
|
|
95
|
+
this._selectionGroupCacheValid = true;
|
|
96
|
+
}
|
|
97
|
+
get activeText() {
|
|
98
|
+
const activeTexts = this._state.objects.filter(o => o instanceof KritzelText && o.isEditing);
|
|
99
|
+
return activeTexts.length > 0 ? activeTexts[0] : null;
|
|
100
|
+
}
|
|
101
|
+
get activeShape() {
|
|
102
|
+
const activeShapes = this._state.objects.filter(o => o instanceof KritzelShape && o.isEditing);
|
|
103
|
+
return activeShapes.length > 0 ? activeShapes[0] : null;
|
|
104
|
+
}
|
|
105
|
+
get currentPath() {
|
|
106
|
+
const drawingPaths = this._state.objects.filter(o => o instanceof KritzelPath && o.isCompleted === false);
|
|
107
|
+
return drawingPaths.length > 0 ? drawingPaths[0] : null;
|
|
108
|
+
}
|
|
109
|
+
get currentLine() {
|
|
110
|
+
const drawingLines = this._state.objects.filter(o => o instanceof KritzelLine && o.isCompleted === false);
|
|
111
|
+
return drawingLines.length > 0 ? drawingLines[0] : null;
|
|
112
|
+
}
|
|
113
|
+
get offsetX() {
|
|
114
|
+
return this._state.host.getBoundingClientRect().left;
|
|
115
|
+
}
|
|
116
|
+
get offsetY() {
|
|
117
|
+
return this._state.host.getBoundingClientRect().top;
|
|
118
|
+
}
|
|
119
|
+
get isDisabled() {
|
|
120
|
+
return this._state.isEnabled === false || this._state.isReady === false || this._state.activeWorkspace === null;
|
|
121
|
+
}
|
|
122
|
+
get isPointerDown() {
|
|
123
|
+
return this._state.pointers.size > 0;
|
|
124
|
+
}
|
|
125
|
+
constructor(state) {
|
|
126
|
+
this._listeners = new Map();
|
|
127
|
+
// Cached references for O(1) access to frequently accessed special objects
|
|
128
|
+
this._cachedSelectionBox = null;
|
|
129
|
+
this._cachedSelectionGroup = null;
|
|
130
|
+
this._selectionBoxCacheValid = false;
|
|
131
|
+
this._selectionGroupCacheValid = false;
|
|
132
|
+
this._state = state;
|
|
133
|
+
this._state.objects = new KritzelObjectMap();
|
|
134
|
+
}
|
|
135
|
+
onStateChange(property, listener) {
|
|
136
|
+
if (!this._listeners.has(property)) {
|
|
137
|
+
this._listeners.set(property, new Set());
|
|
138
|
+
}
|
|
139
|
+
this._listeners.get(property).add(listener);
|
|
140
|
+
}
|
|
141
|
+
getState(property) {
|
|
142
|
+
return this._state[property];
|
|
143
|
+
}
|
|
144
|
+
setState(property, value) {
|
|
145
|
+
const oldValue = this._state[property];
|
|
146
|
+
if (oldValue !== value) {
|
|
147
|
+
this._state[property] = value;
|
|
148
|
+
if (this._listeners.has(property)) {
|
|
149
|
+
this._listeners.get(property).forEach(listener => listener(value, oldValue, String(property)));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { KritzelMouseButton } from '../../enums/event-button.enum';
|
|
2
|
+
import { KritzelSelectionBox } from '../objects/selection-box.class';
|
|
3
|
+
import { debounce } from 'lodash';
|
|
4
|
+
export class KritzelViewport {
|
|
5
|
+
constructor(core, host) {
|
|
6
|
+
this._animationFrameId = null;
|
|
7
|
+
this.initialTouchDistance = 0;
|
|
8
|
+
this.startX = 0;
|
|
9
|
+
this.startY = 0;
|
|
10
|
+
this._core = core;
|
|
11
|
+
this._core.store.state.host = host;
|
|
12
|
+
this._core.store.state.viewportWidth = host.clientWidth;
|
|
13
|
+
this._core.store.state.viewportHeight = host.clientHeight;
|
|
14
|
+
this._core.store.state.startX = 0;
|
|
15
|
+
this._core.store.state.startY = 0;
|
|
16
|
+
this._core.store.state.translateX = 0;
|
|
17
|
+
this._core.store.state.translateY = 0;
|
|
18
|
+
this._debounceUpdate = debounce(() => {
|
|
19
|
+
this._core.updateWorkspaceViewport(this._core.store.state.translateX, this._core.store.state.translateY, this._core.store.state.scale);
|
|
20
|
+
}, 300);
|
|
21
|
+
this._debounceEndScaling = debounce(() => {
|
|
22
|
+
this._core.store.state.isScaling = false;
|
|
23
|
+
this._core.rerender();
|
|
24
|
+
}, 100);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Cancels any pending debounced viewport updates.
|
|
28
|
+
* Should be called before switching workspaces to prevent the old workspace's
|
|
29
|
+
* viewport state from being saved to the new workspace.
|
|
30
|
+
*/
|
|
31
|
+
cancelPendingUpdates() {
|
|
32
|
+
this._debounceUpdate.cancel();
|
|
33
|
+
}
|
|
34
|
+
handleResize() {
|
|
35
|
+
this._core.store.state.viewportWidth = this._core.store.state.host.clientWidth;
|
|
36
|
+
this._core.store.state.viewportHeight = this._core.store.state.host.clientHeight;
|
|
37
|
+
this._core.store.state.hasViewportChanged = true;
|
|
38
|
+
this._core.rerender();
|
|
39
|
+
}
|
|
40
|
+
handlePointerDown(event) {
|
|
41
|
+
// Cancel any ongoing viewport animation when user starts interacting
|
|
42
|
+
this.cancelViewportAnimation();
|
|
43
|
+
if (event.pointerType === 'mouse') {
|
|
44
|
+
const adjustedClientX = event.clientX - this._core.store.offsetX;
|
|
45
|
+
const adjustedClientY = event.clientY - this._core.store.offsetY;
|
|
46
|
+
if (event.button === KritzelMouseButton.Right) {
|
|
47
|
+
this._core.store.state.isPanning = true;
|
|
48
|
+
this._core.store.state.startX = adjustedClientX;
|
|
49
|
+
this._core.store.state.startY = adjustedClientY;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (event.pointerType === 'touch' || event.pointerType === 'pen') {
|
|
53
|
+
const activePointers = Array.from(this._core.store.state.pointers.values());
|
|
54
|
+
if (activePointers.length === 2) {
|
|
55
|
+
const currentPath = this._core.store.currentPath;
|
|
56
|
+
if (currentPath) {
|
|
57
|
+
this._core.store.state.objects.remove(obj => obj.id === currentPath.id);
|
|
58
|
+
}
|
|
59
|
+
// Clear selection box when scaling starts to prevent it from remaining on screen
|
|
60
|
+
if (this._core.store.state.isSelecting) {
|
|
61
|
+
this._core.store.state.isSelecting = false;
|
|
62
|
+
this._core.store.state.objects.remove(obj => obj instanceof KritzelSelectionBox);
|
|
63
|
+
this._core.store.setSelectionBox(null);
|
|
64
|
+
// Clear selection preview state from objects - use selectedObjects for efficiency
|
|
65
|
+
this._core.store.selectedObjects.forEach(object => {
|
|
66
|
+
object.isSelected = false;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
this._core.store.state.isScaling = true;
|
|
70
|
+
const firstTouchX = activePointers[0].clientX - this._core.store.offsetX;
|
|
71
|
+
const firstTouchY = activePointers[0].clientY - this._core.store.offsetY;
|
|
72
|
+
const secondTouchX = activePointers[1].clientX - this._core.store.offsetX;
|
|
73
|
+
const secondTouchY = activePointers[1].clientY - this._core.store.offsetY;
|
|
74
|
+
this.initialTouchDistance = Math.sqrt(Math.pow(firstTouchX - secondTouchX, 2) + Math.pow(firstTouchY - secondTouchY, 2));
|
|
75
|
+
this.startX = (firstTouchX + secondTouchX) / 2;
|
|
76
|
+
this.startY = (firstTouchY + secondTouchY) / 2;
|
|
77
|
+
this._core.rerender();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
handlePointerMove(event) {
|
|
82
|
+
if (event.pointerType === 'mouse') {
|
|
83
|
+
const hostRect = this._core.store.state.host.getBoundingClientRect();
|
|
84
|
+
const xRelativeToHost = event.clientX - hostRect.left;
|
|
85
|
+
const yRelativeToHost = event.clientY - hostRect.top;
|
|
86
|
+
this._core.store.state.pointerX = (xRelativeToHost - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
87
|
+
this._core.store.state.pointerY = (yRelativeToHost - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
88
|
+
if (this._core.store.state.isPanning) {
|
|
89
|
+
const dx = xRelativeToHost - this._core.store.state.startX;
|
|
90
|
+
const dy = yRelativeToHost - this._core.store.state.startY;
|
|
91
|
+
this._core.store.state.translateX += dx;
|
|
92
|
+
this._core.store.state.translateY += dy;
|
|
93
|
+
this._core.store.state.startX = xRelativeToHost;
|
|
94
|
+
this._core.store.state.startY = yRelativeToHost;
|
|
95
|
+
this._core.store.state.hasViewportChanged = true;
|
|
96
|
+
this._core.store.state.skipContextMenu = true;
|
|
97
|
+
this._core.rerender();
|
|
98
|
+
this._debounceUpdate();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (event.pointerType === 'touch' || event.pointerType === 'pen') {
|
|
102
|
+
const hostRect = this._core.store.state.host.getBoundingClientRect();
|
|
103
|
+
const xRelativeToHost = event.clientX - hostRect.left;
|
|
104
|
+
const yRelativeToHost = event.clientY - hostRect.top;
|
|
105
|
+
this._core.store.state.pointerX = (xRelativeToHost - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
106
|
+
this._core.store.state.pointerY = (yRelativeToHost - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
107
|
+
const activePointers = Array.from(this._core.store.state.pointers.values());
|
|
108
|
+
if (activePointers.length === 2) {
|
|
109
|
+
const firstTouchX = activePointers[0].clientX - this._core.store.offsetX;
|
|
110
|
+
const firstTouchY = activePointers[0].clientY - this._core.store.offsetY;
|
|
111
|
+
const secondTouchX = activePointers[1].clientX - this._core.store.offsetX;
|
|
112
|
+
const secondTouchY = activePointers[1].clientY - this._core.store.offsetY;
|
|
113
|
+
const currentTouchDistance = Math.sqrt(Math.pow(firstTouchX - secondTouchX, 2) + Math.pow(firstTouchY - secondTouchY, 2));
|
|
114
|
+
const midpointX = (firstTouchX + secondTouchX) / 2;
|
|
115
|
+
const midpointY = (firstTouchY + secondTouchY) / 2;
|
|
116
|
+
const scaleRatio = currentTouchDistance / this.initialTouchDistance;
|
|
117
|
+
const newScale = this._core.store.state.scale * scaleRatio;
|
|
118
|
+
if (newScale > this._core.store.state.scaleMax || newScale < this._core.store.state.scaleMin) {
|
|
119
|
+
this._core.store.state.translateX = this._core.store.state.translateX + midpointX - this.startX;
|
|
120
|
+
this._core.store.state.translateY = this._core.store.state.translateY + midpointY - this.startY;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
const translateXAdjustment = (midpointX - this._core.store.state.translateX) * (scaleRatio - 1);
|
|
124
|
+
const translateYAdjustment = (midpointY - this._core.store.state.translateY) * (scaleRatio - 1);
|
|
125
|
+
this._core.store.state.translateX = this._core.store.state.translateX + midpointX - this.startX - translateXAdjustment;
|
|
126
|
+
this._core.store.state.translateY = this._core.store.state.translateY + midpointY - this.startY - translateYAdjustment;
|
|
127
|
+
this._core.store.state.scale = newScale;
|
|
128
|
+
this.initialTouchDistance = currentTouchDistance;
|
|
129
|
+
}
|
|
130
|
+
this.startX = midpointX;
|
|
131
|
+
this.startY = midpointY;
|
|
132
|
+
this._core.store.state.hasViewportChanged = true;
|
|
133
|
+
this._core.rerender();
|
|
134
|
+
this._debounceUpdate();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
handlePointerUp(event) {
|
|
139
|
+
if (event.pointerType === 'mouse') {
|
|
140
|
+
if (this._core.store.state.isPanning) {
|
|
141
|
+
this._core.store.state.isPanning = false;
|
|
142
|
+
this._core.rerender();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (event.pointerType === 'touch' || event.pointerType === 'pen') {
|
|
146
|
+
if (this._core.store.state.pointers.size === 0) {
|
|
147
|
+
this._debounceEndScaling();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
handleWheel(event) {
|
|
152
|
+
// Cancel any ongoing viewport animation when user scrolls
|
|
153
|
+
this.cancelViewportAnimation();
|
|
154
|
+
if (event.ctrlKey) {
|
|
155
|
+
this.handleZoom(event);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
this.handlePan(event);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
centerInViewport(object) {
|
|
162
|
+
const { scale, viewportWidth, viewportHeight } = this._core.store.state;
|
|
163
|
+
const { x, y, width, height } = object.rotatedBoundingBox;
|
|
164
|
+
const objectCenterX = x + width / 2;
|
|
165
|
+
const objectCenterY = y + height / 2;
|
|
166
|
+
this._core.store.state.translateX = viewportWidth / 2 - objectCenterX * scale;
|
|
167
|
+
this._core.store.state.translateY = viewportHeight / 2 - objectCenterY * scale;
|
|
168
|
+
this._core.store.state.hasViewportChanged = true;
|
|
169
|
+
this._core.rerender();
|
|
170
|
+
this._debounceUpdate();
|
|
171
|
+
}
|
|
172
|
+
centerFitInViewport(object) {
|
|
173
|
+
const scaleX = this._core.store.state.viewportWidth / (object.rotatedBoundingBox.width * 1.1);
|
|
174
|
+
const scaleY = this._core.store.state.viewportHeight / (object.rotatedBoundingBox.height * 1.1);
|
|
175
|
+
const newScale = Math.min(scaleX, scaleY, this._core.store.state.scaleMax);
|
|
176
|
+
const clampedScale = Math.max(newScale, this._core.store.state.scaleMin);
|
|
177
|
+
this._core.store.state.scale = clampedScale;
|
|
178
|
+
this.centerInViewport(object);
|
|
179
|
+
}
|
|
180
|
+
handleZoom(event) {
|
|
181
|
+
this._core.store.state.isScaling = true;
|
|
182
|
+
const rect = this._core.store.state.host.getBoundingClientRect();
|
|
183
|
+
const xRelativeToHost = event.clientX - rect.left;
|
|
184
|
+
const yRelativeToHost = event.clientY - rect.top;
|
|
185
|
+
this._core.store.state.pointerX = (xRelativeToHost - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
186
|
+
this._core.store.state.pointerY = (yRelativeToHost - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
187
|
+
const rawScaleFactor = 1 + (event.deltaY * -0.012);
|
|
188
|
+
const scaleFactor = Math.max(0.8, Math.min(1.2, rawScaleFactor));
|
|
189
|
+
const newScale = Math.min(this._core.store.state.scaleMax, Math.max(this._core.store.state.scaleMin, this._core.store.state.scale * scaleFactor));
|
|
190
|
+
const scaleRatio = newScale / this._core.store.state.scale;
|
|
191
|
+
const translateXAdjustment = (xRelativeToHost - this._core.store.state.translateX) * (scaleRatio - 1);
|
|
192
|
+
const translateYAdjustment = (yRelativeToHost - this._core.store.state.translateY) * (scaleRatio - 1);
|
|
193
|
+
this._core.store.state.scale = newScale;
|
|
194
|
+
this._core.store.state.translateX = this._core.store.state.translateX - translateXAdjustment;
|
|
195
|
+
this._core.store.state.translateY = this._core.store.state.translateY - translateYAdjustment;
|
|
196
|
+
// Apply horizontal pan from trackpad pinch+drag gestures
|
|
197
|
+
if (event.deltaX !== 0) {
|
|
198
|
+
this._core.store.state.translateX -= event.deltaX * 0.8;
|
|
199
|
+
}
|
|
200
|
+
this._core.store.state.hasViewportChanged = true;
|
|
201
|
+
this._core.rerender();
|
|
202
|
+
this._debounceUpdate();
|
|
203
|
+
this._debounceEndScaling();
|
|
204
|
+
}
|
|
205
|
+
handlePan(event) {
|
|
206
|
+
const panSpeed = 0.8;
|
|
207
|
+
this._core.store.state.translateX = this._core.store.state.translateX - event.deltaX * panSpeed;
|
|
208
|
+
this._core.store.state.translateY = this._core.store.state.translateY - event.deltaY * panSpeed;
|
|
209
|
+
this._core.store.state.hasViewportChanged = true;
|
|
210
|
+
this._core.rerender();
|
|
211
|
+
this._debounceUpdate();
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Cancels any ongoing viewport animation.
|
|
215
|
+
* Called when user interacts with the canvas during animation.
|
|
216
|
+
*/
|
|
217
|
+
cancelViewportAnimation() {
|
|
218
|
+
if (this._animationFrameId !== null) {
|
|
219
|
+
cancelAnimationFrame(this._animationFrameId);
|
|
220
|
+
this._animationFrameId = null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Animates the viewport to the specified position and scale.
|
|
225
|
+
* @param targetTranslateX - Target X translation
|
|
226
|
+
* @param targetTranslateY - Target Y translation
|
|
227
|
+
* @param targetScale - Target scale
|
|
228
|
+
* @param duration - Animation duration in milliseconds (default 150)
|
|
229
|
+
*/
|
|
230
|
+
animateViewportTo(targetTranslateX, targetTranslateY, targetScale, duration = 150) {
|
|
231
|
+
// Cancel any existing animation
|
|
232
|
+
this.cancelViewportAnimation();
|
|
233
|
+
const startTranslateX = this._core.store.state.translateX;
|
|
234
|
+
const startTranslateY = this._core.store.state.translateY;
|
|
235
|
+
const startScale = this._core.store.state.scale;
|
|
236
|
+
const startTime = performance.now();
|
|
237
|
+
const animate = (currentTime) => {
|
|
238
|
+
const elapsed = currentTime - startTime;
|
|
239
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
240
|
+
// easeOutCubic easing function
|
|
241
|
+
const eased = 1 - Math.pow(1 - progress, 3);
|
|
242
|
+
this._core.store.state.translateX = startTranslateX + (targetTranslateX - startTranslateX) * eased;
|
|
243
|
+
this._core.store.state.translateY = startTranslateY + (targetTranslateY - startTranslateY) * eased;
|
|
244
|
+
this._core.store.state.scale = startScale + (targetScale - startScale) * eased;
|
|
245
|
+
this._core.store.state.hasViewportChanged = true;
|
|
246
|
+
this._core.rerender();
|
|
247
|
+
if (progress < 1) {
|
|
248
|
+
this._animationFrameId = requestAnimationFrame(animate);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
this._animationFrameId = null;
|
|
252
|
+
this._debounceUpdate();
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
this._animationFrameId = requestAnimationFrame(animate);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Centers and fits the nearest content objects in the viewport.
|
|
259
|
+
* Finds up to maxObjects closest objects to the current viewport center
|
|
260
|
+
* and animates the viewport to show them.
|
|
261
|
+
* @param maxObjects - Maximum number of objects to consider (default 20)
|
|
262
|
+
* @returns true if content was found and viewport is animating, false if no content exists
|
|
263
|
+
*/
|
|
264
|
+
centerFitNearestContent(maxObjects = 20) {
|
|
265
|
+
const allObjects = this._core.store.allNonSelectionObjects;
|
|
266
|
+
if (allObjects.length === 0) {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
// Calculate current viewport center in canvas coordinates
|
|
270
|
+
const viewportCenterX = -this._core.store.state.translateX / this._core.store.state.scale + this._core.store.state.viewportWidth / 2 / this._core.store.state.scale;
|
|
271
|
+
const viewportCenterY = -this._core.store.state.translateY / this._core.store.state.scale + this._core.store.state.viewportHeight / 2 / this._core.store.state.scale;
|
|
272
|
+
// Sort objects by distance from viewport center (using object center)
|
|
273
|
+
const objectsWithDistance = allObjects.map(obj => {
|
|
274
|
+
const bounds = obj.rotatedBoundingBox;
|
|
275
|
+
const objectCenterX = bounds.x + bounds.width / 2;
|
|
276
|
+
const objectCenterY = bounds.y + bounds.height / 2;
|
|
277
|
+
const distance = Math.sqrt(Math.pow(objectCenterX - viewportCenterX, 2) + Math.pow(objectCenterY - viewportCenterY, 2));
|
|
278
|
+
return { obj, distance };
|
|
279
|
+
});
|
|
280
|
+
objectsWithDistance.sort((a, b) => a.distance - b.distance);
|
|
281
|
+
// Take up to maxObjects closest objects
|
|
282
|
+
const nearestObjects = objectsWithDistance.slice(0, maxObjects).map(item => item.obj);
|
|
283
|
+
// Calculate combined bounds of nearest objects
|
|
284
|
+
let minX = Infinity;
|
|
285
|
+
let minY = Infinity;
|
|
286
|
+
let maxX = -Infinity;
|
|
287
|
+
let maxY = -Infinity;
|
|
288
|
+
for (const obj of nearestObjects) {
|
|
289
|
+
const bounds = obj.rotatedBoundingBox;
|
|
290
|
+
minX = Math.min(minX, bounds.x);
|
|
291
|
+
minY = Math.min(minY, bounds.y);
|
|
292
|
+
maxX = Math.max(maxX, bounds.x + bounds.width);
|
|
293
|
+
maxY = Math.max(maxY, bounds.y + bounds.height);
|
|
294
|
+
}
|
|
295
|
+
const contentWidth = maxX - minX;
|
|
296
|
+
const contentHeight = maxY - minY;
|
|
297
|
+
const contentCenterX = minX + contentWidth / 2;
|
|
298
|
+
const contentCenterY = minY + contentHeight / 2;
|
|
299
|
+
// Calculate scale to fit with 10% padding
|
|
300
|
+
const scaleX = this._core.store.state.viewportWidth / (contentWidth * 1.1);
|
|
301
|
+
const scaleY = this._core.store.state.viewportHeight / (contentHeight * 1.1);
|
|
302
|
+
const newScale = Math.min(scaleX, scaleY, this._core.store.state.scaleMax);
|
|
303
|
+
const clampedScale = Math.max(newScale, this._core.store.state.scaleMin);
|
|
304
|
+
// Calculate target translation to center content
|
|
305
|
+
const targetTranslateX = this._core.store.state.viewportWidth / 2 - contentCenterX * clampedScale;
|
|
306
|
+
const targetTranslateY = this._core.store.state.viewportHeight / 2 - contentCenterY * clampedScale;
|
|
307
|
+
// Animate to the target position
|
|
308
|
+
this.animateViewportTo(targetTranslateX, targetTranslateY, clampedScale);
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export class KritzelWorkspace {
|
|
2
|
+
constructor(id, name, viewport = { translateX: 0, translateY: 0, scale: 1 }) {
|
|
3
|
+
this.__class__ = 'KritzelWorkspace';
|
|
4
|
+
this.id = id;
|
|
5
|
+
this.name = name;
|
|
6
|
+
this.createdAt = new Date();
|
|
7
|
+
this.updatedAt = new Date();
|
|
8
|
+
this.viewport = viewport;
|
|
9
|
+
}
|
|
10
|
+
static create(core, obj) {
|
|
11
|
+
const workspace = new KritzelWorkspace(obj.id, obj.name, obj.viewport);
|
|
12
|
+
workspace._core = core;
|
|
13
|
+
return workspace;
|
|
14
|
+
}
|
|
15
|
+
addObject(obj) {
|
|
16
|
+
this._core.engine.addObject(obj);
|
|
17
|
+
}
|
|
18
|
+
serialize() {
|
|
19
|
+
return {
|
|
20
|
+
__class__: this.__class__,
|
|
21
|
+
id: this.id,
|
|
22
|
+
name: this.name,
|
|
23
|
+
createdAt: this.createdAt.toISOString(),
|
|
24
|
+
updatedAt: this.updatedAt.toISOString(),
|
|
25
|
+
viewport: this.viewport,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
deserialize(object) {
|
|
29
|
+
Object.assign(this, object);
|
|
30
|
+
this.createdAt = new Date(object.createdAt);
|
|
31
|
+
this.updatedAt = new Date(object.updatedAt);
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { KritzelSelectionGroup } from '../objects/selection-group.class';
|
|
2
|
+
import { KritzelBaseHandler } from './base.handler';
|
|
3
|
+
import { KritzelSelectionTool } from '../tools/selection-tool.class';
|
|
4
|
+
import { KritzelSelectionBox } from '../objects/selection-box.class';
|
|
5
|
+
export class KritzelContextMenuHandler extends KritzelBaseHandler {
|
|
6
|
+
constructor(core, globalContextMenuItems, objectContextMenuItems) {
|
|
7
|
+
super(core);
|
|
8
|
+
this.globalContextMenuItems = [];
|
|
9
|
+
this.objectContextMenuItems = [];
|
|
10
|
+
this.globalContextMenuItems = globalContextMenuItems;
|
|
11
|
+
this.objectContextMenuItems = objectContextMenuItems;
|
|
12
|
+
}
|
|
13
|
+
handleContextMenu(event) {
|
|
14
|
+
var _a;
|
|
15
|
+
if (!(this._core.store.state.activeTool instanceof KritzelSelectionTool)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (this._core.store.state.skipContextMenu) {
|
|
19
|
+
this._core.store.state.skipContextMenu = false;
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const selectionTool = this._core.store.state.activeTool;
|
|
23
|
+
(_a = selectionTool === null || selectionTool === void 0 ? void 0 : selectionTool.moveHandler) === null || _a === void 0 ? void 0 : _a.cancelPendingDrag();
|
|
24
|
+
if (this._core.store.selectionBox) {
|
|
25
|
+
this._core.store.state.objects.remove(object => object instanceof KritzelSelectionBox);
|
|
26
|
+
this._core.store.setSelectionBox(null);
|
|
27
|
+
this._core.store.state.isSelecting = false;
|
|
28
|
+
}
|
|
29
|
+
const selectedObject = this._core.getObjectFromPointerEvent(event, '.object');
|
|
30
|
+
if (selectedObject && !(selectedObject instanceof KritzelSelectionGroup) && !(selectedObject instanceof KritzelSelectionBox)) {
|
|
31
|
+
const selectionGroup = KritzelSelectionGroup.create(this._core);
|
|
32
|
+
selectionGroup.addOrRemove(selectedObject);
|
|
33
|
+
selectionGroup.isSelected = true;
|
|
34
|
+
selectionGroup.rotation = selectedObject.rotation;
|
|
35
|
+
this._core.store.state.isSelecting = false;
|
|
36
|
+
const currentSelectionGroup = this._core.store.selectionGroup;
|
|
37
|
+
if (currentSelectionGroup) {
|
|
38
|
+
this._core.removeSelectionGroup();
|
|
39
|
+
}
|
|
40
|
+
this._core.addSelectionGroup(selectionGroup);
|
|
41
|
+
this._core.rerender();
|
|
42
|
+
}
|
|
43
|
+
this._core.store.state.contextMenuItems = this._core.store.selectionGroup ? this.objectContextMenuItems : this.globalContextMenuItems;
|
|
44
|
+
let x = event.clientX - this._core.store.offsetX;
|
|
45
|
+
let y = event.clientY - this._core.store.offsetY;
|
|
46
|
+
const menuWidthEstimate = 150;
|
|
47
|
+
const menuHeightEstimate = 200;
|
|
48
|
+
const margin = 10;
|
|
49
|
+
if (x + menuWidthEstimate > window.innerWidth - margin) {
|
|
50
|
+
x = window.innerWidth - menuWidthEstimate - margin;
|
|
51
|
+
}
|
|
52
|
+
if (y + menuHeightEstimate > window.innerHeight - margin) {
|
|
53
|
+
y = window.innerHeight - menuHeightEstimate - margin;
|
|
54
|
+
}
|
|
55
|
+
x = Math.max(margin, x);
|
|
56
|
+
y = Math.max(margin, y);
|
|
57
|
+
this._core.store.state.contextMenuX = x;
|
|
58
|
+
this._core.store.state.contextMenuY = y;
|
|
59
|
+
this._core.store.state.isContextMenuVisible = true;
|
|
60
|
+
this._core.store.state.isEnabled = false;
|
|
61
|
+
this._core.rerender();
|
|
62
|
+
}
|
|
63
|
+
}
|