diff --git a/packages/core-instance/src/CoreInstance.ts b/packages/core-instance/src/CoreInstance.ts index 7ebf7bf86..6bb7e233b 100644 --- a/packages/core-instance/src/CoreInstance.ts +++ b/packages/core-instance/src/CoreInstance.ts @@ -39,6 +39,8 @@ class CoreInstance extends AbstractInstance implements CoreInstanceInterface { isVisible: boolean; + isInjected?: true; + hasToTransform!: boolean; animatedFrame: number | null; @@ -50,6 +52,7 @@ class CoreInstance extends AbstractInstance implements CoreInstanceInterface { super(element, opts); + this.isInjected = opts.isInjected; this.order = order; this.keys = keys; this.depth = depth; diff --git a/packages/core-instance/src/types.ts b/packages/core-instance/src/types.ts index 4fe179d93..16b1f0303 100644 --- a/packages/core-instance/src/types.ts +++ b/packages/core-instance/src/types.ts @@ -10,6 +10,7 @@ import type { export interface AbstractOpts { isInitialized: boolean; isPaused: boolean; + isInjected?: true; } export type AbstractInput = { @@ -102,6 +103,15 @@ export interface CoreInstanceInterface extends AbstractInterface { /** Element visibility in the scroll container. */ isVisible: boolean; + /** + * True when element is injected in the registry. + * Represents the higher node element in the registered tree and prevents + * recursive fetching child/parent. + * So when the store reads `isInjected: true` it knows this is it and whe have + * to stop fetching higher nodes. + */ + isInjected?: true; + /** Animated frame if the element is transforming */ readonly animatedFrame: number | null; diff --git a/packages/dnd/src/DnD.ts b/packages/dnd/src/DnD.ts index 9603daa43..f229fbf8c 100644 --- a/packages/dnd/src/DnD.ts +++ b/packages/dnd/src/DnD.ts @@ -38,7 +38,9 @@ class DnD extends Droppable { const options = extractOpts(opts); - const { SK } = store.registry[id].keys; + const { + keys: { SK }, + } = store.registry[id]; if (!SK) { throw new Error(`DFlex: unable to find element list key in the Store.`); @@ -48,7 +50,7 @@ class DnD extends Droppable { * In case it is not already initiated in the store. We do it here guarantee * all the branch is updated. */ - store.initSiblingsScrollAndVisibilityIfNecessary(SK); + store.onLoadListeners(); const draggable = new DraggableInteractive( id, diff --git a/packages/dnd/src/DnDStore/DnDStoreImp.ts b/packages/dnd/src/DnDStore/DnDStoreImp.ts index 4def126ca..c40da0a42 100644 --- a/packages/dnd/src/DnDStore/DnDStoreImp.ts +++ b/packages/dnd/src/DnDStore/DnDStoreImp.ts @@ -45,6 +45,9 @@ class DnDStoreImp extends Store implements DnDStoreInterface { layoutState: DnDStoreInterface["layoutState"]; + /** has list of child-id that needs parent container. */ + #nodesNeedParents: string[]; + private events: Events; private isDOM: boolean; @@ -73,6 +76,8 @@ class DnDStoreImp extends Store implements DnDStoreInterface { this.siblingsGrid = {}; this.siblingsGridContainer = {}; + this.#nodesNeedParents = []; + this.layoutState = "pending"; // @ts-expect-error Should be initialized when calling DnD instance. @@ -155,7 +160,7 @@ class DnDStoreImp extends Store implements DnDStoreInterface { updateElementVisibility( elmID: string, - parenID: string, + parentID: string | null, scroll: ScrollInterface, allowDynamicVisibility: boolean, permitExceptionToOverride: boolean @@ -174,6 +179,12 @@ class DnDStoreImp extends Store implements DnDStoreInterface { this.registry[elmID].keys.SK, this.registry[elmID].offset! ); + + if (parentID) { + this.registry[parentID].assignBoundaries( + this.registry[elmID].offset! + ); + } } } @@ -244,25 +255,23 @@ class DnDStoreImp extends Store implements DnDStoreInterface { const firstElemID = branch[0]; // Check the parent of first element. - const { parent } = this.getElmTreeById(firstElemID); + const { parent, element } = this.getElmTreeById(firstElemID); - let parentID: string; + let parentID: string | null = null; /** - * If parent is not registered, but children are then extract it and use it. + * If parent is not registered, and element not injected in the registry. */ - if (!parent) { - const childInstance = this.registry[firstElemID]; - this.register({ - ref: childInstance.getELmParentRef()!, - depth: childInstance.depth + 1, - }); - - ({ id: parentID } = this.getElmTreeById(firstElemID).parent!); - } else { - parentID = parent.id; + if (!parent && !element.isInjected) { + this.#nodesNeedParents.push(firstElemID); + + // No need to continue, because the parent is not registered and we cant + // detect the boundaries rect. + return; } + parentID = parent?.id || null; + branch.forEach((elmID, i) => { if (elmID.length > 0) { const permitExceptionToOverride = i > prevIndex; @@ -400,6 +409,7 @@ class DnDStoreImp extends Store implements DnDStoreInterface { if (scroll.allowDynamicVisibility) { this.updateBranchVisibility(key, true); + scroll.scrollEventCallback = this.updateBranchVisibility; } else { this.updateBranchVisibility(key, false); @@ -410,6 +420,37 @@ class DnDStoreImp extends Store implements DnDStoreInterface { Object.keys(this.DOMGen.branches).forEach((branchKey) => { this.initSiblingsScrollAndVisibilityIfNecessary(branchKey); }); + + this.#nodesNeedParents.forEach((id) => { + const childInstance = this.registry[id]; + + const ref = childInstance.getELmParentRef(); + + if (ref) { + this.register( + { + ref, + depth: childInstance.depth + 1, + }, + true + ); + } + }); + + this.#nodesNeedParents.forEach((id) => { + const { + keys: { SK }, + } = this.registry[id]; + + this.updateBranchVisibility( + SK, + this.siblingsScrollElement[SK].allowDynamicVisibility + ); + }); + + if (this.#nodesNeedParents.length > 0) { + this.#nodesNeedParents = []; + } } #assignSiblingsGrid(id: string, SK: string, rect: RectDimensions) { @@ -548,7 +589,7 @@ class DnDStoreImp extends Store implements DnDStoreInterface { * * @param element - */ - register(element: RegisterInput) { + register(element: RegisterInput, isInjected?: true) { const hasRef = !!element.ref; if (!hasRef && !element.id) { @@ -599,6 +640,7 @@ class DnDStoreImp extends Store implements DnDStoreInterface { depth: element.depth || 0, ref: element.ref, isInitialized: hasRef, + isInjected, isPaused: true, scrollX: 0, scrollY: 0, @@ -647,7 +689,7 @@ class DnDStoreImp extends Store implements DnDStoreInterface { const { keys: { SK, PK }, - order: { parent: pi }, + order, } = element; /** @@ -661,7 +703,9 @@ class DnDStoreImp extends Store implements DnDStoreInterface { */ let parent = null; if (parents !== undefined) { - const parentsID = Array.isArray(parents) ? parents[pi] : parents; + const parentsID = Array.isArray(parents) + ? parents[order.parent] + : parents; parent = this.registry[parentsID as string]; } diff --git a/packages/dnd/src/Draggable/DraggableAxes.ts b/packages/dnd/src/Draggable/DraggableAxes.ts index fef4c31dc..2a1a012a0 100644 --- a/packages/dnd/src/Draggable/DraggableAxes.ts +++ b/packages/dnd/src/Draggable/DraggableAxes.ts @@ -114,6 +114,46 @@ class DraggableAxes siblings !== null && (opts.restrictionsStatus.isContainerRestricted || opts.restrictionsStatus.isSelfRestricted); + + this.#iniLayoutsThreshold(); + } + + #iniLayoutsThreshold() { + const { + offset: { width, height }, + currentPosition, + id, + } = this.draggedElm; + + this.threshold.setMainThreshold(id, { + width, + height, + left: currentPosition.x, + top: currentPosition.y, + }); + + const { + branches: { parents }, + } = store.getElmTreeById(id); + + (Array.isArray(parents) ? parents : [parents]).forEach((parentID) => { + if (!parentID) return; + + const { boundaries } = store.registry[parentID]; + + if (!boundaries) { + if (process.env.NODE_ENV !== "production") { + // eslint-disable-next-line no-console + throw new Error( + `Unable to find boundaries Rect for parent ${parentID}` + ); + } + + return; + } + + this.threshold.setContainerThreshold(parentID, boundaries); + }); } private axesYFilter( @@ -180,7 +220,11 @@ class DraggableAxes let filteredY = y; let filteredX = x; - const { SK } = store.registry[this.draggedElm.id].keys; + const { id } = this.draggedElm; + + const { + keys: { SK }, + } = store.registry[id]; if (this.axesFilterNeeded) { const { @@ -188,7 +232,7 @@ class DraggableAxes bottom, left: maxLeft, right: minRight, - } = store.siblingsBoundaries[SK]; + } = store.getElmTreeById(id).parent!.boundaries!; if (this.restrictionsStatus.isContainerRestricted) { filteredX = this.axesXFilter( @@ -256,7 +300,7 @@ class DraggableAxes ); } - isOutThreshold(SK?: string) { + isOutThreshold(parentID?: string) { const { id, offset: { height, width }, @@ -264,7 +308,7 @@ class DraggableAxes const { x, y } = this.positionPlaceholder; - const key = SK || id; + const key = parentID || id; return ( this.threshold.isOutThresholdV(key, y, y + height) || @@ -283,11 +327,11 @@ class DraggableAxes } isNotSettled() { - const { SK } = store.registry[this.draggedElm.id].keys; + const { id } = store.getElmTreeById(this.draggedElm.id).parent!; return ( !this.#isLeavingFromTail() && - (this.isOutThreshold() || this.isOutThreshold(SK)) + (this.isOutThreshold() || this.isOutThreshold(id)) ); } } diff --git a/packages/dnd/test/Store/__snapshots__/dndStore.test.ts.snap b/packages/dnd/test/Store/__snapshots__/dndStore.test.ts.snap index db0314b43..f1fc73e9e 100644 --- a/packages/dnd/test/Store/__snapshots__/dndStore.test.ts.snap +++ b/packages/dnd/test/Store/__snapshots__/dndStore.test.ts.snap @@ -7,6 +7,7 @@ Object { "depth": 0, "id": "id-1", "isInitialized": true, + "isInjected": undefined, "isPaused": true, "isVisible": false, "keys": Object { @@ -27,6 +28,7 @@ Object { "depth": 0, "id": "id-2", "isInitialized": true, + "isInjected": undefined, "isPaused": true, "isVisible": false, "keys": Object { @@ -47,6 +49,7 @@ Object { "depth": 0, "id": "id-3", "isInitialized": true, + "isInjected": undefined, "isPaused": true, "isVisible": false, "keys": Object { @@ -67,6 +70,7 @@ Object { "depth": 0, "id": "id-4", "isInitialized": true, + "isInjected": undefined, "isPaused": true, "isVisible": false, "keys": Object { @@ -101,6 +105,7 @@ Object { "depth": 0, "id": "id-1", "isInitialized": true, + "isInjected": undefined, "isPaused": true, "isVisible": false, "keys": Object { diff --git a/packages/draggable/test/__snapshots__/draggableStore.test.ts.snap b/packages/draggable/test/__snapshots__/draggableStore.test.ts.snap index dfed6cb04..314ee6e62 100644 --- a/packages/draggable/test/__snapshots__/draggableStore.test.ts.snap +++ b/packages/draggable/test/__snapshots__/draggableStore.test.ts.snap @@ -17,6 +17,7 @@ Object { "hasToTransform": false, "id": "id-0", "isInitialized": true, + "isInjected": undefined, "isPaused": false, "isVisible": true, "keys": Object { diff --git a/packages/store/src/Store.ts b/packages/store/src/Store.ts index 6d1dfa576..2d53a5cd5 100644 --- a/packages/store/src/Store.ts +++ b/packages/store/src/Store.ts @@ -23,7 +23,7 @@ class Store { } private submitElementToRegistry(element: RegisterInput) { - const { id, depth, isPaused, isInitialized, ...rest } = element; + const { id, depth, isPaused, isInitialized, isInjected, ...rest } = element; const { order, keys } = this.DOMGen.getElmPointer(id, depth); @@ -38,6 +38,7 @@ class Store { this.registry[id] = new CoreInstance(coreElement, { isInitialized, isPaused, + isInjected, }); } diff --git a/packages/store/src/types.ts b/packages/store/src/types.ts index 3ae56d51d..d95f4a94f 100644 --- a/packages/store/src/types.ts +++ b/packages/store/src/types.ts @@ -36,6 +36,7 @@ export type RegisterInput = { depth: number; isInitialized: boolean; isPaused: boolean; + isInjected?: true; scrollX: number; scrollY: number; }; diff --git a/packages/store/test/__snapshots__/store.test.ts.snap b/packages/store/test/__snapshots__/store.test.ts.snap index 0b27a1527..1b512c8cb 100644 --- a/packages/store/test/__snapshots__/store.test.ts.snap +++ b/packages/store/test/__snapshots__/store.test.ts.snap @@ -16,6 +16,7 @@ CoreInstance { "hasToTransform": false, "id": "id-0", "isInitialized": true, + "isInjected": undefined, "isPaused": false, "isVisible": true, "keys": Object { @@ -60,6 +61,7 @@ Object { "hasToTransform": false, "id": "id-0", "isInitialized": true, + "isInjected": undefined, "isPaused": false, "isVisible": true, "keys": Object { @@ -100,6 +102,7 @@ Object { "hasToTransform": false, "id": "id-1", "isInitialized": true, + "isInjected": undefined, "isPaused": false, "isVisible": true, "keys": Object { @@ -140,6 +143,7 @@ Object { "hasToTransform": false, "id": "id-2", "isInitialized": true, + "isInjected": undefined, "isPaused": false, "isVisible": true, "keys": Object { @@ -180,6 +184,7 @@ Object { "hasToTransform": false, "id": "p-id-0", "isInitialized": true, + "isInjected": undefined, "isPaused": false, "isVisible": true, "keys": Object { @@ -225,6 +230,7 @@ Object { "hasToTransform": false, "id": "id-0", "isInitialized": true, + "isInjected": undefined, "isPaused": false, "isVisible": true, "keys": Object { @@ -265,6 +271,7 @@ Object { "hasToTransform": false, "id": "id-1", "isInitialized": true, + "isInjected": undefined, "isPaused": false, "isVisible": true, "keys": Object { @@ -305,6 +312,7 @@ Object { "hasToTransform": false, "id": "id-2", "isInitialized": true, + "isInjected": undefined, "isPaused": false, "isVisible": true, "keys": Object { @@ -345,6 +353,7 @@ Object { "hasToTransform": false, "id": "p-id-0", "isInitialized": true, + "isInjected": undefined, "isPaused": false, "isVisible": true, "keys": Object {