kritzel-stencil 0.0.169 → 0.0.171
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/cjs/{default-line-tool.config-Bs88k0jE.js → default-line-tool.config-JuTDR6PF.js} +514 -13
- package/dist/cjs/default-line-tool.config-JuTDR6PF.js.map +1 -0
- package/dist/cjs/index.cjs.js +2 -1
- package/dist/cjs/index.cjs.js.map +1 -1
- package/dist/cjs/kritzel-color_22.cjs.entry.js +170 -41
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/stencil.cjs.js +1 -1
- package/dist/collection/classes/core/core.class.js +89 -1
- package/dist/collection/classes/core/core.class.js.map +1 -1
- package/dist/collection/classes/core/reviver.class.js +4 -0
- package/dist/collection/classes/core/reviver.class.js.map +1 -1
- package/dist/collection/classes/handlers/key.handler.js +3 -0
- package/dist/collection/classes/handlers/key.handler.js.map +1 -1
- package/dist/collection/classes/handlers/move.handler.js +63 -0
- package/dist/collection/classes/handlers/move.handler.js.map +1 -1
- package/dist/collection/classes/handlers/rotation.handler.js +2 -0
- package/dist/collection/classes/handlers/rotation.handler.js.map +1 -1
- package/dist/collection/classes/handlers/selection.handler.js +23 -5
- package/dist/collection/classes/handlers/selection.handler.js.map +1 -1
- package/dist/collection/classes/objects/group.class.js +394 -0
- package/dist/collection/classes/objects/group.class.js.map +1 -0
- package/dist/collection/classes/objects/line.class.js +2 -1
- package/dist/collection/classes/objects/line.class.js.map +1 -1
- package/dist/collection/classes/objects/selection-group.class.js +19 -3
- package/dist/collection/classes/objects/selection-group.class.js.map +1 -1
- package/dist/collection/classes/registries/icon-registry.class.js +2 -0
- package/dist/collection/classes/registries/icon-registry.class.js.map +1 -1
- package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +17 -2
- package/dist/collection/components/core/kritzel-editor/kritzel-editor.js.map +1 -1
- package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +44 -4
- package/dist/collection/components/core/kritzel-engine/kritzel-engine.js.map +1 -1
- package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.js +3 -66
- package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.js.map +1 -1
- package/dist/collection/components/ui/kritzel-context-menu/kritzel-context-menu.js +38 -1
- package/dist/collection/components/ui/kritzel-context-menu/kritzel-context-menu.js.map +1 -1
- package/dist/collection/components/ui/kritzel-control-brush-config/kritzel-control-brush-config.js +2 -2
- package/dist/collection/components/ui/kritzel-control-text-config/kritzel-control-text-config.js +2 -2
- package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +4 -4
- package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js.map +1 -1
- package/dist/collection/components/ui/kritzel-utility-panel/kritzel-utility-panel.js +1 -1
- package/dist/collection/helpers/event.helper.js +10 -2
- package/dist/collection/helpers/event.helper.js.map +1 -1
- package/dist/collection/index.js +1 -0
- package/dist/collection/index.js.map +1 -1
- package/dist/components/index.js +3 -3
- package/dist/components/kritzel-brush-style.js +1 -1
- package/dist/components/kritzel-context-menu.js +1 -1
- package/dist/components/kritzel-control-brush-config.js +1 -1
- package/dist/components/kritzel-control-text-config.js +1 -1
- package/dist/components/kritzel-controls.js +1 -1
- package/dist/components/kritzel-editor.js +28 -13
- package/dist/components/kritzel-editor.js.map +1 -1
- package/dist/components/kritzel-engine.js +1 -1
- package/dist/components/kritzel-icon.js +1 -1
- package/dist/components/kritzel-menu-item.js +1 -1
- package/dist/components/kritzel-menu.js +1 -1
- package/dist/components/kritzel-split-button.js +1 -1
- package/dist/components/kritzel-tooltip.js +1 -1
- package/dist/components/kritzel-utility-panel.js +1 -1
- package/dist/components/kritzel-workspace-manager.js +1 -1
- package/dist/components/{p-Bwv1dxAB.js → p-BUsg2vtg.js} +12 -4
- package/dist/components/{p-Bwv1dxAB.js.map → p-BUsg2vtg.js.map} +1 -1
- package/dist/components/{p-xcQV8l_c.js → p-BhLtP5Cg.js} +12 -12
- package/dist/components/p-BhLtP5Cg.js.map +1 -0
- package/dist/components/{p-5OECjGHq.js → p-C29Efgmc.js} +5 -5
- package/dist/components/{p-5OECjGHq.js.map → p-C29Efgmc.js.map} +1 -1
- package/dist/components/{p-BoazmhlG.js → p-C6-tSCMR.js} +3 -3
- package/dist/components/{p-BoazmhlG.js.map → p-C6-tSCMR.js.map} +1 -1
- package/dist/components/{p-Cv4BGNPb.js → p-CIts5Uma.js} +4 -2
- package/dist/components/p-CIts5Uma.js.map +1 -0
- package/dist/components/{p-D8L0t-Ro.js → p-CO8bwl_3.js} +5 -5
- package/dist/components/{p-D8L0t-Ro.js.map → p-CO8bwl_3.js.map} +1 -1
- package/dist/components/{p-f_ut_1_F.js → p-D0UgEnEL.js} +611 -20
- package/dist/components/p-D0UgEnEL.js.map +1 -0
- package/dist/components/{p-BSBMBjhq.js → p-DGnDUmrk.js} +4 -4
- package/dist/components/{p-BSBMBjhq.js.map → p-DGnDUmrk.js.map} +1 -1
- package/dist/components/{p-C1S1zPH-.js → p-DO4auCYf.js} +4 -29
- package/dist/components/p-DO4auCYf.js.map +1 -0
- package/dist/components/{p-D1YAsWrL.js → p-nIBAWFcK.js} +41 -4
- package/dist/components/p-nIBAWFcK.js.map +1 -0
- package/dist/components/{p-BmdYFhLx.js → p-nZdy-Ii5.js} +4 -4
- package/dist/components/{p-BmdYFhLx.js.map → p-nZdy-Ii5.js.map} +1 -1
- package/dist/components/{p-CiM-IPaD.js → p-y25EBKEA.js} +5 -5
- package/dist/components/{p-CiM-IPaD.js.map → p-y25EBKEA.js.map} +1 -1
- package/dist/esm/{default-line-tool.config-PvsWHpO2.js → default-line-tool.config-CuBm2vpW.js} +514 -14
- package/dist/esm/default-line-tool.config-CuBm2vpW.js.map +1 -0
- package/dist/esm/index.js +2 -2
- package/dist/esm/kritzel-color_22.entry.js +170 -41
- package/dist/esm/loader.js +1 -1
- package/dist/esm/stencil.js +1 -1
- package/dist/stencil/index.esm.js +1 -1
- package/dist/stencil/p-CuBm2vpW.js +2 -0
- package/dist/stencil/p-CuBm2vpW.js.map +1 -0
- package/dist/stencil/p-d3f7214e.entry.js +10 -0
- package/dist/stencil/p-d3f7214e.entry.js.map +1 -0
- package/dist/stencil/stencil.esm.js +1 -1
- package/dist/types/classes/core/core.class.d.ts +11 -1
- package/dist/types/classes/handlers/move.handler.d.ts +11 -0
- package/dist/types/classes/objects/group.class.d.ts +97 -0
- package/dist/types/classes/objects/selection-group.class.d.ts +5 -0
- package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +2 -0
- package/dist/types/components/shared/kritzel-tooltip/kritzel-tooltip.d.ts +0 -3
- package/dist/types/components/ui/kritzel-context-menu/kritzel-context-menu.d.ts +3 -0
- package/dist/types/components.d.ts +4 -18
- package/dist/types/index.d.ts +1 -0
- package/package.json +1 -1
- package/dist/cjs/default-line-tool.config-Bs88k0jE.js.map +0 -1
- package/dist/components/p-C1S1zPH-.js.map +0 -1
- package/dist/components/p-Cv4BGNPb.js.map +0 -1
- package/dist/components/p-D1YAsWrL.js.map +0 -1
- package/dist/components/p-f_ut_1_F.js.map +0 -1
- package/dist/components/p-xcQV8l_c.js.map +0 -1
- package/dist/esm/default-line-tool.config-PvsWHpO2.js.map +0 -1
- package/dist/stencil/p-9913896b.entry.js +0 -10
- package/dist/stencil/p-9913896b.entry.js.map +0 -1
- package/dist/stencil/p-PvsWHpO2.js +0 -2
- package/dist/stencil/p-PvsWHpO2.js.map +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { p as proxyCustomElement, H, c as createEvent, h, d as Host } from './p-CwkUrTy1.js';
|
|
2
2
|
import { K as KritzelMouseButton } from './p-D8W6LE-c.js';
|
|
3
|
-
import { e as KritzelBaseObject, f as KritzelGeometryHelper, g as KritzelBaseTool, h as KritzelEventHelper, i as KritzelToolRegistry, S as Schema, s as schema, j as addListNodes, E as EditorView, k as EditorState, l as keymap, T as TextSelection, d as KritzelKeyboardHelper, m as baseKeymap, c as KritzelTextTool, b as KritzelBrushTool, K as KritzelText, a as KritzelPath } from './p-
|
|
4
|
-
import { K as KritzelContextMenu, d as defineCustomElement$3 } from './p-
|
|
3
|
+
import { e as KritzelBaseObject, f as KritzelGeometryHelper, g as KritzelBaseTool, h as KritzelEventHelper, i as KritzelToolRegistry, S as Schema, s as schema, j as addListNodes, E as EditorView, k as EditorState, l as keymap, T as TextSelection, d as KritzelKeyboardHelper, m as baseKeymap, c as KritzelTextTool, b as KritzelBrushTool, K as KritzelText, a as KritzelPath } from './p-BUsg2vtg.js';
|
|
4
|
+
import { K as KritzelContextMenu, d as defineCustomElement$3 } from './p-nIBAWFcK.js';
|
|
5
5
|
import { O as ObjectHelper } from './p-B0kd2rUI.js';
|
|
6
6
|
import { K as KritzelDevicesHelper } from './p-l10It7Nm.js';
|
|
7
|
-
import { K as KritzelIconRegistry, d as defineCustomElement$1 } from './p-
|
|
7
|
+
import { K as KritzelIconRegistry, d as defineCustomElement$1 } from './p-CIts5Uma.js';
|
|
8
8
|
import { K as KritzelWorkspace } from './p-n789Y3S-.js';
|
|
9
9
|
import { d as defineCustomElement$2 } from './p-B_3OZeom.js';
|
|
10
10
|
|
|
@@ -195,7 +195,8 @@ class KritzelLine extends KritzelBaseObject {
|
|
|
195
195
|
this._core.store.state.objects.update(this);
|
|
196
196
|
}
|
|
197
197
|
hitTest(x, y) {
|
|
198
|
-
const
|
|
198
|
+
const minHitThreshold = 20;
|
|
199
|
+
const strokeWidth = Math.max(this.strokeWidth, minHitThreshold);
|
|
199
200
|
const halfStroke = strokeWidth / this.scale / 2;
|
|
200
201
|
if (this._adjustedPoints === null) {
|
|
201
202
|
this._adjustedPoints = this.computeAdjustedPoints();
|
|
@@ -628,6 +629,399 @@ class KritzelLine extends KritzelBaseObject {
|
|
|
628
629
|
}
|
|
629
630
|
}
|
|
630
631
|
|
|
632
|
+
/**
|
|
633
|
+
* KritzelGroup represents a permanent grouping of objects that act as a single unit.
|
|
634
|
+
*
|
|
635
|
+
* Unlike KritzelSelectionGroup (which is ephemeral and used for temporary multi-selection),
|
|
636
|
+
* KritzelGroup is persisted and saved with the workspace. Grouped objects move, resize,
|
|
637
|
+
* rotate, copy, and delete together as one unit.
|
|
638
|
+
*
|
|
639
|
+
* Key features:
|
|
640
|
+
* - Supports nested groups (groups can contain other groups)
|
|
641
|
+
* - Clicking any child selects the entire group
|
|
642
|
+
* - Can be ungrouped to restore children to top-level objects
|
|
643
|
+
*/
|
|
644
|
+
class KritzelGroup extends KritzelBaseObject {
|
|
645
|
+
__class__ = 'KritzelGroup';
|
|
646
|
+
/**
|
|
647
|
+
* IDs of child objects within this group.
|
|
648
|
+
* Children can be any KritzelBaseObject, including other KritzelGroups for nesting.
|
|
649
|
+
*/
|
|
650
|
+
childIds = [];
|
|
651
|
+
/**
|
|
652
|
+
* Snapshots of child states for transformation operations (resize, rotate).
|
|
653
|
+
* Similar pattern to KritzelSelectionGroup.
|
|
654
|
+
*/
|
|
655
|
+
unchangedChildSnapshots = new Map();
|
|
656
|
+
snapshotRotation = 0;
|
|
657
|
+
snapshotTranslateX = 0;
|
|
658
|
+
snapshotTranslateY = 0;
|
|
659
|
+
snapshotTotalWidth = 0;
|
|
660
|
+
snapshotTotalHeight = 0;
|
|
661
|
+
snapshotScale = 1;
|
|
662
|
+
/**
|
|
663
|
+
* Retrieves the actual child objects from the store by their IDs.
|
|
664
|
+
*/
|
|
665
|
+
get children() {
|
|
666
|
+
return this.childIds
|
|
667
|
+
.map(id => {
|
|
668
|
+
const found = this._core.store.state.objects.filter(obj => obj.id === id);
|
|
669
|
+
return found.length > 0 ? found[0] : null;
|
|
670
|
+
})
|
|
671
|
+
.filter((obj) => obj !== null);
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Returns the number of children in this group.
|
|
675
|
+
*/
|
|
676
|
+
get length() {
|
|
677
|
+
return this.childIds.length;
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Creates a new empty KritzelGroup.
|
|
681
|
+
*/
|
|
682
|
+
static create(core) {
|
|
683
|
+
const group = new KritzelGroup();
|
|
684
|
+
group._core = core;
|
|
685
|
+
group.id = group.generateId();
|
|
686
|
+
group.workspaceId = core.store.state.activeWorkspace.id;
|
|
687
|
+
group.scale = core.store.state.scale;
|
|
688
|
+
group.zIndex = core.store.currentZIndex;
|
|
689
|
+
return group;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Creates a KritzelGroup from an array of objects.
|
|
693
|
+
* The objects are added as children and their bounding box is calculated.
|
|
694
|
+
*/
|
|
695
|
+
static createFromObjects(core, objects) {
|
|
696
|
+
const group = KritzelGroup.create(core);
|
|
697
|
+
// Add all objects as children
|
|
698
|
+
objects.forEach(obj => {
|
|
699
|
+
group.childIds.push(obj.id);
|
|
700
|
+
});
|
|
701
|
+
// Calculate bounding box from children
|
|
702
|
+
group.refreshBoundingBox();
|
|
703
|
+
group.captureChildSnapshots();
|
|
704
|
+
return group;
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Finds the parent KritzelGroup that contains the given object as a child.
|
|
708
|
+
* Returns null if the object is not a child of any group.
|
|
709
|
+
*/
|
|
710
|
+
static findParentGroup(core, objectId) {
|
|
711
|
+
const allGroups = core.store.state.objects
|
|
712
|
+
.filter(obj => obj.__class__ === 'KritzelGroup');
|
|
713
|
+
for (const group of allGroups) {
|
|
714
|
+
if (group.childIds.includes(objectId)) {
|
|
715
|
+
return group;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
return null;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Adds a child object to this group.
|
|
722
|
+
*/
|
|
723
|
+
addChild(object) {
|
|
724
|
+
if (!this.childIds.includes(object.id)) {
|
|
725
|
+
this.childIds.push(object.id);
|
|
726
|
+
this.refreshBoundingBox();
|
|
727
|
+
this.captureChildSnapshots();
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Removes a child object from this group.
|
|
732
|
+
*/
|
|
733
|
+
removeChild(objectId) {
|
|
734
|
+
const index = this.childIds.indexOf(objectId);
|
|
735
|
+
if (index !== -1) {
|
|
736
|
+
this.childIds.splice(index, 1);
|
|
737
|
+
this.refreshBoundingBox();
|
|
738
|
+
this.captureChildSnapshots();
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Ungroups this group, returning all children to top-level objects.
|
|
743
|
+
* The group itself should be removed after calling this.
|
|
744
|
+
*/
|
|
745
|
+
ungroup() {
|
|
746
|
+
return this.children;
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Finalizes the group after children have been positioned (e.g., after paste).
|
|
750
|
+
* Refreshes the bounding box and captures child snapshots.
|
|
751
|
+
*/
|
|
752
|
+
finalize() {
|
|
753
|
+
this.refreshBoundingBox();
|
|
754
|
+
this.captureChildSnapshots();
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Recalculates the group's bounding box based on its children.
|
|
758
|
+
*/
|
|
759
|
+
refreshBoundingBox() {
|
|
760
|
+
if (this.children.length === 0) {
|
|
761
|
+
this.width = 0;
|
|
762
|
+
this.height = 0;
|
|
763
|
+
this.translateX = 0;
|
|
764
|
+
this.translateY = 0;
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
if (this.children.length === 1) {
|
|
768
|
+
// Single child: inherit its bounding box
|
|
769
|
+
const child = this.children[0];
|
|
770
|
+
this.translateX = child.boundingBox.x;
|
|
771
|
+
this.translateY = child.boundingBox.y;
|
|
772
|
+
this.width = child.boundingBox.width * this.scale;
|
|
773
|
+
this.height = child.boundingBox.height * this.scale;
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
// Multiple children: calculate encompassing bounding box
|
|
777
|
+
const rotation = this.rotation;
|
|
778
|
+
const cos = Math.cos(-rotation);
|
|
779
|
+
const sin = Math.sin(-rotation);
|
|
780
|
+
let minX = Infinity;
|
|
781
|
+
let maxX = -Infinity;
|
|
782
|
+
let minY = Infinity;
|
|
783
|
+
let maxY = -Infinity;
|
|
784
|
+
this.children.forEach(child => {
|
|
785
|
+
const polygon = child.rotatedPolygon;
|
|
786
|
+
const corners = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
|
|
787
|
+
corners.forEach(corner => {
|
|
788
|
+
// Rotate corner into local space (aligned with group rotation)
|
|
789
|
+
const rx = corner.x * cos - corner.y * sin;
|
|
790
|
+
const ry = corner.x * sin + corner.y * cos;
|
|
791
|
+
if (rx < minX)
|
|
792
|
+
minX = rx;
|
|
793
|
+
if (rx > maxX)
|
|
794
|
+
maxX = rx;
|
|
795
|
+
if (ry < minY)
|
|
796
|
+
minY = ry;
|
|
797
|
+
if (ry > maxY)
|
|
798
|
+
maxY = ry;
|
|
799
|
+
});
|
|
800
|
+
});
|
|
801
|
+
// Dimensions in world units (unrotated)
|
|
802
|
+
const worldWidth = maxX - minX;
|
|
803
|
+
const worldHeight = maxY - minY;
|
|
804
|
+
this.width = (worldWidth - this.padding) * this.scale;
|
|
805
|
+
this.height = (worldHeight - this.padding) * this.scale;
|
|
806
|
+
// Center of the box in rotated space
|
|
807
|
+
const cRx = (minX + maxX) / 2;
|
|
808
|
+
const cRy = (minY + maxY) / 2;
|
|
809
|
+
// Rotate center back to world space
|
|
810
|
+
const cosR = Math.cos(rotation);
|
|
811
|
+
const sinR = Math.sin(rotation);
|
|
812
|
+
const cx = cRx * cosR - cRy * sinR;
|
|
813
|
+
const cy = cRx * sinR + cRy * cosR;
|
|
814
|
+
this.translateX = cx - (this.width / this.scale + 2 * this.padding) / 2;
|
|
815
|
+
this.translateY = cy - (this.height / this.scale + 2 * this.padding) / 2;
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Captures snapshots of child states for transformation operations.
|
|
819
|
+
*/
|
|
820
|
+
captureChildSnapshots() {
|
|
821
|
+
this.unchangedChildSnapshots.clear();
|
|
822
|
+
this.snapshotRotation = this.rotation;
|
|
823
|
+
this.snapshotTranslateX = this.translateX;
|
|
824
|
+
this.snapshotTranslateY = this.translateY;
|
|
825
|
+
this.snapshotTotalWidth = this.totalWidth;
|
|
826
|
+
this.snapshotTotalHeight = this.totalHeight;
|
|
827
|
+
this.snapshotScale = this.scale || 1;
|
|
828
|
+
this.children.forEach(child => {
|
|
829
|
+
this.unchangedChildSnapshots.set(child.id, {
|
|
830
|
+
id: child.id,
|
|
831
|
+
translateX: child.translateX,
|
|
832
|
+
translateY: child.translateY,
|
|
833
|
+
rotation: child.rotation,
|
|
834
|
+
width: child.width,
|
|
835
|
+
height: child.height,
|
|
836
|
+
totalWidth: child.totalWidth,
|
|
837
|
+
totalHeight: child.totalHeight,
|
|
838
|
+
scale: child.scale,
|
|
839
|
+
});
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
843
|
+
// TRANSFORMATION METHODS
|
|
844
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
845
|
+
move(startX, startY, endX, endY) {
|
|
846
|
+
const deltaX = (startX - endX) / this._core.store.state.scale;
|
|
847
|
+
const deltaY = (startY - endY) / this._core.store.state.scale;
|
|
848
|
+
this.translateX += deltaX;
|
|
849
|
+
this.translateY += deltaY;
|
|
850
|
+
this._core.store.state.objects.transaction(() => {
|
|
851
|
+
this._core.store.state.objects.update(this);
|
|
852
|
+
this.children.forEach(child => {
|
|
853
|
+
child.move(startX, startY, endX, endY);
|
|
854
|
+
// Update any lines anchored to this child
|
|
855
|
+
this._core.anchorManager.updateAnchorsForObject(child.id);
|
|
856
|
+
});
|
|
857
|
+
});
|
|
858
|
+
// Update snapshots
|
|
859
|
+
this.unchangedChildSnapshots.forEach(snapshot => {
|
|
860
|
+
snapshot.translateX += deltaX;
|
|
861
|
+
snapshot.translateY += deltaY;
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
resize(x, y, width, height) {
|
|
865
|
+
const widthScaleFactor = width / this.width;
|
|
866
|
+
const heightScaleFactor = height / this.height;
|
|
867
|
+
// Calculate old center
|
|
868
|
+
const oldCenterX = this.translateX + this.totalWidth / 2 / this.scale;
|
|
869
|
+
const oldCenterY = this.translateY + this.totalHeight / 2 / this.scale;
|
|
870
|
+
// Calculate new center
|
|
871
|
+
const newTotalWidth = width + this.padding * 2;
|
|
872
|
+
const newTotalHeight = height + this.padding * 2;
|
|
873
|
+
const newCenterX = x + newTotalWidth / 2 / this.scale;
|
|
874
|
+
const newCenterY = y + newTotalHeight / 2 / this.scale;
|
|
875
|
+
const rotation = this.rotation;
|
|
876
|
+
const cos = Math.cos(-rotation);
|
|
877
|
+
const sin = Math.sin(-rotation);
|
|
878
|
+
const cosR = Math.cos(rotation);
|
|
879
|
+
const sinR = Math.sin(rotation);
|
|
880
|
+
this._core.store.state.objects.transaction(() => {
|
|
881
|
+
this.children.forEach(child => {
|
|
882
|
+
// Calculate child center
|
|
883
|
+
const childCenterX = child.translateX + child.totalWidth / 2 / child.scale;
|
|
884
|
+
const childCenterY = child.translateY + child.totalHeight / 2 / child.scale;
|
|
885
|
+
// Vector from old group center to child center
|
|
886
|
+
const dx = childCenterX - oldCenterX;
|
|
887
|
+
const dy = childCenterY - oldCenterY;
|
|
888
|
+
// Rotate to local space
|
|
889
|
+
const localX = dx * cos - dy * sin;
|
|
890
|
+
const localY = dx * sin + dy * cos;
|
|
891
|
+
// Scale in local space
|
|
892
|
+
const scaledLocalX = localX * widthScaleFactor;
|
|
893
|
+
const scaledLocalY = localY * heightScaleFactor;
|
|
894
|
+
// Rotate back to world space
|
|
895
|
+
const rotatedX = scaledLocalX * cosR - scaledLocalY * sinR;
|
|
896
|
+
const rotatedY = scaledLocalX * sinR + scaledLocalY * cosR;
|
|
897
|
+
// New child center
|
|
898
|
+
const newChildCenterX = newCenterX + rotatedX;
|
|
899
|
+
const newChildCenterY = newCenterY + rotatedY;
|
|
900
|
+
// Calculate relative rotation for scaling
|
|
901
|
+
const relativeRotation = child.rotation - rotation;
|
|
902
|
+
const cosRel = Math.cos(relativeRotation);
|
|
903
|
+
const sinRel = Math.sin(relativeRotation);
|
|
904
|
+
const newChildWidthScale = Math.sqrt(Math.pow(widthScaleFactor * cosRel, 2) + Math.pow(heightScaleFactor * sinRel, 2));
|
|
905
|
+
const newChildHeightScale = Math.sqrt(Math.pow(widthScaleFactor * sinRel, 2) + Math.pow(heightScaleFactor * cosRel, 2));
|
|
906
|
+
const updatedWidth = child.width * newChildWidthScale;
|
|
907
|
+
const updatedHeight = child.height * newChildHeightScale;
|
|
908
|
+
const updatedTotalWidth = updatedWidth + child.padding * 2;
|
|
909
|
+
const updatedTotalHeight = updatedHeight + child.padding * 2;
|
|
910
|
+
const updatedX = newChildCenterX - updatedTotalWidth / 2 / child.scale;
|
|
911
|
+
const updatedY = newChildCenterY - updatedTotalHeight / 2 / child.scale;
|
|
912
|
+
child.resize(updatedX, updatedY, updatedWidth, updatedHeight);
|
|
913
|
+
// Update anchored lines
|
|
914
|
+
this._core.anchorManager.updateAnchorsForObject(child.id);
|
|
915
|
+
});
|
|
916
|
+
this.refreshBoundingBox();
|
|
917
|
+
this.captureChildSnapshots();
|
|
918
|
+
this._core.store.state.objects.update(this);
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
rotate(value) {
|
|
922
|
+
this.rotation = value;
|
|
923
|
+
const centerX = this.translateX + this.totalWidth / 2 / this.scale;
|
|
924
|
+
const centerY = this.translateY + this.totalHeight / 2 / this.scale;
|
|
925
|
+
const angle = value - this.snapshotRotation;
|
|
926
|
+
const cos = Math.cos(angle);
|
|
927
|
+
const sin = Math.sin(angle);
|
|
928
|
+
this._core.store.state.objects.transaction(() => {
|
|
929
|
+
this._core.store.state.objects.update(this);
|
|
930
|
+
this.children.forEach(child => {
|
|
931
|
+
const unchangedSnapshot = this.unchangedChildSnapshots.get(child.id);
|
|
932
|
+
if (!unchangedSnapshot)
|
|
933
|
+
return;
|
|
934
|
+
const offsetX = this.getOffsetXToCenterFromSnapshot(unchangedSnapshot);
|
|
935
|
+
const offsetY = this.getOffsetYToCenterFromSnapshot(unchangedSnapshot);
|
|
936
|
+
const rotatedX = cos * offsetX - sin * offsetY;
|
|
937
|
+
const rotatedY = sin * offsetX + cos * offsetY;
|
|
938
|
+
child.translateX = centerX + rotatedX - child.totalWidth / 2 / child.scale;
|
|
939
|
+
child.translateY = centerY + rotatedY - child.totalHeight / 2 / child.scale;
|
|
940
|
+
child.rotate(this.children.length === 1 ? value : unchangedSnapshot.rotation + angle);
|
|
941
|
+
});
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
getOffsetXToCenterFromSnapshot(snapshot) {
|
|
945
|
+
const childCenterX = snapshot.translateX + snapshot.totalWidth / snapshot.scale / 2;
|
|
946
|
+
const groupScale = this.snapshotScale || this.scale || 1;
|
|
947
|
+
const groupCenterX = this.snapshotTranslateX + this.snapshotTotalWidth / groupScale / 2;
|
|
948
|
+
return childCenterX - groupCenterX;
|
|
949
|
+
}
|
|
950
|
+
getOffsetYToCenterFromSnapshot(snapshot) {
|
|
951
|
+
const childCenterY = snapshot.translateY + snapshot.totalHeight / snapshot.scale / 2;
|
|
952
|
+
const groupScale = this.snapshotScale || this.scale || 1;
|
|
953
|
+
const groupCenterY = this.snapshotTranslateY + this.snapshotTotalHeight / groupScale / 2;
|
|
954
|
+
return childCenterY - groupCenterY;
|
|
955
|
+
}
|
|
956
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
957
|
+
// HIT TESTING
|
|
958
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
959
|
+
hitTest(x, y) {
|
|
960
|
+
// Check if point is inside any child
|
|
961
|
+
return this.children.some(child => child.hitTest(x, y));
|
|
962
|
+
}
|
|
963
|
+
hitTestPolygon(polygon) {
|
|
964
|
+
// Check if polygon intersects with any child
|
|
965
|
+
return this.children.some(child => child.hitTestPolygon(polygon));
|
|
966
|
+
}
|
|
967
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
968
|
+
// COPY & SERIALIZATION
|
|
969
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
970
|
+
copy() {
|
|
971
|
+
const copiedGroup = KritzelGroup.create(this._core);
|
|
972
|
+
// Deep copy all children but DON'T add to store yet
|
|
973
|
+
// Store the copied children temporarily for paste() to handle
|
|
974
|
+
const copiedChildren = [];
|
|
975
|
+
this.children
|
|
976
|
+
.sort((a, b) => a.zIndex - b.zIndex)
|
|
977
|
+
.forEach(child => {
|
|
978
|
+
const copiedChild = child.copy();
|
|
979
|
+
copiedChildren.push(copiedChild);
|
|
980
|
+
copiedGroup.childIds.push(copiedChild.id);
|
|
981
|
+
});
|
|
982
|
+
// Store copied children on the group for paste() to access
|
|
983
|
+
copiedGroup._pendingChildren = copiedChildren;
|
|
984
|
+
// Copy group properties
|
|
985
|
+
copiedGroup.rotation = this.rotation;
|
|
986
|
+
copiedGroup.translateX = this.translateX;
|
|
987
|
+
copiedGroup.translateY = this.translateY;
|
|
988
|
+
copiedGroup.width = this.width;
|
|
989
|
+
copiedGroup.height = this.height;
|
|
990
|
+
return copiedGroup;
|
|
991
|
+
}
|
|
992
|
+
serialize() {
|
|
993
|
+
const { _core, _elementRef, element, totalWidth, totalHeight, unchangedChildSnapshots, ...remainingProps } = this;
|
|
994
|
+
const clonedProps = structuredClone(remainingProps);
|
|
995
|
+
// Convert Map to plain object for serialization
|
|
996
|
+
clonedProps.unchangedChildSnapshots = Object.fromEntries(this.unchangedChildSnapshots);
|
|
997
|
+
return clonedProps;
|
|
998
|
+
}
|
|
999
|
+
deserialize(object) {
|
|
1000
|
+
super.deserialize(object);
|
|
1001
|
+
// Restore the Map from serialized object
|
|
1002
|
+
if (object.unchangedChildSnapshots) {
|
|
1003
|
+
this.unchangedChildSnapshots = new Map(Object.entries(object.unchangedChildSnapshots));
|
|
1004
|
+
}
|
|
1005
|
+
return this;
|
|
1006
|
+
}
|
|
1007
|
+
update() {
|
|
1008
|
+
this._core.store.state.objects.update(this);
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Updates the workspace ID for this group and all its children.
|
|
1012
|
+
*/
|
|
1013
|
+
updateWorkspaceId(workspaceId) {
|
|
1014
|
+
this.workspaceId = workspaceId;
|
|
1015
|
+
this.children.forEach(child => {
|
|
1016
|
+
child.workspaceId = workspaceId;
|
|
1017
|
+
// Recursively update nested groups
|
|
1018
|
+
if (child instanceof KritzelGroup) {
|
|
1019
|
+
child.updateWorkspaceId(workspaceId);
|
|
1020
|
+
}
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
|
|
631
1025
|
class KritzelSelectionGroup extends KritzelBaseObject {
|
|
632
1026
|
__class__ = 'KritzelSelectionGroup';
|
|
633
1027
|
// Store only object IDs instead of full objects
|
|
@@ -671,6 +1065,21 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
671
1065
|
this.captureUnchangedSnapshots();
|
|
672
1066
|
this.refreshObjectDimensions();
|
|
673
1067
|
}
|
|
1068
|
+
/**
|
|
1069
|
+
* Prepares the selection group and its children for a transform interaction (rotate/resize).
|
|
1070
|
+
* Ensures snapshot state is aligned with the current visual state.
|
|
1071
|
+
*/
|
|
1072
|
+
beginTransform() {
|
|
1073
|
+
// Keep the selection group's bounding box current before snapshotting.
|
|
1074
|
+
this.refreshObjectDimensions();
|
|
1075
|
+
this.captureUnchangedSnapshots();
|
|
1076
|
+
// Groups need their own child snapshots aligned with the current transform.
|
|
1077
|
+
this.objects.forEach(obj => {
|
|
1078
|
+
if (obj instanceof KritzelGroup) {
|
|
1079
|
+
obj.finalize();
|
|
1080
|
+
}
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
674
1083
|
deselectAllChildren() {
|
|
675
1084
|
this.objects.forEach(obj => (obj.isSelected = false));
|
|
676
1085
|
}
|
|
@@ -931,9 +1340,9 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
931
1340
|
return objCenterY - groupCenterY;
|
|
932
1341
|
}
|
|
933
1342
|
hitTest(x, y) {
|
|
934
|
-
const
|
|
935
|
-
if (
|
|
936
|
-
return this.objects.
|
|
1343
|
+
const isSingleLine = this.objects.length === 1 && this.objects[0].__class__ === 'KritzelLine';
|
|
1344
|
+
if (isSingleLine) {
|
|
1345
|
+
return this.objects[0].hitTest(x, y);
|
|
937
1346
|
}
|
|
938
1347
|
const polygon = this.rotatedPolygon;
|
|
939
1348
|
const polyPoints = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
|
|
@@ -1346,6 +1755,8 @@ class KritzelBaseHandler {
|
|
|
1346
1755
|
}
|
|
1347
1756
|
}
|
|
1348
1757
|
|
|
1758
|
+
/** Threshold in screen pixels for disconnecting anchored lines during drag */
|
|
1759
|
+
const ANCHOR_DISCONNECT_THRESHOLD = 30;
|
|
1349
1760
|
class KritzelMoveHandler extends KritzelBaseHandler {
|
|
1350
1761
|
dragStartX;
|
|
1351
1762
|
dragStartY;
|
|
@@ -1353,6 +1764,11 @@ class KritzelMoveHandler extends KritzelBaseHandler {
|
|
|
1353
1764
|
startY;
|
|
1354
1765
|
endX;
|
|
1355
1766
|
endY;
|
|
1767
|
+
/** Initial position when drag started (for calculating total accumulated distance) */
|
|
1768
|
+
initialDragX = 0;
|
|
1769
|
+
initialDragY = 0;
|
|
1770
|
+
/** Set of line IDs that have been disconnected from anchors during this drag */
|
|
1771
|
+
disconnectedLineIds = new Set();
|
|
1356
1772
|
hasMoved = false;
|
|
1357
1773
|
trackedPointerId = null;
|
|
1358
1774
|
constructor(core) {
|
|
@@ -1365,6 +1781,9 @@ class KritzelMoveHandler extends KritzelBaseHandler {
|
|
|
1365
1781
|
this.startY = 0;
|
|
1366
1782
|
this.endX = 0;
|
|
1367
1783
|
this.endY = 0;
|
|
1784
|
+
this.initialDragX = 0;
|
|
1785
|
+
this.initialDragY = 0;
|
|
1786
|
+
this.disconnectedLineIds.clear();
|
|
1368
1787
|
this.hasMoved = false;
|
|
1369
1788
|
this.trackedPointerId = null;
|
|
1370
1789
|
}
|
|
@@ -1384,6 +1803,9 @@ class KritzelMoveHandler extends KritzelBaseHandler {
|
|
|
1384
1803
|
this.dragStartY = clientY;
|
|
1385
1804
|
this.startX = this.dragStartX;
|
|
1386
1805
|
this.startY = this.dragStartY;
|
|
1806
|
+
this.initialDragX = clientX;
|
|
1807
|
+
this.initialDragY = clientY;
|
|
1808
|
+
this.disconnectedLineIds.clear();
|
|
1387
1809
|
this.trackedPointerId = event.pointerId;
|
|
1388
1810
|
}
|
|
1389
1811
|
else {
|
|
@@ -1409,6 +1831,9 @@ class KritzelMoveHandler extends KritzelBaseHandler {
|
|
|
1409
1831
|
this.dragStartY = y;
|
|
1410
1832
|
this.startX = x;
|
|
1411
1833
|
this.startY = y;
|
|
1834
|
+
this.initialDragX = x;
|
|
1835
|
+
this.initialDragY = y;
|
|
1836
|
+
this.disconnectedLineIds.clear();
|
|
1412
1837
|
this.trackedPointerId = event.pointerId;
|
|
1413
1838
|
}
|
|
1414
1839
|
else {
|
|
@@ -1432,6 +1857,8 @@ class KritzelMoveHandler extends KritzelBaseHandler {
|
|
|
1432
1857
|
const moveDeltaY = Math.abs(clientY - this.startY);
|
|
1433
1858
|
const moveThreshold = 5;
|
|
1434
1859
|
if (moveDeltaX > moveThreshold || moveDeltaY > moveThreshold) {
|
|
1860
|
+
// Check for anchor disconnect threshold on lines
|
|
1861
|
+
this.checkAndDisconnectAnchors(clientX, clientY);
|
|
1435
1862
|
selectionGroup.move(clientX, clientY, this.dragStartX, this.dragStartY);
|
|
1436
1863
|
this.dragStartX = clientX;
|
|
1437
1864
|
this.dragStartY = clientY;
|
|
@@ -1457,6 +1884,8 @@ class KritzelMoveHandler extends KritzelBaseHandler {
|
|
|
1457
1884
|
const moveThreshold = 5;
|
|
1458
1885
|
if (moveDeltaX > moveThreshold || moveDeltaY > moveThreshold) {
|
|
1459
1886
|
clearTimeout(this._core.store.state.longTouchTimeout);
|
|
1887
|
+
// Check for anchor disconnect threshold on lines
|
|
1888
|
+
this.checkAndDisconnectAnchors(x, y);
|
|
1460
1889
|
selectionGroup.move(x, y, this.dragStartX, this.dragStartY);
|
|
1461
1890
|
this.dragStartX = x;
|
|
1462
1891
|
this.dragStartY = y;
|
|
@@ -1491,6 +1920,48 @@ class KritzelMoveHandler extends KritzelBaseHandler {
|
|
|
1491
1920
|
}
|
|
1492
1921
|
this.reset();
|
|
1493
1922
|
}
|
|
1923
|
+
/**
|
|
1924
|
+
* Checks if the total accumulated drag distance exceeds the anchor disconnect threshold.
|
|
1925
|
+
* If so, disconnects all anchors from any KritzelLine objects in the selection group.
|
|
1926
|
+
* This allows anchored lines to be freely moved after dragging beyond the threshold.
|
|
1927
|
+
*/
|
|
1928
|
+
checkAndDisconnectAnchors(currentX, currentY) {
|
|
1929
|
+
const totalDeltaX = currentX - this.initialDragX;
|
|
1930
|
+
const totalDeltaY = currentY - this.initialDragY;
|
|
1931
|
+
const totalDistance = Math.sqrt(totalDeltaX * totalDeltaX + totalDeltaY * totalDeltaY);
|
|
1932
|
+
if (totalDistance < ANCHOR_DISCONNECT_THRESHOLD) {
|
|
1933
|
+
return;
|
|
1934
|
+
}
|
|
1935
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
1936
|
+
if (!selectionGroup) {
|
|
1937
|
+
return;
|
|
1938
|
+
}
|
|
1939
|
+
// Find and disconnect anchors from all anchored lines in the selection
|
|
1940
|
+
for (const obj of selectionGroup.objects) {
|
|
1941
|
+
// Skip if already disconnected in this drag session
|
|
1942
|
+
if (this.disconnectedLineIds.has(obj.id)) {
|
|
1943
|
+
continue;
|
|
1944
|
+
}
|
|
1945
|
+
// Check if this is a line with anchors
|
|
1946
|
+
if (obj instanceof KritzelLine) {
|
|
1947
|
+
const line = obj;
|
|
1948
|
+
const hasAnchors = line.startAnchor || line.endAnchor;
|
|
1949
|
+
if (hasAnchors) {
|
|
1950
|
+
// Disconnect both anchors
|
|
1951
|
+
if (line.startAnchor) {
|
|
1952
|
+
this._core.anchorManager.removeAnchor(line.id, 'start');
|
|
1953
|
+
}
|
|
1954
|
+
if (line.endAnchor) {
|
|
1955
|
+
this._core.anchorManager.removeAnchor(line.id, 'end');
|
|
1956
|
+
}
|
|
1957
|
+
// Mark as disconnected so we don't try again
|
|
1958
|
+
this.disconnectedLineIds.add(line.id);
|
|
1959
|
+
// Update the line to persist the change
|
|
1960
|
+
this._core.store.state.objects.update(line);
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1494
1965
|
}
|
|
1495
1966
|
|
|
1496
1967
|
var KritzelHandleType;
|
|
@@ -1722,6 +2193,7 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
1722
2193
|
if (KritzelEventHelper.isLeftClick(event)) {
|
|
1723
2194
|
const selectionGroup = this._core.store.selectionGroup;
|
|
1724
2195
|
if (selectionGroup && this._core.store.state.isRotationHandleSelected) {
|
|
2196
|
+
selectionGroup.beginTransform();
|
|
1725
2197
|
const clientX = event.clientX - this._core.store.offsetX;
|
|
1726
2198
|
const clientY = event.clientY - this._core.store.offsetY;
|
|
1727
2199
|
this._core.store.state.isRotating = true;
|
|
@@ -1745,6 +2217,7 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
1745
2217
|
if (activePointers.length === 1) {
|
|
1746
2218
|
const selectionGroup = this._core.store.selectionGroup;
|
|
1747
2219
|
if (selectionGroup && this._core.store.state.isRotationHandleSelected) {
|
|
2220
|
+
selectionGroup.beginTransform();
|
|
1748
2221
|
const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
|
|
1749
2222
|
const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
|
|
1750
2223
|
this._core.store.state.isRotating = true;
|
|
@@ -2055,11 +2528,14 @@ class KritzelSelectionHandler extends KritzelBaseHandler {
|
|
|
2055
2528
|
if (!object) {
|
|
2056
2529
|
return;
|
|
2057
2530
|
}
|
|
2058
|
-
|
|
2531
|
+
// Resolve child objects to their parent groups
|
|
2532
|
+
const parentGroup = KritzelGroup.findParentGroup(this._core, object.id);
|
|
2533
|
+
const objectToSelect = parentGroup || object;
|
|
2534
|
+
objectToSelect.isSelected = false;
|
|
2059
2535
|
const selectionGroup = KritzelSelectionGroup.create(this._core);
|
|
2060
|
-
selectionGroup.addOrRemove(
|
|
2536
|
+
selectionGroup.addOrRemove(objectToSelect);
|
|
2061
2537
|
selectionGroup.isSelected = true;
|
|
2062
|
-
selectionGroup.rotation =
|
|
2538
|
+
selectionGroup.rotation = objectToSelect.rotation;
|
|
2063
2539
|
this._core.addSelectionGroup(selectionGroup);
|
|
2064
2540
|
this._core.rerender();
|
|
2065
2541
|
}
|
|
@@ -2068,10 +2544,24 @@ class KritzelSelectionHandler extends KritzelBaseHandler {
|
|
|
2068
2544
|
if (selectedObjects.length === 0) {
|
|
2069
2545
|
return;
|
|
2070
2546
|
}
|
|
2547
|
+
// Resolve child objects to their parent groups to avoid double-move
|
|
2548
|
+
const resolvedObjects = new Map();
|
|
2549
|
+
selectedObjects.forEach(obj => {
|
|
2550
|
+
// Check if this object is a child of a KritzelGroup
|
|
2551
|
+
const parentGroup = KritzelGroup.findParentGroup(this._core, obj.id);
|
|
2552
|
+
if (parentGroup) {
|
|
2553
|
+
// Add the parent group instead of the child
|
|
2554
|
+
resolvedObjects.set(parentGroup.id, parentGroup);
|
|
2555
|
+
}
|
|
2556
|
+
else {
|
|
2557
|
+
// Not a child of any group, add the object itself
|
|
2558
|
+
resolvedObjects.set(obj.id, obj);
|
|
2559
|
+
}
|
|
2560
|
+
obj.isSelected = false;
|
|
2561
|
+
});
|
|
2071
2562
|
const selectionGroup = KritzelSelectionGroup.create(this._core);
|
|
2072
|
-
|
|
2563
|
+
resolvedObjects.forEach(o => {
|
|
2073
2564
|
selectionGroup.addOrRemove(o);
|
|
2074
|
-
o.isSelected = false;
|
|
2075
2565
|
});
|
|
2076
2566
|
selectionGroup.isSelected = true;
|
|
2077
2567
|
if (selectionGroup.length === 1) {
|
|
@@ -36735,6 +37225,9 @@ class KritzelKeyHandler extends KritzelBaseHandler {
|
|
|
36735
37225
|
{ key: '-', ctrl: true, condition: c => !!c.store.selectionGroup, action: c => c.sendBackward() },
|
|
36736
37226
|
{ key: '*', shift: true, condition: c => !!c.store.selectionGroup, action: c => c.bringToFront() },
|
|
36737
37227
|
{ key: '_', shift: true, condition: c => !!c.store.selectionGroup, action: c => c.sendToBack() },
|
|
37228
|
+
// Grouping
|
|
37229
|
+
{ key: 'g', ctrl: true, condition: c => !!c.store.selectionGroup && c.store.selectionGroup.objects.length >= 2, action: c => c.group() },
|
|
37230
|
+
{ key: 'G', ctrl: true, shift: true, condition: c => !!c.store.selectionGroup, action: c => c.ungroup() },
|
|
36738
37231
|
];
|
|
36739
37232
|
constructor(core) {
|
|
36740
37233
|
super(core);
|
|
@@ -36979,6 +37472,9 @@ class KritzelReviver {
|
|
|
36979
37472
|
case 'KritzelSelectionGroup':
|
|
36980
37473
|
revivedObj = KritzelSelectionGroup.create(this._core).deserialize(obj);
|
|
36981
37474
|
break;
|
|
37475
|
+
case 'KritzelGroup':
|
|
37476
|
+
revivedObj = KritzelGroup.create(this._core).deserialize(obj);
|
|
37477
|
+
break;
|
|
36982
37478
|
case 'KritzelWorkspace':
|
|
36983
37479
|
revivedObj = KritzelWorkspace.create(this._core, obj).deserialize(obj);
|
|
36984
37480
|
break;
|
|
@@ -37814,7 +38310,15 @@ class KritzelCore {
|
|
|
37814
38310
|
addObject(object) {
|
|
37815
38311
|
this._store.state.objects.insert(object);
|
|
37816
38312
|
}
|
|
37817
|
-
removeObject(object) {
|
|
38313
|
+
removeObject(object, preserveChildren = false) {
|
|
38314
|
+
// Handle KritzelGroup: recursively delete all children first (unless preserveChildren is true)
|
|
38315
|
+
if (object instanceof KritzelGroup && !preserveChildren) {
|
|
38316
|
+
// Get children before removing the group
|
|
38317
|
+
const children = [...object.children];
|
|
38318
|
+
children.forEach(child => {
|
|
38319
|
+
this.removeObject(child);
|
|
38320
|
+
});
|
|
38321
|
+
}
|
|
37818
38322
|
// Handle anchor cleanup
|
|
37819
38323
|
if (object instanceof KritzelLine) {
|
|
37820
38324
|
// If removing a line, clean up its anchor index entries
|
|
@@ -37951,6 +38455,28 @@ class KritzelCore {
|
|
|
37951
38455
|
obj.updatePosition(obj.translateX + offsetX, obj.translateY + offsetY);
|
|
37952
38456
|
// Update z-index
|
|
37953
38457
|
obj.zIndex = this._store.currentZIndex + i;
|
|
38458
|
+
// Handle KritzelGroup: also add pending children with offset
|
|
38459
|
+
if (obj instanceof KritzelGroup) {
|
|
38460
|
+
const pendingChildren = obj._pendingChildren;
|
|
38461
|
+
if (pendingChildren) {
|
|
38462
|
+
pendingChildren.forEach((child, childIndex) => {
|
|
38463
|
+
// Update workspace
|
|
38464
|
+
if (child.workspaceId !== activeWorkspace.id) {
|
|
38465
|
+
child.workspaceId = activeWorkspace.id;
|
|
38466
|
+
}
|
|
38467
|
+
// Update position with offset
|
|
38468
|
+
child.updatePosition(child.translateX + offsetX, child.translateY + offsetY);
|
|
38469
|
+
// Update z-index
|
|
38470
|
+
child.zIndex = this._store.currentZIndex + i + childIndex;
|
|
38471
|
+
// Add child to store
|
|
38472
|
+
this.addObject(child);
|
|
38473
|
+
});
|
|
38474
|
+
// Clear pending children
|
|
38475
|
+
delete obj._pendingChildren;
|
|
38476
|
+
// Finalize the group's bounding box and snapshots
|
|
38477
|
+
obj.finalize();
|
|
38478
|
+
}
|
|
38479
|
+
}
|
|
37954
38480
|
// Add to objectsMap
|
|
37955
38481
|
this.addObject(obj);
|
|
37956
38482
|
// Add to selection group
|
|
@@ -38062,6 +38588,63 @@ class KritzelCore {
|
|
|
38062
38588
|
});
|
|
38063
38589
|
this.rerender();
|
|
38064
38590
|
}
|
|
38591
|
+
/**
|
|
38592
|
+
* Groups the currently selected objects into a KritzelGroup.
|
|
38593
|
+
* Requires at least 2 objects to be selected.
|
|
38594
|
+
*/
|
|
38595
|
+
group() {
|
|
38596
|
+
const selectionGroup = this._store.selectionGroup;
|
|
38597
|
+
if (!selectionGroup || selectionGroup.objects.length < 2) {
|
|
38598
|
+
return;
|
|
38599
|
+
}
|
|
38600
|
+
// Create a group from the selected objects
|
|
38601
|
+
const group = KritzelGroup.createFromObjects(this, selectionGroup.objects);
|
|
38602
|
+
// Add the group to the object store
|
|
38603
|
+
this.addObject(group);
|
|
38604
|
+
// Remove the selection group and create a new one with just the group
|
|
38605
|
+
this.removeSelectionGroup();
|
|
38606
|
+
const newSelectionGroup = KritzelSelectionGroup.create(this);
|
|
38607
|
+
newSelectionGroup.addOrRemove(group);
|
|
38608
|
+
newSelectionGroup.isSelected = true;
|
|
38609
|
+
newSelectionGroup.rotation = group.rotation;
|
|
38610
|
+
this.addSelectionGroup(newSelectionGroup);
|
|
38611
|
+
this.engine.emitObjectsChange();
|
|
38612
|
+
this.rerender();
|
|
38613
|
+
}
|
|
38614
|
+
/**
|
|
38615
|
+
* Ungroups any KritzelGroup objects in the current selection.
|
|
38616
|
+
* Children are extracted and become top-level objects.
|
|
38617
|
+
*/
|
|
38618
|
+
ungroup() {
|
|
38619
|
+
const selectionGroup = this._store.selectionGroup;
|
|
38620
|
+
if (!selectionGroup) {
|
|
38621
|
+
return;
|
|
38622
|
+
}
|
|
38623
|
+
const groups = selectionGroup.objects.filter(obj => obj instanceof KritzelGroup);
|
|
38624
|
+
if (groups.length === 0) {
|
|
38625
|
+
return;
|
|
38626
|
+
}
|
|
38627
|
+
// Collect all children from all groups
|
|
38628
|
+
const allChildren = [];
|
|
38629
|
+
const nonGroupObjects = selectionGroup.objects.filter(obj => !(obj instanceof KritzelGroup));
|
|
38630
|
+
groups.forEach(group => {
|
|
38631
|
+
// Get children before removing the group
|
|
38632
|
+
const children = group.ungroup();
|
|
38633
|
+
allChildren.push(...children);
|
|
38634
|
+
// Remove the group from the store (but keep children)
|
|
38635
|
+
this.removeObject(group, true);
|
|
38636
|
+
});
|
|
38637
|
+
// Create a new selection group with all the ungrouped children and non-group objects
|
|
38638
|
+
this.removeSelectionGroup();
|
|
38639
|
+
const newSelectionGroup = KritzelSelectionGroup.create(this);
|
|
38640
|
+
[...allChildren, ...nonGroupObjects].forEach(child => {
|
|
38641
|
+
newSelectionGroup.addOrRemove(child);
|
|
38642
|
+
});
|
|
38643
|
+
newSelectionGroup.isSelected = true;
|
|
38644
|
+
this.addSelectionGroup(newSelectionGroup);
|
|
38645
|
+
this.engine.emitObjectsChange();
|
|
38646
|
+
this.rerender();
|
|
38647
|
+
}
|
|
38065
38648
|
selectObjects(objects) {
|
|
38066
38649
|
if (objects.length === 0) {
|
|
38067
38650
|
return;
|
|
@@ -38460,6 +39043,12 @@ const KritzelEngine = /*@__PURE__*/ proxyCustomElement(class KritzelEngine exten
|
|
|
38460
39043
|
async sendToBack(object) {
|
|
38461
39044
|
this.core.sendToBack(object);
|
|
38462
39045
|
}
|
|
39046
|
+
async group() {
|
|
39047
|
+
this.core.group();
|
|
39048
|
+
}
|
|
39049
|
+
async ungroup() {
|
|
39050
|
+
this.core.ungroup();
|
|
39051
|
+
}
|
|
38463
39052
|
async undo() {
|
|
38464
39053
|
this.core.undo();
|
|
38465
39054
|
}
|
|
@@ -38646,7 +39235,7 @@ const KritzelEngine = /*@__PURE__*/ proxyCustomElement(class KritzelEngine exten
|
|
|
38646
39235
|
};
|
|
38647
39236
|
const visibleObjects = this.core.store.state.objects.query(viewportBounds).sort((a, b) => a.zIndex - b.zIndex);
|
|
38648
39237
|
this.core.cursorManager.applyCursor();
|
|
38649
|
-
return (h(Host, { key: '
|
|
39238
|
+
return (h(Host, { key: 'd0f13eb1d34a6edcba439a501edbaab1875112f1' }, this.core.store.state.debugInfo.showViewportInfo && (h("div", { key: 'f722474cfd43ab13e306cea908871af77015d015', class: "debug-panel" }, h("div", { key: 'efd483bc92223d78f6a0f661a1911820de83682a' }, "ActiveWorkspaceId: ", this.core.store.state?.activeWorkspace?.id), h("div", { key: '37c213c1e3d57cb79ccb99ff65404fa3e03f07e0' }, "ActiveWorkspaceName: ", this.core.store.state?.activeWorkspace?.name), h("div", { key: '68bcdc9e10d89a6a4284dbdc11d679f6c8bed6df' }, "TranslateX: ", this.core.store.state?.translateX), h("div", { key: 'c653966e412a801d536bfdcf8ab79176376693a5' }, "TranslateY: ", this.core.store.state?.translateY), h("div", { key: 'db83b7d58514296daa1465926867d099df6988f9' }, "ViewportWidth: ", this.core.store.state?.viewportWidth), h("div", { key: '1f15e3edec3165920207ba8f1b2886aa72049d08' }, "ViewportHeight: ", this.core.store.state?.viewportHeight), h("div", { key: 'c4e468e3c602f1fd62d4b13de1ec39dd1302768f' }, "PointerCount: ", this.core.store.state.pointers.size), h("div", { key: '8eae67aac27495a611ba125e029473119665bb7e' }, "Scale: ", this.core.store.state?.scale), h("div", { key: '8b22934652db52d915cb21f48f8340ceb2fe1327' }, "ActiveTool: ", this.core.store.state?.activeTool?.name), h("div", { key: 'd4d1872b8e1c33f0925a2f3cfcf4bc652cc5637e' }, "HasViewportChanged: ", this.core.store.state?.hasViewportChanged ? 'true' : 'false'), h("div", { key: '76fc159e2f38324248db21cab17fe76dd7add144' }, "IsEnabled: ", this.core.store.state?.isEnabled ? 'true' : 'false'), h("div", { key: 'aa5a5a7c5faac899492f9c87cb9bf5cf960329cb' }, "IsScaling: ", this.core.store.state?.isScaling ? 'true' : 'false'), h("div", { key: '21d4d43e7fc91a187055906c9f38529407f4ec6f' }, "IsPanning: ", this.core.store.state?.isPanning ? 'true' : 'false'), h("div", { key: 'b43ba7bafd3ac52f5622e7d980456e7cbf5ff0fb' }, "IsSelecting: ", this.isSelecting ? 'true' : 'false'), h("div", { key: 'ecf9749bf4eece2970a62230d323fc205ce62fed' }, "IsSelectionActive: ", this.isSelectionActive ? 'true' : 'false'), h("div", { key: '6f9c7b7fa357f86c05aa76c02428ab0584856f3d' }, "IsResizeHandleSelected: ", this.core.store.state.isResizeHandleSelected ? 'true' : 'false'), h("div", { key: '7b61ed971fd7b4a029204861b19677421c049a33' }, "IsRotationHandleSelected: ", this.core.store.state.isRotationHandleSelected ? 'true' : 'false'), h("div", { key: '875ed8cdf18b46c362b6b0c331a8f9fcba6bc1e2' }, "IsRotationHandleHovered: ", this.core.store.state.isRotationHandleHovered ? 'true' : 'false'), h("div", { key: 'f56f27eff2c7e715528a32b4fe874da5220bc1ef' }, "IsDrawing: ", this.core.store.state.isDrawing ? 'true' : 'false'), h("div", { key: '7831c3571031b029766b0fa16fe2b0e29e8c19b5' }, "IsWriting: ", this.core.store.state.isWriting ? 'true' : 'false'), h("div", { key: 'd4167e465da2f690b9d1f6d3cb8778f4a618843f' }, "IsPointerDown: ", this.core.store.isPointerDown ? 'true' : 'false'), h("div", { key: '00dfeba01da344a0e3c40f2f92ca539ef59d5da5' }, "PointerX: ", this.core.store.state?.pointerX), h("div", { key: '30948e66636501df9863caa67da468d45517a44f' }, "PointerY: ", this.core.store.state?.pointerY), h("div", { key: '9ee2c0bb69e9a150b7ab7c0ca9881897b5ec2a72' }, "SelectedObjects: ", this.core.store.selectionGroup?.objects.length || 0), h("div", { key: '9d470ca319b0c32de7565c46b28c6483d4b9c1cb' }, "ViewportCenter: (", viewportCenterX.toFixed(2), ", ", viewportCenterY.toFixed(2), ")"))), h("div", { key: '4eaaf1e928e62c872b354523e55f33f01c25c5e7', id: "origin", class: "origin", style: {
|
|
38650
39239
|
transform: `matrix(${this.core.store.state?.scale}, 0, 0, ${this.core.store.state?.scale}, ${this.core.store.state?.translateX}, ${this.core.store.state?.translateY})`,
|
|
38651
39240
|
} }, visibleObjects?.map(object => {
|
|
38652
39241
|
return (h("div", { key: object.id, style: {
|
|
@@ -38895,18 +39484,18 @@ const KritzelEngine = /*@__PURE__*/ proxyCustomElement(class KritzelEngine exten
|
|
|
38895
39484
|
stroke: 'var(--kritzel-snap-indicator-stroke, #3b82f6)',
|
|
38896
39485
|
strokeWidth: data.indicatorStrokeWidth,
|
|
38897
39486
|
} }))));
|
|
38898
|
-
})()), this.core.store.state.isContextMenuVisible && (h("kritzel-context-menu", { key: '
|
|
39487
|
+
})()), this.core.store.state.isContextMenuVisible && (h("kritzel-context-menu", { key: 'f726cabc77f66c24618dc0b54cca8bd681608dde', class: "context-menu", ref: el => (this.contextMenuElement = el), items: this.core.store.state.contextMenuItems, objects: this.core.store.selectionGroup?.objects || [], style: {
|
|
38899
39488
|
position: 'fixed',
|
|
38900
39489
|
left: `${this.core.store.state.contextMenuX}px`,
|
|
38901
39490
|
top: `${this.core.store.state.contextMenuY}px`,
|
|
38902
|
-
zIndex: '
|
|
39491
|
+
zIndex: '10002',
|
|
38903
39492
|
}, onActionSelected: event => {
|
|
38904
39493
|
event.detail.action({
|
|
38905
39494
|
x: (-this.core.store.state.translateX + this.core.store.state.contextMenuX) / this.core.store.state.scale,
|
|
38906
39495
|
y: (-this.core.store.state.translateY + this.core.store.state.contextMenuY) / this.core.store.state.scale,
|
|
38907
39496
|
}, this.core.store.selectionGroup?.objects);
|
|
38908
39497
|
this.hideContextMenu();
|
|
38909
|
-
}, onClose: () => this.hideContextMenu() })), this.core.store.state?.activeTool instanceof KritzelEraserTool && !this.core.store.state.isScaling && h("kritzel-cursor-trail", { key: '
|
|
39498
|
+
}, onClose: () => this.hideContextMenu() })), this.core.store.state?.activeTool instanceof KritzelEraserTool && !this.core.store.state.isScaling && h("kritzel-cursor-trail", { key: 'bf42f27e52c03f9d00a5459883ac45ed39bb1dcd', core: this.core })));
|
|
38910
39499
|
}
|
|
38911
39500
|
static get watchers() { return {
|
|
38912
39501
|
"workspace": ["onWorkspaceChange"],
|
|
@@ -38936,6 +39525,8 @@ const KritzelEngine = /*@__PURE__*/ proxyCustomElement(class KritzelEngine exten
|
|
|
38936
39525
|
"sendBackward": [64],
|
|
38937
39526
|
"bringToFront": [64],
|
|
38938
39527
|
"sendToBack": [64],
|
|
39528
|
+
"group": [64],
|
|
39529
|
+
"ungroup": [64],
|
|
38939
39530
|
"undo": [64],
|
|
38940
39531
|
"redo": [64],
|
|
38941
39532
|
"hideContextMenu": [64],
|
|
@@ -38989,7 +39580,7 @@ function defineCustomElement() {
|
|
|
38989
39580
|
} });
|
|
38990
39581
|
}
|
|
38991
39582
|
|
|
38992
|
-
export { isNode as A, min$2 as B, pow as C, HocuspocusProviderWebsocket as D, KritzelLine as E,
|
|
38993
|
-
//# sourceMappingURL=p-
|
|
39583
|
+
export { isNode as A, min$2 as B, pow as C, HocuspocusProviderWebsocket as D, KritzelLine as E, KritzelGroup as F, KritzelLineTool as G, HocuspocusProvider as H, KritzelEraserTool as I, KritzelImageTool as J, KritzelImage as K, KritzelCursorHelper as L, KritzelSelectionTool as M, IndexedDBSyncProvider as N, Observable$1 as O, KritzelAppStateMap as P, KritzelAnchorManager as Q, ABSOLUTE_SCALE_MAX as R, ShapeType as S, ABSOLUTE_SCALE_MIN as T, KritzelShapeTool as U, defineCustomElement as V, KritzelEngine as W, writeVarUint8Array$2 as a, readVarUint8Array$2 as b, applyUpdate as c, encodeStateVector as d, encodeStateAsUpdate as e, createEncoder$1 as f, createDecoder$1 as g, create$8 as h, fromBase64 as i, toBase64 as j, createUint8ArrayFromArrayBuffer as k, offChange as l, readVarString$2 as m, floor$2 as n, onChange as o, getUnixTime$1 as p, equalityDeep$1 as q, readVarUint$2 as r, setIfUndefined$1 as s, toUint8Array$1 as t, writeVarString$2 as u, varStorage as v, writeVarUint$2 as w, map as x, ObservableV2 as y, length$3 as z };
|
|
39584
|
+
//# sourceMappingURL=p-D0UgEnEL.js.map
|
|
38994
39585
|
|
|
38995
|
-
//# sourceMappingURL=p-
|
|
39586
|
+
//# sourceMappingURL=p-D0UgEnEL.js.map
|