import {useComputed, useSignal, useSignalEffect} from '@preact/signals'; import type {Node} from '@revideo/2d'; import {NODE_NAME} from '@revideo/2d'; import {useRef} from 'preact/hooks'; import {usePluginState} from '../Provider'; import {IconMap} from '../icons/IconMap'; import {TreeElement} from './TreeElement'; interface NodeElementProps { node: Node; depth?: number; } export function NodeElement({node, depth = 0}: NodeElementProps) { const {selectedKey, hoveredKey, openNodes, selectedChain, afterRender} = usePluginState(); const ref = useRef(null); const open = useSignal( selectedChain.peek().has(node.key) || (openNodes.get(node.key) ?? false), ); const nodeSignal = useSignal(node); nodeSignal.value = node; const children = useComputed(() => { afterRender.value; return nodeSignal.value.peekChildren(); }); useSignalEffect(() => { open.value = openNodes.get(nodeSignal.value.key) ?? false; }); useSignalEffect(() => { const chain = selectedChain.value; if (chain.has(nodeSignal.value.key)) { open.value = true; } }); useSignalEffect(() => { openNodes.set(nodeSignal.value.key, open.value); }); useSignalEffect(() => { const key = selectedKey.value; if (node.key === key) { ref.current?.scrollIntoView({block: 'nearest', behavior: 'instant'}); } }); const Icon = IconMap[node[NODE_NAME]] ?? IconMap.Node; return ( } label={node.key} selected={selectedKey.value === node.key} onClick={event => { selectedKey.value = node.key; event.stopPropagation(); }} onPointerEnter={() => (hoveredKey.value = node.key)} onPointerLeave={() => (hoveredKey.value = null)} > {children.value.length > 0 && children.value.map(child => ( ))} ); }