import { PushNode } from 'utils/variableHeight/buildPushTree' import { GROUP } from '@adalo/constants' import { overlapsRangeWithDifferentY } from './range' import { isSection } from './runtime' export const alreadyPushes = (head: PushNode, toFind: PushNode): boolean => { if (!head.pushes) { return false } for (const obj of head.pushes) { if (obj.objectId === toFind.objectId) { return true } if (alreadyPushes(obj, toFind)) { return true } } return false } export const searchConnected = ( search: PushNode, toSearch: PushNode[], visited: Set ): void => { for (const index in toSearch) { const obj = toSearch[index] if ( !overlapsRangeWithDifferentY(obj.range, search.range) || alreadyPushes(search, obj) ) { continue } if (!visited.has(obj.objectId)) { const i = parseInt(index) searchConnected(obj, toSearch.slice(i + 1), visited) visited.add(obj.objectId) } search.pushes.push(obj) obj.pushedBy.push(search) } } export const sortByY = (obj1: PushNode, obj2: PushNode) => obj1.range.y - obj2.range.y const hydrateFromSavedPushGraph = (container: PushNode) => { const { pushGraph } = container if (!pushGraph) { throw new Error(`Container does not have a push graph`) } const nodesById: Record = {} container.children.forEach(child => { nodesById[child.objectId] = child }) for (const edge of pushGraph.edges) { const { startNodeId, endNodeId } = edge const startNode = nodesById[startNodeId] const endNode = nodesById[endNodeId] if (startNode === undefined || endNode === undefined) { // Not all nodes in saved graphs have content. Ignore these edges and continue. continue } startNode.pushes.push(endNode) endNode.pushedBy.push(startNode) } } export const LIST_ITEM_PUSH_NODE_NESTING_LIMIT = 20 export const hasListItemContainerParent = ( node: PushNode, nesting = 0 ): boolean => { const nodeIsSection = isSection(node) || node.type === GROUP if (nodeIsSection && node.parent?.type === 'LIST_ITEM') { return true } // In case a maker nests a section very deep within a list item if (nesting >= LIST_ITEM_PUSH_NODE_NESTING_LIMIT) { return false } if (node.parent) { return hasListItemContainerParent(node.parent, nesting + 1) } return false } // Can't rehydrate from sections if the parent is LIST_ITEM or push relations won't work, items with // variable height won't push other's down in a list const isSafeToRehydrateFromSavedPushGraph = (node: PushNode): boolean => { const hasPushGraph = Boolean(node.pushGraph) return hasPushGraph && !hasListItemContainerParent(node) } export const addChildrenToPushTree = (node: PushNode): void => { if (isSafeToRehydrateFromSavedPushGraph(node)) { return hydrateFromSavedPushGraph(node) } node.children.sort(sortByY) const visited = new Set() for (const index in node.children) { const obj = node.children[index] if (!visited.has(obj.objectId)) { const i = parseInt(index) searchConnected(obj, node.children.slice(i + 1), visited) } } } export const calculatePushRelations = (node: PushNode): void => { if (!node?.children?.length) { return } addChildrenToPushTree(node) } export default calculatePushRelations