Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions code/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- [#248](https://github.com/InditexTech/weavejs/issues/248) Mouse wheel panning only when over stage
- [#250](https://github.com/InditexTech/weavejs/issues/250) Copy / paste on frame doesn't set copied element on it
- [#253](https://github.com/InditexTech/weavejs/issues/253) Selected nodes not triggering snapping lines

## [0.14.2] - 2025-05-20

Expand Down
24 changes: 12 additions & 12 deletions code/packages/sdk/src/nodes/frame/frame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,22 +91,22 @@ export class WeaveFrameNode extends WeaveNode {
id,
containerId: `${id}-group-internal`,
containerOffsetX: 0,
containerOffsetY: titleHeight + borderWidth,
width: props.frameWidth + borderWidth * 2,
height: props.frameHeight + titleHeight + borderWidth * 2,
containerOffsetY: titleHeight,
width: props.frameWidth,
height: props.frameHeight + titleHeight,
fill: '#ffffffff',
clipX: 0,
clipY: 0,
clipWidth: props.frameWidth + borderWidth * 2,
clipHeight: props.frameHeight + titleHeight + borderWidth * 2,
clipWidth: props.frameWidth,
clipHeight: props.frameHeight + titleHeight,
name: 'node',
});

const background = new Konva.Rect({
id: `${id}-bg`,
nodeId: id,
x: borderWidth,
y: titleHeight + borderWidth,
x: 0,
y: titleHeight,
width: props.frameWidth,
stroke: borderColor,
strokeWidth: borderWidth,
Expand Down Expand Up @@ -141,18 +141,18 @@ export class WeaveFrameNode extends WeaveNode {
const frameInternal = new Konva.Group({
id: `${id}-group-internal`,
nodeId: id,
x: borderWidth * 2,
y: titleHeight + borderWidth * 2,
x: 0,
y: titleHeight,
width: props.frameWidth,
height: props.frameHeight,
draggable: false,
stroke: 'transparent',
strokeScaleEnabled: false,
borderWidth: borderWidth,
borderWidth: 0,
clipX: 0,
clipY: 0,
clipWidth: props.frameWidth - borderWidth * 2,
clipHeight: props.frameHeight - borderWidth * 2,
clipWidth: props.frameWidth,
clipHeight: props.frameHeight,
});

frame.add(frameInternal);
Expand Down
8 changes: 5 additions & 3 deletions code/packages/sdk/src/nodes/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,17 +168,19 @@ export abstract class WeaveNode implements WeaveNodeBase {
});
}

this.instance.updateNode(this.serialize(node as WeaveElementInstance));
this.instance.updateNode(
this.serialize(e.target as WeaveElementInstance)
);
}
});

node.on('dragend', (e) => {
if (this.isSelecting() && this.isNodeSelected(node)) {
clearContainerTargets(this.instance);

const layerToMove = moveNodeToContainer(this.instance, e.target);
const containerToMove = moveNodeToContainer(this.instance, e.target);

if (layerToMove) {
if (containerToMove) {
return;
}

Expand Down
111 changes: 103 additions & 8 deletions code/packages/sdk/src/plugins/nodes-snapping/nodes-snapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
} from './constants';
import type { KonvaEventObject } from 'konva/lib/Node';
import type { WeaveNodesSelectionPlugin } from '../nodes-selection/nodes-selection';
import type { Vector2d } from 'konva/lib/types';

export class WeaveNodesSnappingPlugin extends WeavePlugin {
private guideLineConfig: Konva.LineConfig;
Expand Down Expand Up @@ -56,6 +57,48 @@ export class WeaveNodesSnappingPlugin extends WeavePlugin {
this.enabled = enabled;
}

getSelectedNodesMetadata(transformer: Konva.Transformer): {
width: number;
height: number;
nodes: string[];
} {
const firstNode = transformer.getNodes()[0];
const firstNodeClientRect = firstNode.getClientRect();

const rectCoordsMin: Vector2d = {
x: firstNodeClientRect.x,
y: firstNodeClientRect.y,
};
const rectCoordsMax: Vector2d = {
x: firstNodeClientRect.x + firstNodeClientRect.width,
y: firstNodeClientRect.y + firstNodeClientRect.height,
};

const nodes = [];
for (const node of transformer.getNodes()) {
const clientRect = node.getClientRect();
if (clientRect.x < rectCoordsMin.x) {
rectCoordsMin.x = clientRect.x;
}
if (clientRect.y < rectCoordsMin.y) {
rectCoordsMin.y = clientRect.y;
}
if (clientRect.x + clientRect.width > rectCoordsMax.x) {
rectCoordsMax.x = clientRect.x + clientRect.width;
}
if (clientRect.y + clientRect.height > rectCoordsMax.y) {
rectCoordsMax.y = clientRect.y + clientRect.height;
}
nodes.push(node.getAttrs().id as string);
}

return {
width: rectCoordsMax.x - rectCoordsMin.x,
height: rectCoordsMax.y - rectCoordsMin.y,
nodes,
};
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
evaluateGuidelines(e: KonvaEventObject<any>): void {
const utilityLayer = this.instance.getUtilityLayer();
Expand All @@ -68,11 +111,31 @@ export class WeaveNodesSnappingPlugin extends WeavePlugin {
return;
}

const nodesSelectionPlugin =
this.instance.getPlugin<WeaveNodesSelectionPlugin>('nodesSelection');

let skipNodes = [];
let node: Konva.Node | undefined = undefined;
if (e.type === 'dragmove' && e.target instanceof Konva.Transformer) {
if (
e.type === 'dragmove' &&
nodesSelectionPlugin &&
e.target instanceof Konva.Transformer &&
e.target.getNodes().length === 1
) {
const actualTarget: Konva.Transformer =
e.target as unknown as Konva.Transformer;
node = actualTarget.getNodes()[0];
skipNodes.push(node.getAttrs().id ?? '');
}
if (
e.type === 'dragmove' &&
nodesSelectionPlugin &&
e.target instanceof Konva.Transformer &&
e.target.getNodes().length > 1
) {
const { nodes } = this.getSelectedNodesMetadata(e.target);
node = e.target;
skipNodes = [...nodes];
}
if (e.type === 'transform') {
node = e.target;
Expand All @@ -83,13 +146,15 @@ export class WeaveNodesSnappingPlugin extends WeavePlugin {
}

// find possible snapping lines
const lineGuideStops = this.getLineGuideStops(node);
const lineGuideStops = this.getLineGuideStops(skipNodes);
// find snapping points of current object
const itemBounds = this.getObjectSnappingEdges(node);

// now find where can we snap current object
const guides = this.getGuides(lineGuideStops, itemBounds, e.type);

utilityLayer.destroyChildren();

// do nothing of no snapping
if (!guides.length) {
return;
Expand All @@ -101,6 +166,7 @@ export class WeaveNodesSnappingPlugin extends WeavePlugin {
this.drawGuides(guides);

if (e.type === 'dragmove') {
const orgAbsPos = node.absolutePosition();
const absPos = node.absolutePosition();
// now force object position
guides.forEach((lg) => {
Expand All @@ -115,7 +181,26 @@ export class WeaveNodesSnappingPlugin extends WeavePlugin {
}
}
});
node.absolutePosition(absPos);

const vecDiff = {
x: orgAbsPos.x - absPos.x,
y: orgAbsPos.y - absPos.y,
};

if (node instanceof Konva.Transformer) {
node.getNodes().forEach((n) => {
const nodeAbsPos = n.getAbsolutePosition();

const newPos = {
x: nodeAbsPos.x - vecDiff.x,
y: nodeAbsPos.y - vecDiff.y,
};

n.setAbsolutePosition(newPos);
});
} else {
node.absolutePosition(absPos);
}
}
if (e.type === 'transform') {
const nodesSelectionPlugin =
Expand Down Expand Up @@ -167,8 +252,7 @@ export class WeaveNodesSnappingPlugin extends WeavePlugin {
return;
}

// clear all previous lines on the screen
utilityLayer.find(`.${GUIDE_LINE_NAME}`).forEach((l) => l.destroy());
utilityLayer.destroyChildren();
}

private initEvents() {
Expand All @@ -181,7 +265,7 @@ export class WeaveNodesSnappingPlugin extends WeavePlugin {
}
}

getLineGuideStops(skipShape: Konva.Node): LineGuideStop {
getLineGuideStops(skipNodes: string[]): LineGuideStop {
const stage = this.instance.getStage();

const nodesSelection =
Expand All @@ -205,9 +289,10 @@ export class WeaveNodesSnappingPlugin extends WeavePlugin {

// and we snap over edges and center of each object on the canvas
stage.find('.node').forEach((guideItem) => {
if (guideItem === skipShape) {
if (skipNodes.includes(guideItem.getAttrs().id ?? '')) {
return;
}

const box = guideItem.getClientRect({ skipStroke: true });

// and we can snap to all edges of shapes
Expand All @@ -226,7 +311,17 @@ export class WeaveNodesSnappingPlugin extends WeavePlugin {
}

getObjectSnappingEdges(node: Konva.Node): NodeSnappingEdges {
const box = node.getClientRect({ skipStroke: true });
let box = node.getClientRect({ skipStroke: true });

if (node instanceof Konva.Transformer) {
const transformerRect = node.getChildren((node) => {
return node.getAttrs().name === 'back';
})[0];
box = transformerRect.getClientRect({
skipStroke: true,
});
}

const absPos = node.absolutePosition();

const snappingEdges: NodeSnappingEdges = {
Expand Down
2 changes: 1 addition & 1 deletion code/packages/sdk/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export function moveNodeToContainer(
const actualNode = nodeHandler.serialize(node as WeaveElementInstance);

instance.removeNode(actualNode);
instance.addNode(actualNode, layerToMove?.getAttrs().id);
instance.addNode(actualNode, layerToMove.getAttrs().id);
}

return layerToMove;
Expand Down
1 change: 1 addition & 0 deletions docs/content/docs/main/changelog/prerelease/0.14.3.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ description: Several bugfixes

- [#248](https://github.com/InditexTech/weavejs/issues/248) Mouse wheel panning only when over stage
- [#250](https://github.com/InditexTech/weavejs/issues/250) Copy / paste on frame doesn't set copied element on it
- [#253](https://github.com/InditexTech/weavejs/issues/253) Selected nodes not triggering snapping lines
Loading