-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathAddNodePanel.tsx
More file actions
98 lines (91 loc) · 2.94 KB
/
Copy pathAddNodePanel.tsx
File metadata and controls
98 lines (91 loc) · 2.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import { memo, useCallback, useMemo } from 'react';
import { useSafeLingui } from '../../../i18n';
import { useOptionalNodeTypeRegistry } from '../../core';
import { usePreviewNode } from '../../hooks';
import { type ListItem, Toolbox } from '../Toolbox';
import type { AddNodePanelProps } from './AddNodePanel.types';
/**
* Panel component that displays available nodes for adding to the canvas.
*
* This component filters nodes based on connection constraints when adding nodes
* from existing handles. It uses a two-stage filtering approach for optimal performance:
* 1. Category-level filtering to quickly eliminate entire invalid categories
* 2. Node-level filtering for comprehensive constraint validation
*/
export const AddNodePanel = memo(function AddNodePanel({
onNodeSelect,
onClose,
onSearch,
onNodeHover,
items,
title,
loading,
renderEmptyState,
searchPlaceholder,
}: AddNodePanelProps) {
const { _ } = useSafeLingui();
const registry = useOptionalNodeTypeRegistry();
const { previewNodeConnectionInfo } = usePreviewNode();
/**
* Compute the list of available node options with constraint-based filtering.
*
* Uses the registry's getAvailableNodes API with:
* - Connection constraint filtering
* - List item format for UI rendering
* - UI optimizations (single path flattening)
* - Icon resolution
*/
const nodeListOptions = useMemo((): ListItem[] => {
if (items) return items;
// Prevent premature access to registry during loading state
if (loading || !registry) return [];
return registry.getNodeOptions({
connections: previewNodeConnectionInfo,
flattenSinglePath: true,
});
}, [items, registry, previewNodeConnectionInfo, loading]);
const handleSearch = useCallback(
(
query: string,
isTopLevelSearch: boolean,
{ currentItems, category }: { currentItems?: ListItem[]; category?: string }
) => {
if (!onSearch && registry) {
const listItems = registry.getNodeOptions({
connections: previewNodeConnectionInfo,
category,
search: query,
flattenAll: true,
});
return Promise.resolve(listItems);
}
return (
onSearch?.(query, isTopLevelSearch, currentItems ?? [])?.then((nodes) => {
return nodes;
}) ?? Promise.resolve([])
);
},
[registry, previewNodeConnectionInfo, onSearch]
);
const handleNodeListItemSelect = useCallback(
(item: ListItem) => {
onNodeSelect(item);
},
[onNodeSelect]
);
return (
<Toolbox
title={title ?? 'Add node'}
initialItems={nodeListOptions}
loading={loading}
onItemSelect={handleNodeListItemSelect}
onSearch={handleSearch}
onClose={onClose}
onItemHover={onNodeHover}
renderEmptyState={renderEmptyState}
searchPlaceholder={
searchPlaceholder ?? _({ id: 'add-node-panel.search-nodes', message: 'Search nodes' })
}
/>
);
});