mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-21 17:54:23 +01:00
wip: desktop work
This commit is contained in:
1
packages/ui/src/hooks/index.ts
Normal file
1
packages/ui/src/hooks/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./use-filtered-list"
|
||||
89
packages/ui/src/hooks/use-filtered-list.tsx
Normal file
89
packages/ui/src/hooks/use-filtered-list.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import fuzzysort from "fuzzysort"
|
||||
import { entries, flatMap, groupBy, map, pipe } from "remeda"
|
||||
import { createMemo, createResource } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { createList } from "solid-list"
|
||||
|
||||
export interface FilteredListProps<T> {
|
||||
items: T[] | ((filter: string) => Promise<T[]>)
|
||||
key: (item: T) => string
|
||||
filterKeys?: string[]
|
||||
current?: T
|
||||
groupBy?: (x: T) => string
|
||||
sortBy?: (a: T, b: T) => number
|
||||
sortGroupsBy?: (a: { category: string; items: T[] }, b: { category: string; items: T[] }) => number
|
||||
onSelect?: (value: T | undefined) => void
|
||||
}
|
||||
|
||||
export function useFilteredList<T>(props: FilteredListProps<T>) {
|
||||
const [store, setStore] = createStore<{ filter: string }>({ filter: "" })
|
||||
|
||||
const [grouped] = createResource(
|
||||
() => store.filter,
|
||||
async (filter) => {
|
||||
const needle = filter?.toLowerCase()
|
||||
const all = (typeof props.items === "function" ? await props.items(needle) : props.items) || []
|
||||
const result = pipe(
|
||||
all,
|
||||
(x) => {
|
||||
if (!needle) return x
|
||||
if (!props.filterKeys && Array.isArray(x) && x.every((e) => typeof e === "string")) {
|
||||
return fuzzysort.go(needle, x).map((x) => x.target) as T[]
|
||||
}
|
||||
return fuzzysort.go(needle, x, { keys: props.filterKeys! }).map((x) => x.obj)
|
||||
},
|
||||
groupBy((x) => (props.groupBy ? props.groupBy(x) : "")),
|
||||
entries(),
|
||||
map(([k, v]) => ({ category: k, items: props.sortBy ? v.sort(props.sortBy) : v })),
|
||||
(groups) => (props.sortGroupsBy ? groups.sort(props.sortGroupsBy) : groups),
|
||||
)
|
||||
return result
|
||||
},
|
||||
)
|
||||
|
||||
const flat = createMemo(() => {
|
||||
return pipe(
|
||||
grouped() || [],
|
||||
flatMap((x) => x.items),
|
||||
)
|
||||
})
|
||||
|
||||
const list = createList({
|
||||
items: () => flat().map(props.key),
|
||||
initialActive: props.current ? props.key(props.current) : props.key(flat()[0]),
|
||||
loop: true,
|
||||
})
|
||||
|
||||
const reset = () => {
|
||||
const all = flat()
|
||||
if (all.length === 0) return
|
||||
list.setActive(props.key(all[0]))
|
||||
}
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault()
|
||||
const selected = flat().find((x) => props.key(x) === list.active())
|
||||
if (selected) props.onSelect?.(selected)
|
||||
} else {
|
||||
list.onKeyDown(event)
|
||||
}
|
||||
}
|
||||
|
||||
const onInput = (value: string) => {
|
||||
setStore("filter", value)
|
||||
reset()
|
||||
}
|
||||
|
||||
return {
|
||||
filter: () => store.filter,
|
||||
grouped,
|
||||
flat,
|
||||
reset,
|
||||
clear: () => setStore("filter", ""),
|
||||
onKeyDown,
|
||||
onInput,
|
||||
active: list.active,
|
||||
setActive: list.setActive,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user