Kaynağa Gözat

[web] Header search bar

Innokentiy Enikeev 4 yıl önce
ebeveyn
işleme
0eaa3b7843
6 değiştirilmiş dosya ile 63 ekleme ve 43 silme
  1. 0 19
      web/src/App.css
  2. 2 14
      web/src/App.js
  3. 7 3
      web/src/Browser.js
  4. 7 7
      web/src/Filters.js
  5. 18 0
      web/src/Header.css
  6. 29 0
      web/src/Header.js

+ 0 - 19
web/src/App.css

@@ -35,25 +35,6 @@
         / 4fr 4fr auto;
 }
 
-.App-header {
-  grid-area: header;
-  background-color: var(--header-background);
-  display: grid;
-  grid: 1fr / auto 1fr 1fr auto;
-  align-items: center;
-}
-
-.App-logo {
-  min-width: 64px;
-  height: 64px;
-  padding: 8px;
-}
-
-.App-title {
-  color: var(--header-color);
-  font-size: 1.5em;
-}
-
 .App-intro {
   font-size: large;
 }

+ 2 - 14
web/src/App.js

@@ -1,11 +1,8 @@
-import { BaseLink, useRoute } from 'react-router5'
 import {useReducer} from 'react';
 import './App.css';
-import logo from './chad-logo-256.png';
 import {storeToken, loadToken} from './Api';
 import Login from './Login';
-import Settings from './Settings';
-import UserInfo from './UserInfo';
+import Header from './Header';
 import Player from './Player';
 
 function storeSettings(settings) {
@@ -32,7 +29,6 @@ function settingsReducer(state, action) {
 }
 
 function App() {
-  const { router } = useRoute()
   const [settings, dispatch] = useReducer(settingsReducer, loadSettings());
   if(!loadToken()) {
     return <Login state={settings} dispatch={dispatch} />
@@ -40,15 +36,7 @@ function App() {
 
   return (
     <div className="App" data-theme={settings.theme} data-layout={settings.layout}>
-      <header className="App-header">
-        <BaseLink router={router} routeName="browser" routeOptions={{ reload: true }}>
-          <img src={logo} className="App-logo" alt="Chad music" />
-        </BaseLink>
-        <h1 className="App-title">Chad Music</h1>
-        <Settings state={settings} dispatch={dispatch} />
-        <UserInfo/>
-      </header>
-
+      <Header settings={settings} dispatch={dispatch} />
       <Player />
     </div>
   );

+ 7 - 3
web/src/Browser.js

@@ -11,9 +11,13 @@ export function browserReducer(state, action) {
     const {more} = action.payload;
     const items = state.albums.items.concat(action.payload.items);
     return {...state, albums: {...state.albums, items, more}}}
+  case 'SET_FILTER': {
+    const filter = action.payload;
+    return {...state, filter}}
   case 'SET_FILTERS': {
     const albums = ALBUMS_EMPTY;
-    return {...state, albums, filters: action.payload}}
+    const filters = action.payload;
+    return {...state, albums, filters, filter: filters.filter}}
   case 'SET_PAGE': {
     const albums = {...state.albums, page: action.payload, more: false};
     return {...state, albums}}
@@ -24,7 +28,7 @@ export function browserReducer(state, action) {
 export default function Browser({dispatch, scrollRef}) {
   const { router } = useRoute();
   const { params } = router.getState();
-  const INIT = {albums: ALBUMS_EMPTY, error: false, filters:params};
+  const INIT = {albums: ALBUMS_EMPTY, error: false, filters: params, filter: params.filter};
   const [state, dispatchBrowser] = useReducer(browserReducer, INIT);
 
   useEffect(() => router.subscribe(({ route, previousRoute }) => {
@@ -34,7 +38,7 @@ export default function Browser({dispatch, scrollRef}) {
 
   return (
     <>
-      <Filters {...state.filters} />
+      <Filters {...state} dispatch={dispatchBrowser} />
       <AlbumList {...state} dispatch={dispatchBrowser} playerDispatch={dispatch} scrollRef={scrollRef} />
     </>
   );

+ 7 - 7
web/src/Filters.js

@@ -26,15 +26,15 @@ function handleCategoryChange(router, cat, value) {
   router.navigate('browser', {...params, [cat]:(value || null)});
 }
 
-function handleFilterChange(router, timeout, value) {
+function handleFilterChange(dispatch, router, timeout, value) {
   if (timeout.current) clearInterval(timeout.current);
   timeout.current = setTimeout(() => handleCategoryChange(router, 'filter', value), 500);
+  dispatch({type: 'SET_FILTER', payload: value});
 }
 
-export default function Filters() {
+export default function Filters({dispatch, filters, filter}) {
   const timeout = useRef();
   const { router } = useRoute();
-  const { params } = router.getState();
 
   return (
     <div className="Filters">
@@ -44,7 +44,7 @@ export default function Filters() {
           <AsyncSelect
             isClearable
             cacheOptions
-            defaultValue={params[cat] && {item: params[cat]}}
+            defaultValue={filters[cat] && {item: filters[cat]}}
             defaultOptions
             loadOptions={(q) => handleLoadOptions(cat, q)}
             getOptionLabel={(i)=>i.count ? `${i.item} (${i.count})` : i.item}
@@ -57,7 +57,7 @@ export default function Filters() {
       <h3>Latest</h3>
           <input className="Latest"
              type="checkbox"
-             checked={ !params.alpha }
+             checked={ !filters.alpha }
              onChange={(e) => handleCategoryChange(router, 'alpha', !e.target.checked)} />
       </div>
       <div>
@@ -65,8 +65,8 @@ export default function Filters() {
         <input className="Filter"
           type="text"
           placeholder="Search albums"
-          defaultValue={params.filter}
-          onChange={(e)=>handleFilterChange(router, timeout, e.target.value)} />
+          value={filter}
+          onChange={(e)=>handleFilterChange(dispatch, router, timeout, e.target.value)} />
       </div>
     </div>
   );

+ 18 - 0
web/src/Header.css

@@ -0,0 +1,18 @@
+.Header {
+  grid-area: header;
+  background-color: var(--header-background);
+  display: grid;
+  grid: 1fr / auto 1fr 2fr 2fr auto;
+  align-items: center;
+}
+
+.Header-logo {
+  min-width: 64px;
+  height: 64px;
+  padding: 8px;
+}
+
+.Header-title {
+  color: var(--header-color);
+  font-size: 1.5em;
+}

+ 29 - 0
web/src/Header.js

@@ -0,0 +1,29 @@
+import './Header.css';
+import logo from './chad-logo-256.png';
+import { BaseLink, useRoute } from 'react-router5'
+import Settings from './Settings';
+import UserInfo from './UserInfo';
+
+function handleSearch(e, router) {
+  if (e.charCode === 13) router.navigate('browser', {filter: e.target.value});
+}
+
+export default function Header({settings, dispatch}) {
+  const { router } = useRoute()
+  const { name, params } = router.getState();
+
+  return (
+    <header className="Header">
+      <BaseLink router={router} routeName="browser" routeOptions={{ reload: true }}>
+        <img src={logo} className="Header-logo" alt="Chad music" />
+      </BaseLink>
+      <h1 className="Header-title">Chad Music</h1>
+      <input className="Header-search" type="text"
+             placeholder="Search albums"
+             defaultValue={name === "browser" ? params.filter : ''}
+             onKeyPress={(e) => handleSearch(e, router)}/>
+      <Settings state={settings} dispatch={dispatch} />
+      <UserInfo />
+    </header>
+  );
+}