add search bar

This commit is contained in:
b1ek 2024-11-11 21:17:21 +10:00
parent cec41d9cbb
commit c23994091b
Signed by: blek
GPG Key ID: A622C22C9BC616B2
6 changed files with 92 additions and 7 deletions

View File

@ -9,6 +9,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"delay": "^6.0.0",
"ky": "^1.7.2", "ky": "^1.7.2",
"preact": "^10.24.3", "preact": "^10.24.3",
"react-draggable-list": "^4.2.1", "react-draggable-list": "^4.2.1",

View File

@ -0,0 +1,24 @@
.search {
margin: 1rem 0;
border: 1px solid gray;
display: flex;
* {
padding: 1rem;
}
input {
border: none;
background: none;
height: 100%; width: 100%;
}
button {
cursor: pointer;
color: white;
background: royalblue;
border: none;
}
}

View File

@ -0,0 +1,27 @@
import { useState } from "preact/hooks";
import styles from './SearchBar.module.scss';
import { SearchIcon } from "./SearchIcon";
import { TargetedEvent } from "preact/compat";
export type SearchBarProps = {
default?: string,
placeholder?: string,
onChange?: (data: string) => void,
onSearch?: (data: string) => void
};
export function SearchBar({ default: def, placeholder, onChange, onSearch }: SearchBarProps) {
const [ query, setQuery ] = useState(def ?? '');
function updateValue(e: TargetedEvent<HTMLInputElement>) {
setQuery(e.currentTarget.value);
if (onChange) onChange(e.currentTarget.value);
}
return (
<div className={styles.search}>
<SearchIcon />
<input type='text' value={query} onChange={updateValue} placeholder={placeholder} />
<button onClick={onSearch ? () => onSearch(query) : undefined}>go</button>
</div>
)
}

View File

@ -0,0 +1,9 @@
import type React from "preact/compat";
export function SearchIcon(props: React.JSX.SVGAttributes<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-search" viewBox="0 0 16 16" {...props}>
<path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0"/>
</svg>
)
}

View File

@ -1,11 +1,13 @@
import { useCallback, useEffect, useState } from "preact/hooks"; import { useCallback, useEffect, useState } from "preact/hooks";
import { DataRow } from "../api/DataRow"; import { DataRow, DataRowSearchOptions } from "../api/DataRow";
import style from './index.module.scss'; import style from './index.module.scss';
import { Posts } from "../components/Posts"; import { Posts } from "../components/Posts";
import InfiniteScroller from 'react-infinite-scroller'; import InfiniteScroller from 'react-infinite-scroller';
import { DraggablePostsList } from "../components/DraggablePostsList"; import { DraggablePostsList } from "../components/DraggablePostsList";
import { DataRowSelection } from "../api/DataRowSelection"; import { DataRowSelection } from "../api/DataRowSelection";
import { SearchBar } from "../components/display/SearchBar";
import delay from "delay";
export function Index() { export function Index() {
const [ data, setData ] = useState(null as null | DataRow[]); const [ data, setData ] = useState(null as null | DataRow[]);
@ -15,6 +17,8 @@ export function Index() {
const [ hasMore, setHasMore ] = useState(true); const [ hasMore, setHasMore ] = useState(true);
const [ fetching, setFetching ] = useState(false); const [ fetching, setFetching ] = useState(false);
const [ filter, setFilter ] = useState(null as null | string);
useEffect(() => { useEffect(() => {
(async () => { (async () => {
setSelected(await dataRowSelection.get()); setSelected(await dataRowSelection.get());
@ -22,14 +26,17 @@ export function Index() {
}, []); }, []);
const fetchMore = useCallback( const fetchMore = useCallback(
async function(_page: number) { async function() {
if (fetching) return; if (fetching) return;
setFetching(true); setFetching(true);
// await new Promise(r => setTimeout(r, 1000));
const newData = await DataRow.find({ const query = {
page: data ? Math.floor(data.length / 20) : 0 page: data ? Math.floor(data.length / 20) : 0,
}); } as DataRowSearchOptions;
if (filter) query.search = `%${filter}%`;
const newData = await DataRow.find(query);
setData([ ...data ?? [], ...newData ]); setData([ ...data ?? [], ...newData ]);
setHasMore(newData.length === 20); setHasMore(newData.length === 20);
setFetching(false); setFetching(false);
@ -52,9 +59,19 @@ export function Index() {
} }
} }
async function search(query: string) {
setFetching(true);
setHasMore(true);
setFilter(query);
setData(null);
await delay(0); // stupid thing won't work w/o delay
setFetching(false);
}
return ( return (
<div className={style.index}> <div className={style.index}>
{ {
selected selected
? ?
@ -65,6 +82,8 @@ export function Index() {
: null : null
} }
<SearchBar onSearch={search} placeholder="filter" />
<InfiniteScroller <InfiniteScroller
pageStart={0} pageStart={0}
loadMore={fetchMore} loadMore={fetchMore}

View File

@ -678,6 +678,11 @@ debug@^4.1.0, debug@^4.3.1, debug@^4.3.4:
dependencies: dependencies:
ms "^2.1.3" ms "^2.1.3"
delay@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/delay/-/delay-6.0.0.tgz#43749aefdf6cabd9e17b0d00bd3904525137e607"
integrity sha512-2NJozoOHQ4NuZuVIr5CWd0iiLVIRSDepakaovIN+9eIDHEhdCAEvSy2cuf1DCrPPQLvHmbqTHODlhHg8UCy4zw==
detect-libc@^1.0.3: detect-libc@^1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"