diff --git a/src/frontend/components/UI/SearchBar/index.css b/src/frontend/components/UI/SearchBar/index.css deleted file mode 100644 index 83aea366e4..0000000000 --- a/src/frontend/components/UI/SearchBar/index.css +++ /dev/null @@ -1,68 +0,0 @@ -.SearchBar { - grid-area: search; - width: 100%; - position: relative; - display: inline-flex; - background: var(--search-bar-background, var(--input-background)); - border-radius: var(--space-md); - padding: var(--space-xs); -} - -.SearchBar:focus-within { - box-shadow: 0px 0px 0px 3px var(--search-bar-border, var(--input-backgroundd)); -} - -.autoComplete { - position: absolute; - top: 75%; - max-height: 200px; - width: 100%; - background-color: var(--input-background); - overflow: auto; - list-style: none; - margin: -2px -8px; - display: none; - padding: var(--space-xs) var(--space-md); - text-align: start; - overflow-x: hidden; - z-index: 1; - border-bottom-left-radius: var(--space-md); - border-bottom-right-radius: var(--space-md); -} - -.SearchBar:focus-within ul.autoComplete { - display: block; -} - -li { - padding: 2px 0px; -} - -.autoComplete li:hover { - background-color: var(--accent); - color: var(--background); -} - -.searchButton { - padding: var(--space-2xs) var(--space-2xs) 0 var(--space-2xs); -} - -.clearSearchButton { - padding-inline-end: var(--space-md); - transition: color 250ms; - background: transparent; - border: none; - color: var(--text-secondary); -} - -.searchBarInput { - width: 100%; - appearance: none; - background: transparent; - font: var(--font-secondary-bold); - color: var(--text-secondary); - padding: 0 var(--space-lg); - border: none; - outline: none; - transition: color 250ms; -} diff --git a/src/frontend/components/UI/SearchBar/index.scss b/src/frontend/components/UI/SearchBar/index.scss new file mode 100644 index 0000000000..df95207acf --- /dev/null +++ b/src/frontend/components/UI/SearchBar/index.scss @@ -0,0 +1,73 @@ +.SearchBar { + grid-area: search; + width: 100%; + position: relative; + display: inline-flex; + background: var(--search-bar-background, var(--input-background)); + border-radius: var(--space-md); + padding: var(--space-xs); + + &:focus-within { + box-shadow: 0px 0px 0px 3px + var(--search-bar-border, var(--input-backgroundd)); + } + + .autoComplete { + position: absolute; + top: 75%; + max-height: 200px; + width: 100%; + background-color: var(--input-background); + overflow: auto; + list-style: none; + margin: -2px -8px; + display: none; + padding: var(--space-xs) var(--space-md); + text-align: start; + overflow-x: hidden; + z-index: 1; + border-bottom-left-radius: var(--space-md); + border-bottom-right-radius: var(--space-md); + + li { + padding: 2px 0px; + + span { + opacity: 0.3; + } + + &:hover { + background-color: var(--accent); + color: var(--background); + } + } + } + + &:focus-within ul.autoComplete { + display: block; + } + + .searchButton { + padding: var(--space-2xs) var(--space-2xs) 0 var(--space-2xs); + } + + .clearSearchButton { + padding-inline-end: var(--space-md); + transition: color 250ms; + background: transparent; + border: none; + color: var(--text-secondary); + } + + .searchBarInput { + width: 100%; + appearance: none; + background: transparent; + font: var(--font-secondary-bold); + color: var(--text-secondary); + padding: 0 var(--space-lg); + border: none; + outline: none; + transition: color 250ms; + } +} diff --git a/src/frontend/components/UI/SearchBar/index.tsx b/src/frontend/components/UI/SearchBar/index.tsx index 36af038e6b..4492114aa9 100644 --- a/src/frontend/components/UI/SearchBar/index.tsx +++ b/src/frontend/components/UI/SearchBar/index.tsx @@ -9,7 +9,7 @@ import React, { import { useTranslation } from 'react-i18next' import { useNavigate } from 'react-router-dom' import ContextProvider from 'frontend/state/ContextProvider' -import './index.css' +import './index.scss' import { faXmark } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { GameInfo } from '../../../../common/types' @@ -19,6 +19,11 @@ function fixFilter(text: string) { return text.replaceAll(regex, '') } +const RUNNER_TO_STORE = { + legendary: 'Epic', + gog: 'GOG' +} + export default React.memo(function SearchBar() { const { handleSearch, filterText, epic, gog, sideloadedLibrary } = useContext(ContextProvider) @@ -28,21 +33,19 @@ export default React.memo(function SearchBar() { const input = useRef(null) const list = useMemo(() => { - // Set can't handle spread of undefined. Leading to - // TypeError. If undefined we just pass empty array. - const library = new Set( - [ - ...(epic.library ?? []), - ...(gog.library ?? []), - ...(sideloadedLibrary ?? []) - ] - .filter(Boolean) - .map((g) => g.title) - .sort() - ) - return [...library].filter((i) => - new RegExp(fixFilter(filterText), 'i').test(i) - ) + return [ + ...(epic.library ?? []), + ...(gog.library ?? []), + ...(sideloadedLibrary ?? []) + ] + .filter(Boolean) + .filter((el) => { + return ( + !el.install.is_dlc && + new RegExp(fixFilter(filterText), 'i').test(el.title) + ) + }) + .sort((g1, g2) => (g1.title < g2.title ? -1 : 1)) }, [epic.library, gog.library, filterText]) // we have to use an event listener instead of the react @@ -70,36 +73,17 @@ export default React.memo(function SearchBar() { } }, [input]) - const handleClick = (title: string) => { + const handleClick = (game: GameInfo) => { handleSearch('') if (input.current) { input.current.value = '' - const game: GameInfo | undefined = getGameInfoByAppTitle(title) - - if (game !== undefined) { - navigate(`/gamepage/${game.runner}/${game.app_name}`, { - state: { gameInfo: game } - }) - } + navigate(`/gamepage/${game.runner}/${game.app_name}`, { + state: { gameInfo: game } + }) } } - const getGameInfoByAppTitle = (title: string) => { - return ( - getGameInfoByAppTitleAndLibrary(epic.library, title) || - getGameInfoByAppTitleAndLibrary(gog.library, title) || - getGameInfoByAppTitleAndLibrary(sideloadedLibrary, title) - ) - } - - const getGameInfoByAppTitleAndLibrary = ( - library: GameInfo[], - title: string - ) => { - return library.filter((g: GameInfo) => g.title === title).at(0) - } - return (
@@ -119,12 +103,10 @@ export default React.memo(function SearchBar() { <>
    {list.length > 0 && - list.map((title, i) => ( -
  • handleClick(e.currentTarget.innerText)} - key={i} - > - {title} + list.map((game) => ( +
  • handleClick(game)} key={game.app_name}> + {game.title}{' '} + ({RUNNER_TO_STORE[game.runner] || game.runner})
  • ))}