diff --git a/src/App.tsx b/src/App.tsx index 419edab9..61496f66 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,3 +1,5 @@ +import { SearchBarInput } from "components/SearchBar"; +import { useSearchQuery } from "hooks/useSearchQuery"; import { MWMediaType } from "providers"; import { Redirect, Route, Switch } from "react-router-dom"; import { BookmarkContextProvider } from "state/bookmark"; @@ -7,8 +9,17 @@ import "./index.css"; import { MediaView } from "./views/MediaView"; import { SearchView } from "./views/SearchView"; -function App() { +function TestInput() { + const [q1, c1, b1] = useSearchQuery(); + return ( +
+

Normal:

+ +
+ ); +} +function App() { return ( @@ -19,6 +30,9 @@ function App() { + + + diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx index 17fa15b9..9caef7da 100644 --- a/src/components/SearchBar.tsx +++ b/src/components/SearchBar.tsx @@ -7,31 +7,39 @@ import { TextInputControl } from "./text-inputs/TextInputControl"; export interface SearchBarProps { buttonText?: string; placeholder?: string; - onChange: (value: MWQuery) => void; + onChange: (value: MWQuery, force: boolean) => void; + onUnFocus: () => void; value: MWQuery; } export function SearchBarInput(props: SearchBarProps) { const [dropdownOpen, setDropdownOpen] = useState(false); function setSearch(value: string) { - props.onChange({ - ...props.value, - searchQuery: value, - }); + props.onChange( + { + ...props.value, + searchQuery: value, + }, + false + ); } function setType(type: string) { - props.onChange({ - ...props.value, - type: type as MWMediaType, - }); + props.onChange( + { + ...props.value, + type: type as MWMediaType, + }, + true + ); } return ( -
+
setSearch(val)} value={props.value.searchQuery} - className="placeholder-denim-700 w-full flex-1 bg-transparent text-white focus:outline-none" + className="w-full flex-1 bg-transparent text-white placeholder-denim-700 focus:outline-none" placeholder={props.placeholder} /> diff --git a/src/components/text-inputs/TextInputControl.tsx b/src/components/text-inputs/TextInputControl.tsx index 65830fc8..a6d18994 100644 --- a/src/components/text-inputs/TextInputControl.tsx +++ b/src/components/text-inputs/TextInputControl.tsx @@ -1,5 +1,6 @@ export interface TextInputControlPropsNoLabel { onChange?: (data: string) => void; + onUnFocus?: () => void; value?: string; placeholder?: string; className?: string; @@ -11,6 +12,7 @@ export interface TextInputControlProps extends TextInputControlPropsNoLabel { export function TextInputControl({ onChange, + onUnFocus, value, label, className, @@ -23,6 +25,7 @@ export function TextInputControl({ placeholder={placeholder} onChange={(e) => onChange && onChange(e.target.value)} value={value} + onBlur={() => onUnFocus && onUnFocus()} /> ); diff --git a/src/hooks/useSearchQuery.ts b/src/hooks/useSearchQuery.ts index f83e7715..f03d4668 100644 --- a/src/hooks/useSearchQuery.ts +++ b/src/hooks/useSearchQuery.ts @@ -1,19 +1,25 @@ import { MWMediaType, MWQuery } from "providers"; -import React, { useState } from "react"; +import React, { useRef, useState } from "react"; import { generatePath, useHistory, useRouteMatch } from "react-router-dom"; -export function useSearchQuery(): [MWQuery, (inp: Partial) => void] { +export function useSearchQuery(): [ + MWQuery, + (inp: Partial, force: boolean) => void, + () => void +] { const history = useHistory(); + const isFirstRender = useRef(false); const { path, params } = useRouteMatch<{ type: string; query: string }>(); const [search, setSearch] = useState({ searchQuery: "", type: MWMediaType.MOVIE, }); - const updateParams = (inp: Partial) => { + const updateParams = (inp: Partial, force: boolean) => { const copySearch: MWQuery = { ...search }; Object.assign(copySearch, inp); setSearch(copySearch); + if (!force) return; history.replace( generatePath(path, { query: @@ -23,13 +29,28 @@ export function useSearchQuery(): [MWQuery, (inp: Partial) => void] { ); }; + const onUnFocus = () => { + history.replace( + generatePath(path, { + query: search.searchQuery.length === 0 ? undefined : search.searchQuery, + type: search.type, + }) + ); + }; + + // only run on first load of the page React.useEffect(() => { + if (isFirstRender.current === true) { + isFirstRender.current = false; + return; + } + isFirstRender.current = true; const type = Object.values(MWMediaType).find((v) => params.type === v) || MWMediaType.MOVIE; const searchQuery = params.query || ""; setSearch({ type, searchQuery }); - }, [params, setSearch]); + }, [setSearch, params, isFirstRender]); - return [search, updateParams]; + return [search, updateParams, onUnFocus]; } diff --git a/src/views/SearchView.tsx b/src/views/SearchView.tsx index 3054a8f9..871ba69a 100644 --- a/src/views/SearchView.tsx +++ b/src/views/SearchView.tsx @@ -165,7 +165,7 @@ function ExtraItems() { export function SearchView() { const [searching, setSearching] = useState(false); const [loading, setLoading] = useState(false); - const [search, setSearch] = useSearchQuery(); + const [search, setSearch, setSearchUnFocus] = useSearchQuery(); const debouncedSearch = useDebounce(search, 2000); useEffect(() => { @@ -182,7 +182,7 @@ export function SearchView() { return ( setSearch({ searchQuery: "" })} + clear={() => setSearch({ searchQuery: "" }, true)} /> ); return ; @@ -201,6 +201,7 @@ export function SearchView() {