Code/UI/NodePick.razor
@using Sandbox.UI
@namespace Nodebox.UI
@inherits Panel
@implements IGraphStyle

<root>
    <TextEntry
        Placeholder="Filter Nodes..."
        onchange=@(() => Update())
        @ref=TextEntryRef
    />
    <div class="separator"/>
    <VirtualList Items=@Found ItemHeight=@(22)>
        <Item Context="item">
            @if (item is Entry entry) {
                <button
                    class="entry @(entry.Index % 2 == 0 ? "" : "odd")"
                    Text=@(DisplayInfo.ForGenericType(entry.Type).Name)
                    onclick=@(() => NodeSelected?.Invoke(entry.Type))
                />
            }
        </Item>
    </VirtualList>
</root>

@code
{
    public TextEntry TextEntryRef { get; protected set; }

    public string Query => TextEntryRef?.Text;

    protected record Entry(int Index, SerializableType Type);
    protected IEnumerable<Entry> Found { get; set; }
    public Node.Collection Collection { get; set; }

    public PinRef? PinRef { get; set; } = null;

    public Action<SerializableType> NodeSelected { get; set; }

    protected void Update() {
        IEnumerable<SerializableType> result;
        if (PinRef is not PinRef pinRef) {
            result = Collection.Find(Query);
        } else {
            result = Collection.FindWithPin(pinRef.Flow.Opposite, pinRef.Type, Query);
        }

        Found = result.Enumerate().Select(x => new Entry(x.Index, x.Item));
    }

    protected override void OnAfterTreeRender(bool firstTime) {
        base.OnAfterTreeRender(firstTime);
        if (firstTime) {
            Update();
        }
    }

    protected override int BuildHash() => HashCode.Combine(Found, Collection);
}