mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 18:44:16 +00:00
improve searcher
This commit is contained in:
parent
70fb020842
commit
a03321fcef
@ -1,90 +1,132 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
import classnames from "classnames"
|
import classnames from "classnames"
|
||||||
import useUrlQueryActiveKey from "hooks/useUrlQueryActiveKey"
|
|
||||||
import lodash from "lodash"
|
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 { 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 SearchModel from "models/search"
|
||||||
|
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
const ResultRenders = {
|
|
||||||
users: (props) => {
|
|
||||||
const { item, onClick } = props
|
|
||||||
|
|
||||||
return <div className="suggestion">
|
|
||||||
<UserPreview onClick={onClick} user={item} />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const ResultsTypeDecorators = {
|
const ResultsTypeDecorators = {
|
||||||
users: {
|
users: {
|
||||||
icon: "Users",
|
icon: "Users",
|
||||||
label: "Users"
|
label: "Users",
|
||||||
|
renderItem: (props) => {
|
||||||
|
const { item, onClick } = props
|
||||||
|
|
||||||
|
return <div className="suggestion">
|
||||||
|
<UserPreview onClick={onClick} user={item} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tracks: {
|
||||||
|
icon: "Album",
|
||||||
|
label: "Tracks",
|
||||||
|
renderItem: (props) => {
|
||||||
|
const { item, onClick } = props
|
||||||
|
|
||||||
|
return <div className="suggestion" onClick={onClick}>
|
||||||
|
<MusicTrack track={item} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
playlists: {
|
||||||
|
icon: "Album",
|
||||||
|
label: "Playlists",
|
||||||
|
renderItem: (props) => {
|
||||||
|
return <div className="suggestion">
|
||||||
|
<PlaylistItem playlist={props.item} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Results = (props) => {
|
const Results = (props) => {
|
||||||
let { results } = props
|
let { results } = props
|
||||||
|
|
||||||
if (!results) {
|
|
||||||
return <antd.Empty />
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof results !== "object") {
|
if (typeof results !== "object") {
|
||||||
return <antd.Empty />
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
let keys = Object.keys(results)
|
let groupsKeys = Object.keys(results)
|
||||||
|
|
||||||
if (keys.length === 0) {
|
// filter out empty groups
|
||||||
return <antd.Empty />
|
groupsKeys = groupsKeys.filter((key) => {
|
||||||
}
|
return results[key].length > 0
|
||||||
|
|
||||||
// check if all keys are valid, if not replace as "others"
|
|
||||||
keys = keys.map((type) => {
|
|
||||||
if (ResultRenders[type]) {
|
|
||||||
return type
|
|
||||||
}
|
|
||||||
|
|
||||||
return "others"
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleOnClick = (type, value) => {
|
if (groupsKeys.length === 0) {
|
||||||
if (typeof props.onClick !== "function") {
|
return <div className="searcher no_results">
|
||||||
console.warn("Searcher: onClick is not a function")
|
<antd.Result
|
||||||
return
|
status="info"
|
||||||
}
|
title="No results"
|
||||||
|
subTitle="We are sorry, but we could not find any results for your search."
|
||||||
return props.onClick(type, value)
|
/>
|
||||||
}
|
|
||||||
|
|
||||||
return keys.map((type) => {
|
|
||||||
const decorator = ResultsTypeDecorators[type] ?? {
|
|
||||||
label: keys,
|
|
||||||
icon: <Icons.Search />
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div className="category" id={type}>
|
|
||||||
<div className="suggestions">
|
|
||||||
<span>
|
|
||||||
<span className="icon">{createIconRender(decorator.icon)}</span>
|
|
||||||
<span className="label">{decorator.label}</span>
|
|
||||||
</span>
|
|
||||||
{
|
|
||||||
results[type].map((item) => {
|
|
||||||
return React.createElement(ResultRenders[type], { item, onClick: (...props) => handleOnClick(type, ...props) })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
const handleClick = (props) => {
|
||||||
|
if (props.close) {
|
||||||
|
props.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div
|
||||||
|
className={classnames(
|
||||||
|
"searcher_results",
|
||||||
|
{
|
||||||
|
["one_column"]: groupsKeys.length === 1,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
groupsKeys.map((key, index) => {
|
||||||
|
const decorator = ResultsTypeDecorators[key] ?? {
|
||||||
|
icon: null,
|
||||||
|
label: key,
|
||||||
|
renderItem: () => null
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div
|
||||||
|
className="searcher_results_category"
|
||||||
|
key={index}
|
||||||
|
>
|
||||||
|
<div className="searcher_results_category_header">
|
||||||
|
<h1>
|
||||||
|
{
|
||||||
|
createIconRender(decorator.icon)
|
||||||
|
}
|
||||||
|
<Translation>
|
||||||
|
{(t) => t(decorator.label)}
|
||||||
|
</Translation>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="searcher_results_category_suggestions" id={key}>
|
||||||
|
{
|
||||||
|
results[key].map((item, index) => {
|
||||||
|
return decorator.renderItem({
|
||||||
|
key: index,
|
||||||
|
item,
|
||||||
|
onClick: handleClick,
|
||||||
|
...decorator.props,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
export default (props) => {
|
export default (props) => {
|
||||||
const [loading, setLoading] = React.useState(false)
|
const [loading, setLoading] = React.useState(false)
|
||||||
|
@ -9,7 +9,14 @@
|
|||||||
|
|
||||||
transition: all 0.3s ease-in-out;
|
transition: all 0.3s ease-in-out;
|
||||||
|
|
||||||
gap: 20px;
|
gap: 10px;
|
||||||
|
|
||||||
|
.results {
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: overlay;
|
||||||
|
}
|
||||||
|
|
||||||
&.small {
|
&.small {
|
||||||
.ant-input-affix-wrapper {
|
.ant-input-affix-wrapper {
|
||||||
@ -28,6 +35,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ant-input-affix-wrapper {
|
.ant-input-affix-wrapper {
|
||||||
|
position: sticky;
|
||||||
|
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
||||||
@ -37,6 +49,7 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
|
min-height: 60px;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
|
||||||
@ -59,57 +72,90 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.results {
|
.searcher_results {
|
||||||
position: relative;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
width: 50vw;
|
||||||
|
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
&.one_column {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.no_results {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
align-items: flex-start;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
width: 100%;
|
.searcher_results_category {
|
||||||
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
background-color: var(--background-color-primary);
|
background-color: var(--background-color-primary);
|
||||||
|
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
gap: 10px;
|
height: fit-content;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
.category {
|
gap: 20px;
|
||||||
h3 {
|
|
||||||
font-size: 1rem;
|
.searcher_results_category_header {
|
||||||
font-weight: 600;
|
h1 {
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
width: 100%;
|
.searcher_results_category_suggestions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|
||||||
.suggestions {
|
padding: 10px;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
|
|
||||||
gap: 10px;
|
.playlistItem {
|
||||||
|
background-color: var(--background-color-accent);
|
||||||
|
|
||||||
.suggestion {
|
max-width: 300px;
|
||||||
width: 100%;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user