From 18756b2a60e27ad1b01c99c3bb6bd665ff8c3463 Mon Sep 17 00:00:00 2001 From: Georgi Gardev Date: Sat, 18 Nov 2023 17:13:59 +0200 Subject: [PATCH] search providers & more --- src/api/get-search-providers.ts | 11 ++++ src/components/apps/AppList.module.css | 11 ++-- src/components/apps/AppList.tsx | 4 +- src/components/ui/Sidebar.module.css | 8 +-- src/components/ui/Sidebar.tsx | 8 +-- src/components/widgets/Search.module.css | 20 ++++++- src/components/widgets/Search.tsx | 67 ++++++++++++++++++------ src/lib/date.ts | 4 +- src/routes/index.module.css | 2 +- src/routes/index.tsx | 10 ++-- src/types.ts | 7 +++ 11 files changed, 116 insertions(+), 36 deletions(-) create mode 100644 src/api/get-search-providers.ts diff --git a/src/api/get-search-providers.ts b/src/api/get-search-providers.ts new file mode 100644 index 0000000..bde0466 --- /dev/null +++ b/src/api/get-search-providers.ts @@ -0,0 +1,11 @@ +import { createResource } from 'solid-js'; +import { SearchProvider } from '~/types'; + +async function getSearchProviders(): Promise { + return await import('../../data/search-providers.json').then((m) => m.default); +} + +export function createSearchProviders() { + const [searchProviders] = createResource(getSearchProviders); + return searchProviders; +} diff --git a/src/components/apps/AppList.module.css b/src/components/apps/AppList.module.css index fbfe7d5..9435659 100644 --- a/src/components/apps/AppList.module.css +++ b/src/components/apps/AppList.module.css @@ -7,14 +7,19 @@ .Header { display: flex; align-items: center; - justify-content: space-between; + gap: max(1vw, 1rem); margin-bottom: 1rem; padding-bottom: 8px; - font-size: 2rem; - border-bottom: 1px solid var(--color-text); + font-size: max(1.8vw, 1.3rem); text-shadow: 1px 1px 5px #000; } +.Close { + font-size: 2rem; + cursor: pointer; + color: #ccc; +} + .AppList { display: flex; gap: .5rem; diff --git a/src/components/apps/AppList.tsx b/src/components/apps/AppList.tsx index 6dea326..951c378 100644 --- a/src/components/apps/AppList.tsx +++ b/src/components/apps/AppList.tsx @@ -21,10 +21,10 @@ export function AppList({ return (
{tag?.() ? ( -
+

{tag()} - + ×

diff --git a/src/components/ui/Sidebar.module.css b/src/components/ui/Sidebar.module.css index 9759a29..e85e6b8 100644 --- a/src/components/ui/Sidebar.module.css +++ b/src/components/ui/Sidebar.module.css @@ -12,15 +12,15 @@ display: flex; flex-direction: column; text-align: right; - gap: max(1vw, .5rem); - font-size: max(1.8vw, 1.3rem); + gap: clamp(.5rem, 1vw, .5rem); + font-size: clamp(1.3rem, 1.8vw, 3rem); } .Menu li { position: relative; cursor: pointer; text-shadow: 1px 1px 5px #000; - padding: max(1vw, .5rem) 1.5rem; + padding: clamp(.5rem, 1vw, 1.5rem) 1.5rem; } .Menu li::after { @@ -37,6 +37,6 @@ transition: height 0.2s ease; } -.Menu li:hover::after { +.Menu li:hover::after, .Menu li.Active::after { height: 100%; } diff --git a/src/components/ui/Sidebar.tsx b/src/components/ui/Sidebar.tsx index 0c1ff78..92daf54 100644 --- a/src/components/ui/Sidebar.tsx +++ b/src/components/ui/Sidebar.tsx @@ -2,7 +2,8 @@ import { createTagOrder } from '~/api/get-apps'; import style from './Sidebar.module.css'; type SidebarProps = { - onSelectTag?(tag: string): void; + activeTag?: string | null; + onSelectTag?(tag: string, mode: 'permanent' | 'temporary'): void; }; export function Sidebar(props: SidebarProps) { @@ -13,8 +14,9 @@ export function Sidebar(props: SidebarProps) {
    {tagOrder()?.map((tag) => (
  • props.onSelectTag?.(tag)} - onMouseEnter={() => props.onSelectTag?.(tag)} + class={props.activeTag === tag ? style.Active : ''} + onClick={() => props.onSelectTag?.(tag, 'permanent')} + onMouseEnter={() => props.onSelectTag?.(tag, 'temporary')} > {tag}
  • diff --git a/src/components/widgets/Search.module.css b/src/components/widgets/Search.module.css index 3e4e910..e03ad17 100644 --- a/src/components/widgets/Search.module.css +++ b/src/components/widgets/Search.module.css @@ -1,6 +1,24 @@ +.SearchWrapper { + display: flex; + align-items: center; + gap: 1px; + width: clamp(300px, 40vw, 600px); +} + +.Provider { + text-transform: uppercase; + backdrop-filter: blur(10px); + padding: 11px 20px; + width: 100px; + text-align: center; + background-color: rgba(0, 0, 0, 0.2); + border-bottom: 2px solid rgba(0, 0, 0, 0.2); + font-family: Nova Mono; +} + .Search { + flex-grow: 1; padding: 10px 20px; - width: max(40vw, 300px); border: 0; font-size: 1rem; backdrop-filter: blur(10px); diff --git a/src/components/widgets/Search.tsx b/src/components/widgets/Search.tsx index 8cb08e4..329bb88 100644 --- a/src/components/widgets/Search.tsx +++ b/src/components/widgets/Search.tsx @@ -1,24 +1,59 @@ -import { createSignal } from 'solid-js'; +import { createComputed, createMemo, createSignal } from 'solid-js'; +import { createSearchProviders } from '~/api/get-search-providers'; + import style from './Search.module.css'; +import { SearchProvider } from '~/types'; export function Search() { const [term, setTerm] = createSignal(''); + const searchProviders = createSearchProviders(); + + const defaultProvider = createMemo( + () => searchProviders()?.find((provider) => provider.default) ?? searchProviders()?.[0] + ); + + const searchPerPrefix = createMemo(() => + searchProviders()?.reduce>((acc, provider) => { + acc[provider.prefix] = provider; + return acc; + }, {}) + ); + + const activeProvider = createMemo(() => { + if (term().startsWith('!') && searchPerPrefix()?.[term().charAt(1)]) { + return searchPerPrefix()?.[term().charAt(1)]; + } else { + return defaultProvider(); + } + }); + + const searchTerm = createMemo(() => { + if (term().startsWith('!') && searchPerPrefix()?.[term().charAt(1)]) { + return term().slice(2); + } else { + return term(); + } + }); + return ( - setTerm(e.target.value)} - onKeyPress={(e) => { - e.stopPropagation(); - if (e.key === 'Enter') { - window.open(`https://kagi.com/search?q=${term()}`, '_blank', 'noreferrer'); - setTerm(''); - } - }} - /> +
    +
    {activeProvider()?.name}
    + setTerm(e.target.value)} + onKeyPress={(e) => { + e.stopPropagation(); + if (e.key === 'Enter') { + window.open(`${activeProvider()?.url}${searchTerm()}`, '_blank', 'noreferrer'); + setTerm(''); + } + }} + /> +
    ); } diff --git a/src/lib/date.ts b/src/lib/date.ts index d04650b..703d0f5 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -1,5 +1,5 @@ import { format } from 'date-fns'; -export const getDate = () => format(new Date(), 'EEEE, dd MMMM yyyy'); +export const getDate = () => format(new Date(), 'EEEE, dd MMM yyyy'); -export const getTime = () => format(new Date(), 'HH:mm:ss'); +export const getTime = (time: Date | number = new Date()) => format(time, 'HH:mm:ss'); diff --git a/src/routes/index.module.css b/src/routes/index.module.css index 131cf35..ac85ca7 100644 --- a/src/routes/index.module.css +++ b/src/routes/index.module.css @@ -10,7 +10,7 @@ gap: 2rem; overflow: scroll; - padding: 0 max(10vw, 2rem); + padding: 0 clamp(1rem, 10vw, 10rem); } .Widgets { diff --git a/src/routes/index.tsx b/src/routes/index.tsx index c0ba291..68b91ab 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -21,11 +21,13 @@ export default function Home() { const [activeTag, setActiveTag] = createSignal(null); const [timeout, storeTimeout] = createSignal(null); - const switchToTag = (tag: string) => { + const switchToTag = (tag: string, mode: 'permanent' | 'temporary') => { setActiveTag(tag); clearTimeout(timeout() as NodeJS.Timeout); - storeTimeout(setTimeout(() => setActiveTag(null), 3000)); + if (mode === 'temporary') { + storeTimeout(setTimeout(() => setActiveTag(null), 3000)); + } }; const resetTag = () => { @@ -59,15 +61,15 @@ export default function Home() { return (
    - +
    {activeTag() ? ( ) : ( <> - + )} diff --git a/src/types.ts b/src/types.ts index b94acf8..7f18804 100644 --- a/src/types.ts +++ b/src/types.ts @@ -7,6 +7,13 @@ export type AppDefinition = { shortcut?: string; }; +export type SearchProvider = { + name: string; + prefix: string; + url: string; + default?: boolean; +}; + export type WeatherData = { id: string; main: string;