diff --git a/code/CHANGELOG.md b/code/CHANGELOG.md index 54aeb6583..2a456d0e2 100644 --- a/code/CHANGELOG.md +++ b/code/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#1036](https://github.com/InditexTech/weavejs/issues/1036) Corrupt Image nodes prevent frames to complete initial loading - [#1038](https://github.com/InditexTech/weavejs/issues/1038) Is possible to add a frame inside a frame by pasting with right mouse button - [#1043](https://github.com/InditexTech/weavejs/issues/1043) Canvas does not auto-scroll when dragging an element near the border +- [#1044](https://github.com/InditexTech/weavejs/issues/1044) Nodes disappear when moving between frames ## [3.8.0] - 2026-04-24 diff --git a/code/packages/renderer-konva-base/src/reconciler.ts b/code/packages/renderer-konva-base/src/reconciler.ts index 5179602a4..18646e21c 100644 --- a/code/packages/renderer-konva-base/src/reconciler.ts +++ b/code/packages/renderer-konva-base/src/reconciler.ts @@ -114,11 +114,7 @@ export const SIMPLE_RECONCILER = { child.setZIndex(index); } }, - removeChild( - instance: Weave, - parent: WeaveElementInstance | undefined, - child: WeaveElementInstance - ) { + removeChild(instance: Weave, child: WeaveElementInstance) { const type = child.getAttrs().nodeType; const handler = instance.getNodeHandler(type); diff --git a/code/packages/renderer-konva-base/src/renderer.ts b/code/packages/renderer-konva-base/src/renderer.ts index b290239dc..26d3c2521 100644 --- a/code/packages/renderer-konva-base/src/renderer.ts +++ b/code/packages/renderer-konva-base/src/renderer.ts @@ -11,9 +11,6 @@ import { } from '@inditextech/weave-types'; import Konva from 'konva'; import { isEqual } from 'lodash'; -import type { Stage } from 'konva/lib/Stage'; -import type { Layer } from 'konva/lib/Layer'; -import type { Group } from 'konva/lib/Group'; import type { RendererInstruction } from './types'; import { SIMPLE_RECONCILER } from './reconciler'; import { WeaveRenderer } from '@inditextech/weave-sdk'; @@ -299,23 +296,22 @@ export class WeaveKonvaBaseRenderer extends WeaveRenderer { const stage = this.instance.getStage(); - const parentInstance = stage.findOne(`#${instruction.parentKey}`) as - | Stage - | Layer - | Group; - - const childInstance = stage.findOne( + const childInstances: WeaveElementInstance[] = stage.find( `#${instruction.key}` - ) as WeaveElementInstance; + ); - if (!childInstance) { - console.warn( - `Trying to remove non existing node with key ${instruction.key}` - ); - return; - } + for (const childInstance of childInstances) { + let parent = childInstance.getParent(); + if (parent?.getAttrs().nodeId) { + parent = stage.findOne( + `#${parent.getAttrs().nodeId}` + ) as Konva.Container; + } - this.reconciler.removeChild(this.instance, parentInstance, childInstance); + if (parent?.id() === instruction.parentKey) { + this.reconciler.removeChild(this.instance, childInstance); + } + } } private updateProps(instruction: RendererInstruction) { @@ -330,9 +326,6 @@ export class WeaveKonvaBaseRenderer extends WeaveRenderer { const node = stage.findOne(`#${instruction.key}`) as WeaveElementInstance; if (!node) { - console.warn( - `Trying to update non existing node with key ${instruction.key}` - ); return; } diff --git a/code/packages/sdk/src/actions/image-tool/image-tool.ts b/code/packages/sdk/src/actions/image-tool/image-tool.ts index f37f49886..a4cfaed7c 100644 --- a/code/packages/sdk/src/actions/image-tool/image-tool.ts +++ b/code/packages/sdk/src/actions/image-tool/image-tool.ts @@ -29,10 +29,7 @@ import { WeaveNodesSelectionPlugin } from '@/plugins/nodes-selection/nodes-selec import Konva from 'konva'; import type { WeaveImageNode } from '@/nodes/image/image'; import { SELECTION_TOOL_ACTION_NAME } from '../selection-tool/constants'; -import { - getPositionRelativeToContainerOnPosition, - mergeExceptArrays, -} from '@/utils/utils'; +import { mergeExceptArrays } from '@/utils/utils'; import type { WeaveElementAttributes, WeaveElementInstance, @@ -109,8 +106,9 @@ export class WeaveImageToolAction extends WeaveAction { if (dragProperties && dragId === WEAVE_IMAGE_TOOL_ACTION_NAME) { this.instance.getStage().setPointersPositions(e); - const position: Konva.Vector2d | null | undefined = - getPositionRelativeToContainerOnPosition(this.instance); + const position: Konva.Vector2d | null | undefined = this.instance + .getStage() + .getRelativePointerPosition(); if (!position) { return; diff --git a/code/packages/sdk/src/actions/images-tool/images-tool.ts b/code/packages/sdk/src/actions/images-tool/images-tool.ts index 651d9d1ee..7ef4ed14e 100644 --- a/code/packages/sdk/src/actions/images-tool/images-tool.ts +++ b/code/packages/sdk/src/actions/images-tool/images-tool.ts @@ -29,10 +29,7 @@ import { WEAVE_IMAGES_TOOL_UPLOAD_TYPE, } from './constants'; import { WeaveAction } from '../action'; -import { - getPositionRelativeToContainerOnPosition, - mergeExceptArrays, -} from '@/utils/utils'; +import { mergeExceptArrays } from '@/utils/utils'; import type { WeaveImageToolAction } from '../image-tool/image-tool'; import { WEAVE_IMAGE_TOOL_ACTION_NAME, @@ -120,8 +117,9 @@ export class WeaveImagesToolAction extends WeaveAction { if (dragProperties && dragId === WEAVE_IMAGES_TOOL_ACTION_NAME) { this.instance.getStage().setPointersPositions(e); - const position: Konva.Vector2d | null | undefined = - getPositionRelativeToContainerOnPosition(this.instance); + const position: Konva.Vector2d | null | undefined = this.instance + .getStage() + .getRelativePointerPosition(); if (!position) { return; diff --git a/code/packages/sdk/src/actions/video-tool/video-tool.ts b/code/packages/sdk/src/actions/video-tool/video-tool.ts index 5a673624a..30742e06b 100644 --- a/code/packages/sdk/src/actions/video-tool/video-tool.ts +++ b/code/packages/sdk/src/actions/video-tool/video-tool.ts @@ -18,7 +18,6 @@ import { WeaveNodesSelectionPlugin } from '@/plugins/nodes-selection/nodes-selec import Konva from 'konva'; import { SELECTION_TOOL_ACTION_NAME } from '../selection-tool/constants'; import type { WeaveVideoNode } from '@/nodes/video/video'; -import { getPositionRelativeToContainerOnPosition } from '@/utils/utils'; export class WeaveVideoToolAction extends WeaveAction { protected initialized: boolean = false; @@ -82,8 +81,9 @@ export class WeaveVideoToolAction extends WeaveAction { if (dragProperties && dragId === VIDEO_TOOL_ACTION_NAME) { this.instance.getStage().setPointersPositions(e); - const position: Konva.Vector2d | null | undefined = - getPositionRelativeToContainerOnPosition(this.instance); + const position: Konva.Vector2d | null | undefined = this.instance + .getStage() + .getRelativePointerPosition(); this.instance.triggerAction(VIDEO_TOOL_ACTION_NAME, { videoId: dragProperties.videoId, diff --git a/code/packages/sdk/src/managers/targeting.ts b/code/packages/sdk/src/managers/targeting.ts index 5bb4ebcd4..ed13742b9 100644 --- a/code/packages/sdk/src/managers/targeting.ts +++ b/code/packages/sdk/src/managers/targeting.ts @@ -10,7 +10,7 @@ import type { WeaveMousePointInfoSimple, } from '@inditextech/weave-types'; import type { WeaveNodesSelectionPlugin } from '@/plugins/nodes-selection/nodes-selection'; -import { getBoundingBox } from '@/utils/utils'; +import { containerOverCursor, getBoundingBox } from '@/utils/utils'; export class WeaveTargetingManager { private instance: Weave; @@ -181,21 +181,28 @@ export class WeaveTargetingManager { getMousePointer(point?: Konva.Vector2d): WeaveMousePointInfo { this.logger.debug({ point }, 'getMousePointer'); - const stage = this.instance.getStage(); const mainLayer = this.instance.getMainLayer(); let relativeMousePointer = typeof point !== 'undefined' ? point : mainLayer?.getRelativePointerPosition() ?? { x: 0, y: 0 }; - let measureContainer: Konva.Layer | Konva.Group | undefined = mainLayer; - let container: Konva.Layer | Konva.Node | undefined = mainLayer; const utilityLayer = this.instance.getUtilityLayer(); if (utilityLayer) { utilityLayer.visible(false); } + let containerAlt = containerOverCursor( + this.instance, + [], + relativeMousePointer + ); + + if (!containerAlt) { + containerAlt = this.instance.getMainLayer(); + } + const nodesSelection = this.instance.getPlugin('nodesSelection'); @@ -203,42 +210,8 @@ export class WeaveTargetingManager { nodesSelection.getTransformer().visible(false); } - const dummyRect = new Konva.Rect({ - width: 10, - height: 10, - x: relativeMousePointer.x, - y: relativeMousePointer.y, - }); - mainLayer?.add(dummyRect); - - const intersectedNode = this.nodeIntersectsContainerElement(dummyRect); - if (intersectedNode) { - const containerOfNode = stage.findOne( - `#${intersectedNode.getAttrs().containerId}` - ) as Konva.Group | undefined; - if (containerOfNode) { - container = intersectedNode; - measureContainer = containerOfNode; - } - } - - if ( - typeof point === 'undefined' && - container?.getAttrs().nodeType !== 'layer' - ) { - relativeMousePointer = - measureContainer?.getRelativePointerPosition() ?? relativeMousePointer; - } - - if ( - typeof point === 'undefined' && - container?.getAttrs().nodeType === 'layer' - ) { - relativeMousePointer = measureContainer?.getRelativePointerPosition() ?? { - x: 0, - y: 0, - }; - } + relativeMousePointer = + containerAlt?.getRelativePointerPosition() ?? relativeMousePointer; if (utilityLayer) { utilityLayer.visible(true); @@ -248,9 +221,11 @@ export class WeaveTargetingManager { nodesSelection.getTransformer().visible(true); } - dummyRect.destroy(); - - return { mousePoint: relativeMousePointer, container, measureContainer }; + return { + mousePoint: relativeMousePointer, + container: containerAlt, + measureContainer: containerAlt, + }; } getMousePointerRelativeToContainer( diff --git a/code/packages/sdk/src/nodes/connector/connector.ts b/code/packages/sdk/src/nodes/connector/connector.ts index f4cd235ac..daf432a32 100644 --- a/code/packages/sdk/src/nodes/connector/connector.ts +++ b/code/packages/sdk/src/nodes/connector/connector.ts @@ -1222,7 +1222,6 @@ export class WeaveConnectorNode extends WeaveNode { delete cleanedAttrs.startInfoLoaded; delete cleanedAttrs.endInfoLoaded; delete cleanedAttrs.overridesMouseControl; - delete cleanedAttrs.onMoveContainer; delete cleanedAttrs.dragBoundFunc; return { diff --git a/code/packages/sdk/src/nodes/extensions.d.ts b/code/packages/sdk/src/nodes/extensions.d.ts index aa97a9fb0..b0bc49a79 100644 --- a/code/packages/sdk/src/nodes/extensions.d.ts +++ b/code/packages/sdk/src/nodes/extensions.d.ts @@ -48,7 +48,6 @@ declare module 'konva/lib/Node' { updatePosition(position: Vector2d): void; dblClick(): void; isSelectable(): boolean; - movedToContainer(container: Konva.Layer | Konva.Group): void; handleMouseover(e: KonvaEventObject): void; handleMouseout(e: KonvaEventObject): void; handleSelectNode(): void; diff --git a/code/packages/sdk/src/nodes/frame/frame.ts b/code/packages/sdk/src/nodes/frame/frame.ts index 3beb17c67..3f0a50bb5 100644 --- a/code/packages/sdk/src/nodes/frame/frame.ts +++ b/code/packages/sdk/src/nodes/frame/frame.ts @@ -411,7 +411,6 @@ export class WeaveFrameNode extends WeaveNode { delete cleanedAttrs.draggable; delete cleanedAttrs.onTargetEnter; delete cleanedAttrs.overridesMouseControl; - delete cleanedAttrs.onMoveContainer; delete cleanedAttrs.dragBoundFunc; return { diff --git a/code/packages/sdk/src/nodes/group/group.ts b/code/packages/sdk/src/nodes/group/group.ts index 388b5d4b8..76b815d56 100644 --- a/code/packages/sdk/src/nodes/group/group.ts +++ b/code/packages/sdk/src/nodes/group/group.ts @@ -129,7 +129,6 @@ export class WeaveGroupNode extends WeaveNode { delete cleanedAttrs.mutexUserId; delete cleanedAttrs.draggable; delete cleanedAttrs.overridesMouseControl; - delete cleanedAttrs.onMoveContainer; delete cleanedAttrs.dragBoundFunc; return { diff --git a/code/packages/sdk/src/nodes/image/constants.ts b/code/packages/sdk/src/nodes/image/constants.ts index 16c327cf0..6e547f660 100644 --- a/code/packages/sdk/src/nodes/image/constants.ts +++ b/code/packages/sdk/src/nodes/image/constants.ts @@ -26,6 +26,9 @@ export const WEAVE_IMAGE_CROP_ANCHOR_POSITION = { } as const; export const WEAVE_IMAGE_DEFAULT_CONFIG: WeaveImageProperties = { + cleanup: { + intervalMs: 60 * 1000, // 1 minute + }, performance: { cache: { enabled: false, diff --git a/code/packages/sdk/src/nodes/image/image.ts b/code/packages/sdk/src/nodes/image/image.ts index a9bd253b0..90a2900a3 100644 --- a/code/packages/sdk/src/nodes/image/image.ts +++ b/code/packages/sdk/src/nodes/image/image.ts @@ -43,6 +43,7 @@ export class WeaveImageNode extends WeaveNode { protected tapStart!: { x: number; y: number; time: number } | null; protected imageCrop!: WeaveImageCrop | null; protected nodeType: string = WEAVE_IMAGE_NODE_TYPE; + protected notUsedImagesCleanup!: NodeJS.Timeout | null; private readonly cursorsFallback: WeaveImageCursors = { loading: 'wait', }; @@ -69,6 +70,34 @@ export class WeaveImageNode extends WeaveNode { this.imageFallback = {}; } + private setupNotUsedImagesCleanup() { + const cleanupHandler = () => { + this.notUsedImagesCleanup = null; + const stage = this.instance.getStage(); + + const nodesIds = Object.keys(this.imageState); + + for (const nodeId of nodesIds) { + const node = stage.findOne(`#${nodeId}`); + + if (!node) { + delete this.imageSource[nodeId]; + delete this.imageState[nodeId]; + delete this.imageTryoutAttempts[nodeId]; + delete this.imageFallback[nodeId]; + } + } + + this.setupNotUsedImagesCleanup(); + }; + + const bindedCleanupHandler = cleanupHandler.bind(this); + + if (!this.notUsedImagesCleanup) { + setTimeout(bindedCleanupHandler, this.config.cleanup.intervalMs); + } + } + preloadCursors() { return new Promise((resolve) => { (async () => { @@ -235,7 +264,7 @@ export class WeaveImageNode extends WeaveNode { } onRender(props: WeaveElementAttributes): WeaveElementInstance { - // this.initGlobalEvents(); + this.setupNotUsedImagesCleanup(); // eslint-disable-next-line @typescript-eslint/no-explicit-any const imageProperties: any = props.imageProperties; @@ -280,15 +309,6 @@ export class WeaveImageNode extends WeaveNode { return 'pointer'; }; - image.movedToContainer = () => { - const stage = this.instance.getStage(); - const image = stage.findOne(`#${id}`) as Konva.Group | undefined; - - if (!image) { - return; - } - }; - if (this.config.cropMode.enabled) { image.triggerCrop = () => { this.triggerCrop(image, { @@ -837,29 +857,12 @@ export class WeaveImageNode extends WeaveNode { } // Loaded but image is corrupted if (this.imageState[id ?? '']?.loaded && this.imageState[id ?? '']?.error) { - imagePlaceholder?.setAttrs({ - ...internalImageProps, - ...(nodeAttrs.imageProperties ?? {}), - name: undefined, - id: `${id}-placeholder`, - nodeId: id, - x: 0, - y: 0, - scaleX: 1, - scaleY: 1, - rotation: 0, - visible: true, - fill: this.config.style.placeholder.fill, - strokeWidth: 0, - draggable: false, - zIndex: 0, - }); internalImage?.setAttrs({ ...internalImageProps, ...(nodeAttrs.imageProperties ?? {}), name: undefined, id: `${id}-image`, - image: undefined, + image: this.imageFallback[id ?? ''], nodeId: id, x: 0, y: 0, @@ -870,6 +873,8 @@ export class WeaveImageNode extends WeaveNode { draggable: false, zIndex: 1, }); + internalImage?.visible(true); + this.updateImageCrop(nodeInstance as Konva.Group); } // Loaded if ( @@ -961,15 +966,20 @@ export class WeaveImageNode extends WeaveNode { ): void { const imageURLToLoad = imageURL ?? 'http://localhost/false-image'; + if (imageURLToLoad === '') { + this.setErrorState(imageId); + return; + } + this.imageSource[imageId] = Konva.Util.createImageElement(); this.imageSource[imageId].crossOrigin = this.config.crossOrigin; this.imageSource[imageId].onerror = (error) => { if (!loadingTryout) { - this.imageState[imageId] = { - status: 'error', - loaded: false, - error: true, - }; + const stage = this.instance.getStage(); + const image = stage.findOne(`#${imageId}`); + if (image) { + this.setErrorState(imageId, image as Konva.Group); + } } onError(error); @@ -1052,6 +1062,7 @@ export class WeaveImageNode extends WeaveNode { if (node) { this.imageTryoutAttempts[id] = (this.imageTryoutAttempts[id] ?? 0) + 1; + this.loadImage( node.getAttrs(), node as Konva.Group, @@ -1145,37 +1156,30 @@ export class WeaveImageNode extends WeaveNode { }, onError: (error) => { if (!this.config.useFallbackImage) { - this.imageTryoutIds[id] = setTimeout(() => { - const node = this.instance.getStage().findOne(`#${id}`); + const tryoutAttempts = this.imageTryoutAttempts[id] ?? 0; - if (node) { - this.imageTryoutAttempts[id] = - (this.imageTryoutAttempts[id] ?? 0) + 1; - this.loadImage( - node.getAttrs(), - node as Konva.Group, - false, - true - ); - } - }, this.config.imageLoading.retryDelayMs); + if ( + tryoutAttempts - 1 < + this.config.imageLoading.maxRetryAttempts + ) { + this.loadImageTryout(id); + return; + } else { + this.setErrorState(id, image); + } } if (loadTryout) { const tryoutAttempts = this.imageTryoutAttempts[id] ?? 0; - if (tryoutAttempts < this.config.imageLoading.maxRetryAttempts) { - this.imageTryoutIds[id] = setTimeout(() => { - const node = this.instance.getStage().findOne(`#${id}`); - if (node) { - this.imageTryoutAttempts[id] = tryoutAttempts + 1; - this.loadImage( - node.getAttrs(), - node as Konva.Group, - false, - true - ); - } - }, this.config.imageLoading.retryDelayMs); + + if ( + tryoutAttempts - 1 < + this.config.imageLoading.maxRetryAttempts + ) { + this.loadImageTryout(id); + return; + } else { + this.setErrorState(id, image); } return; } @@ -1196,19 +1200,15 @@ export class WeaveImageNode extends WeaveNode { return; } - this.imageState[id] = { - status: 'error', - loaded: false, - error: true, - }; + this.setErrorState(id, image); image.setAttrs({ image: undefined, }); - this.resolveAsyncElement(id); + console.error('Error loading image', error); - console.error('Error loading image', realImageURL, error); + this.resolveAsyncElement(id); imagePlaceholder?.setAttrs({ visible: true, @@ -1403,9 +1403,6 @@ export class WeaveImageNode extends WeaveNode { onDestroy(nodeInstance: WeaveElementInstance) { const nodeId = nodeInstance.getAttrs().id ?? ''; - const isMoveContainer = nodeInstance.getAttr('onMoveContainer'); - nodeInstance.setAttr('onMoveContainer', undefined); - const utilityLayer = this.instance.getUtilityLayer(); const nodes = utilityLayer?.find('.cropMode') ?? []; nodes.forEach((n) => { @@ -1417,13 +1414,32 @@ export class WeaveImageNode extends WeaveNode { delete this.imageTryoutIds[nodeId]; } - if (!isMoveContainer) { - delete this.imageSource[nodeId]; - delete this.imageState[nodeId]; - delete this.imageTryoutAttempts[nodeId]; - delete this.imageFallback[nodeId]; - } - nodeInstance.destroy(); } + + private loadImageTryout(imageId: string): void { + this.imageTryoutIds[imageId] = setTimeout(() => { + const node = this.instance.getStage().findOne(`#${imageId}`); + if (node) { + const tryoutAttempts = this.imageTryoutAttempts[imageId] ?? 0; + this.imageTryoutAttempts[imageId] = tryoutAttempts + 1; + + this.loadImage(node.getAttrs(), node as Konva.Group, false, true); + } + }, this.config.imageLoading.retryDelayMs); + } + + private setErrorState(imageId: string, image?: Konva.Group): void { + this.imageState[imageId] = { + status: 'loaded', + loaded: true, + error: true, + }; + + this.resolveAsyncElement(imageId); + + if (image) { + this.cacheNode(image); + } + } } diff --git a/code/packages/sdk/src/nodes/image/types.ts b/code/packages/sdk/src/nodes/image/types.ts index 8321efc9e..1669a804e 100644 --- a/code/packages/sdk/src/nodes/image/types.ts +++ b/code/packages/sdk/src/nodes/image/types.ts @@ -47,6 +47,9 @@ export type WeaveImageCursors = { }; export type WeaveImageProperties = { + cleanup: { + intervalMs: number; + }; performance: { cache: WeaveImageCache; }; diff --git a/code/packages/sdk/src/nodes/layer/layer.ts b/code/packages/sdk/src/nodes/layer/layer.ts index 913e3bfec..b7bbb0e29 100644 --- a/code/packages/sdk/src/nodes/layer/layer.ts +++ b/code/packages/sdk/src/nodes/layer/layer.ts @@ -58,7 +58,6 @@ export class WeaveLayerNode extends WeaveNode { delete cleanedAttrs.mutexUserId; delete cleanedAttrs.draggable; delete cleanedAttrs.overridesMouseControl; - delete cleanedAttrs.onMoveContainer; delete cleanedAttrs.dragBoundFunc; return { diff --git a/code/packages/sdk/src/nodes/node.ts b/code/packages/sdk/src/nodes/node.ts index 0bbcd84a3..915374637 100644 --- a/code/packages/sdk/src/nodes/node.ts +++ b/code/packages/sdk/src/nodes/node.ts @@ -56,7 +56,6 @@ export const augmentKonvaNodeClass = ( Konva.Node.prototype.getRealClientRect = function (config) { return this.getClientRect(config); }; - Konva.Node.prototype.movedToContainer = function () {}; Konva.Node.prototype.updatePosition = function () {}; Konva.Node.prototype.triggerCrop = function () {}; Konva.Node.prototype.closeCrop = function () {}; @@ -130,7 +129,6 @@ export abstract class WeaveNode implements WeaveNodeBase { 'bottom-right', ]; }; - node.movedToContainer = function () {}; node.updatePosition = function () {}; node.resetCrop = function () {}; node.handleMouseover = function () {}; @@ -414,7 +412,13 @@ export abstract class WeaveNode implements WeaveNodeBase { nodesEdgeSnappingPlugin.evaluateGuidelines(e); } - this.getUsersPresencePlugin()?.setPresence(node.id(), { + let parentId: string = node.getParent()?.id() ?? ''; + const parent = node.getParent(); + if (parent?.getAttrs().nodeId) { + parentId = parent.getAttrs().nodeId; + } + + this.getUsersPresencePlugin()?.setPresence(node.id(), parentId, { x: node.x(), y: node.y(), width: node.width(), @@ -523,11 +527,6 @@ export abstract class WeaveNode implements WeaveNodeBase { this.getNodesSelectionFeedbackPlugin()?.hideSelectionHalo(nodeTarget); - this.getSelectionPlugin()?.saveDragSelectedNodes(); - if (this.getSelectionPlugin()?.getDragSelectedNodes().length === 1) { - this.getSelectionPlugin()?.setNodesOpacityOnDrag(); - } - const canMove = nodeTarget?.canDrag() ?? false; if (!canMove) { @@ -676,10 +675,20 @@ export abstract class WeaveNode implements WeaveNodeBase { ) { clearContainerTargets(this.instance); - this.getUsersPresencePlugin()?.setPresence(realNodeTarget.id(), { - x: realNodeTarget.x(), - y: realNodeTarget.y(), - }); + let parentId: string = realNodeTarget.getParent()?.id() ?? ''; + const parent = realNodeTarget.getParent(); + if (parent?.getAttrs().nodeId) { + parentId = parent.getAttrs().nodeId; + } + + this.getUsersPresencePlugin()?.setPresence( + realNodeTarget.id(), + parentId, + { + x: realNodeTarget.x(), + y: realNodeTarget.y(), + } + ); const layerToMove = containerOverCursor(this.instance, [ realNodeTarget, @@ -744,9 +753,9 @@ export abstract class WeaveNode implements WeaveNodeBase { lockedAxis = null; isShiftPressed = false; - if (this.getSelectionPlugin()?.getDragSelectedNodes().length === 1) { - this.getSelectionPlugin()?.restoreNodesOpacityOnDrag(); - } + // if (this.getSelectionPlugin()?.getDragSelectedNodes().length === 1) { + this.getSelectionPlugin()?.restoreNodesOpacityOnDrag(); + // } if (this.getSelectionPlugin()?.getSelectedNodes().length === 1) { this.instance.releaseMutexLock(); @@ -1044,10 +1053,10 @@ export abstract class WeaveNode implements WeaveNodeBase { } handleMouseout(e: KonvaEventObject, node: Konva.Node) { - const isCtrlOrMetaPressed = e.evt.ctrlKey || e.evt.metaKey; + const isCtrlOrMetaPressed = e.evt?.ctrlKey || e.evt?.metaKey; if (isCtrlOrMetaPressed) { - return; + return false; } const realNode = this.instance.getInstanceRecursive(node); @@ -1103,7 +1112,6 @@ export abstract class WeaveNode implements WeaveNodeBase { delete cleanedAttrs.mutexUserId; delete cleanedAttrs.draggable; delete cleanedAttrs.overridesMouseControl; - delete cleanedAttrs.onMoveContainer; delete cleanedAttrs.dragBoundFunc; return { diff --git a/code/packages/sdk/src/nodes/stroke/stroke.ts b/code/packages/sdk/src/nodes/stroke/stroke.ts index b7173cfec..f44792deb 100644 --- a/code/packages/sdk/src/nodes/stroke/stroke.ts +++ b/code/packages/sdk/src/nodes/stroke/stroke.ts @@ -295,7 +295,6 @@ export class WeaveStrokeNode extends WeaveNode { delete cleanedAttrs.sceneFunc; delete cleanedAttrs.hitFunc; delete cleanedAttrs.overridesMouseControl; - delete cleanedAttrs.onMoveContainer; delete cleanedAttrs.dragBoundFunc; return { diff --git a/code/packages/sdk/src/nodes/text/text.ts b/code/packages/sdk/src/nodes/text/text.ts index eea451d02..703ab55a4 100644 --- a/code/packages/sdk/src/nodes/text/text.ts +++ b/code/packages/sdk/src/nodes/text/text.ts @@ -467,7 +467,6 @@ export class WeaveTextNode extends WeaveNode { delete cleanedAttrs.measureMultilineText; delete cleanedAttrs.overridesMouseControl; delete cleanedAttrs.shouldUpdateOnTransform; - delete cleanedAttrs.onMoveContainer; delete cleanedAttrs.dragBoundFunc; return { diff --git a/code/packages/sdk/src/nodes/video/video.ts b/code/packages/sdk/src/nodes/video/video.ts index 574965f63..ef86bd2a4 100644 --- a/code/packages/sdk/src/nodes/video/video.ts +++ b/code/packages/sdk/src/nodes/video/video.ts @@ -80,6 +80,10 @@ export class WeaveVideoNode extends WeaveNode { video ) ?? videoProps.videoPlaceholderURL; + if (!this.videoPlaceholder) { + this.initialize(); + } + this.videoPlaceholder[id] = Konva.Util.createImageElement(); this.videoPlaceholder[id].crossOrigin = this.config.crossOrigin; this.videoPlaceholder[id].src = realVideoPlaceholderURL; diff --git a/code/packages/sdk/src/plugins/copy-paste-nodes/types.ts b/code/packages/sdk/src/plugins/copy-paste-nodes/types.ts index ab2dae12b..5ad6d57dc 100644 --- a/code/packages/sdk/src/plugins/copy-paste-nodes/types.ts +++ b/code/packages/sdk/src/plugins/copy-paste-nodes/types.ts @@ -61,8 +61,13 @@ export type PaddingOnPaste = { paddingY: number; }; +export type WeaveCanPasteOntoFunction = ( + node: WeaveStateElement, + atTarget: Konva.Container +) => boolean; + export type WeaveCopyPasteNodesPluginConfig = { - canPasteOnto: (node: WeaveStateElement, atTarget: Konva.Container) => boolean; + canPasteOnto: WeaveCanPasteOntoFunction; paddingOnPaste: PaddingOnPaste; }; diff --git a/code/packages/sdk/src/plugins/nodes-selection/nodes-selection.ts b/code/packages/sdk/src/plugins/nodes-selection/nodes-selection.ts index 07db52056..c542cb153 100644 --- a/code/packages/sdk/src/plugins/nodes-selection/nodes-selection.ts +++ b/code/packages/sdk/src/plugins/nodes-selection/nodes-selection.ts @@ -369,8 +369,15 @@ export class WeaveNodesSelectionPlugin extends WeavePlugin { if (this.getUsersPresencePlugin()) { for (const node of tr.nodes()) { + let parentId: string = node.getParent()?.id() ?? ''; + const parent = node.getParent(); + if (parent?.getAttrs().nodeId) { + parentId = parent.getAttrs().nodeId; + } + this.getUsersPresencePlugin()?.setPresence( node.id(), + parentId, { x: node.x(), y: node.y(), @@ -440,10 +447,7 @@ export class WeaveNodesSelectionPlugin extends WeavePlugin { const stage = this.instance.getStage(); this.saveDragSelectedNodes(); - - if (this.getDragSelectedNodes().length > 1) { - this.setNodesOpacityOnDrag(); - } + this.setNodesOpacityOnDrag(); selectedNodes = tr.nodes(); @@ -523,8 +527,15 @@ export class WeaveNodesSelectionPlugin extends WeavePlugin { if (this.getUsersPresencePlugin() && this.dragInProcess) { for (const node of selectedNodes) { + let parentId: string = node.getParent()?.id() ?? ''; + const parent = node.getParent(); + if (parent?.getAttrs().nodeId) { + parentId = parent.getAttrs().nodeId; + } + this.getUsersPresencePlugin()?.setPresence( node.id(), + parentId, { x: node.x(), y: node.y(), @@ -559,10 +570,6 @@ export class WeaveNodesSelectionPlugin extends WeavePlugin { this.instance.getSelectionLayer()?.hitGraphEnabled(true); this.instance.getMainLayer()?.hitGraphEnabled(true); - if (this.getDragSelectedNodes().length > 1) { - this.restoreNodesOpacityOnDrag(); - } - if (!this.didMove) { return; } diff --git a/code/packages/sdk/src/plugins/users-presence/types.ts b/code/packages/sdk/src/plugins/users-presence/types.ts index e8499eae5..b22b0d3bb 100644 --- a/code/packages/sdk/src/plugins/users-presence/types.ts +++ b/code/packages/sdk/src/plugins/users-presence/types.ts @@ -21,6 +21,7 @@ export type WeaveUserPresenceInformation = Record< export type WeaveUserPresence = { userId: string; + parentId: string; nodeId: string; attrs: T; }; diff --git a/code/packages/sdk/src/plugins/users-presence/users-presence.ts b/code/packages/sdk/src/plugins/users-presence/users-presence.ts index 28f375167..bdecc262b 100644 --- a/code/packages/sdk/src/plugins/users-presence/users-presence.ts +++ b/code/packages/sdk/src/plugins/users-presence/users-presence.ts @@ -75,7 +75,17 @@ export class WeaveUsersPresencePlugin extends WeavePlugin { const nodeInstance = stage.findOne(`#${presenceInfo.nodeId}`); - if (nodeInstance) { + if (!nodeInstance) { + continue; + } + + let parentId: string = nodeInstance.getParent()?.id() ?? ''; + const parent = nodeInstance.getParent(); + if (parent?.getAttrs().nodeId) { + parentId = parent.getAttrs().nodeId; + } + + if (nodeInstance && presenceInfo.parentId === parentId) { const newProps = { ...nodeInstance.getAttrs(), ...(presenceInfo.attrs as Record), @@ -93,11 +103,17 @@ export class WeaveUsersPresencePlugin extends WeavePlugin { store.setAwarenessInfo(WEAVE_USER_PRESENCE_KEY, this.userPresence); } - setPresence(nodeId: string, attrs: T, forceUpdate = true) { + setPresence( + nodeId: string, + parentId: string, + attrs: T, + forceUpdate = true + ) { const userInfo = this.config.getUser(); this.userPresence[nodeId] = { userId: userInfo.id, + parentId, nodeId, attrs, }; diff --git a/code/packages/sdk/src/utils/utils.ts b/code/packages/sdk/src/utils/utils.ts index 6d5fcf6e3..87a4617b1 100644 --- a/code/packages/sdk/src/utils/utils.ts +++ b/code/packages/sdk/src/utils/utils.ts @@ -195,10 +195,11 @@ export function moveNodeToContainerNT( node.rotation(nodeRotation); node.x(node.x() - (layerToMoveAttrs.containerOffsetX ?? 0)); node.y(node.y() - (layerToMoveAttrs.containerOffsetY ?? 0)); - node.movedToContainer(layerToMove); + node.destroy(); + const newNode: Konva.Node = node.clone(); instance.emitEvent('onNodeMovedToContainer', { - node: node.clone(), + node: newNode, container: layerToMove, originalNode, originalContainer, @@ -209,10 +210,14 @@ export function moveNodeToContainerNT( ); if (nodeHandler) { - node.setAttrs({ onMoveContainer: true }); - const actualNode = nodeHandler.serialize(node as WeaveElementInstance); - instance.removeNodeNT(actualNode, { emitUserChangeEvent: false }); - instance.addNodeNT(actualNode, layerToMoveAttrs.id, { + const actualNodeState = nodeHandler.serialize( + node as WeaveElementInstance + ); + const newNodeState = nodeHandler.serialize( + newNode as WeaveElementInstance + ); + instance.removeNodeNT(actualNodeState, { emitUserChangeEvent: false }); + instance.addNodeNT(newNodeState, layerToMoveAttrs.id, { // emitUserChangeEvent: true, emitUserChangeEvent: false, overrideUserChangeType: WEAVE_NODE_CHANGE_TYPE.UPDATE, @@ -633,40 +638,6 @@ export function isIOS() { export const isServer = () => typeof window === 'undefined'; -export const getPositionRelativeToContainerOnPosition = ( - instance: Weave -): Konva.Vector2d | null | undefined => { - let position: Konva.Vector2d | null | undefined = instance - .getStage() - .getRelativePointerPosition(); - - if (!position) { - return position; - } - - const container = containerOverCursor(instance, [], position); - - if (container) { - if (container.getAttrs().containerId) { - const containerNode = container.findOne( - `#${container.getAttrs().containerId}` - ) as Konva.Group; - - if (containerNode) { - position = containerNode?.getRelativePointerPosition(); - } - } else { - position = container?.getRelativePointerPosition(); - } - } - - if (!position) { - return position; - } - - return position; -}; - export const canComposite = (node: Konva.Node) => { const parent = node.getParent(); diff --git a/code/packages/sdk/src/weave.ts b/code/packages/sdk/src/weave.ts index 877e2d7e9..1e3e3eaed 100644 --- a/code/packages/sdk/src/weave.ts +++ b/code/packages/sdk/src/weave.ts @@ -232,10 +232,7 @@ export class Weave { if (!this.isServerSide()) { this.eventsController = new AbortController(); - // Setup the instance on the weave global variable - if (!window.weave) { - window.weave = this; - } + window.weave = this; } this.emitEvent('onRoomLoaded', false); diff --git a/docs/content/docs/main/changelog/3.x/3.8.1.mdx b/docs/content/docs/main/changelog/3.x/3.8.1.mdx index c06980aae..03d9d69e1 100644 --- a/docs/content/docs/main/changelog/3.x/3.8.1.mdx +++ b/docs/content/docs/main/changelog/3.x/3.8.1.mdx @@ -12,3 +12,4 @@ description: Fix to pan stage when dragging elements near border, avoid locking - [#1036](https://github.com/InditexTech/weavejs/issues/1036) Corrupt Image nodes prevent frames to complete initial loading - [#1038](https://github.com/InditexTech/weavejs/issues/1038) Is possible to add a frame inside a frame by pasting with right mouse button - [#1043](https://github.com/InditexTech/weavejs/issues/1043) Canvas does not auto-scroll when dragging an element near the border +- [#1044](https://github.com/InditexTech/weavejs/issues/1044) Nodes disappear when moving between frames diff --git a/docs/content/docs/sdk/api-reference/plugins/copy-paste-nodes.mdx b/docs/content/docs/sdk/api-reference/plugins/copy-paste-nodes.mdx index eca3bbf1f..fdf788344 100644 --- a/docs/content/docs/sdk/api-reference/plugins/copy-paste-nodes.mdx +++ b/docs/content/docs/sdk/api-reference/plugins/copy-paste-nodes.mdx @@ -49,6 +49,15 @@ const WEAVE_COPY_PASTE_PASTE_MODES = { } as const; const WEAVE_COPY_PASTE_CONFIG_DEFAULT = { + canPasteOnto: (node: WeaveStateElement, atTarget: Konva.Container) => { + const targetType = atTarget.getAttrs().nodeType; + + if (targetType === "frame" && node.type === "frame") { + return false; + } + + return true; + }, paddingOnPaste: { enabled: false, paddingX: 0, @@ -90,7 +99,13 @@ type PaddingOnPaste = { paddingY: number; }; +export type WeaveCanPasteOntoFunction = ( + node: WeaveStateElement, + atTarget: Konva.Container, +) => boolean; + type WeaveCopyPasteNodesPluginConfig = { + canPasteOnto: WeaveCanPasteOntoFunction; paddingOnPaste: PaddingOnPaste; }; @@ -122,6 +137,12 @@ For `WeaveCopyPasteNodesPluginConfig`: