Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"dotenv": "^16.3.1",
"highlight.js": "^11.8.0",
"markdown-it": "^13.0.1",
"meilisearch": "^0.35.0",
"motion": "^10.16.2"
}
}
46 changes: 46 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

224 changes: 224 additions & 0 deletions src/lib/components/Search.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
<script lang="ts">
import { fade } from 'svelte/transition';
import type { Action } from 'svelte/action';
import { MeiliSearch, type Hits, type Hit } from 'meilisearch';

export let open: boolean = true;

let value: string;
let container: HTMLDivElement;

const client = new MeiliSearch({
host: 'https://search.appwrite.org',
apiKey: 'd7e83e21c0daf2a471ef4c463c7872e55b91b0cd02e2d20e9c6f6f1c4cd09ed3'
});
const index = client.index<Props>('website');

type Props = {
url: string;
title?: string;
uid?: string;
meta?: Record<string, string>;
page_block?: number;
urls_tags?: Array<string>;
h1?: string;
h2?: string;
h3?: string;
h4?: string;
h5?: string;
h6?: string;
p?: string;
anchor?: string;
};

let results: Hits<Props> = [];

async function search(value: string) {
return index.search(value, {
limit: 5
});
}

async function handleInput(value: string) {
const response = await search(value);
results = response.hits.filter((hit) => hit.h1);
}

function handleExit(event: MouseEvent & { currentTarget: EventTarget & HTMLDivElement }) {
if (event.target === container) {
open = false;
}
}

function createHref(hit: Hit<Props>): string {
const anchor = hit.anchor === '#' ? '' : hit.anchor ?? '';
const target = new URL(hit.url + anchor);

return target.toString();
}

const recommended: Hits<Props> = [
{
uid: 'recommended-references-account',
url: 'https://website-appwrite.vercel.app/docs/references/cloud/client-web/databases',
h1: 'References',
h2: 'Databases'
},
{
uid: 'recommended-references-teans',
url: 'https://website-appwrite.vercel.app/docs/references/cloud/client-web/teams',
h1: 'References',
h2: 'Teams'
},
{
uid: 'recommended-references-databases',
url: 'https://website-appwrite.vercel.app/docs/references/cloud/client-web/databases',
h1: 'References',
h2: 'Databases'
},
{
uid: 'recommended-references-storage',
url: 'https://website-appwrite.vercel.app/docs/references/cloud/client-web/storage',
h1: 'References',
h2: 'Storage'
}
];

const elements = new Map<number, HTMLElement>();

const arrowKeyFocus: Action<HTMLElement, number> = (node, index) => {
elements.set(index, node);
const callback = (event: KeyboardEvent) => {
switch (event.key) {
case 'ArrowDown':
{
event.preventDefault();
const target = index + 1;
if (elements.has(target)) {
elements.get(target)?.focus();
}
}
break;
case 'ArrowUp':
{
event.preventDefault();
const target = index - 1;
if (elements.has(target)) {
elements.get(target)?.focus();
}
}
break;
}
};

node.addEventListener('keydown', callback);

return {
destroy() {
elements.delete(index);
node.removeEventListener('keydown', callback);
}
};
};

$: value && handleInput(value);
</script>

<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events -->
{#if open}
<div
class="u-position-fixed u-padding-0 u-inset-0 u-flex u-main-center u-cross-center"
style:z-index="100"
style:background="hsl(var(--aw-color-black) / 0.3)"
style:backdrop-filter="blur(15px)"
style:-webkit-backdrop-filter="blur(15px)"
bind:this={container}
on:click={handleExit}
transition:fade={{ duration: 50 }}
>
<div
class="aw-input-text-search-wrapper aw-u-max-width-680 aw-u-margin-inline-20 u-width-full-line"
>
<span class="icon-search u-z-index-5" aria-hidden="true" style="" />
<div id="searchbox" />

<!-- svelte-ignore a11y-autofocus -->
<input
autofocus
class="aw-input-button -u-padding-block-0 u-position-relative u-z-index-1"
type="text"
id="search"
bind:value
placeholder="Search"
style="border-end-start-radius:0; border-end-end-radius:0;"
use:arrowKeyFocus={-1}
/>
<div
class="aw-card is-normal u-overflow-y-auto"
style="--card-padding-mobile:1rem; border-radius:0 0 0.5rem 0.5rem; margin-block-start:-0.0625rem; max-block-size: min(18.75rem, calc(100vh - 5.5rem)); border-block-start-width:0;"
>
<div class="u-flex-vertical u-gap-24">
{#if value}
{#if results.length > 0}
<section>
<h6 class="aw-eyebrow">{results.length} results found</h6>
<ul class="u-margin-block-start-8">
{#each results as hit, i (hit.uid)}
<li>
<a
href={createHref(hit)}
use:arrowKeyFocus={i}
class="aw-button aw-caption-400 is-text u-flex-vertical u-gap-8 u-min-width-100-percent aw-u-padding-block-4 aw-u-cross-start u-max-width-100-percent"
>
<div class="aw-u-trim-1">
<span class="aw-u-color-text-secondary">{hit.h1}</span>
{#if hit.h2}
<span class="aw-u-color-text-secondary"> / </span>
<span class="aw-u-color-text-primary">{hit.h2}</span>
{/if}
</div>
{#if hit.p}
<div class="u-inline aw-u-color-text-secondary aw-u-trim-1">
{hit.p}
</div>
{/if}
</a>
</li>
{/each}
</ul>
</section>
{:else}
<p class="aw-caption-400">No results found for <span class="u-bold">{value}</span></p>
{/if}
{/if}
<section>
<h6 class="aw-eyebrow">Recommended</h6>
<ul class="u-margin-block-start-8">
{#key results.length}
{#each recommended as hit, i (hit.uid)}
{@const index = i + (results.length ? results.length : 0)}
<li>
<a
href={createHref(hit)}
use:arrowKeyFocus={index}
class="aw-button aw-caption-400 is-text u-flex-vertical u-gap-8 u-min-width-100-percent aw-u-padding-block-4 aw-u-cross-start"
>
<div class="aw-u-trim-1">
<span class="aw-u-color-text-secondary">{hit.h1}</span>
{#if hit.h2}
<span class="aw-u-color-text-secondary"> / </span>
<span class="aw-u-color-text-primary">{hit.h2}</span>
{/if}
</div>
</a>
</li>
{/each}
{/key}
</ul>
</section>
</div>
</div>
</div>
</div>
{/if}
11 changes: 10 additions & 1 deletion src/lib/layouts/Docs.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
export type DocsLayoutState = {
showReferences: boolean;
showSidenav: boolean;
showSearch: boolean;
currentVariant: DocsLayoutVariant | null;
};
export const layoutState = writable<DocsLayoutState>({
showReferences: false,
showSidenav: false,
showSearch: false,
currentVariant: null
});
export function toggleReferences() {
Expand All @@ -30,6 +32,8 @@
</script>

<script lang="ts">
import Search from '$lib/components/Search.svelte';

export let variant: DocsLayoutVariant = 'default';

const variantClasses: Record<DocsLayoutVariant, string> = {
Expand Down Expand Up @@ -117,7 +121,10 @@
</ul>
</nav>
<div class="u-flex u-stretch aw-u-margin-inline-start-48">
<button class="aw-input-button aw-u-flex-basis-400">
<button
class="aw-input-button aw-u-flex-basis-400"
on:click={() => ($layoutState.showSearch = true)}
>
<span class="icon-search" aria-hidden="true" />
<span class="text">Search in docs</span>

Expand Down Expand Up @@ -165,3 +172,5 @@
<slot />
</div>
</div>

<Search bind:open={$layoutState.showSearch} />
5 changes: 4 additions & 1 deletion src/lib/layouts/Sidebar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@
class:is-transparent={$layoutState.currentVariant !== 'two-side-navs'}
>
<div class="aw-side-nav-wrapper">
<button class="aw-input-text aw-is-not-desktop">
<button
class="aw-input-text aw-is-not-desktop"
on:click={() => ($layoutState.showSearch = true)}
>
<span class="icon-search" />
<span class="text">Search in docs</span>
</button>
Expand Down
7 changes: 1 addition & 6 deletions src/markdoc/nodes/Link.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,4 @@
const rel = isExternal ? 'noopener nofollow' : undefined;
</script>

<a class="aw-link" {href} {title} {target} {rel}>
<slot />
{#if !isExternal}
<!-- <span class="icon-cheveron-right" /> -->
{/if}
</a>
<a class="aw-link" {href} {title} {target} {rel}><slot /></a>