From 32c95a1a700660aa8a2ffb8af2acaa138c81ad56 Mon Sep 17 00:00:00 2001 From: Nathan Marrs Date: Thu, 8 Aug 2024 10:42:24 -0600 Subject: [PATCH] Canvas: Fix selection box glitch when adding certain elements (#91654) * fix selecto glitch when adding certain elements * minor doc updates (cherry picked from commit 787abccfbc9e16b68b3295b17269cb3b46b88527) --- .../canvas/runtime/sceneAbleManagement.ts | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/public/app/features/canvas/runtime/sceneAbleManagement.ts b/public/app/features/canvas/runtime/sceneAbleManagement.ts index b5b0aa06cc4..0befab40297 100644 --- a/public/app/features/canvas/runtime/sceneAbleManagement.ts +++ b/public/app/features/canvas/runtime/sceneAbleManagement.ts @@ -33,6 +33,38 @@ const enableCustomables = (moveable: Moveable) => { }; }; +/* + Helper function that determines if the selected DOM target is currently selected in selecto state. + + For context canvas elements each have a different level of nesting. + Given this, we need to traverse up the DOM tree from the selected target to find + the element's registered selecto div to determine if the selected target is already selected in selecto state. + See `initMoveable` and `generateTargetElements` for more context. +*/ +const isTargetAlreadySelected = (selectedTarget: HTMLElement, scene: Scene) => { + let selectedTargetParent = selectedTarget.parentElement; + let isTargetAlreadySelected = false; + + // Traverse up the DOM tree to check if the selected target is already selected + while (selectedTargetParent) { + // If the selected target is the scene's root element div, break the loop + if (selectedTargetParent === scene.root.div) { + break; + } + + // Check if the selected target is already selected + isTargetAlreadySelected = scene.selecto?.getSelectedTargets().includes(selectedTargetParent) ?? false; + if (isTargetAlreadySelected) { + break; + } + + // Move up the DOM tree to the next parent element to check + selectedTargetParent = selectedTargetParent.parentElement; + } + + return isTargetAlreadySelected; +}; + // Generate HTML element divs for every canvas element to configure selecto / moveable const generateTargetElements = (rootElements: ElementState[]): HTMLDivElement[] => { let targetElements: HTMLDivElement[] = []; @@ -335,9 +367,7 @@ export const initMoveable = (destroySelecto = false, allowChanges = true, scene: scene.moveable!.isMoveableElement(selectedTarget) || targets.some((target) => target === selectedTarget || target.contains(selectedTarget)); - const isTargetAlreadySelected = scene.selecto - ?.getSelectedTargets() - .includes(selectedTarget.parentElement.parentElement); + const isElementSelected = isTargetAlreadySelected(selectedTarget, scene); // Apply grabbing cursor while dragging, applyLayoutStylesToDiv() resets it to grab when done if ( @@ -349,7 +379,7 @@ export const initMoveable = (destroySelecto = false, allowChanges = true, scene: scene.selecto.getSelectedTargets()[0].style.cursor = 'grabbing'; } - if (isTargetMoveableElement || isTargetAlreadySelected || !scene.isEditingEnabled) { + if (isTargetMoveableElement || isElementSelected || !scene.isEditingEnabled) { // Prevent drawing selection box when selected target is a moveable element or already selected event.stop(); }