From a03321fceff6f06ce428e1d8b34ceb1e8596ff6d Mon Sep 17 00:00:00 2001 From: SrGooglo Date: Wed, 12 Jul 2023 17:23:59 +0000 Subject: [PATCH] improve searcher --- .../app/src/components/Searcher/index.jsx | 160 +++++++++++------- .../app/src/components/Searcher/index.less | 102 ++++++++--- 2 files changed, 175 insertions(+), 87 deletions(-) diff --git a/packages/app/src/components/Searcher/index.jsx b/packages/app/src/components/Searcher/index.jsx index 612efd47..4672b8ba 100755 --- a/packages/app/src/components/Searcher/index.jsx +++ b/packages/app/src/components/Searcher/index.jsx @@ -1,90 +1,132 @@ import React from "react" import * as antd from "antd" import classnames from "classnames" -import useUrlQueryActiveKey from "hooks/useUrlQueryActiveKey" import lodash from "lodash" +import useUrlQueryActiveKey from "hooks/useUrlQueryActiveKey" +import { Translation } from "react-i18next" -import { UserPreview } from "components" import { Icons, createIconRender } from "components/Icons" +import UserPreview from "components/UserPreview" +import MusicTrack from "components/MusicTrack" +import PlaylistItem from "components/PlaylistItem" + import SearchModel from "models/search" import "./index.less" -const ResultRenders = { - users: (props) => { - const { item, onClick } = props - - return
- -
- } -} - const ResultsTypeDecorators = { users: { icon: "Users", - label: "Users" + label: "Users", + renderItem: (props) => { + const { item, onClick } = props + + return
+ +
+ } + }, + tracks: { + icon: "Album", + label: "Tracks", + renderItem: (props) => { + const { item, onClick } = props + + return
+ +
+ } + }, + playlists: { + icon: "Album", + label: "Playlists", + renderItem: (props) => { + return
+ +
+ } } } const Results = (props) => { let { results } = props - if (!results) { - return - } - if (typeof results !== "object") { - return + return null } - let keys = Object.keys(results) + let groupsKeys = Object.keys(results) - if (keys.length === 0) { - return - } - - // check if all keys are valid, if not replace as "others" - keys = keys.map((type) => { - if (ResultRenders[type]) { - return type - } - - return "others" + // filter out empty groups + groupsKeys = groupsKeys.filter((key) => { + return results[key].length > 0 }) - const handleOnClick = (type, value) => { - if (typeof props.onClick !== "function") { - console.warn("Searcher: onClick is not a function") - return - } - - return props.onClick(type, value) - } - - return keys.map((type) => { - const decorator = ResultsTypeDecorators[type] ?? { - label: keys, - icon: - } - - return
-
- - {createIconRender(decorator.icon)} - {decorator.label} - - { - results[type].map((item) => { - return React.createElement(ResultRenders[type], { item, onClick: (...props) => handleOnClick(type, ...props) }) - }) - } -
+ if (groupsKeys.length === 0) { + return
+
- }) -} + } + const handleClick = (props) => { + if (props.close) { + props.close() + } + } + + return
+ { + groupsKeys.map((key, index) => { + const decorator = ResultsTypeDecorators[key] ?? { + icon: null, + label: key, + renderItem: () => null + } + + return
+
+

+ { + createIconRender(decorator.icon) + } + + {(t) => t(decorator.label)} + +

+
+ +
+ { + results[key].map((item, index) => { + return decorator.renderItem({ + key: index, + item, + onClick: handleClick, + ...decorator.props, + }) + }) + } +
+
+ }) + } +
+} export default (props) => { const [loading, setLoading] = React.useState(false) diff --git a/packages/app/src/components/Searcher/index.less b/packages/app/src/components/Searcher/index.less index e5478897..c47fab6e 100755 --- a/packages/app/src/components/Searcher/index.less +++ b/packages/app/src/components/Searcher/index.less @@ -9,7 +9,14 @@ transition: all 0.3s ease-in-out; - gap: 20px; + gap: 10px; + + .results { + border-radius: 10px; + + overflow-x: hidden; + overflow-y: overlay; + } &.small { .ant-input-affix-wrapper { @@ -28,6 +35,11 @@ } .ant-input-affix-wrapper { + position: sticky; + + top: 0; + left: 0; + display: inline-flex; flex-direction: row; @@ -37,6 +49,7 @@ padding: 0; margin: 0; + min-height: 60px; height: 60px; border-radius: 10px; @@ -59,57 +72,90 @@ margin: 0; } } +} - .results { - position: relative; +.searcher_results { + display: flex; + flex-direction: row; + flex-wrap: wrap; + width: 50vw; + + gap: 10px; + + &.one_column { + grid-template-columns: 1fr; + } + + &.no_results { display: flex; flex-direction: column; - align-items: flex-start; + align-items: center; justify-content: center; + } - width: 100%; - - padding: 10px; - + .searcher_results_category { background-color: var(--background-color-primary); + padding: 20px; + border-radius: 8px; - gap: 10px; + height: fit-content; + width: 100%; - .category { - h3 { - font-size: 1rem; - font-weight: 600; + gap: 20px; + + .searcher_results_category_header { + h1 { + margin: 0; } + } - width: 100%; + .searcher_results_category_suggestions { + display: flex; + flex-direction: column; gap: 10px; - .suggestions { - display: flex; - flex-direction: column; - align-items: flex-start; + padding: 10px; - gap: 10px; + .playlistItem { + background-color: var(--background-color-accent); - .suggestion { - width: 100%; + max-width: 300px; + height: 80px; - padding: 10px; + .playlistItem_cover { + width: 80px; + height: 80px; - border-radius: 8px; + img { + height: 80px; + width: 80px; + } + } - background-color: var(--background-color-accent); + .playlistItem_info { + max-width: unset; + + h1 { + font-size: 1rem; + white-space: break-spaces; + } } } + + .music-track { + background-color: var(--background-color-accent); + } + } + + #playlists { + display: grid; + + grid-template-columns: 1fr 1fr 1fr; } } - - .ant-empty { - align-self: center; - } } \ No newline at end of file