@kritzel/engine 0.4.31
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/LICENSE.md +50 -0
- package/dist/cjs/app-globals-V2Kpy_OQ.js +5 -0
- package/dist/cjs/engine.constants-CakmdKMW.js +28206 -0
- package/dist/cjs/index-BbbWU2aN.js +1742 -0
- package/dist/cjs/index.cjs.js +4232 -0
- package/dist/cjs/kritzel-awareness-cursors_5.cjs.entry.js +28004 -0
- package/dist/cjs/loader.cjs.js +13 -0
- package/dist/cjs/stencil.cjs.js +25 -0
- package/dist/collection/classes/core/core.class.js +1389 -0
- package/dist/collection/classes/core/reviver.class.js +124 -0
- package/dist/collection/classes/core/store.class.js +337 -0
- package/dist/collection/classes/core/viewport.class.js +794 -0
- package/dist/collection/classes/core/workspace.class.js +112 -0
- package/dist/collection/classes/handlers/base.handler.js +20 -0
- package/dist/collection/classes/handlers/context-menu.handler.js +170 -0
- package/dist/collection/classes/handlers/hover.handler.js +35 -0
- package/dist/collection/classes/handlers/key.handler.js +162 -0
- package/dist/collection/classes/handlers/line-handle.handler.js +475 -0
- package/dist/collection/classes/handlers/move.handler.js +295 -0
- package/dist/collection/classes/handlers/resize.handler.js +327 -0
- package/dist/collection/classes/handlers/rotation.handler.js +174 -0
- package/dist/collection/classes/handlers/selection.handler.js +526 -0
- package/dist/collection/classes/managers/anchor.manager.js +1476 -0
- package/dist/collection/classes/managers/browser-permission.manager.js +64 -0
- package/dist/collection/classes/managers/clipboard.manager.js +346 -0
- package/dist/collection/classes/managers/clipboard.types.js +1 -0
- package/dist/collection/classes/managers/cursor.manager.js +139 -0
- package/dist/collection/classes/managers/license.manager.js +341 -0
- package/dist/collection/classes/managers/localization.manager.js +227 -0
- package/dist/collection/classes/managers/theme.manager.js +206 -0
- package/dist/collection/classes/objects/base-object.class.js +462 -0
- package/dist/collection/classes/objects/dynamic-object.class.js +437 -0
- package/dist/collection/classes/objects/group.class.js +590 -0
- package/dist/collection/classes/objects/image.class.js +500 -0
- package/dist/collection/classes/objects/line.class.js +789 -0
- package/dist/collection/classes/objects/path.class.js +578 -0
- package/dist/collection/classes/objects/selection-box.class.js +32 -0
- package/dist/collection/classes/objects/selection-group.class.js +752 -0
- package/dist/collection/classes/objects/shape.class.js +628 -0
- package/dist/collection/classes/objects/text.class.js +461 -0
- package/dist/collection/classes/providers/assets/asset-resolver.class.js +536 -0
- package/dist/collection/classes/providers/assets/http-asset-provider.class.js +111 -0
- package/dist/collection/classes/providers/assets/indexeddb-asset-provider.class.js +241 -0
- package/dist/collection/classes/providers/assets/presigned-asset-provider.class.js +86 -0
- package/dist/collection/classes/providers/paste-intent/event-paste-intent.provider.js +48 -0
- package/dist/collection/classes/providers/paste-intent/ios-paste-intent.provider.js +16 -0
- package/dist/collection/classes/providers/paste-intent/navigator-paste-intent.provider.js +86 -0
- package/dist/collection/classes/providers/sync/broadcast-sync-provider.class.js +100 -0
- package/dist/collection/classes/providers/sync/hocuspocus-sync-provider.class.js +333 -0
- package/dist/collection/classes/providers/sync/in-memory-sync-provider.class.js +69 -0
- package/dist/collection/classes/providers/sync/indexeddb-sync-provider.class.js +39 -0
- package/dist/collection/classes/providers/sync/websocket-sync-provider.class.js +110 -0
- package/dist/collection/classes/registries/dynamic-object-renderer.registry.js +52 -0
- package/dist/collection/classes/registries/font-registry.class.js +161 -0
- package/dist/collection/classes/registries/icon-registry.class.js +118 -0
- package/dist/collection/classes/registries/paste-intent-provider.registry.js +26 -0
- package/dist/collection/classes/registries/tool.registry.js +69 -0
- package/dist/collection/classes/structures/app-state-map.structure.js +461 -0
- package/dist/collection/classes/structures/object-map.structure.js +1103 -0
- package/dist/collection/classes/structures/quadtree.structure.js +251 -0
- package/dist/collection/classes/tools/base-tool.class.js +88 -0
- package/dist/collection/classes/tools/brush-tool.class.js +236 -0
- package/dist/collection/classes/tools/eraser-tool.class.js +133 -0
- package/dist/collection/classes/tools/image-tool.class.js +152 -0
- package/dist/collection/classes/tools/line-tool.class.js +249 -0
- package/dist/collection/classes/tools/selection-tool.class.js +611 -0
- package/dist/collection/classes/tools/shape-tool.class.js +251 -0
- package/dist/collection/classes/tools/text-tool.class.js +179 -0
- package/dist/collection/collection-manifest.json +17 -0
- package/dist/collection/components/kritzel-awareness-cursors/kritzel-awareness-cursors.css +115 -0
- package/dist/collection/components/kritzel-awareness-cursors/kritzel-awareness-cursors.js +380 -0
- package/dist/collection/components/kritzel-cursor-trail/kritzel-cursor-trail.css +10 -0
- package/dist/collection/components/kritzel-cursor-trail/kritzel-cursor-trail.js +153 -0
- package/dist/collection/components/kritzel-engine/kritzel-engine.css +81 -0
- package/dist/collection/components/kritzel-engine/kritzel-engine.js +6345 -0
- package/dist/collection/components/kritzel-icon/kritzel-icon.css +18 -0
- package/dist/collection/components/kritzel-icon/kritzel-icon.js +93 -0
- package/dist/collection/components/kritzel-watermark/kritzel-watermark.css +29 -0
- package/dist/collection/components/kritzel-watermark/kritzel-watermark.js +83 -0
- package/dist/collection/configs/default-asset-storage.config.js +9 -0
- package/dist/collection/configs/default-brush-tool.config.js +9 -0
- package/dist/collection/configs/default-engine-config.js +73 -0
- package/dist/collection/configs/default-line-tool.config.js +12 -0
- package/dist/collection/configs/default-shape-tool.config.js +15 -0
- package/dist/collection/configs/default-sync.config.js +6 -0
- package/dist/collection/configs/default-text-tool.config.js +12 -0
- package/dist/collection/constants/color-palette.constants.js +40 -0
- package/dist/collection/constants/core.constants.js +1 -0
- package/dist/collection/constants/engine.constants.js +4 -0
- package/dist/collection/constants/license.constants.js +25 -0
- package/dist/collection/constants/schema.constants.js +4 -0
- package/dist/collection/constants/storage.constants.js +1 -0
- package/dist/collection/constants/stroke-size.constants.js +2 -0
- package/dist/collection/constants/text-tool-fonts.constants.js +18 -0
- package/dist/collection/constants/version.js +6 -0
- package/dist/collection/enums/alignment.enum.js +9 -0
- package/dist/collection/enums/event-button.enum.js +6 -0
- package/dist/collection/enums/handle-type.enum.js +7 -0
- package/dist/collection/enums/shape-type.enum.js +6 -0
- package/dist/collection/events/active-workspace-change.event.js +1 -0
- package/dist/collection/events/editor-is-ready.event.js +1 -0
- package/dist/collection/events/login.event.js +1 -0
- package/dist/collection/helpers/alignment.helper.js +73 -0
- package/dist/collection/helpers/class.helper.js +5 -0
- package/dist/collection/helpers/color.helper.js +106 -0
- package/dist/collection/helpers/cursor.helper.js +57 -0
- package/dist/collection/helpers/devices.helper.js +28 -0
- package/dist/collection/helpers/event.helper.js +58 -0
- package/dist/collection/helpers/geometry.helper.js +149 -0
- package/dist/collection/helpers/html.helper.js +116 -0
- package/dist/collection/helpers/image-file.helper.js +6 -0
- package/dist/collection/helpers/keyboard.helper.js +55 -0
- package/dist/collection/helpers/localization.helper.js +25 -0
- package/dist/collection/helpers/math.helper.js +8 -0
- package/dist/collection/helpers/migration.helper.js +45 -0
- package/dist/collection/helpers/object.helper.js +29 -0
- package/dist/collection/helpers/svg-export.helper.js +655 -0
- package/dist/collection/helpers/theme.helper.js +77 -0
- package/dist/collection/helpers/tool-config.helper.js +63 -0
- package/dist/collection/helpers/wheel.helper.js +113 -0
- package/dist/collection/index.js +107 -0
- package/dist/collection/interfaces/anchor.interface.js +1 -0
- package/dist/collection/interfaces/arrow-head.interface.js +1 -0
- package/dist/collection/interfaces/asset-provider.interface.js +1 -0
- package/dist/collection/interfaces/asset-storage-config.interface.js +1 -0
- package/dist/collection/interfaces/asset.interface.js +1 -0
- package/dist/collection/interfaces/bounding-box.interface.js +1 -0
- package/dist/collection/interfaces/clonable.interface.js +1 -0
- package/dist/collection/interfaces/context-menu-item.interface.js +1 -0
- package/dist/collection/interfaces/context-menu-state.interface.js +1 -0
- package/dist/collection/interfaces/debug-info.interface.js +1 -0
- package/dist/collection/interfaces/dialog.interface.js +1 -0
- package/dist/collection/interfaces/document-metadata.interface.js +1 -0
- package/dist/collection/interfaces/dynamic-object-renderer.interface.js +1 -0
- package/dist/collection/interfaces/engine-state.interface.js +1 -0
- package/dist/collection/interfaces/font.interface.js +1 -0
- package/dist/collection/interfaces/localization.interface.js +1 -0
- package/dist/collection/interfaces/login-config.interface.js +1 -0
- package/dist/collection/interfaces/master-detail.interface.js +1 -0
- package/dist/collection/interfaces/menu-item.interface.js +1 -0
- package/dist/collection/interfaces/migration.interface.js +1 -0
- package/dist/collection/interfaces/notification.interface.js +1 -0
- package/dist/collection/interfaces/object-change-event.interface.js +1 -0
- package/dist/collection/interfaces/object-export.interface.js +1 -0
- package/dist/collection/interfaces/object.interface.js +1 -0
- package/dist/collection/interfaces/paste-intent-provider.interface.js +1 -0
- package/dist/collection/interfaces/point.interface.js +1 -0
- package/dist/collection/interfaces/polygon.interface.js +1 -0
- package/dist/collection/interfaces/remote-cursor.interface.js +1 -0
- package/dist/collection/interfaces/selection-state.interface.js +1 -0
- package/dist/collection/interfaces/serializable.interface.js +1 -0
- package/dist/collection/interfaces/settings.interface.js +1 -0
- package/dist/collection/interfaces/share.interface.js +1 -0
- package/dist/collection/interfaces/shortcut.interface.js +1 -0
- package/dist/collection/interfaces/sync-config.interface.js +21 -0
- package/dist/collection/interfaces/sync-provider.interface.js +1 -0
- package/dist/collection/interfaces/theme.interface.js +1 -0
- package/dist/collection/interfaces/tool-config.interface.js +1 -0
- package/dist/collection/interfaces/tool-type.interface.js +1 -0
- package/dist/collection/interfaces/tool.interface.js +1 -0
- package/dist/collection/interfaces/toolbar-item.interface.js +1 -0
- package/dist/collection/interfaces/undo-state.interface.js +1 -0
- package/dist/collection/interfaces/user.interface.js +1 -0
- package/dist/collection/interfaces/viewport-state.interface.js +1 -0
- package/dist/collection/locales/de-locale.js +119 -0
- package/dist/collection/locales/en-locale.js +120 -0
- package/dist/collection/locales/fr-locale.js +119 -0
- package/dist/collection/migrations/app-state.migrations.js +47 -0
- package/dist/collection/migrations/workspace.migrations.js +67 -0
- package/dist/collection/themes/dark-theme.js +340 -0
- package/dist/collection/themes/light-theme.js +346 -0
- package/dist/collection/types/deep-readonly.type.js +1 -0
- package/dist/collection/types/state.types.js +1 -0
- package/dist/components/index.d.ts +47 -0
- package/dist/components/index.js +4143 -0
- package/dist/components/kritzel-awareness-cursors.d.ts +11 -0
- package/dist/components/kritzel-awareness-cursors.js +6 -0
- package/dist/components/kritzel-cursor-trail.d.ts +11 -0
- package/dist/components/kritzel-cursor-trail.js +6 -0
- package/dist/components/kritzel-engine.d.ts +11 -0
- package/dist/components/kritzel-engine.js +52641 -0
- package/dist/components/kritzel-icon.d.ts +11 -0
- package/dist/components/kritzel-icon.js +6 -0
- package/dist/components/kritzel-watermark.d.ts +11 -0
- package/dist/components/kritzel-watermark.js +6 -0
- package/dist/components/p-B2Htv9ou.js +49 -0
- package/dist/components/p-BvRGQvVI.js +3456 -0
- package/dist/components/p-CfSx6HU_.js +1469 -0
- package/dist/components/p-YP8wAcF4.js +122 -0
- package/dist/components/p-fx6msYHJ.js +167 -0
- package/dist/esm/app-globals-DQuL1Twl.js +3 -0
- package/dist/esm/engine.constants-Bl5-pbq-.js +28143 -0
- package/dist/esm/index-BU9Mv7y0.js +1715 -0
- package/dist/esm/index.js +4134 -0
- package/dist/esm/kritzel-awareness-cursors_5.entry.js +27978 -0
- package/dist/esm/loader.js +11 -0
- package/dist/esm/stencil.js +21 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.js +1 -0
- package/dist/stencil/index.esm.js +4134 -0
- package/dist/stencil/p-BU9Mv7y0.js +1702 -0
- package/dist/stencil/p-Bl5-pbq-.js +28143 -0
- package/dist/stencil/p-DQuL1Twl.js +3 -0
- package/dist/stencil/p-f0a8b7af.entry.js +27978 -0
- package/dist/stencil/stencil.esm.js +21 -0
- package/dist/types/classes/core/core.class.d.ts +532 -0
- package/dist/types/classes/core/reviver.class.d.ts +32 -0
- package/dist/types/classes/core/store.class.d.ts +198 -0
- package/dist/types/classes/core/viewport.class.d.ts +256 -0
- package/dist/types/classes/core/workspace.class.d.ts +95 -0
- package/dist/types/classes/handlers/base.handler.d.ts +18 -0
- package/dist/types/classes/handlers/context-menu.handler.d.ts +67 -0
- package/dist/types/classes/handlers/hover.handler.d.ts +23 -0
- package/dist/types/classes/handlers/key.handler.d.ts +44 -0
- package/dist/types/classes/handlers/line-handle.handler.d.ts +115 -0
- package/dist/types/classes/handlers/move.handler.d.ts +94 -0
- package/dist/types/classes/handlers/resize.handler.d.ts +95 -0
- package/dist/types/classes/handlers/rotation.handler.d.ts +50 -0
- package/dist/types/classes/handlers/selection.handler.d.ts +153 -0
- package/dist/types/classes/managers/anchor.manager.d.ts +513 -0
- package/dist/types/classes/managers/browser-permission.manager.d.ts +41 -0
- package/dist/types/classes/managers/clipboard.manager.d.ts +71 -0
- package/dist/types/classes/managers/clipboard.types.d.ts +23 -0
- package/dist/types/classes/managers/cursor.manager.d.ts +66 -0
- package/dist/types/classes/managers/license.manager.d.ts +147 -0
- package/dist/types/classes/managers/localization.manager.d.ts +145 -0
- package/dist/types/classes/managers/theme.manager.d.ts +112 -0
- package/dist/types/classes/objects/base-object.class.d.ts +266 -0
- package/dist/types/classes/objects/dynamic-object.class.d.ts +148 -0
- package/dist/types/classes/objects/group.class.d.ts +231 -0
- package/dist/types/classes/objects/image.class.d.ts +246 -0
- package/dist/types/classes/objects/line.class.d.ts +285 -0
- package/dist/types/classes/objects/path.class.d.ts +220 -0
- package/dist/types/classes/objects/selection-box.class.d.ts +19 -0
- package/dist/types/classes/objects/selection-group.class.d.ts +267 -0
- package/dist/types/classes/objects/shape.class.d.ts +322 -0
- package/dist/types/classes/objects/text.class.d.ts +197 -0
- package/dist/types/classes/providers/assets/asset-resolver.class.d.ts +141 -0
- package/dist/types/classes/providers/assets/http-asset-provider.class.d.ts +79 -0
- package/dist/types/classes/providers/assets/indexeddb-asset-provider.class.d.ts +69 -0
- package/dist/types/classes/providers/assets/presigned-asset-provider.class.d.ts +69 -0
- package/dist/types/classes/providers/paste-intent/event-paste-intent.provider.d.ts +8 -0
- package/dist/types/classes/providers/paste-intent/ios-paste-intent.provider.d.ts +6 -0
- package/dist/types/classes/providers/paste-intent/navigator-paste-intent.provider.d.ts +9 -0
- package/dist/types/classes/providers/sync/broadcast-sync-provider.class.d.ts +23 -0
- package/dist/types/classes/providers/sync/hocuspocus-sync-provider.class.d.ts +163 -0
- package/dist/types/classes/providers/sync/in-memory-sync-provider.class.d.ts +39 -0
- package/dist/types/classes/providers/sync/indexeddb-sync-provider.class.d.ts +25 -0
- package/dist/types/classes/providers/sync/websocket-sync-provider.class.d.ts +63 -0
- package/dist/types/classes/registries/dynamic-object-renderer.registry.d.ts +32 -0
- package/dist/types/classes/registries/font-registry.class.d.ts +19 -0
- package/dist/types/classes/registries/icon-registry.class.d.ts +39 -0
- package/dist/types/classes/registries/paste-intent-provider.registry.d.ts +10 -0
- package/dist/types/classes/registries/tool.registry.d.ts +53 -0
- package/dist/types/classes/structures/app-state-map.structure.d.ts +171 -0
- package/dist/types/classes/structures/object-map.structure.d.ts +403 -0
- package/dist/types/classes/structures/quadtree.structure.d.ts +128 -0
- package/dist/types/classes/tools/base-tool.class.d.ts +72 -0
- package/dist/types/classes/tools/brush-tool.class.d.ts +68 -0
- package/dist/types/classes/tools/eraser-tool.class.d.ts +38 -0
- package/dist/types/classes/tools/image-tool.class.d.ts +74 -0
- package/dist/types/classes/tools/line-tool.class.d.ts +63 -0
- package/dist/types/classes/tools/selection-tool.class.d.ts +188 -0
- package/dist/types/classes/tools/shape-tool.class.d.ts +90 -0
- package/dist/types/classes/tools/text-tool.class.d.ts +66 -0
- package/dist/types/components/kritzel-awareness-cursors/kritzel-awareness-cursors.d.ts +29 -0
- package/dist/types/components/kritzel-cursor-trail/kritzel-cursor-trail.d.ts +19 -0
- package/dist/types/components/kritzel-engine/kritzel-engine.d.ts +695 -0
- package/dist/types/components/kritzel-icon/kritzel-icon.d.ts +6 -0
- package/dist/types/components/kritzel-watermark/kritzel-watermark.d.ts +20 -0
- package/dist/types/components.d.ts +981 -0
- package/dist/types/configs/default-asset-storage.config.d.ts +8 -0
- package/dist/types/configs/default-brush-tool.config.d.ts +2 -0
- package/dist/types/configs/default-engine-config.d.ts +2 -0
- package/dist/types/configs/default-line-tool.config.d.ts +2 -0
- package/dist/types/configs/default-shape-tool.config.d.ts +2 -0
- package/dist/types/configs/default-sync.config.d.ts +5 -0
- package/dist/types/configs/default-text-tool.config.d.ts +2 -0
- package/dist/types/constants/color-palette.constants.d.ts +31 -0
- package/dist/types/constants/core.constants.d.ts +0 -0
- package/dist/types/constants/engine.constants.d.ts +4 -0
- package/dist/types/constants/license.constants.d.ts +25 -0
- package/dist/types/constants/schema.constants.d.ts +4 -0
- package/dist/types/constants/storage.constants.d.ts +1 -0
- package/dist/types/constants/stroke-size.constants.d.ts +2 -0
- package/dist/types/constants/text-tool-fonts.constants.d.ts +3 -0
- package/dist/types/constants/version.d.ts +6 -0
- package/dist/types/enums/alignment.enum.d.ts +8 -0
- package/dist/types/enums/event-button.enum.d.ts +5 -0
- package/dist/types/enums/handle-type.enum.d.ts +6 -0
- package/dist/types/enums/shape-type.enum.d.ts +5 -0
- package/dist/types/events/active-workspace-change.event.d.ts +7 -0
- package/dist/types/events/editor-is-ready.event.d.ts +18 -0
- package/dist/types/events/login.event.d.ts +3 -0
- package/dist/types/helpers/alignment.helper.d.ts +21 -0
- package/dist/types/helpers/class.helper.d.ts +3 -0
- package/dist/types/helpers/color.helper.d.ts +34 -0
- package/dist/types/helpers/cursor.helper.d.ts +22 -0
- package/dist/types/helpers/devices.helper.d.ts +8 -0
- package/dist/types/helpers/event.helper.d.ts +6 -0
- package/dist/types/helpers/geometry.helper.d.ts +38 -0
- package/dist/types/helpers/html.helper.d.ts +8 -0
- package/dist/types/helpers/image-file.helper.d.ts +3 -0
- package/dist/types/helpers/keyboard.helper.d.ts +6 -0
- package/dist/types/helpers/localization.helper.d.ts +18 -0
- package/dist/types/helpers/math.helper.d.ts +4 -0
- package/dist/types/helpers/migration.helper.d.ts +30 -0
- package/dist/types/helpers/object.helper.d.ts +9 -0
- package/dist/types/helpers/svg-export.helper.d.ts +231 -0
- package/dist/types/helpers/theme.helper.d.ts +42 -0
- package/dist/types/helpers/tool-config.helper.d.ts +4 -0
- package/dist/types/helpers/wheel.helper.d.ts +9 -0
- package/dist/types/index.d.ts +109 -0
- package/dist/types/interfaces/anchor.interface.d.ts +147 -0
- package/dist/types/interfaces/arrow-head.interface.d.ts +27 -0
- package/dist/types/interfaces/asset-provider.interface.d.ts +88 -0
- package/dist/types/interfaces/asset-storage-config.interface.d.ts +40 -0
- package/dist/types/interfaces/asset.interface.d.ts +43 -0
- package/dist/types/interfaces/bounding-box.interface.d.ts +8 -0
- package/dist/types/interfaces/clonable.interface.d.ts +3 -0
- package/dist/types/interfaces/context-menu-item.interface.d.ts +25 -0
- package/dist/types/interfaces/context-menu-state.interface.d.ts +16 -0
- package/dist/types/interfaces/debug-info.interface.d.ts +7 -0
- package/dist/types/interfaces/dialog.interface.d.ts +4 -0
- package/dist/types/interfaces/document-metadata.interface.d.ts +6 -0
- package/dist/types/interfaces/dynamic-object-renderer.interface.d.ts +40 -0
- package/dist/types/interfaces/engine-state.interface.d.ts +82 -0
- package/dist/types/interfaces/font.interface.d.ts +15 -0
- package/dist/types/interfaces/localization.interface.d.ts +143 -0
- package/dist/types/interfaces/login-config.interface.d.ts +24 -0
- package/dist/types/interfaces/master-detail.interface.d.ts +14 -0
- package/dist/types/interfaces/menu-item.interface.d.ts +27 -0
- package/dist/types/interfaces/migration.interface.d.ts +16 -0
- package/dist/types/interfaces/notification.interface.d.ts +10 -0
- package/dist/types/interfaces/object-change-event.interface.d.ts +31 -0
- package/dist/types/interfaces/object-export.interface.d.ts +20 -0
- package/dist/types/interfaces/object.interface.d.ts +53 -0
- package/dist/types/interfaces/paste-intent-provider.interface.d.ts +10 -0
- package/dist/types/interfaces/point.interface.d.ts +4 -0
- package/dist/types/interfaces/polygon.interface.d.ts +7 -0
- package/dist/types/interfaces/remote-cursor.interface.d.ts +18 -0
- package/dist/types/interfaces/selection-state.interface.d.ts +7 -0
- package/dist/types/interfaces/serializable.interface.d.ts +5 -0
- package/dist/types/interfaces/settings.interface.d.ts +25 -0
- package/dist/types/interfaces/share.interface.d.ts +9 -0
- package/dist/types/interfaces/shortcut.interface.d.ts +10 -0
- package/dist/types/interfaces/sync-config.interface.d.ts +41 -0
- package/dist/types/interfaces/sync-provider.interface.d.ts +66 -0
- package/dist/types/interfaces/theme.interface.d.ts +553 -0
- package/dist/types/interfaces/tool-config.interface.d.ts +24 -0
- package/dist/types/interfaces/tool-type.interface.d.ts +1 -0
- package/dist/types/interfaces/tool.interface.d.ts +8 -0
- package/dist/types/interfaces/toolbar-item.interface.d.ts +66 -0
- package/dist/types/interfaces/undo-state.interface.d.ts +6 -0
- package/dist/types/interfaces/user.interface.d.ts +22 -0
- package/dist/types/interfaces/viewport-state.interface.d.ts +7 -0
- package/dist/types/locales/de-locale.d.ts +5 -0
- package/dist/types/locales/en-locale.d.ts +6 -0
- package/dist/types/locales/fr-locale.d.ts +5 -0
- package/dist/types/migrations/app-state.migrations.d.ts +11 -0
- package/dist/types/migrations/workspace.migrations.d.ts +11 -0
- package/dist/types/stencil-public-runtime.d.ts +1873 -0
- package/dist/types/themes/dark-theme.d.ts +5 -0
- package/dist/types/themes/light-theme.d.ts +5 -0
- package/dist/types/types/deep-readonly.type.d.ts +7 -0
- package/dist/types/types/state.types.d.ts +3 -0
- package/loader/cdn.js +1 -0
- package/loader/index.cjs.js +1 -0
- package/loader/index.d.ts +24 -0
- package/loader/index.es2017.js +1 -0
- package/loader/index.js +2 -0
- package/package.json +101 -0
- package/readme.md +11 -0
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
import { KritzelGeometryHelper } from "../../helpers/geometry.helper";
|
|
2
|
+
import { KritzelBaseObject } from "./base-object.class";
|
|
3
|
+
import { KritzelGroup } from "./group.class";
|
|
4
|
+
import { KritzelClassHelper } from "../../helpers/class.helper";
|
|
5
|
+
import { KritzelMathHelper } from "../../helpers/math.helper";
|
|
6
|
+
/**
|
|
7
|
+
* Represents a selection group that manages multiple selected objects on the canvas.
|
|
8
|
+
* The selection group allows batch operations like move, resize, and rotate on multiple objects simultaneously.
|
|
9
|
+
* It maintains its own bounding box that encompasses all selected objects.
|
|
10
|
+
*/
|
|
11
|
+
export class KritzelSelectionGroup extends KritzelBaseObject {
|
|
12
|
+
__class__ = 'KritzelSelectionGroup';
|
|
13
|
+
constructor(config) {
|
|
14
|
+
super();
|
|
15
|
+
this.rotation = KritzelMathHelper.degreesToRadians(config?.rotation ?? 0);
|
|
16
|
+
}
|
|
17
|
+
// Store only object IDs instead of full objects
|
|
18
|
+
_objectIds = [];
|
|
19
|
+
// Cached objects array - invalidated when objectIds changes
|
|
20
|
+
_cachedObjects = null;
|
|
21
|
+
_cachedObjectIdsHash = '';
|
|
22
|
+
// Store snapshots of object state for transformations (rotation, resize)
|
|
23
|
+
unchangedObjectSnapshots = new Map();
|
|
24
|
+
snapshotRotation = 0;
|
|
25
|
+
snapshotWidth = 0;
|
|
26
|
+
snapshotHeight = 0;
|
|
27
|
+
snapshotTranslateX = 0;
|
|
28
|
+
snapshotTranslateY = 0;
|
|
29
|
+
clientId;
|
|
30
|
+
minX = 0;
|
|
31
|
+
maxX = 0;
|
|
32
|
+
minY = 0;
|
|
33
|
+
maxY = 0;
|
|
34
|
+
// Selection styling properties
|
|
35
|
+
handleColor;
|
|
36
|
+
handleStrokeColor;
|
|
37
|
+
handleSize = 6;
|
|
38
|
+
/** Timestamp of the last Yjs persist for children during a drag */
|
|
39
|
+
_lastChildPersistTime = 0;
|
|
40
|
+
/** Minimum interval (ms) between child Yjs persists during drag */
|
|
41
|
+
static CHILD_PERSIST_THROTTLE_MS = 100;
|
|
42
|
+
/** Ignore tiny floating-point diffs when comparing refreshed bounds */
|
|
43
|
+
static DIMENSION_EPSILON = 0.001;
|
|
44
|
+
/**
|
|
45
|
+
* Gets the array of object IDs contained in this selection group.
|
|
46
|
+
* @returns Array of string IDs for the selected objects
|
|
47
|
+
*/
|
|
48
|
+
get objectIds() {
|
|
49
|
+
return this._objectIds;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Sets the array of object IDs contained in this selection group.
|
|
53
|
+
* Invalidates the cached objects array when changed.
|
|
54
|
+
* @param value - Array of string IDs to set as the selected objects
|
|
55
|
+
*/
|
|
56
|
+
set objectIds(value) {
|
|
57
|
+
this._objectIds = value;
|
|
58
|
+
this._cachedObjects = null; // Invalidate cache
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Invalidate the cached objects array. Call this when objects might have changed
|
|
62
|
+
* externally (e.g., after undo/redo operations).
|
|
63
|
+
*/
|
|
64
|
+
invalidateObjectsCache() {
|
|
65
|
+
this._cachedObjects = null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Retrieves the actual object instances from the store by their IDs.
|
|
69
|
+
* Uses caching to avoid redundant lookups - only recomputes when objectIds changes.
|
|
70
|
+
* Preserves the original insertion order of objects.
|
|
71
|
+
* @returns Array of KritzelBaseObject instances corresponding to the stored IDs
|
|
72
|
+
*/
|
|
73
|
+
get objects() {
|
|
74
|
+
const currentHash = this._objectIds.join(',');
|
|
75
|
+
if (this._cachedObjects !== null && this._cachedObjectIdsHash === currentHash) {
|
|
76
|
+
return this._cachedObjects;
|
|
77
|
+
}
|
|
78
|
+
const idSet = new Set(this._objectIds);
|
|
79
|
+
const foundObjects = this._core.store.objects.filter(obj => idSet.has(obj.id));
|
|
80
|
+
// Preserve the original order from objectIds
|
|
81
|
+
const objectMap = new Map(foundObjects.map(obj => [obj.id, obj]));
|
|
82
|
+
this._cachedObjects = this._objectIds
|
|
83
|
+
.map(id => objectMap.get(id))
|
|
84
|
+
.filter((obj) => obj !== undefined);
|
|
85
|
+
this._cachedObjectIdsHash = currentHash;
|
|
86
|
+
return this._cachedObjects;
|
|
87
|
+
}
|
|
88
|
+
syncChildSelectionState(children) {
|
|
89
|
+
const shouldBeSelected = this.isSelected === true;
|
|
90
|
+
for (const child of children) {
|
|
91
|
+
child.isSelected = shouldBeSelected;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
shouldSyncChildSelectionState() {
|
|
95
|
+
const localClientId = this._core.store.objects?.localClientId ?? null;
|
|
96
|
+
if (localClientId != null && this.clientId != null) {
|
|
97
|
+
return this.clientId === localClientId;
|
|
98
|
+
}
|
|
99
|
+
const localUserId = this._core.user?.id ?? null;
|
|
100
|
+
if (localUserId != null && this.userId != null) {
|
|
101
|
+
return this.userId === localUserId;
|
|
102
|
+
}
|
|
103
|
+
return this.userId == null;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Gets the number of objects in the selection group.
|
|
107
|
+
* @returns The count of selected objects
|
|
108
|
+
*/
|
|
109
|
+
get length() {
|
|
110
|
+
return this.objectIds.length;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Factory method to create a new KritzelSelectionGroup instance.
|
|
114
|
+
* Initializes the selection group with default properties and associates it with the core instance.
|
|
115
|
+
* Default styling uses values from the theme system (selection section).
|
|
116
|
+
* @param core - The KritzelCore instance to associate with this selection group
|
|
117
|
+
* @returns A new KritzelSelectionGroup instance configured with default settings
|
|
118
|
+
*/
|
|
119
|
+
static create(core, config) {
|
|
120
|
+
const object = new KritzelSelectionGroup(config);
|
|
121
|
+
object._core = core;
|
|
122
|
+
object.id = object.generateId();
|
|
123
|
+
object.workspaceId = core.store.state.activeWorkspace.id;
|
|
124
|
+
object.userId = core.user?.id;
|
|
125
|
+
object.clientId = core.store.objects?.localClientId ?? undefined;
|
|
126
|
+
object.scale = core.store.state.scale;
|
|
127
|
+
object.zIndex = 99999;
|
|
128
|
+
// Initialize styling with theme-aware defaults
|
|
129
|
+
object.borderColor = undefined;
|
|
130
|
+
object.borderWidth = 2;
|
|
131
|
+
object.handleColor = undefined;
|
|
132
|
+
object.handleStrokeColor = undefined;
|
|
133
|
+
object.handleSize = 6;
|
|
134
|
+
return object;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Adds multiple objects to the selection group.
|
|
138
|
+
* Only adds objects that are not already in the selection.
|
|
139
|
+
* Updates the group's rotation based on the number of selected objects:
|
|
140
|
+
* - Single object: inherits the object's rotation
|
|
141
|
+
* - Multiple objects: resets rotation to 0
|
|
142
|
+
* Refreshes bounding box dimensions after adding objects.
|
|
143
|
+
* @param objects - Array of KritzelBaseObject instances to add to the selection
|
|
144
|
+
*/
|
|
145
|
+
addObjects(objects) {
|
|
146
|
+
let changed = false;
|
|
147
|
+
for (const object of objects) {
|
|
148
|
+
if (!this._objectIds.includes(object.id)) {
|
|
149
|
+
this._objectIds.push(object.id);
|
|
150
|
+
object.isSelected = true;
|
|
151
|
+
changed = true;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (changed) {
|
|
155
|
+
this._cachedObjects = null;
|
|
156
|
+
const children = this.objects;
|
|
157
|
+
if (children.length === 1) {
|
|
158
|
+
this.rotation = children[0].rotation;
|
|
159
|
+
}
|
|
160
|
+
else if (children.length > 1) {
|
|
161
|
+
this.rotation = 0;
|
|
162
|
+
}
|
|
163
|
+
this.captureUnchangedSnapshots(children);
|
|
164
|
+
this.refreshObjectDimensions(children);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Toggles an object's presence in the selection group.
|
|
169
|
+
* If the object is already selected, it will be removed.
|
|
170
|
+
* If the object is not selected, it will be added.
|
|
171
|
+
* Updates group rotation and refreshes bounding box dimensions after the change.
|
|
172
|
+
* @param object - The KritzelBaseObject instance to toggle in the selection
|
|
173
|
+
*/
|
|
174
|
+
addOrRemove(object) {
|
|
175
|
+
const index = this._objectIds.findIndex(id => id === object.id);
|
|
176
|
+
if (index === -1) {
|
|
177
|
+
this._objectIds.push(object.id);
|
|
178
|
+
object.isSelected = true;
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
this._objectIds.splice(index, 1);
|
|
182
|
+
object.isSelected = false;
|
|
183
|
+
}
|
|
184
|
+
this._cachedObjects = null; // Invalidate cache
|
|
185
|
+
const children = this.objects; // Get cached objects once
|
|
186
|
+
if (children.length === 1) {
|
|
187
|
+
this.rotation = children[0].rotation;
|
|
188
|
+
}
|
|
189
|
+
else if (children.length > 1) {
|
|
190
|
+
this.rotation = 0;
|
|
191
|
+
}
|
|
192
|
+
this.captureUnchangedSnapshots(children);
|
|
193
|
+
this.refreshObjectDimensions(children);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Prepares the selection group and its children for a transform interaction (rotate/resize).
|
|
197
|
+
* Ensures snapshot state is aligned with the current visual state.
|
|
198
|
+
*/
|
|
199
|
+
beginTransform() {
|
|
200
|
+
const children = this.objects; // Get cached objects once
|
|
201
|
+
// Keep the selection group's bounding box current before snapshotting.
|
|
202
|
+
this.refreshObjectDimensions(children);
|
|
203
|
+
this.captureUnchangedSnapshots(children);
|
|
204
|
+
// Groups need their own child snapshots aligned with the current transform.
|
|
205
|
+
for (const obj of children) {
|
|
206
|
+
if (KritzelClassHelper.isInstanceOf(obj, 'KritzelGroup')) {
|
|
207
|
+
obj.finalize();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Sets the isSelected flag to false for all objects in the selection group.
|
|
213
|
+
* Used when clearing or changing the selection state.
|
|
214
|
+
*
|
|
215
|
+
* For KritzelGroup members, also clears the isSelected flag on all nested
|
|
216
|
+
* descendants. A descendant can retain a lingering isSelected from the marquee
|
|
217
|
+
* preview before it was resolved to its parent group during selection, which
|
|
218
|
+
* would otherwise leave that object selected after the group is deselected and
|
|
219
|
+
* cause the whole group to be re-selected on the next interaction.
|
|
220
|
+
*/
|
|
221
|
+
deselectAllChildren() {
|
|
222
|
+
const children = this.objects;
|
|
223
|
+
const descendantIds = new Set();
|
|
224
|
+
for (const obj of children) {
|
|
225
|
+
obj.isSelected = false;
|
|
226
|
+
if (KritzelClassHelper.isInstanceOf(obj, 'KritzelGroup')) {
|
|
227
|
+
KritzelGroup.collectDescendantIds(obj, descendantIds);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (descendantIds.size > 0) {
|
|
231
|
+
const descendants = this._core.store.objects.filter(o => descendantIds.has(o.id));
|
|
232
|
+
for (const descendant of descendants) {
|
|
233
|
+
descendant.isSelected = false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Updates the workspace ID for the selection group and all its contained objects.
|
|
239
|
+
* Used when moving objects between workspaces or updating workspace associations.
|
|
240
|
+
* @param workspaceId - The new workspace ID to assign to the group and its children
|
|
241
|
+
*/
|
|
242
|
+
updateWorkspaceId(workspaceId) {
|
|
243
|
+
this.workspaceId = workspaceId;
|
|
244
|
+
const children = this.objects;
|
|
245
|
+
for (const obj of children) {
|
|
246
|
+
obj.workspaceId = workspaceId;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Updates the z-index values for all objects in the selection group.
|
|
251
|
+
* Assigns sequential z-indices starting from the provided value.
|
|
252
|
+
* @param startZIndex - The starting z-index value; subsequent objects get startZIndex + 1, startZIndex + 2, etc.
|
|
253
|
+
*/
|
|
254
|
+
updateZIndices(startZIndex) {
|
|
255
|
+
const children = this.objects;
|
|
256
|
+
for (let i = 0; i < children.length; i++) {
|
|
257
|
+
children[i].zIndex = startZIndex + i;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Updates the absolute position of the selection group and all its contained objects.
|
|
262
|
+
* Maintains the relative positions of child objects within the group.
|
|
263
|
+
* Also updates the unchangedObjectSnapshots to reflect the new positions.
|
|
264
|
+
* @param x - The new x-coordinate for the selection group's top-left corner
|
|
265
|
+
* @param y - The new y-coordinate for the selection group's top-left corner
|
|
266
|
+
*/
|
|
267
|
+
updatePosition(x, y) {
|
|
268
|
+
const children = this.objects;
|
|
269
|
+
for (const obj of children) {
|
|
270
|
+
const deltaX = obj.translateX - this.translateX;
|
|
271
|
+
const deltaY = obj.translateY - this.translateY;
|
|
272
|
+
obj.updatePosition(x + deltaX, y + deltaY);
|
|
273
|
+
}
|
|
274
|
+
// Update snapshots
|
|
275
|
+
this.unchangedObjectSnapshots.forEach(snapshot => {
|
|
276
|
+
const deltaX = snapshot.translateX - this.translateX;
|
|
277
|
+
const deltaY = snapshot.translateY - this.translateY;
|
|
278
|
+
snapshot.translateX = x + deltaX;
|
|
279
|
+
snapshot.translateY = y + deltaY;
|
|
280
|
+
});
|
|
281
|
+
this.translateX = x;
|
|
282
|
+
this.translateY = y;
|
|
283
|
+
this._core.store.objects.update(this);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Capture snapshots of current object states for undo/redo operations
|
|
287
|
+
* @param cachedObjects Optional pre-fetched objects array to avoid redundant getter calls
|
|
288
|
+
*/
|
|
289
|
+
captureUnchangedSnapshots(cachedObjects) {
|
|
290
|
+
this.unchangedObjectSnapshots.clear();
|
|
291
|
+
this.snapshotRotation = this.rotation;
|
|
292
|
+
this.snapshotWidth = this.width;
|
|
293
|
+
this.snapshotHeight = this.height;
|
|
294
|
+
this.snapshotTranslateX = this.translateX;
|
|
295
|
+
this.snapshotTranslateY = this.translateY;
|
|
296
|
+
const children = cachedObjects ?? this.objects;
|
|
297
|
+
children.forEach(obj => {
|
|
298
|
+
this.unchangedObjectSnapshots.set(obj.id, {
|
|
299
|
+
id: obj.id,
|
|
300
|
+
translateX: obj.translateX,
|
|
301
|
+
translateY: obj.translateY,
|
|
302
|
+
rotation: obj.rotation,
|
|
303
|
+
width: obj.width,
|
|
304
|
+
height: obj.height,
|
|
305
|
+
totalWidth: obj.totalWidth,
|
|
306
|
+
totalHeight: obj.totalHeight,
|
|
307
|
+
scale: obj.scale,
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Serializes the selection group to a plain object for persistence or transmission.
|
|
313
|
+
* Excludes internal references like _core, element refs, and cached data.
|
|
314
|
+
* Converts the unchangedObjectSnapshots Map to a plain object for JSON compatibility.
|
|
315
|
+
* @returns A serializable object representation of the selection group
|
|
316
|
+
*/
|
|
317
|
+
serialize() {
|
|
318
|
+
const { _core, _elementRef, element, totalWidth, totalHeight, unchangedObjectSnapshots, _cachedObjects, _cachedObjectIdsHash, snapshotWidth, snapshotHeight, snapshotTranslateX, snapshotTranslateY, snapshotRotation, ...remainingProps } = this;
|
|
319
|
+
const clonedProps = structuredClone(remainingProps);
|
|
320
|
+
// Ensure objectIds is serialized with the correct key name (getter returns _objectIds)
|
|
321
|
+
clonedProps.objectIds = this.objectIds;
|
|
322
|
+
delete clonedProps._objectIds;
|
|
323
|
+
if (element && typeof element === 'object' && 'nodeType' in element && element.nodeType === 1) {
|
|
324
|
+
clonedProps.element = element.cloneNode(true);
|
|
325
|
+
}
|
|
326
|
+
// Convert Map to plain object for serialization
|
|
327
|
+
clonedProps.unchangedObjectSnapshots = Object.fromEntries(this.unchangedObjectSnapshots);
|
|
328
|
+
return clonedProps;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Deserializes a plain object back into a KritzelSelectionGroup instance.
|
|
332
|
+
* Restores objectIds through the setter and reconstructs the unchangedObjectSnapshots Map.
|
|
333
|
+
* @template T - The return type for the deserialized object
|
|
334
|
+
* @param object - The serialized object containing the selection group data
|
|
335
|
+
* @returns The deserialized selection group instance cast to type T
|
|
336
|
+
*/
|
|
337
|
+
deserialize(object) {
|
|
338
|
+
// First, deserialize all base properties using parent's deserialize
|
|
339
|
+
super.deserialize(object);
|
|
340
|
+
// Selection groups intentionally persist their selected state.
|
|
341
|
+
this.isSelected = object.isSelected === true;
|
|
342
|
+
// Restore objectIds through setter
|
|
343
|
+
if (object.objectIds) {
|
|
344
|
+
this.objectIds = object.objectIds;
|
|
345
|
+
}
|
|
346
|
+
// Restore the Map from serialized object
|
|
347
|
+
if (object.unchangedObjectSnapshots) {
|
|
348
|
+
this.unchangedObjectSnapshots = new Map(Object.entries(object.unchangedObjectSnapshots));
|
|
349
|
+
}
|
|
350
|
+
if (this.shouldSyncChildSelectionState()) {
|
|
351
|
+
this.syncChildSelectionState(this.objects);
|
|
352
|
+
}
|
|
353
|
+
return this;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Persists the selection group's current state to the store.
|
|
357
|
+
* Only updates the selection group itself, not child objects.
|
|
358
|
+
* Child objects are updated during their respective move/resize/rotate operations
|
|
359
|
+
* to avoid redundant Yjs updates.
|
|
360
|
+
*/
|
|
361
|
+
update() {
|
|
362
|
+
// Only update the selection group itself
|
|
363
|
+
// Child objects are already updated during move/resize/rotate operations
|
|
364
|
+
// Updating them again here would create redundant y.js updates
|
|
365
|
+
this._core.store.objects.update(this);
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Moves the selection group and all its contained objects by calculating the delta from drag coordinates.
|
|
369
|
+
* Updates anchor points for any lines connected to the moved objects.
|
|
370
|
+
*
|
|
371
|
+
* Child objects are moved locally without writing to Yjs during the drag to avoid
|
|
372
|
+
* N+1 full serializations per frame, which can overwhelm the WebSocket server.
|
|
373
|
+
* Children must be persisted at drag end via {@link persistChildren}.
|
|
374
|
+
*
|
|
375
|
+
* @param startX - The starting x-coordinate of the drag operation (in screen space)
|
|
376
|
+
* @param startY - The starting y-coordinate of the drag operation (in screen space)
|
|
377
|
+
* @param endX - The ending x-coordinate of the drag operation (in screen space)
|
|
378
|
+
* @param endY - The ending y-coordinate of the drag operation (in screen space)
|
|
379
|
+
*/
|
|
380
|
+
move(startX, startY, endX, endY) {
|
|
381
|
+
const deltaX = (startX - endX) / this._core.store.state.scale;
|
|
382
|
+
const deltaY = (startY - endY) / this._core.store.state.scale;
|
|
383
|
+
this.translateX += deltaX;
|
|
384
|
+
this.translateY += deltaY;
|
|
385
|
+
const now = Date.now();
|
|
386
|
+
const shouldPersistChildren = now - this._lastChildPersistTime >= KritzelSelectionGroup.CHILD_PERSIST_THROTTLE_MS;
|
|
387
|
+
if (shouldPersistChildren) {
|
|
388
|
+
// Periodically persist children to Yjs so remote peers see movement in real-time
|
|
389
|
+
this._core.store.objects.transaction(() => {
|
|
390
|
+
this._core.store.objects.update(this);
|
|
391
|
+
const children = this.objects;
|
|
392
|
+
for (const obj of children) {
|
|
393
|
+
obj.move(startX, startY, endX, endY);
|
|
394
|
+
this._core.anchorManager.updateAnchorsForObject(obj.id);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
this._lastChildPersistTime = now;
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
// Between throttle intervals, only persist the selection group itself.
|
|
401
|
+
// Children are moved locally for smooth local rendering.
|
|
402
|
+
this._core.store.objects.update(this);
|
|
403
|
+
this._core.store.objects.withLocalUpdatesOnly(() => {
|
|
404
|
+
const children = this.objects;
|
|
405
|
+
for (const obj of children) {
|
|
406
|
+
obj.move(startX, startY, endX, endY);
|
|
407
|
+
this._core.anchorManager.updateAnchorsForObject(obj.id);
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
// Update snapshots
|
|
412
|
+
this.unchangedObjectSnapshots.forEach(snapshot => {
|
|
413
|
+
snapshot.translateX += deltaX;
|
|
414
|
+
snapshot.translateY += deltaY;
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Persists all child objects to Yjs in a single transaction.
|
|
419
|
+
* Must be called after a drag operation completes to sync the final
|
|
420
|
+
* positions of children that were only updated locally during the drag.
|
|
421
|
+
*/
|
|
422
|
+
persistChildren() {
|
|
423
|
+
this._core.store.objects.transaction(() => {
|
|
424
|
+
const children = this.objects;
|
|
425
|
+
for (const obj of children) {
|
|
426
|
+
this._core.store.objects.update(obj);
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Returns true when the selection contains a line anchored to an object
|
|
432
|
+
* that is not part of this selection group.
|
|
433
|
+
*/
|
|
434
|
+
hasExternalAnchoredLineConnections(cachedObjects) {
|
|
435
|
+
const children = cachedObjects ?? this.objects;
|
|
436
|
+
if (children.length === 0) {
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
const selectedIds = new Set(children.map(child => child.id));
|
|
440
|
+
for (const child of children) {
|
|
441
|
+
if (!KritzelClassHelper.isInstanceOf(child, 'KritzelLine')) {
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
const line = child;
|
|
445
|
+
if (line.startAnchor && !selectedIds.has(line.startAnchor.objectId)) {
|
|
446
|
+
return true;
|
|
447
|
+
}
|
|
448
|
+
if (line.endAnchor && !selectedIds.has(line.endAnchor.objectId)) {
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Resizes the selection group and scales all contained objects proportionally.
|
|
456
|
+
* Uses snapshot values to avoid compounding errors during continuous drag operations.
|
|
457
|
+
* Handles rotation by transforming coordinates into and out of the group's local space.
|
|
458
|
+
* Updates anchor points for any lines connected to resized objects.
|
|
459
|
+
* @param x - The new x-coordinate for the selection group's position
|
|
460
|
+
* @param y - The new y-coordinate for the selection group's position
|
|
461
|
+
* @param width - The new width for the selection group
|
|
462
|
+
* @param height - The new height for the selection group
|
|
463
|
+
*/
|
|
464
|
+
resize(x, y, width, height) {
|
|
465
|
+
// Use snapshot dimensions for scale factor calculation to avoid compounding errors during drag
|
|
466
|
+
const widthScaleFactor = width / this.snapshotWidth;
|
|
467
|
+
const heightScaleFactor = height / this.snapshotHeight;
|
|
468
|
+
// Calculate old center from snapshot position
|
|
469
|
+
const snapshotTotalWidth = this.snapshotWidth + this.padding * 2;
|
|
470
|
+
const snapshotTotalHeight = this.snapshotHeight + this.padding * 2;
|
|
471
|
+
const oldCenterX = this.snapshotTranslateX + snapshotTotalWidth / 2 / this.scale;
|
|
472
|
+
const oldCenterY = this.snapshotTranslateY + snapshotTotalHeight / 2 / this.scale;
|
|
473
|
+
// Calculate new center
|
|
474
|
+
const newTotalWidth = width + this.padding * 2;
|
|
475
|
+
const newTotalHeight = height + this.padding * 2;
|
|
476
|
+
const newCenterX = x + newTotalWidth / 2 / this.scale;
|
|
477
|
+
const newCenterY = y + newTotalHeight / 2 / this.scale;
|
|
478
|
+
const rotation = this.rotation;
|
|
479
|
+
const cos = Math.cos(-rotation);
|
|
480
|
+
const sin = Math.sin(-rotation);
|
|
481
|
+
const cosR = Math.cos(rotation);
|
|
482
|
+
const sinR = Math.sin(rotation);
|
|
483
|
+
this._core.store.objects.transaction(() => {
|
|
484
|
+
// Cache objects once to avoid repeated getter calls during resize
|
|
485
|
+
const children = this.objects;
|
|
486
|
+
children.forEach(child => {
|
|
487
|
+
// Use snapshot values for child to avoid compounding errors during drag
|
|
488
|
+
const snapshot = this.unchangedObjectSnapshots.get(child.id);
|
|
489
|
+
if (!snapshot)
|
|
490
|
+
return;
|
|
491
|
+
// Calculate child center from snapshot position
|
|
492
|
+
const childCenterX = snapshot.translateX + snapshot.totalWidth / 2 / snapshot.scale;
|
|
493
|
+
const childCenterY = snapshot.translateY + snapshot.totalHeight / 2 / snapshot.scale;
|
|
494
|
+
// Vector from old group center to child center
|
|
495
|
+
const dx = childCenterX - oldCenterX;
|
|
496
|
+
const dy = childCenterY - oldCenterY;
|
|
497
|
+
// Rotate to local space (align with group axes)
|
|
498
|
+
const localX = dx * cos - dy * sin;
|
|
499
|
+
const localY = dx * sin + dy * cos;
|
|
500
|
+
// Scale in local space
|
|
501
|
+
const scaledLocalX = localX * widthScaleFactor;
|
|
502
|
+
const scaledLocalY = localY * heightScaleFactor;
|
|
503
|
+
// Rotate back to world space
|
|
504
|
+
const rotatedX = scaledLocalX * cosR - scaledLocalY * sinR;
|
|
505
|
+
const rotatedY = scaledLocalX * sinR + scaledLocalY * cosR;
|
|
506
|
+
// New child center
|
|
507
|
+
const newChildCenterX = newCenterX + rotatedX;
|
|
508
|
+
const newChildCenterY = newCenterY + rotatedY;
|
|
509
|
+
// New child top-left
|
|
510
|
+
// Calculate relative rotation using snapshot rotation
|
|
511
|
+
const relativeRotation = snapshot.rotation - rotation;
|
|
512
|
+
const cosRel = Math.cos(relativeRotation);
|
|
513
|
+
const sinRel = Math.sin(relativeRotation);
|
|
514
|
+
// Project the group's scale factors onto the child's local axes
|
|
515
|
+
// We use absolute values because scaling is magnitude-based
|
|
516
|
+
// If the child is aligned (0 deg), cos=1, sin=0 -> scales match
|
|
517
|
+
// If the child is 90 deg, cos=0, sin=1 -> scales swap
|
|
518
|
+
const newChildWidthScale = Math.sqrt(Math.pow(widthScaleFactor * cosRel, 2) + Math.pow(heightScaleFactor * sinRel, 2));
|
|
519
|
+
const newChildHeightScale = Math.sqrt(Math.pow(widthScaleFactor * sinRel, 2) + Math.pow(heightScaleFactor * cosRel, 2));
|
|
520
|
+
// Use snapshot dimensions to calculate new size, avoiding compounding errors
|
|
521
|
+
const updatedWidth = snapshot.width * newChildWidthScale;
|
|
522
|
+
const updatedHeight = snapshot.height * newChildHeightScale;
|
|
523
|
+
const updatedTotalWidth = updatedWidth + child.padding * 2;
|
|
524
|
+
const updatedTotalHeight = updatedHeight + child.padding * 2;
|
|
525
|
+
const updatedX = newChildCenterX - updatedTotalWidth / 2 / child.scale;
|
|
526
|
+
const updatedY = newChildCenterY - updatedTotalHeight / 2 / child.scale;
|
|
527
|
+
child.resize(updatedX, updatedY, updatedWidth, updatedHeight);
|
|
528
|
+
// Update any lines that are anchored to this child object
|
|
529
|
+
this._core.anchorManager.updateAnchorsForObject(child.id);
|
|
530
|
+
});
|
|
531
|
+
// Refresh dimensions and update the SelectionGroup to propagate changes to other tabs
|
|
532
|
+
// Note: Don't capture new snapshots here - we need to preserve the original snapshots
|
|
533
|
+
// until the transform operation is complete to avoid compounding errors during drag
|
|
534
|
+
this.refreshObjectDimensions(children);
|
|
535
|
+
this._core.store.objects.update(this);
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Rotates the selection group and all contained objects around the group's center.
|
|
540
|
+
* Objects rotate around the group center while also adjusting their individual rotations.
|
|
541
|
+
* Uses snapshot values to calculate incremental rotation from the initial state.
|
|
542
|
+
* @param value - The new rotation angle in radians for the selection group
|
|
543
|
+
*/
|
|
544
|
+
rotate(value) {
|
|
545
|
+
this.rotation = value;
|
|
546
|
+
// Use the snapshot center as the rotation pivot rather than the live center.
|
|
547
|
+
// refreshObjectDimensions() below can grow the box when an anchored line is pinned
|
|
548
|
+
// to an external object; keeping the pivot fixed prevents the children from drifting.
|
|
549
|
+
const snapshotTotalWidth = this.snapshotWidth + this.padding * 2;
|
|
550
|
+
const snapshotTotalHeight = this.snapshotHeight + this.padding * 2;
|
|
551
|
+
const centerX = this.snapshotTranslateX + snapshotTotalWidth / 2 / this.scale;
|
|
552
|
+
const centerY = this.snapshotTranslateY + snapshotTotalHeight / 2 / this.scale;
|
|
553
|
+
const angle = value - this.snapshotRotation;
|
|
554
|
+
const cos = Math.cos(angle);
|
|
555
|
+
const sin = Math.sin(angle);
|
|
556
|
+
// Cache objects once to avoid repeated getter calls during rotation
|
|
557
|
+
const children = this.objects;
|
|
558
|
+
this._core.store.objects.transaction(() => {
|
|
559
|
+
// Update the SelectionGroup itself to propagate rotation to other tabs
|
|
560
|
+
this._core.store.objects.update(this);
|
|
561
|
+
const childCount = children.length;
|
|
562
|
+
for (const child of children) {
|
|
563
|
+
const unchangedSnapshot = this.unchangedObjectSnapshots.get(child.id);
|
|
564
|
+
if (!unchangedSnapshot)
|
|
565
|
+
continue;
|
|
566
|
+
const offsetX = this.getOffsetXToCenterFromSnapshot(unchangedSnapshot);
|
|
567
|
+
const offsetY = this.getOffsetYToCenterFromSnapshot(unchangedSnapshot);
|
|
568
|
+
const rotatedX = cos * offsetX - sin * offsetY;
|
|
569
|
+
const rotatedY = sin * offsetX + cos * offsetY;
|
|
570
|
+
child.translateX = centerX + rotatedX - child.totalWidth / 2 / child.scale;
|
|
571
|
+
child.translateY = centerY + rotatedY - child.totalHeight / 2 / child.scale;
|
|
572
|
+
child.rotate(childCount === 1 ? value : unchangedSnapshot.rotation + angle);
|
|
573
|
+
}
|
|
574
|
+
// Re-sync any anchored lines so their stored geometry follows the rotated objects
|
|
575
|
+
// (including lines anchored to an object outside this selection), then grow the
|
|
576
|
+
// selection box to enclose the reshaped lines. Rotation sets state.isRotating
|
|
577
|
+
// rather than state.isDragging, so the refresh must be triggered explicitly here.
|
|
578
|
+
const anchorTargetIds = new Set();
|
|
579
|
+
for (const child of children) {
|
|
580
|
+
anchorTargetIds.add(child.id);
|
|
581
|
+
if (KritzelClassHelper.isInstanceOf(child, 'KritzelLine')) {
|
|
582
|
+
const line = child;
|
|
583
|
+
if (line.startAnchor)
|
|
584
|
+
anchorTargetIds.add(line.startAnchor.objectId);
|
|
585
|
+
if (line.endAnchor)
|
|
586
|
+
anchorTargetIds.add(line.endAnchor.objectId);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
let hasAnchoredLines = false;
|
|
590
|
+
for (const targetId of anchorTargetIds) {
|
|
591
|
+
if (this._core.anchorManager.getLinesAnchoredTo(targetId).length > 0) {
|
|
592
|
+
hasAnchoredLines = true;
|
|
593
|
+
this._core.anchorManager.updateAnchorsForObject(targetId);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
if (hasAnchoredLines) {
|
|
597
|
+
this.refreshObjectDimensions(children);
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Creates a deep copy of the selection group including copies of all contained objects.
|
|
603
|
+
* Preserves z-index ordering by sorting children before copying.
|
|
604
|
+
* Copies maintain their relative positions within the new selection group.
|
|
605
|
+
* @returns A new KritzelSelectionGroup with copies of all contained objects
|
|
606
|
+
*/
|
|
607
|
+
copy() {
|
|
608
|
+
const selectionGroup = KritzelSelectionGroup.create(this._core);
|
|
609
|
+
const children = this.objects;
|
|
610
|
+
// Sort children by zIndex and copy them
|
|
611
|
+
const sortedChildren = [...children].sort((a, b) => a.zIndex - b.zIndex);
|
|
612
|
+
for (const obj of sortedChildren) {
|
|
613
|
+
const copiedObject = obj.copy();
|
|
614
|
+
selectionGroup.addOrRemove(copiedObject);
|
|
615
|
+
}
|
|
616
|
+
selectionGroup.captureUnchangedSnapshots();
|
|
617
|
+
if (children.length === 1) {
|
|
618
|
+
selectionGroup.rotation = children[0].rotation;
|
|
619
|
+
}
|
|
620
|
+
return selectionGroup;
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Refreshes the selection group's bounding box dimensions based on contained objects.
|
|
624
|
+
* @param cachedObjects Optional pre-fetched objects array to avoid redundant getter calls
|
|
625
|
+
* @param skipPersist If true, only updates local state without persisting to Yjs (used during undo/redo)
|
|
626
|
+
*/
|
|
627
|
+
refreshObjectDimensions(cachedObjects, skipPersist = false) {
|
|
628
|
+
const previous = {
|
|
629
|
+
minX: this.minX,
|
|
630
|
+
minY: this.minY,
|
|
631
|
+
maxX: this.maxX,
|
|
632
|
+
maxY: this.maxY,
|
|
633
|
+
width: this.width,
|
|
634
|
+
height: this.height,
|
|
635
|
+
translateX: this.translateX,
|
|
636
|
+
translateY: this.translateY,
|
|
637
|
+
};
|
|
638
|
+
const children = cachedObjects ?? this.objects;
|
|
639
|
+
if (children.length === 1) {
|
|
640
|
+
const obj = children[0];
|
|
641
|
+
this.minX = obj.boundingBox.x / this.scale;
|
|
642
|
+
this.maxX = obj.boundingBox.x / this.scale + obj.boundingBox.width;
|
|
643
|
+
this.minY = obj.boundingBox.y / this.scale;
|
|
644
|
+
this.maxY = obj.boundingBox.y / this.scale + obj.boundingBox.height;
|
|
645
|
+
this.translateX = (this.minX - this.padding) * this.scale;
|
|
646
|
+
this.translateY = (this.minY - this.padding) * this.scale;
|
|
647
|
+
this.width = (this.maxX - this.minX - this.padding) * this.scale;
|
|
648
|
+
this.height = (this.maxY - this.minY - this.padding) * this.scale;
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
const rotation = this.rotation;
|
|
652
|
+
const cos = Math.cos(-rotation);
|
|
653
|
+
const sin = Math.sin(-rotation);
|
|
654
|
+
let minX = Infinity;
|
|
655
|
+
let maxX = -Infinity;
|
|
656
|
+
let minY = Infinity;
|
|
657
|
+
let maxY = -Infinity;
|
|
658
|
+
children.forEach(obj => {
|
|
659
|
+
const polygon = obj.rotatedPolygon;
|
|
660
|
+
const corners = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
|
|
661
|
+
corners.forEach(corner => {
|
|
662
|
+
// Rotate corner into local space (aligned with group rotation)
|
|
663
|
+
const rx = corner.x * cos - corner.y * sin;
|
|
664
|
+
const ry = corner.x * sin + corner.y * cos;
|
|
665
|
+
if (rx < minX)
|
|
666
|
+
minX = rx;
|
|
667
|
+
if (rx > maxX)
|
|
668
|
+
maxX = rx;
|
|
669
|
+
if (ry < minY)
|
|
670
|
+
minY = ry;
|
|
671
|
+
if (ry > maxY)
|
|
672
|
+
maxY = ry;
|
|
673
|
+
});
|
|
674
|
+
});
|
|
675
|
+
// Dimensions in world units (unrotated)
|
|
676
|
+
const worldWidth = maxX - minX;
|
|
677
|
+
const worldHeight = maxY - minY;
|
|
678
|
+
this.width = (worldWidth - this.padding) * this.scale;
|
|
679
|
+
this.height = (worldHeight - this.padding) * this.scale;
|
|
680
|
+
// Center of the box in rotated space
|
|
681
|
+
const cRx = (minX + maxX) / 2;
|
|
682
|
+
const cRy = (minY + maxY) / 2;
|
|
683
|
+
// Rotate center back to world space
|
|
684
|
+
const cosR = Math.cos(rotation);
|
|
685
|
+
const sinR = Math.sin(rotation);
|
|
686
|
+
const cx = cRx * cosR - cRy * sinR;
|
|
687
|
+
const cy = cRx * sinR + cRy * cosR;
|
|
688
|
+
this.translateX = cx - (this.width / this.scale + 2 * this.padding) / 2;
|
|
689
|
+
this.translateY = cy - (this.height / this.scale + 2 * this.padding) / 2;
|
|
690
|
+
}
|
|
691
|
+
const hasChanged = Math.abs(this.minX - previous.minX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
692
|
+
Math.abs(this.minY - previous.minY) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
693
|
+
Math.abs(this.maxX - previous.maxX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
694
|
+
Math.abs(this.maxY - previous.maxY) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
695
|
+
Math.abs(this.width - previous.width) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
696
|
+
Math.abs(this.height - previous.height) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
697
|
+
Math.abs(this.translateX - previous.translateX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
698
|
+
Math.abs(this.translateY - previous.translateY) > KritzelSelectionGroup.DIMENSION_EPSILON;
|
|
699
|
+
if (!skipPersist && hasChanged) {
|
|
700
|
+
this._core.store.objects.update(this);
|
|
701
|
+
}
|
|
702
|
+
return hasChanged;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Calculates the horizontal offset from the group's center to an object's center using snapshot data.
|
|
706
|
+
* Used during rotation to maintain relative positioning of objects.
|
|
707
|
+
* @param snapshot - The snapshot containing the object's recorded position and dimensions
|
|
708
|
+
* @returns The x-axis offset from the group center to the object center
|
|
709
|
+
*/
|
|
710
|
+
getOffsetXToCenterFromSnapshot(snapshot) {
|
|
711
|
+
const objCenterX = snapshot.translateX + snapshot.totalWidth / snapshot.scale / 2;
|
|
712
|
+
const groupCenterX = this.snapshotTranslateX + (this.snapshotWidth + this.padding * 2) / this.scale / 2;
|
|
713
|
+
return objCenterX - groupCenterX;
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Calculates the vertical offset from the group's center to an object's center using snapshot data.
|
|
717
|
+
* Used during rotation to maintain relative positioning of objects.
|
|
718
|
+
* @param snapshot - The snapshot containing the object's recorded position and dimensions
|
|
719
|
+
* @returns The y-axis offset from the group center to the object center
|
|
720
|
+
*/
|
|
721
|
+
getOffsetYToCenterFromSnapshot(snapshot) {
|
|
722
|
+
const objCenterY = snapshot.translateY + snapshot.totalHeight / snapshot.scale / 2;
|
|
723
|
+
const groupCenterY = this.snapshotTranslateY + (this.snapshotHeight + this.padding * 2) / this.scale / 2;
|
|
724
|
+
return objCenterY - groupCenterY;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Tests whether a point intersects with the selection group or any of its contained objects.
|
|
728
|
+
* For a single line object, delegates hit testing to the line's own hit test (more precise).
|
|
729
|
+
* For other cases, first checks the group's rotated polygon bounding box, then individual objects.
|
|
730
|
+
* @param x - The x-coordinate of the point to test (in world space)
|
|
731
|
+
* @param y - The y-coordinate of the point to test (in world space)
|
|
732
|
+
* @returns True if the point is inside the selection group or any contained object, false otherwise
|
|
733
|
+
*/
|
|
734
|
+
hitTest(x, y) {
|
|
735
|
+
const children = this.objects;
|
|
736
|
+
const isSingleLine = children.length === 1 && children[0].__class__ === 'KritzelLine';
|
|
737
|
+
if (isSingleLine) {
|
|
738
|
+
return children[0].hitTest(x, y);
|
|
739
|
+
}
|
|
740
|
+
const polygon = this.rotatedPolygon;
|
|
741
|
+
const polyPoints = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
|
|
742
|
+
if (KritzelGeometryHelper.isPointInPolygon({ x, y }, polyPoints)) {
|
|
743
|
+
return true;
|
|
744
|
+
}
|
|
745
|
+
for (const obj of children) {
|
|
746
|
+
if (obj.hitTest(x, y)) {
|
|
747
|
+
return true;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
}
|