refactor(nodes)!: Change children API to parentNode API

* instead of passing a children arr, pass a parentNode id
* allows for easier adding of new child nodes in options api
This commit is contained in:
Braks
2022-04-04 21:42:48 +02:00
parent 0d0ac9d2e6
commit 0a079afcee
7 changed files with 169 additions and 167 deletions
+44 -49
View File
@@ -12,13 +12,12 @@ const { onConnect, nodes, edges, addEdges, addNodes } = useVueFlow({
position: { x: 100, y: 100 }, position: { x: 100, y: 100 },
class: 'light', class: 'light',
style: { backgroundColor: 'rgba(255, 0, 0, 0.8)', width: '200px', height: '200px' }, style: { backgroundColor: 'rgba(255, 0, 0, 0.8)', width: '200px', height: '200px' },
children: [ },
{ {
id: '2a', id: '2a',
label: 'Node 2a', label: 'Node 2a',
position: { x: 10, y: 50 }, position: { x: 10, y: 50 },
}, parentNode: '2',
],
}, },
{ id: '3', label: 'Node 3', position: { x: 320, y: 100 }, class: 'light' }, { id: '3', label: 'Node 3', position: { x: 320, y: 100 }, class: 'light' },
{ {
@@ -27,36 +26,36 @@ const { onConnect, nodes, edges, addEdges, addNodes } = useVueFlow({
position: { x: 320, y: 200 }, position: { x: 320, y: 200 },
class: 'light', class: 'light',
style: { backgroundColor: 'rgba(255, 0, 0, 0.7)', width: '300px', height: '300px' }, style: { backgroundColor: 'rgba(255, 0, 0, 0.7)', width: '300px', height: '300px' },
children: [ },
{ {
id: '4a', id: '4a',
label: 'Node 4a', label: 'Node 4a',
position: { x: 15, y: 65 }, position: { x: 15, y: 65 },
class: 'light', class: 'light',
extent: 'parent', extent: 'parent',
}, parentNode: '4',
{ },
id: '4b', {
label: 'Node 4b', id: '4b',
position: { x: 15, y: 120 }, label: 'Node 4b',
class: 'light', position: { x: 15, y: 120 },
style: { backgroundColor: 'rgba(255, 0, 255, 0.7)', height: '150px', width: '270px' }, class: 'light',
children: [ style: { backgroundColor: 'rgba(255, 0, 255, 0.7)', height: '150px', width: '270px' },
{ parentNode: '4',
id: '4b1', },
label: 'Node 4b1', {
position: { x: 20, y: 40 }, id: '4b1',
class: 'light', label: 'Node 4b1',
}, position: { x: 20, y: 40 },
{ class: 'light',
id: '4b2', parentNode: '4b',
label: 'Node 4b2', },
position: { x: 100, y: 100 }, {
class: 'light', id: '4b2',
}, label: 'Node 4b2',
], position: { x: 100, y: 100 },
}, class: 'light',
], parentNode: '4b',
}, },
], ],
edges: [ edges: [
@@ -75,21 +74,17 @@ onConnect((params) => addEdges([params]))
onMounted(() => { onMounted(() => {
// add nodes to parent // add nodes to parent
addNodes( addNodes([
[
{
id: '999',
type: 'input',
label: 'Added after mount',
position: { x: 20, y: 100 },
class: 'light',
expandParent: true,
},
],
{ {
id: '999',
type: 'input',
label: 'Added after mount',
position: { x: 20, y: 100 },
class: 'light',
expandParent: true,
parentNode: '2', parentNode: '2',
}, },
) ])
}) })
</script> </script>
<template> <template>
+42 -40
View File
@@ -26,11 +26,11 @@ const scale = controlledComputed(
() => store.transform[2], () => store.transform[2],
) )
watch( watch(
[() => node.value.position, () => node.value.parentNode], [() => node.value.position, () => store.getNode(node.value.parentNode!)],
([pos, parent]) => { ([pos, parent]) => {
const xyzPos = { const xyzPos = {
...pos, ...pos,
z: node.value.computedPosition.z, z: node.value.dragging || node.value.selected ? 1000 : node.value.computedPosition.z,
} }
if (parent) { if (parent) {
node.value.computedPosition = getXYZPos(parent, xyzPos) node.value.computedPosition = getXYZPos(parent, xyzPos)
@@ -95,7 +95,9 @@ const onDragStop: DraggableEventListener = ({ event, data: { deltaX, deltaY } })
store.updateNodePosition({ id: node.value.id, diff: { x: 0, y: 0 } }) store.updateNodePosition({ id: node.value.id, diff: { x: 0, y: 0 } })
onMounted(() => { onMounted(() => {
useResizeObserver(nodeElement, () => store.updateNodeDimensions([{ id: node.value.id, nodeElement: nodeElement.value, forceUpdate: true }])) useResizeObserver(nodeElement, () =>
store.updateNodeDimensions([{ id: node.value.id, nodeElement: nodeElement.value, forceUpdate: true }]),
)
watch([() => node.value.type, () => node.value.sourcePosition, () => node.value.targetPosition], () => watch([() => node.value.type, () => node.value.sourcePosition, () => node.value.targetPosition], () =>
nextTick(() => store.updateNodeDimensions([{ id: node.value.id, nodeElement: nodeElement.value }])), nextTick(() => store.updateNodeDimensions([{ id: node.value.id, nodeElement: nodeElement.value }])),
) )
@@ -134,7 +136,7 @@ export default {
node.class, node.class,
]" ]"
:style="{ :style="{
zIndex: node.dragging || node.selected ? 1000 : node.computedPosition.z, zIndex: node.computedPosition.z,
transform: `translate(${node.computedPosition.x}px,${node.computedPosition.y}px)`, transform: `translate(${node.computedPosition.x}px,${node.computedPosition.y}px)`,
pointerEvents: props.selectable || props.draggable ? 'all' : 'none', pointerEvents: props.selectable || props.draggable ? 'all' : 'none',
...node.style, ...node.style,
@@ -149,47 +151,47 @@ export default {
> >
<slot <slot
v-bind="{ v-bind="{
nodeElement, nodeElement,
id: node.id, id: node.id,
type: node.type, type: node.type,
data: node.data, data: node.data,
selected: !!node.selected, selected: !!node.selected,
isConnectable: props.connectable, isConnectable: props.connectable,
position: node.position, position: node.position,
computedPosition: node.computedPosition, computedPosition: node.computedPosition,
dimensions: node.dimensions, dimensions: node.dimensions,
isValidTargetPos: node.isValidTargetPos, isValidTargetPos: node.isValidTargetPos,
isValidSourcePos: node.isValidSourcePos, isValidSourcePos: node.isValidSourcePos,
parentNode: node.parentNode ? node.parentNode.id : undefined, parentNode: node.parentNode,
dragging: !!node.dragging, dragging: !!node.dragging,
zIndex: node.dragging || node.selected ? 1000 : node.computedPosition.z, zIndex: node.dragging || node.selected ? 1000 : node.computedPosition.z,
targetPosition: node.targetPosition, targetPosition: node.targetPosition,
sourcePosition: node.sourcePosition, sourcePosition: node.sourcePosition,
label: node.label, label: node.label,
dragHandle: node.dragHandle, dragHandle: node.dragHandle,
}" }"
> >
<component <component
:is="props.type ?? node.type" :is="props.type ?? node.type"
v-bind="{ v-bind="{
nodeElement, nodeElement,
id: node.id, id: node.id,
type: node.type, type: node.type,
data: node.data, data: node.data,
selected: !!node.selected, selected: !!node.selected,
connectable: props.connectable, connectable: props.connectable,
position: node.position, position: node.position,
computedPosition: node.computedPosition, computedPosition: node.computedPosition,
dimensions: node.dimensions, dimensions: node.dimensions,
isValidTargetPos: node.isValidTargetPos, isValidTargetPos: node.isValidTargetPos,
isValidSourcePos: node.isValidSourcePos, isValidSourcePos: node.isValidSourcePos,
parentNode: node.parentNode ? node.parentNode.id : undefined, parentNode: node.parentNode,
dragging: !!node.dragging, dragging: !!node.dragging,
zIndex: node.dragging || node.selected ? 1000 : node.computedPosition.z, zIndex: node.dragging || node.selected ? 1000 : node.computedPosition.z,
targetPosition: node.targetPosition, targetPosition: node.targetPosition,
sourcePosition: node.sourcePosition, sourcePosition: node.sourcePosition,
label: node.label, label: node.label,
dragHandle: node.dragHandle, dragHandle: node.dragHandle,
}" }"
/> />
</slot> </slot>
+47 -50
View File
@@ -13,6 +13,7 @@ import {
Connection, Connection,
GraphEdge, GraphEdge,
NodePositionChange, NodePositionChange,
Getters,
} from '~/types' } from '~/types'
import { import {
applyChanges, applyChanges,
@@ -34,16 +35,6 @@ import {
const isDef = <T>(val: T): val is NonNullable<T> => typeof val !== 'undefined' const isDef = <T>(val: T): val is NonNullable<T> => typeof val !== 'undefined'
const getParent = (root: Node[], id: string): GraphNode | undefined => {
let node
root.some((n) => {
if (n.id === id) return (node = n)
if (n.children) return (node = getParent(n.children, id))
return false
})
return node
}
const addEdge = (edgeParams: Edge | Connection, edges: Edge[]) => { const addEdge = (edgeParams: Edge | Connection, edges: Edge[]) => {
if (!edgeParams.source || !edgeParams.target) { if (!edgeParams.source || !edgeParams.target) {
console.warn("Can't create edge. An edge needs a source and a target.") console.warn("Can't create edge. An edge needs a source and a target.")
@@ -92,25 +83,41 @@ const updateEdgeAction = (edge: GraphEdge, newConnection: Connection, edges: Gra
return true return true
} }
const applyEdgeChangesAction = (changes: EdgeChange[], edges: GraphEdge[]) => applyChanges(changes, edges) const applyEdgeChangesAction = (changes: EdgeChange[], edges: GraphEdge[], getNode: Getters['getNode']) =>
const applyNodeChangesAction = (changes: NodeChange[], nodes: GraphNode[]) => applyChanges(changes, nodes) applyChanges(changes, edges, getNode)
const applyNodeChangesAction = (changes: NodeChange[], nodes: GraphNode[], getNode: Getters['getNode']) =>
applyChanges(changes, nodes, getNode)
export const parseChildren = ( const createGraphNodes = (nodes: Node[], getNode: Getters['getNode'], currGraphNodes: GraphNode[], extent: CoordinateExtent) => {
n: Node, const parentNodes: Record<string, true> = {}
p: GraphNode | undefined,
arr: GraphNode[], const graphNodes = nodes.map((node) => {
extent: CoordinateExtent, const parsed = parseNode(node, extent, {
getNode: (id: string) => GraphNode | undefined, ...getNode(node.id),
) => { parentNode: node.parentNode,
const parent = typeof p === 'undefined' || typeof p !== 'object' ? getParent(arr, n.id) : p })
const parsed = parseNode(n, extent, { if (node.parentNode) {
...getNode(n.id), parentNodes[node.parentNode] = true
parentNode: parent, }
return parsed
}) })
arr.push(parsed)
if (n.children && n.children.length) { graphNodes.forEach((node) => {
n.children.forEach((c) => parseChildren(c, parsed, arr, extent, getNode)) if (node.parentNode && ![...graphNodes, ...currGraphNodes].find((n) => n.id === node.parentNode)) {
} throw new Error(`Parent node ${node.parentNode} not found`)
}
if (node.parentNode || parentNodes[node.id]) {
if (parentNodes[node.id]) {
node.isParent = true
}
const parent = node.parentNode ? getNode(node.parentNode) : undefined
if (parent) parent.isParent = true
}
})
return graphNodes
} }
export default (state: State, getters: ComputedGetters): Actions => { export default (state: State, getters: ComputedGetters): Actions => {
@@ -120,12 +127,12 @@ export default (state: State, getters: ComputedGetters): Actions => {
state.nodes.forEach((node) => { state.nodes.forEach((node) => {
if (node.selected) { if (node.selected) {
if (!node.parentNode) { if (!node.parentNode) {
changes.push(createPositionChange({ node, diff, nodeExtent: state.nodeExtent, dragging })) changes.push(createPositionChange({ node, diff, nodeExtent: state.nodeExtent, dragging }, getters.getNode.value))
} else if (!isParentSelected(node)) { } else if (!isParentSelected(node, getters.getNode.value)) {
changes.push(createPositionChange({ node, diff, nodeExtent: state.nodeExtent, dragging })) changes.push(createPositionChange({ node, diff, nodeExtent: state.nodeExtent, dragging }, getters.getNode.value))
} }
} else if (node.id === id) { } else if (node.id === id) {
changes.push(createPositionChange({ node, diff, nodeExtent: state.nodeExtent, dragging })) changes.push(createPositionChange({ node, diff, nodeExtent: state.nodeExtent, dragging }, getters.getNode.value))
} }
}) })
@@ -166,7 +173,7 @@ export default (state: State, getters: ComputedGetters): Actions => {
let changedNodes: NodeChange[] let changedNodes: NodeChange[]
if (state.multiSelectionActive) changedNodes = selectedNodesIds.map((nodeId) => createSelectionChange(nodeId, true)) if (state.multiSelectionActive) changedNodes = selectedNodesIds.map((nodeId) => createSelectionChange(nodeId, true))
else changedNodes = getSelectionChanges(state.nodes, selectedNodesIds) else changedNodes = getSelectionChanges(state.nodes, selectedNodesIds, getters.getNode.value)
if (changedNodes.length) state.hooks.nodesChange.trigger(changedNodes) if (changedNodes.length) state.hooks.nodesChange.trigger(changedNodes)
} }
@@ -175,7 +182,7 @@ export default (state: State, getters: ComputedGetters): Actions => {
let changedEdges: EdgeChange[] let changedEdges: EdgeChange[]
if (state.multiSelectionActive) changedEdges = selectedEdgesIds.map((nodeId) => createSelectionChange(nodeId, true)) if (state.multiSelectionActive) changedEdges = selectedEdgesIds.map((nodeId) => createSelectionChange(nodeId, true))
else changedEdges = getSelectionChanges(state.edges, selectedEdgesIds) else changedEdges = getSelectionChanges(state.edges, selectedEdgesIds, getters.getNode.value)
if (changedEdges.length) state.hooks.edgesChange.trigger(changedEdges) if (changedEdges.length) state.hooks.edgesChange.trigger(changedEdges)
} }
@@ -207,12 +214,7 @@ export default (state: State, getters: ComputedGetters): Actions => {
const setNodes: Actions['setNodes'] = (nodes, extent?: CoordinateExtent) => { const setNodes: Actions['setNodes'] = (nodes, extent?: CoordinateExtent) => {
if (!state.initialized && !nodes.length) return if (!state.initialized && !nodes.length) return
nodes = nodes.flatMap((node) => { state.nodes = createGraphNodes(nodes, getters.getNode.value, state.nodes, extent ?? state.nodeExtent)
const children: GraphNode[] = []
parseChildren(node, undefined, children, extent ?? state.nodeExtent, getters.getNode.value)
return children
})
state.nodes = <GraphNode[]>nodes
} }
const setEdges: Actions['setEdges'] = (edges) => { const setEdges: Actions['setEdges'] = (edges) => {
if (!state.initialized && !edges.length) return if (!state.initialized && !edges.length) return
@@ -241,15 +243,8 @@ export default (state: State, getters: ComputedGetters): Actions => {
setEdges(elements.filter(isEdge)) setEdges(elements.filter(isEdge))
} }
const addNodes: Actions['addNodes'] = (nodes, options) => { const addNodes: Actions['addNodes'] = (nodes, extent) => {
const parsed = nodes.flatMap((node) => { state.nodes.push(...createGraphNodes(nodes, getters.getNode.value, state.nodes, extent ?? state.nodeExtent))
const children: GraphNode[] = []
const parent =
options && (typeof options.parentNode === 'string' ? getters.getNode.value(options.parentNode) : options.parentNode)
parseChildren(node, parent, children, options?.extent ?? state.nodeExtent, getters.getNode.value)
return children
})
state.nodes.push(...parsed)
} }
const addEdges: Actions['addEdges'] = (params) => { const addEdges: Actions['addEdges'] = (params) => {
@@ -275,8 +270,10 @@ export default (state: State, getters: ComputedGetters): Actions => {
const updateEdge: Actions['updateEdge'] = (oldEdge, newConnection) => const updateEdge: Actions['updateEdge'] = (oldEdge, newConnection) =>
updateEdgeAction(oldEdge, newConnection, state.edges, addEdges) updateEdgeAction(oldEdge, newConnection, state.edges, addEdges)
const applyNodeChanges: Actions['applyNodeChanges'] = (changes) => applyNodeChangesAction(changes, state.nodes) const applyNodeChanges: Actions['applyNodeChanges'] = (changes) =>
const applyEdgeChanges: Actions['applyEdgeChanges'] = (changes) => applyEdgeChangesAction(changes, state.edges) applyNodeChangesAction(changes, state.nodes, getters.getNode.value)
const applyEdgeChanges: Actions['applyEdgeChanges'] = (changes) =>
applyEdgeChangesAction(changes, state.edges, getters.getNode.value)
const setState: Actions['setState'] = (opts) => { const setState: Actions['setState'] = (opts) => {
const skip = ['modelValue', 'nodes', 'edges', 'maxZoom', 'minZoom', 'translateExtent'] const skip = ['modelValue', 'nodes', 'edges', 'maxZoom', 'minZoom', 'translateExtent']
+1 -2
View File
@@ -23,7 +23,7 @@ export interface Node<T = any> extends Element<T> {
isValidSourcePos?: ValidConnectionFunc isValidSourcePos?: ValidConnectionFunc
extent?: 'parent' | CoordinateExtent extent?: 'parent' | CoordinateExtent
expandParent?: boolean expandParent?: boolean
children?: Node<T>[] parentNode?: string
} }
export interface GraphNode<T = any> extends Node<T> { export interface GraphNode<T = any> extends Node<T> {
@@ -33,7 +33,6 @@ export interface GraphNode<T = any> extends Node<T> {
isParent: boolean isParent: boolean
selected: boolean selected: boolean
dragging: boolean dragging: boolean
parentNode?: GraphNode<T>
} }
export interface NodeProps<T = any> { export interface NodeProps<T = any> {
+1 -1
View File
@@ -85,7 +85,7 @@ export interface Actions<N = any, E = N> {
setElements: (elements: Elements<N, E>, extent?: CoordinateExtent) => void setElements: (elements: Elements<N, E>, extent?: CoordinateExtent) => void
setNodes: (nodes: Node<N>[], extent?: CoordinateExtent) => void setNodes: (nodes: Node<N>[], extent?: CoordinateExtent) => void
setEdges: (edges: Edge<E>[]) => void setEdges: (edges: Edge<E>[]) => void
addNodes: <NA = N>(nodes: Node<NA>[], options?: { extent?: CoordinateExtent; parentNode: GraphNode | string }) => void addNodes: <NA = N>(nodes: Node<NA>[], extent?: CoordinateExtent) => void
addEdges: <EA = E>(edgesOrConnections: (Edge<EA> | Connection)[]) => void addEdges: <EA = E>(edgesOrConnections: (Edge<EA> | Connection)[]) => void
updateEdge: <EU = E>(oldEdge: GraphEdge<EU>, newConnection: Connection) => boolean updateEdge: <EU = E>(oldEdge: GraphEdge<EU>, newConnection: Connection) => boolean
applyEdgeChanges: <ED = E>(changes: EdgeChange[]) => GraphEdge<ED>[] applyEdgeChanges: <ED = E>(changes: EdgeChange[]) => GraphEdge<ED>[]
+27 -19
View File
@@ -10,10 +10,11 @@ import {
NodeChange, NodeChange,
NodeSelectionChange, NodeSelectionChange,
NodePositionChange, NodePositionChange,
Getters,
} from '~/types' } from '~/types'
function handleParentExpand(updateItem: GraphNode) { function handleParentExpand(updateItem: GraphNode, getNode: Getters['getNode']) {
const parent = updateItem.parentNode const parent = updateItem.parentNode ? getNode(updateItem.parentNode) : undefined
if (parent) { if (parent) {
const extendWidth = updateItem.position.x + updateItem.dimensions.width - parent.dimensions.width const extendWidth = updateItem.position.x + updateItem.dimensions.width - parent.dimensions.width
const extendHeight = updateItem.position.y + updateItem.dimensions.height - parent.dimensions.height const extendHeight = updateItem.position.y + updateItem.dimensions.height - parent.dimensions.height
@@ -69,8 +70,12 @@ function handleParentExpand(updateItem: GraphNode) {
updateItem.position.y = 0 updateItem.position.y = 0
} }
parent.dimensions.width = <number>parent.style.width parent.dimensions.width = (
parent.dimensions.height = <number>parent.style.height typeof parent.style.width === 'string' ? parseInt((<string>parent.style.width)!, 10) : parent.style.width
)!
parent.dimensions.height = (
typeof parent.style.height === 'string' ? parseInt((<string>parent.style.height)!, 10) : parent.style.height
)!
} }
} }
} }
@@ -81,6 +86,7 @@ export const applyChanges = <
>( >(
changes: C[], changes: C[],
elements: T[], elements: T[],
getNode: Getters['getNode'],
): T[] => { ): T[] => {
let elementIds = elements.map((el) => el.id) let elementIds = elements.map((el) => el.id)
changes.forEach((change) => { changes.forEach((change) => {
@@ -95,13 +101,13 @@ export const applyChanges = <
if (isGraphNode(el)) { if (isGraphNode(el)) {
if (typeof change.position !== 'undefined') el.position = change.position if (typeof change.position !== 'undefined') el.position = change.position
if (typeof change.dragging !== 'undefined') el.dragging = change.dragging if (typeof change.dragging !== 'undefined') el.dragging = change.dragging
if (el.expandParent && el.parentNode) handleParentExpand(el) if (el.expandParent && el.parentNode) handleParentExpand(el, getNode)
} }
break break
case 'dimensions': case 'dimensions':
if (isGraphNode(el)) { if (isGraphNode(el)) {
if (typeof change.dimensions !== 'undefined') el.dimensions = change.dimensions if (typeof change.dimensions !== 'undefined') el.dimensions = change.dimensions
if (el.expandParent && el.parentNode) handleParentExpand(el) if (el.expandParent && el.parentNode) handleParentExpand(el, getNode)
} }
break break
case 'remove': case 'remove':
@@ -120,7 +126,11 @@ export const createSelectionChange = (id: string, selected: boolean): NodeSelect
selected, selected,
}) })
export const createPositionChange = ({ node, diff, dragging, nodeExtent }: CreatePositionChangeParams): NodePositionChange => { export const createPositionChange = (
{ node, diff, dragging, nodeExtent }: CreatePositionChangeParams,
getNode: Getters['getNode'],
): NodePositionChange => {
const parent = node.parentNode ? getNode(node.parentNode) : undefined
const change: NodePositionChange = { const change: NodePositionChange = {
id: node.id, id: node.id,
type: 'position', type: 'position',
@@ -131,15 +141,12 @@ export const createPositionChange = ({ node, diff, dragging, nodeExtent }: Creat
const nextPosition = { x: node.position.x + diff.x, y: node.position.y + diff.y } const nextPosition = { x: node.position.x + diff.x, y: node.position.y + diff.y }
let currentExtent = nodeExtent || node.extent let currentExtent = nodeExtent || node.extent
if (node.extent === 'parent' && node.parentNode && node.dimensions.width && node.dimensions.height) { if (node.extent === 'parent' && parent && node.dimensions.width && node.dimensions.height) {
currentExtent = currentExtent =
node.parentNode?.dimensions.width && node.parentNode?.dimensions.height parent.dimensions.width && parent.dimensions.height
? [ ? [
[0, 0], [0, 0],
[ [parent.dimensions.width - node.dimensions.width, parent.dimensions.height - node.dimensions.height],
node.parentNode.dimensions.width - node.dimensions.width,
node.parentNode.dimensions.height - node.dimensions.height,
],
] ]
: currentExtent : currentExtent
} }
@@ -150,16 +157,17 @@ export const createPositionChange = ({ node, diff, dragging, nodeExtent }: Creat
return change return change
} }
const isParentSelected = (node: GraphNode, selectedIds: string[]): boolean => { const isParentSelected = (node: GraphNode, selectedIds: string[], getNode: Getters['getNode']): boolean => {
if (!node.parentNode) return false const parent = node.parentNode ? getNode(node.parentNode) : undefined
if (selectedIds.includes(node.parentNode.id)) return true if (!node.parentNode || !parent) return false
return isParentSelected(node.parentNode, selectedIds) if (selectedIds.includes(node.parentNode)) return true
return isParentSelected(parent, selectedIds, getNode)
} }
export const getSelectionChanges = (items: FlowElements, selectedIds: string[]) => { export const getSelectionChanges = (items: FlowElements, selectedIds: string[], getNode: Getters['getNode']) => {
return items.reduce((res, item) => { return items.reduce((res, item) => {
const willBeSelected = const willBeSelected =
selectedIds.includes(item.id) || !!(isGraphNode(item) && item.parentNode && isParentSelected(item, selectedIds)) selectedIds.includes(item.id) || !!(isGraphNode(item) && item.parentNode && isParentSelected(item, selectedIds, getNode))
if (!item.selected && willBeSelected) { if (!item.selected && willBeSelected) {
res.push(createSelectionChange(item.id, true)) res.push(createSelectionChange(item.id, true))
+7 -6
View File
@@ -16,8 +16,8 @@ import {
Rect, Rect,
Transform, Transform,
XYPosition, XYPosition,
XYZPosition, XYZPosition, Getters
} from '~/types' } from "~/types";
import { useWindow } from '~/composables' import { useWindow } from '~/composables'
const isHTMLElement = (el: EventTarget): el is HTMLElement => ('nodeName' || 'hasAttribute') in el const isHTMLElement = (el: EventTarget): el is HTMLElement => ('nodeName' || 'hasAttribute') in el
@@ -75,7 +75,6 @@ export const parseNode = (node: Node, nodeExtent: CoordinateExtent, defaults?: P
z: typeof node.style?.zIndex === 'string' ? parseInt(node.style?.zIndex as string) : node.style?.zIndex ?? 0, z: typeof node.style?.zIndex === 'string' ? parseInt(node.style?.zIndex as string) : node.style?.zIndex ?? 0,
...clampPosition(node.position, nodeExtent), ...clampPosition(node.position, nodeExtent),
}, },
isParent: !!(node.children && node.children.length),
dragging: false, dragging: false,
draggable: undefined, draggable: undefined,
selectable: undefined, selectable: undefined,
@@ -335,10 +334,12 @@ export const getXYZPos = (parentNode: GraphNode, computedPosition: XYZPosition):
} }
} }
export const isParentSelected = (node: GraphNode): boolean => { export const isParentSelected = (node: GraphNode, getNode: Getters['getNode']): boolean => {
if (!node.parentNode) return false if (!node.parentNode) return false
if (node.parentNode.selected) return true const parent = getNode(node.parentNode)
return isParentSelected(node.parentNode) if (!parent) return false
if (parent.selected) return true
return isParentSelected(parent, getNode)
} }
export const getMarkerId = (marker: EdgeMarkerType | undefined): string => { export const getMarkerId = (marker: EdgeMarkerType | undefined): string => {