Improve search

This commit is contained in:
Georgi Gardev
2023-11-19 10:24:27 +02:00
parent 18ffc1ba03
commit e503facbfc
4 changed files with 87 additions and 34 deletions

View File

@@ -1,27 +1,29 @@
import { Accessor } from 'solid-js';
import { CategoryDefinition } from '~/types';
import { AppDefinition } from '~/types';
import { App } from './App';
import style from './AppList.module.css';
export function AppList({
category,
apps,
resetCategory: resetCategory,
}: {
category?: Accessor<CategoryDefinition | undefined>;
category?: Accessor<string>;
apps: Accessor<AppDefinition[]>;
resetCategory?(): void;
}) {
return (
<div class={style.AppListWrap}>
<div style={{ width: '100%' }}>
<h2 class={style.Header}>
{category?.()?.name}
{category?.()}
<span class={style.Close} onClick={resetCategory}>
×
</span>
</h2>
<div class={style.AppList}>
{category?.()?.apps.map((app) => (
{apps().map((app) => (
<App {...app} />
))}
</div>

View File

@@ -1,8 +1,9 @@
.SearchWrapper {
.SearchBar {
display: flex;
align-items: center;
gap: 1px;
width: clamp(300px, 40vw, 600px);
margin-bottom: 8px;
}
.Provider {
@@ -43,3 +44,8 @@
border-color: var(--color-text);
background-color: rgba(0, 0, 0, 0.6);
}
.Hint {
font-size: .9rem;
color: #aaa;
}

View File

@@ -1,25 +1,27 @@
import { Resource, createMemo, createSignal } from 'solid-js';
import { Accessor, Resource, Setter, createMemo } from 'solid-js';
import { SearchProvider } from '~/types';
import style from './Search.module.css';
type SearchProps = {
term: Accessor<string>;
setTerm: Setter<string>;
providers: Resource<SearchProvider[]>;
onOpenApp: () => void;
canOpenApp: boolean;
};
export function Search(props: SearchProps) {
const [term, setTerm] = createSignal('');
const defaultProvider = createMemo(
() => props.providers()?.find((provider) => provider.default) ?? props.providers()?.[0]
);
const activeProvider = createMemo(() => {
if (!term().startsWith('!')) {
if (!props.term().startsWith('!')) {
return defaultProvider();
}
const prefix = term().slice(1);
const prefix = props.term().slice(1);
const provider = props
.providers()
?.find((provider) => provider.prefix === prefix || provider.prefix === prefix.split(' ')[0]);
@@ -28,31 +30,38 @@ export function Search(props: SearchProps) {
});
const searchTerm = createMemo(() => {
if (!term().startsWith('!')) {
return term();
if (!props.term().startsWith('!')) {
return props.term();
}
return term().split(`!${activeProvider()?.prefix} `)[1];
return props.term().split(`!${activeProvider()?.prefix} `)[1];
});
return (
<div class={style.SearchWrapper}>
<input
id="search"
class={style.Search}
type="text"
placeholder="Search..."
value={term()}
onInput={(e) => setTerm(e.target.value)}
onKeyPress={(e) => {
e.stopPropagation();
if (e.key === 'Enter') {
window.open(`${activeProvider()?.url}${searchTerm()}`, '_blank', 'noreferrer');
setTerm('');
}
}}
/>
<div class={style.Provider}>{activeProvider()?.name}</div>
<div>
<div class={style.SearchBar}>
<input
id="search"
class={style.Search}
type="text"
placeholder="Search..."
value={props.term()}
onInput={(e) => props.setTerm(e.target.value)}
onKeyPress={(e) => {
e.stopPropagation();
if (props.canOpenApp && e.shiftKey && e.key === 'Enter') {
props.onOpenApp();
return;
}
if (e.key === 'Enter') {
window.open(`${activeProvider()?.url}${searchTerm()}`, '_blank', 'noreferrer');
props.setTerm('');
}
}}
/>
<div class={style.Provider}>{activeProvider()?.name}</div>
</div>
{props.canOpenApp && <span class={style.Hint}>Press Shift+Enter to open first result</span>}
</div>
);
}

View File

@@ -7,14 +7,26 @@ import { Search } from '~/components/widgets/Search';
import { Weather } from '~/components/widgets/Weather';
import { Sidebar } from '../components/ui/Sidebar';
import style from './index.module.css';
import { createActiveCategory } from '~/state/create-active-category';
import style from './index.module.css';
export default function Home() {
const searchProviders = createSearchProviders();
const [searchTerm, setSearchTerm] = createSignal('');
const { categories, apps } = createCategories();
const { activeCategory, selectCategory, resetCategory } = createActiveCategory();
const searchResults = createMemo(() => {
if (!searchTerm() || searchTerm().startsWith('!')) {
return [];
}
return (
apps()?.filter((app) => app.name.toLowerCase().includes(searchTerm().toLowerCase())) ?? []
);
});
onMount(() => {
parent.addEventListener('keydown', handleKeypress);
});
@@ -28,6 +40,7 @@ export default function Home() {
case 'Escape': {
document.getElementById('search')?.blur();
resetCategory();
setSearchTerm('');
break;
}
case '/': {
@@ -47,6 +60,14 @@ export default function Home() {
}
}
const activeCategoryApps = createMemo(
() => categories()?.find((c) => c.name === activeCategory())?.apps ?? []
);
const activeCategoryName = createMemo(
() => categories()?.find((c) => c.name === activeCategory())?.name ?? ''
);
return (
<main class={style.SidebarPage}>
<Sidebar
@@ -58,14 +79,29 @@ export default function Home() {
<div class={style.Widgets}>
{activeCategory() ? (
<AppList
category={createMemo(() => categories()?.find((c) => c.name === activeCategory()))}
category={activeCategoryName}
apps={activeCategoryApps}
resetCategory={resetCategory}
/>
) : (
<>
<DateView />
<Search providers={searchProviders} />
<Weather />
<Search
term={searchTerm}
setTerm={setSearchTerm}
providers={searchProviders}
canOpenApp={searchResults().length > 0}
onOpenApp={() => window.open(searchResults()[0].url)}
/>
{searchTerm() ? (
<AppList
category={() => 'search results'}
apps={searchResults}
resetCategory={() => setSearchTerm('')}
/>
) : (
<Weather />
)}
</>
)}
</div>