From 2a61db5fed6cd09f548521ba6a799c05ca02f2b4 Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Sat, 25 Oct 2025 22:44:43 +0530 Subject: [PATCH 1/3] disallow invalid layer drop --- frontend/src/components/panels/Layers.svelte | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/panels/Layers.svelte b/frontend/src/components/panels/Layers.svelte index 82c8a5f068..4610ba9a4b 100644 --- a/frontend/src/components/panels/Layers.svelte +++ b/frontend/src/components/panels/Layers.svelte @@ -308,16 +308,25 @@ let markerHeight = 0; const layerPanel = document.querySelector("[data-layer-panel]"); // Selects the element with the data-layer-panel attribute if (layerPanel !== null && treeChildren !== undefined && treeOffset !== undefined) { + const lastChild = treeChildren[treeChildren.length - 1]; + if (lastChild.getBoundingClientRect().bottom < clientY) { + return { select, insertParentId: undefined, insertDepth: 0, insertIndex: undefined, highlightFolder: false, markerHeight: 0 }; + } + let layerPanelTop = layerPanel.getBoundingClientRect().top; Array.from(treeChildren).forEach((treeChild) => { - const indexAttribute = treeChild.getAttribute("data-index"); + const indexAttribute = parseInt(treeChild.getAttribute("data-index") ?? "0", 10); if (!indexAttribute) return; - const { folderIndex, entry: layer } = layers[parseInt(indexAttribute, 10)]; + const { folderIndex, entry: layer } = layers[indexAttribute]; const rect = treeChild.getBoundingClientRect(); if (rect.top > clientY || rect.bottom < clientY) { return; } + + const isDraggingBtnArtBoards = layers[indexAttribute]?.entry?.depth === 1 && layers[indexAttribute + 1]?.entry?.depth === 1; + if (isDraggingBtnArtBoards) return; + const pointerPercentage = (clientY - rect.top) / rect.height; if (layer.childrenAllowed) { if (pointerPercentage < 0.25) { From f1912387ad532ed7aae51cd784b2961b93b5450b Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Fri, 31 Oct 2025 22:35:06 +0530 Subject: [PATCH 2/3] improve invalidDrag indicator behaviour --- frontend/src/components/panels/Layers.svelte | 37 +++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/panels/Layers.svelte b/frontend/src/components/panels/Layers.svelte index 4610ba9a4b..83e43de04a 100644 --- a/frontend/src/components/panels/Layers.svelte +++ b/frontend/src/components/panels/Layers.svelte @@ -291,7 +291,7 @@ editor.handle.deselectAllLayers(); } - function calculateDragIndex(tree: LayoutCol, clientY: number, select?: () => void): DraggingData { + function calculateDragIndex(tree: LayoutCol, clientY: number, select?: () => void): DraggingData | undefined { const treeChildren = tree.div()?.children; const treeOffset = tree.div()?.getBoundingClientRect().top; @@ -307,25 +307,42 @@ let markerHeight = 0; const layerPanel = document.querySelector("[data-layer-panel]"); // Selects the element with the data-layer-panel attribute + let isInvalidDrag = false; + if (layerPanel !== null && treeChildren !== undefined && treeOffset !== undefined) { const lastChild = treeChildren[treeChildren.length - 1]; - if (lastChild.getBoundingClientRect().bottom < clientY) { - return { select, insertParentId: undefined, insertDepth: 0, insertIndex: undefined, highlightFolder: false, markerHeight: 0 }; + if (clientY + 10 > lastChild.getBoundingClientRect().bottom) { + return; } let layerPanelTop = layerPanel.getBoundingClientRect().top; - Array.from(treeChildren).forEach((treeChild) => { + + for (const treeChild of Array.from(treeChildren)) { + if (isInvalidDrag) break; const indexAttribute = parseInt(treeChild.getAttribute("data-index") ?? "0", 10); - if (!indexAttribute) return; + if (!indexAttribute) continue; const { folderIndex, entry: layer } = layers[indexAttribute]; const rect = treeChild.getBoundingClientRect(); if (rect.top > clientY || rect.bottom < clientY) { - return; + continue; + } + + const prevLayer = layers[indexAttribute]; + const nextLayer = layers[indexAttribute + 1]; + if (prevLayer?.entry?.depth === 1) { + const prevRectTop = treeChildren?.[indexAttribute].getBoundingClientRect().top; + if (prevLayer?.entry?.depth === 1 && prevRectTop + 10 > clientY) { + isInvalidDrag = true; + break; + } } - const isDraggingBtnArtBoards = layers[indexAttribute]?.entry?.depth === 1 && layers[indexAttribute + 1]?.entry?.depth === 1; - if (isDraggingBtnArtBoards) return; + const isDraggingBtnArtBoards = nextLayer?.entry?.depth === 1 && prevLayer?.entry?.depth === 1; + + if (isDraggingBtnArtBoards) { + isInvalidDrag = true; + } const pointerPercentage = (clientY - rect.top) / rect.height; if (layer.childrenAllowed) { @@ -358,7 +375,8 @@ markerHeight = rect.bottom - layerPanelTop; } } - }); + break; + } // Dragging to the empty space below all layers let lastLayer = treeChildren[treeChildren.length - 1]; if (lastLayer.getBoundingClientRect().bottom < clientY) { @@ -368,6 +386,7 @@ insertIndex = numberRootLayers; markerHeight = lastLayer.getBoundingClientRect().bottom - layerPanelTop; } + if (isInvalidDrag) return; } return { From 87aec62240c50631bf31c4c7ff8a396adc99a11d Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Sat, 1 Nov 2025 21:45:57 +0530 Subject: [PATCH 3/3] overall layer,artboard drag behaviour impr --- frontend/src/components/panels/Layers.svelte | 52 +++++++++++++------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/panels/Layers.svelte b/frontend/src/components/panels/Layers.svelte index 83e43de04a..a048a7bf8c 100644 --- a/frontend/src/components/panels/Layers.svelte +++ b/frontend/src/components/panels/Layers.svelte @@ -291,7 +291,7 @@ editor.handle.deselectAllLayers(); } - function calculateDragIndex(tree: LayoutCol, clientY: number, select?: () => void): DraggingData | undefined { + function calculateDragIndex(tree: LayoutCol, clientY: number, dataIndex: number, select?: () => void): DraggingData | undefined { const treeChildren = tree.div()?.children; const treeOffset = tree.div()?.getBoundingClientRect().top; @@ -310,9 +310,11 @@ let isInvalidDrag = false; if (layerPanel !== null && treeChildren !== undefined && treeOffset !== undefined) { - const lastChild = treeChildren[treeChildren.length - 1]; - if (clientY + 10 > lastChild.getBoundingClientRect().bottom) { - return; + const lastLayerDepth = layers[layers.length - 1]?.entry?.depth; + const draggingLayerDepth = layers[dataIndex]?.entry?.depth; + + if (clientY > treeChildren[layers.length - 1].getBoundingClientRect().bottom - 10) { + if (lastLayerDepth === 1 && draggingLayerDepth > 1) isInvalidDrag = true; } let layerPanelTop = layerPanel.getBoundingClientRect().top; @@ -327,23 +329,25 @@ if (rect.top > clientY || rect.bottom < clientY) { continue; } + if (draggingLayerDepth > 1) { + const prevLayer = layers[indexAttribute]; + const nextLayer = layers[indexAttribute + 1]; + if (prevLayer?.entry?.depth === 1) { + const prevRectTop = treeChildren?.[indexAttribute].getBoundingClientRect().top; + if (prevLayer?.entry?.depth === 1 && prevRectTop + 10 > clientY) { + isInvalidDrag = true; + break; + } + } - const prevLayer = layers[indexAttribute]; - const nextLayer = layers[indexAttribute + 1]; - if (prevLayer?.entry?.depth === 1) { - const prevRectTop = treeChildren?.[indexAttribute].getBoundingClientRect().top; - if (prevLayer?.entry?.depth === 1 && prevRectTop + 10 > clientY) { + const isDraggingBtnArtBoards = nextLayer?.entry?.depth === 1 && prevLayer?.entry?.depth === 1; + + if (isDraggingBtnArtBoards) { isInvalidDrag = true; break; } } - const isDraggingBtnArtBoards = nextLayer?.entry?.depth === 1 && prevLayer?.entry?.depth === 1; - - if (isDraggingBtnArtBoards) { - isInvalidDrag = true; - } - const pointerPercentage = (clientY - rect.top) / rect.height; if (layer.childrenAllowed) { if (pointerPercentage < 0.25) { @@ -412,7 +416,11 @@ const target = (event.target instanceof HTMLElement && event.target) || undefined; const closest = target?.closest("[data-layer]") || undefined; const draggingELement = (closest instanceof HTMLElement && closest) || undefined; - if (draggingELement) beginDraggingElement(draggingELement); + if (draggingELement) { + beginDraggingElement(draggingELement); + // Store the index of the layer being dragged + draggedLayerIndex = parseInt(draggingELement.getAttribute("data-index") ?? "0", 10); + } // Set style of cursor for drag if (event.dataTransfer) { @@ -420,9 +428,13 @@ event.dataTransfer.effectAllowed = "move"; } - if (list) draggingData = calculateDragIndex(list, event.clientY, select); + if (list && draggedLayerIndex !== undefined) { + draggingData = calculateDragIndex(list, event.clientY, draggedLayerIndex, select); + } } + let draggedLayerIndex: number | undefined; + function updateInsertLine(event: DragEvent) { if (!draggable) return; @@ -430,7 +442,10 @@ event.preventDefault(); dragInPanel = true; - if (list) draggingData = calculateDragIndex(list, event.clientY, draggingData?.select); + // Use the stored index from dragStart + if (list && draggedLayerIndex !== undefined) { + draggingData = calculateDragIndex(list, event.clientY, draggedLayerIndex, draggingData?.select); + } } function drop(e: DragEvent) { @@ -480,6 +495,7 @@ draggingData = undefined; fakeHighlightOfNotYetSelectedLayerBeingDragged = undefined; dragInPanel = false; + draggedLayerIndex = undefined; } function rebuildLayerHierarchy(updateDocumentLayerStructure: DocumentLayerStructure) {