Improve search
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user