Move Omnisearch select component to the tailwind-ui combobox
This commit is contained in:
parent
de6af8c6b5
commit
87e482e943
@ -14,6 +14,7 @@
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"@tailwindcss/forms": "^0.5.6",
|
||||
"@tanstack/react-query": "^4.35.3",
|
||||
"classnames": "^2.3.2",
|
||||
"ramda": "^0.29.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
||||
102
src/App.tsx
102
src/App.tsx
@ -1,12 +1,14 @@
|
||||
import {ChangeEvent, ReactNode, useCallback, useState} from "react";
|
||||
import {ReactNode, useCallback, useState} from "react";
|
||||
import {FixedSizeList as List} from "react-window";
|
||||
import Select, {createFilter, MenuListProps} from "react-select";
|
||||
import * as R from 'ramda';
|
||||
import classNames from "classnames";
|
||||
|
||||
import {useQuery} from "@tanstack/react-query";
|
||||
import {LinkCollection, Option} from "./aliases";
|
||||
import AsyncSelect from "react-select/async";
|
||||
import {LinkCollection, type Option} from "./aliases";
|
||||
|
||||
import {CheckIcon, ChevronUpDownIcon} from '@heroicons/react/20/solid'
|
||||
import {Combobox} from '@headlessui/react'
|
||||
// TODO: Fix this for wrapping items, esp on phones
|
||||
const height = 35;
|
||||
|
||||
@ -82,6 +84,7 @@ type ResultNoteApi = {
|
||||
basename: string
|
||||
foundWords: string[]
|
||||
matches: SearchMatchApi[]
|
||||
excerpt: string
|
||||
}
|
||||
|
||||
type SearchMatchApi = {
|
||||
@ -89,7 +92,9 @@ type SearchMatchApi = {
|
||||
offset: number
|
||||
}
|
||||
|
||||
export function OmnisearchSelect({ setSelected }: { setSelected: (value: Option) => void }) {
|
||||
export function OmnisearchSelect({setSelected}: {
|
||||
setSelected: (value: Option) => void
|
||||
}) {
|
||||
const {
|
||||
data: metadata,
|
||||
isLoading,
|
||||
@ -108,30 +113,87 @@ export function OmnisearchSelect({ setSelected }: { setSelected: (value: Option)
|
||||
},
|
||||
});
|
||||
|
||||
const [filter, setFilter] = useState('[]');
|
||||
const [query, setQuery] = useState('')
|
||||
const [selectedPerson, setSelectedPerson] = useState(null);
|
||||
|
||||
const onChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
setFilter(e.target.value);
|
||||
}, []);
|
||||
|
||||
const { data: options } = useQuery({
|
||||
queryKey: ['obsidian-omnisearch', filter],
|
||||
const {data: filteredPeople} = useQuery({
|
||||
queryKey: ['obsidian-omnisearch', query],
|
||||
initialData: [],
|
||||
queryFn: async () => {
|
||||
const response = await fetch(`/search?q=${filter}`)
|
||||
const fullData: ResultNoteApi[] = await response.json();
|
||||
if (query === '') {
|
||||
return [];
|
||||
} else {
|
||||
const response = await fetch(`/search?q=${query}`)
|
||||
const fullData: ResultNoteApi[] = await response.json();
|
||||
|
||||
return fullData;
|
||||
return fullData;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (<>
|
||||
<input onChange={onChange}/>
|
||||
return (<Combobox as="div" value={selectedPerson} onChange={setSelectedPerson}>
|
||||
<Combobox.Label className="block text-sm font-medium leading-6 text-gray-900">Assigned to</Combobox.Label>
|
||||
<div className="relative mt-2">
|
||||
<Combobox.Input
|
||||
className="w-full rounded-md border-0 bg-white py-1.5 pl-3 pr-12 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
|
||||
onChange={(event) => setQuery(event.target.value)}
|
||||
displayValue={(person: ResultNoteApi) => person?.basename}
|
||||
/>
|
||||
<Combobox.Button
|
||||
className="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none">
|
||||
<ChevronUpDownIcon className="h-5 w-5 text-gray-400" aria-hidden="true"/>
|
||||
</Combobox.Button>
|
||||
|
||||
{options.map(option => (
|
||||
<div>{option.basename}</div>
|
||||
))}
|
||||
</>);
|
||||
{filteredPeople.length > 0 && (
|
||||
<Combobox.Options
|
||||
className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
|
||||
{filteredPeople.map((person) => (
|
||||
<Combobox.Option
|
||||
key={person.path}
|
||||
value={person}
|
||||
className={({active}) =>
|
||||
classNames(
|
||||
'relative cursor-default select-none py-2 pl-3 pr-9',
|
||||
active ? 'bg-indigo-600 text-white' : 'text-gray-900'
|
||||
)
|
||||
}
|
||||
>
|
||||
{({
|
||||
active,
|
||||
selected
|
||||
}) => (
|
||||
<>
|
||||
<div className="flex">
|
||||
<span
|
||||
className={classNames('truncate', selected && 'font-semibold')}>{person?.basename}</span>
|
||||
<span
|
||||
className={classNames(
|
||||
'ml-2 truncate text-gray-500',
|
||||
active ? 'text-indigo-200' : 'text-gray-500'
|
||||
)}
|
||||
>
|
||||
{person?.path}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{selected && (
|
||||
<span
|
||||
className={classNames(
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4',
|
||||
active ? 'text-white' : 'text-indigo-600'
|
||||
)}
|
||||
>
|
||||
<CheckIcon className="h-5 w-5" aria-hidden="true"/>
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Combobox.Option>
|
||||
))}
|
||||
</Combobox.Options>
|
||||
)}
|
||||
</div>
|
||||
</Combobox>);
|
||||
}
|
||||
|
||||
export function App() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user