Archived
search providers & more
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
import { createResource } from 'solid-js';
|
||||||
|
import { SearchProvider } from '~/types';
|
||||||
|
|
||||||
|
async function getSearchProviders(): Promise<SearchProvider[]> {
|
||||||
|
return await import('../../data/search-providers.json').then((m) => m.default);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createSearchProviders() {
|
||||||
|
const [searchProviders] = createResource<SearchProvider[]>(getSearchProviders);
|
||||||
|
return searchProviders;
|
||||||
|
}
|
||||||
@@ -7,14 +7,19 @@
|
|||||||
.Header {
|
.Header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
gap: max(1vw, 1rem);
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
font-size: 2rem;
|
font-size: max(1.8vw, 1.3rem);
|
||||||
border-bottom: 1px solid var(--color-text);
|
|
||||||
text-shadow: 1px 1px 5px #000;
|
text-shadow: 1px 1px 5px #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Close {
|
||||||
|
font-size: 2rem;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
.AppList {
|
.AppList {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: .5rem;
|
gap: .5rem;
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ export function AppList({
|
|||||||
return (
|
return (
|
||||||
<div class={style.AppListWrap}>
|
<div class={style.AppListWrap}>
|
||||||
{tag?.() ? (
|
{tag?.() ? (
|
||||||
<div>
|
<div style={{ width: '100%' }}>
|
||||||
<h2 class={style.Header}>
|
<h2 class={style.Header}>
|
||||||
{tag()}
|
{tag()}
|
||||||
<span style={{ cursor: 'pointer' }} onClick={resetTag}>
|
<span class={style.Close} onClick={resetTag}>
|
||||||
×
|
×
|
||||||
</span>
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -12,15 +12,15 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
gap: max(1vw, .5rem);
|
gap: clamp(.5rem, 1vw, .5rem);
|
||||||
font-size: max(1.8vw, 1.3rem);
|
font-size: clamp(1.3rem, 1.8vw, 3rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.Menu li {
|
.Menu li {
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-shadow: 1px 1px 5px #000;
|
text-shadow: 1px 1px 5px #000;
|
||||||
padding: max(1vw, .5rem) 1.5rem;
|
padding: clamp(.5rem, 1vw, 1.5rem) 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Menu li::after {
|
.Menu li::after {
|
||||||
@@ -37,6 +37,6 @@
|
|||||||
transition: height 0.2s ease;
|
transition: height 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Menu li:hover::after {
|
.Menu li:hover::after, .Menu li.Active::after {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import { createTagOrder } from '~/api/get-apps';
|
|||||||
import style from './Sidebar.module.css';
|
import style from './Sidebar.module.css';
|
||||||
|
|
||||||
type SidebarProps = {
|
type SidebarProps = {
|
||||||
onSelectTag?(tag: string): void;
|
activeTag?: string | null;
|
||||||
|
onSelectTag?(tag: string, mode: 'permanent' | 'temporary'): void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Sidebar(props: SidebarProps) {
|
export function Sidebar(props: SidebarProps) {
|
||||||
@@ -13,8 +14,9 @@ export function Sidebar(props: SidebarProps) {
|
|||||||
<ul class={style.Menu}>
|
<ul class={style.Menu}>
|
||||||
{tagOrder()?.map((tag) => (
|
{tagOrder()?.map((tag) => (
|
||||||
<li
|
<li
|
||||||
onClick={() => props.onSelectTag?.(tag)}
|
class={props.activeTag === tag ? style.Active : ''}
|
||||||
onMouseEnter={() => props.onSelectTag?.(tag)}
|
onClick={() => props.onSelectTag?.(tag, 'permanent')}
|
||||||
|
onMouseEnter={() => props.onSelectTag?.(tag, 'temporary')}
|
||||||
>
|
>
|
||||||
{tag}
|
{tag}
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -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 {
|
.Search {
|
||||||
|
flex-grow: 1;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
width: max(40vw, 300px);
|
|
||||||
border: 0;
|
border: 0;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
|
|||||||
@@ -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 style from './Search.module.css';
|
||||||
|
import { SearchProvider } from '~/types';
|
||||||
|
|
||||||
export function Search() {
|
export function Search() {
|
||||||
const [term, setTerm] = createSignal('');
|
const [term, setTerm] = createSignal('');
|
||||||
|
|
||||||
|
const searchProviders = createSearchProviders();
|
||||||
|
|
||||||
|
const defaultProvider = createMemo(
|
||||||
|
() => searchProviders()?.find((provider) => provider.default) ?? searchProviders()?.[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
const searchPerPrefix = createMemo(() =>
|
||||||
|
searchProviders()?.reduce<Record<string, SearchProvider>>((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 (
|
return (
|
||||||
<input
|
<div class={style.SearchWrapper}>
|
||||||
id="search"
|
<div class={style.Provider}>{activeProvider()?.name}</div>
|
||||||
class={style.Search}
|
<input
|
||||||
type="text"
|
id="search"
|
||||||
placeholder="Search kagi..."
|
class={style.Search}
|
||||||
value={term()}
|
type="text"
|
||||||
onInput={(e) => setTerm(e.target.value)}
|
placeholder={`Lorem ipsum`}
|
||||||
onKeyPress={(e) => {
|
value={term()}
|
||||||
e.stopPropagation();
|
onInput={(e) => setTerm(e.target.value)}
|
||||||
if (e.key === 'Enter') {
|
onKeyPress={(e) => {
|
||||||
window.open(`https://kagi.com/search?q=${term()}`, '_blank', 'noreferrer');
|
e.stopPropagation();
|
||||||
setTerm('');
|
if (e.key === 'Enter') {
|
||||||
}
|
window.open(`${activeProvider()?.url}${searchTerm()}`, '_blank', 'noreferrer');
|
||||||
}}
|
setTerm('');
|
||||||
/>
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
import { format } from 'date-fns';
|
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');
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
gap: 2rem;
|
gap: 2rem;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
|
|
||||||
padding: 0 max(10vw, 2rem);
|
padding: 0 clamp(1rem, 10vw, 10rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.Widgets {
|
.Widgets {
|
||||||
|
|||||||
@@ -21,11 +21,13 @@ export default function Home() {
|
|||||||
const [activeTag, setActiveTag] = createSignal<string | null>(null);
|
const [activeTag, setActiveTag] = createSignal<string | null>(null);
|
||||||
const [timeout, storeTimeout] = createSignal<NodeJS.Timeout | null>(null);
|
const [timeout, storeTimeout] = createSignal<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
const switchToTag = (tag: string) => {
|
const switchToTag = (tag: string, mode: 'permanent' | 'temporary') => {
|
||||||
setActiveTag(tag);
|
setActiveTag(tag);
|
||||||
clearTimeout(timeout() as NodeJS.Timeout);
|
clearTimeout(timeout() as NodeJS.Timeout);
|
||||||
|
|
||||||
storeTimeout(setTimeout(() => setActiveTag(null), 3000));
|
if (mode === 'temporary') {
|
||||||
|
storeTimeout(setTimeout(() => setActiveTag(null), 3000));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetTag = () => {
|
const resetTag = () => {
|
||||||
@@ -59,15 +61,15 @@ export default function Home() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<main class={style.SidebarPage}>
|
<main class={style.SidebarPage}>
|
||||||
<Sidebar onSelectTag={switchToTag} />
|
<Sidebar onSelectTag={switchToTag} activeTag={activeTag()} />
|
||||||
<div class={style.ContentWrapper}>
|
<div class={style.ContentWrapper}>
|
||||||
<div class={style.Widgets}>
|
<div class={style.Widgets}>
|
||||||
{activeTag() ? (
|
{activeTag() ? (
|
||||||
<AppList apps={apps() ?? []} tag={activeTag} resetTag={resetTag} />
|
<AppList apps={apps() ?? []} tag={activeTag} resetTag={resetTag} />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Search />
|
|
||||||
<DateView />
|
<DateView />
|
||||||
|
<Search />
|
||||||
<Weather />
|
<Weather />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -7,6 +7,13 @@ export type AppDefinition = {
|
|||||||
shortcut?: string;
|
shortcut?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SearchProvider = {
|
||||||
|
name: string;
|
||||||
|
prefix: string;
|
||||||
|
url: string;
|
||||||
|
default?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type WeatherData = {
|
export type WeatherData = {
|
||||||
id: string;
|
id: string;
|
||||||
main: string;
|
main: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user