summaryrefslogtreecommitdiffhomepage
path: root/packages/ui/src/components/list.tsx
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-10-17 12:05:52 -0500
committerAdam <[email protected]>2025-10-17 12:06:36 -0500
commit887a819f2444c8454a43049983d831194883c6cd (patch)
tree7247e5d619c6065a4b1c7d02c74366d43e7e3c05 /packages/ui/src/components/list.tsx
parentfe8b3a25155c0aaad20b506d0ba6fc6b8f2d0e5b (diff)
downloadopencode-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.tsx76
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>
+ )
+}