From e0693dc821239cfd6872fffe0624e37c3b597635 Mon Sep 17 00:00:00 2001 From: Sascha Hahne Date: Sat, 30 Oct 2021 16:32:02 +0200 Subject: [PATCH 1/2] Implement bare bones library search --- src/components/library/LibraryMangaGrid.tsx | 9 +++-- src/components/library/LibrarySearch.tsx | 38 +++++++++++++++++++++ src/screens/Library.tsx | 2 ++ src/util/useLibraryOptions.ts | 16 ++++++--- 4 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 src/components/library/LibrarySearch.tsx diff --git a/src/components/library/LibraryMangaGrid.tsx b/src/components/library/LibraryMangaGrid.tsx index 369abc5a61..dd86300015 100644 --- a/src/components/library/LibraryMangaGrid.tsx +++ b/src/components/library/LibraryMangaGrid.tsx @@ -23,10 +23,15 @@ function unreadFilter(unread: NullAndUndefined, { unreadCount }: IManga } } +function queryFilter(query: NullAndUndefined, { title }: IMangaCard): boolean { + if (!query) return true; + return title.toLowerCase().includes(query.toLowerCase()); +} + function filterManga(mangas: IMangaCard[]): IMangaCard[] { - const { unread } = useLibraryOptions(); + const { unread, query } = useLibraryOptions(); return mangas - .filter((manga) => unreadFilter(unread, manga)); + .filter((manga) => unreadFilter(unread, manga) && queryFilter(query, manga)); } export default function LibraryMangaGrid(props: IMangaGridProps) { diff --git a/src/components/library/LibrarySearch.tsx b/src/components/library/LibrarySearch.tsx new file mode 100644 index 0000000000..80c2b0c650 --- /dev/null +++ b/src/components/library/LibrarySearch.tsx @@ -0,0 +1,38 @@ +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +import React, { useState } from 'react'; +import { TextField } from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import useLibraryOptions from '../../util/useLibraryOptions'; + +export default function LibrarySearch() { + const { + query, + setQuery, + } = useLibraryOptions(); + const [searchOpen, setSearchOpen] = useState(!!query); + + function handleChange(e: React.ChangeEvent) { + setQuery(e.target.value === '' ? undefined : e.target.value); + } + + return ( + <> + {searchOpen + ? ( + + ) : setSearchOpen(true)} /> } + + ); +} diff --git a/src/screens/Library.tsx b/src/screens/Library.tsx index 8421e6bab3..9fdef66814 100644 --- a/src/screens/Library.tsx +++ b/src/screens/Library.tsx @@ -15,6 +15,7 @@ import LoadingPlaceholder from 'components/util/LoadingPlaceholder'; import TabPanel from 'components/util/TabPanel'; import LibraryOptions from 'components/library/LibraryOptions'; import LibraryMangaGrid from 'components/library/LibraryMangaGrid'; +import LibrarySearch from 'components/library/LibrarySearch'; interface IMangaCategory { category: ICategory @@ -27,6 +28,7 @@ export default function Library() { useEffect(() => { setTitle('Library'); setAction( <> + , ); diff --git a/src/util/useLibraryOptions.ts b/src/util/useLibraryOptions.ts index 53a737894d..50d4bba9a2 100644 --- a/src/util/useLibraryOptions.ts +++ b/src/util/useLibraryOptions.ts @@ -6,27 +6,33 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { BooleanParam, useQueryParams } from 'use-query-params'; +import { BooleanParam, useQueryParams, StringParam } from 'use-query-params'; export type NullAndUndefined = T | null | undefined; interface IUseLibraryOptions { unread: NullAndUndefined setUnread: (unread: NullAndUndefined) => void + query: NullAndUndefined + setQuery: (query: NullAndUndefined) => void active: boolean } export default function useLibraryOptions(): IUseLibraryOptions { - const [query, setQuery] = useQueryParams({ + const [searchQuery, setSearchQuery] = useQueryParams({ unread: BooleanParam, + query: StringParam, }); - const { unread } = query; + const { unread, query } = searchQuery; const setUnread = (newUnread: NullAndUndefined) => { - setQuery(Object.assign(query, { unread: newUnread }), 'replace'); + setSearchQuery(Object.assign(searchQuery, { unread: newUnread }), 'replace'); + }; + const setQuery = (newQuery: NullAndUndefined) => { + setSearchQuery(Object.assign(searchQuery, { query: newQuery }), 'replace'); }; // eslint-disable-next-line eqeqeq const active = !(unread == undefined); return { - unread, setUnread, active, + unread, setUnread, active, query, setQuery, }; } From ceb0bd65420d8dd8f9e021221534afeee4f19791 Mon Sep 17 00:00:00 2001 From: Sascha Hahne Date: Sun, 31 Oct 2021 12:32:02 +0100 Subject: [PATCH 2/2] Add nice interactions --- src/components/library/LibrarySearch.tsx | 34 +++++++++++++++++++----- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/components/library/LibrarySearch.tsx b/src/components/library/LibrarySearch.tsx index 80c2b0c650..f679eeb1f6 100644 --- a/src/components/library/LibrarySearch.tsx +++ b/src/components/library/LibrarySearch.tsx @@ -7,8 +7,9 @@ */ import React, { useState } from 'react'; -import { TextField } from '@mui/material'; import SearchIcon from '@mui/icons-material/Search'; +import { IconButton, Input } from '@mui/material'; +import CancelIcon from '@mui/icons-material/Cancel'; import useLibraryOptions from '../../util/useLibraryOptions'; export default function LibrarySearch() { @@ -17,22 +18,41 @@ export default function LibrarySearch() { setQuery, } = useLibraryOptions(); const [searchOpen, setSearchOpen] = useState(!!query); + const inputRef = React.useRef(); function handleChange(e: React.ChangeEvent) { setQuery(e.target.value === '' ? undefined : e.target.value); } - + const cancelSearch = () => { + setQuery(null); + setSearchOpen(false); + }; + const handleBlur = () => { if (!query) setSearchOpen(false); }; + const openSearch = () => { + setSearchOpen(true); + // Put Focus Action at the end of the Callstack so Input actually exists on the dom + setTimeout(() => { + if (inputRef && inputRef.current) inputRef.current.focus(); + }); + }; return ( <> {searchOpen ? ( - + + + )} /> - ) : setSearchOpen(true)} /> } + ) : } ); }