(node: TreeNode, level: number = 0)
| 951 | }; |
| 952 | |
| 953 | const renderNode = (node: TreeNode, level: number = 0) => { |
| 954 | // Normalize paths for comparison (handle forward/backward slashes) |
| 955 | const normalizedNodePath = node.path.replace(/\\/g, '/'); |
| 956 | const normalizedInternalPath = internalSelectedPath?.replace(/\\/g, '/'); |
| 957 | const normalizedSelectedPath = selectedPath?.replace(/\\/g, '/'); |
| 958 | |
| 959 | // Check if this node is selected, normalizing paths for comparison |
| 960 | let isSelected = false; |
| 961 | if (selectedPaths) { |
| 962 | // Check both original path and normalized path in selectedPaths set |
| 963 | isSelected = selectedPaths.has(node.path) || selectedPaths.has(normalizedNodePath); |
| 964 | } else { |
| 965 | isSelected = (normalizedInternalPath || normalizedSelectedPath) === normalizedNodePath; |
| 966 | } |
| 967 | |
| 968 | const isRenaming = renamingNode === node.path; |
| 969 | const indent = level * 16; |
| 970 | |
| 971 | return ( |
| 972 | <div key={node.path}> |
| 973 | <div |
| 974 | className={`tree-node ${isSelected ? 'selected' : ''}`} |
| 975 | style={{ paddingLeft: `${indent}px`, cursor: node.type === 'file' ? 'grab' : 'pointer' }} |
| 976 | onClick={(e) => !isRenaming && handleNodeClick(node, e)} |
| 977 | onDoubleClick={() => !isRenaming && handleNodeDoubleClick(node)} |
| 978 | onContextMenu={(e) => handleContextMenu(e, node)} |
| 979 | draggable={node.type === 'file' && !isRenaming} |
| 980 | onDragStart={(e) => { |
| 981 | if (node.type === 'file' && !isRenaming) { |
| 982 | e.dataTransfer.effectAllowed = 'copy'; |
| 983 | |
| 984 | // Get all selected files for multi-file drag |
| 985 | const selectedFiles = selectedPaths && selectedPaths.has(node.path) && selectedPaths.size > 1 |
| 986 | ? Array.from(selectedPaths).map((p) => { |
| 987 | const name = p.split(/[/\\]/).pop() || ''; |
| 988 | const ext = name.includes('.') ? name.split('.').pop() : ''; |
| 989 | return { type: 'file', path: p, name, extension: ext }; |
| 990 | }) |
| 991 | : [{ |
| 992 | type: 'file', |
| 993 | path: node.path, |
| 994 | name: node.name, |
| 995 | extension: node.name.includes('.') ? node.name.split('.').pop() : '' |
| 996 | }]; |
| 997 | |
| 998 | // Set drag data as JSON array for multi-file support |
| 999 | e.dataTransfer.setData('application/json', JSON.stringify(selectedFiles)); |
| 1000 | e.dataTransfer.setData('asset-path', node.path); |
| 1001 | e.dataTransfer.setData('asset-name', node.name); |
| 1002 | const ext = node.name.includes('.') ? node.name.split('.').pop() : ''; |
| 1003 | e.dataTransfer.setData('asset-extension', ext || ''); |
| 1004 | e.dataTransfer.setData('text/plain', node.path); |
| 1005 | |
| 1006 | // Add GUID for new asset reference system |
| 1007 | const assetRegistry = Core.services.tryResolve(AssetRegistryService) as AssetRegistryService | null; |
| 1008 | if (assetRegistry) { |
| 1009 | // Convert absolute path to relative path for GUID lookup |
| 1010 | const relativePath = assetRegistry.absoluteToRelative(node.path); |
no test coverage detected