diff options
| author | Adam <[email protected]> | 2025-10-17 12:05:52 -0500 |
|---|---|---|
| committer | Adam <[email protected]> | 2025-10-17 12:06:36 -0500 |
| commit | 887a819f2444c8454a43049983d831194883c6cd (patch) | |
| tree | 7247e5d619c6065a4b1c7d02c74366d43e7e3c05 /packages/ui/src/components/list.tsx | |
| parent | fe8b3a25155c0aaad20b506d0ba6fc6b8f2d0e5b (diff) | |
| download | opencode-887a819f2444c8454a43049983d831194883c6cd.tar.gz opencode-887a819f2444c8454a43049983d831194883c6cd.zip | |
wip: desktop work
Diffstat (limited to 'packages/ui/src/components/list.tsx')
| -rw-r--r-- | packages/ui/src/components/list.tsx | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/packages/ui/src/components/list.tsx b/packages/ui/src/components/list.tsx new file mode 100644 index 000000000..9704e4554 --- /dev/null +++ b/packages/ui/src/components/list.tsx @@ -0,0 +1,76 @@ +import { ComponentProps, createEffect, createSignal, type JSX } from "solid-js" +import { VirtualizerHandle, VList } from "virtua/solid" +import { createList } from "solid-list" +import { createStore } from "solid-js/store" + +export interface ListProps<T> { + data: T[] + children: (x: T) => JSX.Element + key: (x: T) => string + current?: T + onSelect?: (value: T | undefined) => void + class?: ComponentProps<"div">["class"] +} + +export function List<T>(props: ListProps<T>) { + const [virtualizer, setVirtualizer] = createSignal<VirtualizerHandle | undefined>(undefined) + const [store, setStore] = createStore({ + mouseActive: false, + }) + const list = createList({ + items: () => props.data.map(props.key), + initialActive: props.current ? props.key(props.current) : undefined, + loop: true, + }) + // const resetSelection = () => { + // if (props.data.length === 0) return + // list.setActive(props.key(props.data[0])) + // } + const handleSelect = (item: T) => { + props.onSelect?.(item) + } + + const handleKey = (e: KeyboardEvent) => { + setStore("mouseActive", false) + + if (e.key === "Enter") { + e.preventDefault() + const selected = props.data.find((x) => props.key(x) === list.active()) + if (selected) handleSelect(selected) + } else { + list.onKeyDown(e) + } + } + + createEffect(() => { + if (store.mouseActive || props.data.length === 0) return + const index = props.data.findIndex((x) => props.key(x) === list.active()) + if (index === 0) { + virtualizer()?.scrollTo(0) + return + } + // virtualizer()?.scrollTo(list.active()) + // const element = virtualizer()?.querySelector(`[data-key="${list.active()}"]`) + // element?.scrollIntoView({ block: "nearest", behavior: "smooth" }) + }) + + return ( + <VList data-component="list" ref={setVirtualizer} data={props.data} onKeyDown={handleKey} class={props.class}> + {(item) => ( + <button + data-slot="item" + data-key={props.key(item)} + data-active={props.key(item) === list.active()} + onClick={() => handleSelect(item)} + onMouseMove={(e) => { + e.currentTarget.focus() + setStore("mouseActive", true) + list.setActive(props.key(item)) + }} + > + {props.children(item)} + </button> + )} + </VList> + ) +} |
