go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/projects/nodes/static/_nextjs/src/components/selectNode.tsx (about) 1 /** 2 * Copyright (c) 2024 - Present. Will Charczuk. All rights reserved. 3 * Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository. 4 */ 5 import { Button, MenuItem } from "@blueprintjs/core"; 6 import { ItemPredicate, ItemRenderer, Select } from "@blueprintjs/select"; 7 import { useEffect, useState } from "react"; 8 import { Node as FlowNode } from 'reactflow'; 9 import * as api from "../api/nodes"; 10 import { nodeTypes } from "../refdata/nodeTypes"; 11 import { NodeData } from "../store/nodeData"; 12 13 export interface SelectNodeProps { 14 id?: string; 15 items: FlowNode<NodeData>[]; 16 value?: string; 17 disabled?: boolean; 18 fill?: boolean; 19 tabIndex?: number; 20 autoFocus?: boolean; 21 onItemSelect: (item: api.Node) => void; 22 } 23 24 export function SelectNode(props: SelectNodeProps) { 25 const items: Array<api.Node> = props.items.map(fn => fn.data.node).sort(api.sortNodesByLabelAsc); 26 const [selectedItem, setSelectedItem] = useState<api.Node | undefined>(); 27 28 const filterItem: ItemPredicate<api.Node> = (query, item, _index, exactMatch) => { 29 const normalizedItem = api.nodeLabelOrID(item); 30 const normalizedQuery = query.toLowerCase(); 31 return normalizedItem.indexOf(normalizedQuery) >= 0; 32 }; 33 34 const renderItem: ItemRenderer<api.Node> = (item, { handleClick, handleFocus, modifiers, query }) => { 35 if (!modifiers.matchesPredicate) { 36 return null; 37 } 38 const nodeType = nodeTypes[item.metadata.node_type] 39 return ( 40 <MenuItem 41 active={modifiers.active} 42 disabled={modifiers.disabled} 43 key={`item_${item.id}`} 44 icon={nodeType.icon} 45 onClick={handleClick} 46 onFocus={handleFocus} 47 roleStructure="listoption" 48 text={`[${item.metadata.node_type}] ${item.label}`} 49 /> 50 ); 51 }; 52 53 const onItemSelect = (item: api.Node) => { 54 props.onItemSelect(item); 55 setSelectedItem(item); 56 } 57 58 useEffect(() => { 59 if (props.value) { 60 setSelectedItem(items.find(i => i.id === props.value)) 61 } 62 }, [props.value]) 63 64 return ( 65 <Select<api.Node> 66 items={items} 67 itemPredicate={filterItem} 68 itemRenderer={renderItem} 69 noResults={<MenuItem disabled={true} text="No results." roleStructure="listoption" />} 70 onItemSelect={onItemSelect} 71 disabled={props.disabled} 72 fill={props.fill} 73 > 74 <Button autoFocus={props.autoFocus} tabIndex={props.tabIndex} id={props.id} text={selectedItem ? api.nodeLabelOrID(selectedItem) : "Please select a node"} fill={props.fill} disabled={props.disabled} rightIcon="double-caret-vertical" placeholder="Please select a node" /> 75 </Select> 76 ) 77 }