mirror of
https://github.com/pesde-pkg/pesde.git
synced 2025-05-04 10:33:47 +01:00
169 lines
5.2 KiB
Svelte
169 lines
5.2 KiB
Svelte
<script lang="ts">
|
|
import { goto } from "$app/navigation"
|
|
import { TARGET_KIND_DISPLAY_NAMES } from "$lib/registry-api.js"
|
|
import { Pagination } from "bits-ui"
|
|
import { formatDistanceToNow } from "date-fns"
|
|
import { ChevronLeft, ChevronRight, SearchX } from "lucide-svelte"
|
|
|
|
const { data } = $props()
|
|
|
|
let displayDates = $state(false)
|
|
|
|
$effect(() => {
|
|
displayDates = true
|
|
})
|
|
</script>
|
|
|
|
<div class="mx-auto max-w-screen-lg px-4">
|
|
{#await data.result}
|
|
<div class="sr-only">Loading...</div>
|
|
<div
|
|
class="my-8 flex flex-col sm:flex-row sm:items-center sm:justify-between"
|
|
aria-hidden="true"
|
|
>
|
|
<div class="bg-card-hover w-20 animate-pulse rounded text-transparent">...</div>
|
|
<div class="bg-card-hover mx-auto mt-8 h-8 w-48 animate-pulse rounded sm:m-0"></div>
|
|
</div>
|
|
<div class="mb-8 space-y-4">
|
|
{#each Array.from({ length: 10 }).map((_, i) => i) as i}
|
|
<div class="bg-card/50 overflow-hidden rounded px-5 py-4">
|
|
<div class="mb-1 flex items-center justify-between">
|
|
<div class="bg-card-hover w-52 animate-pulse rounded text-xl text-transparent">...</div>
|
|
<div
|
|
class="bg-card-hover hidden w-32 animate-pulse rounded text-sm text-transparent sm:block"
|
|
>
|
|
...
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
class="bg-card-hover mb-3 w-96 max-w-full animate-pulse rounded text-sm text-transparent"
|
|
>
|
|
...
|
|
</div>
|
|
|
|
<div class="bg-card-hover w-16 max-w-full animate-pulse rounded text-sm text-transparent">
|
|
...
|
|
</div>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
<div class="bg-card-hover mx-auto my-8 h-8 w-48 animate-pulse rounded"></div>
|
|
{:then result}
|
|
{#if result.data.length === 0}
|
|
<div class="flex flex-col items-center py-32 text-center">
|
|
<SearchX class="mb-6 size-16" />
|
|
<h1 class="text-heading font-bold">No results</h1>
|
|
<p class="text-balance">We didn't find any packages matching your search query.</p>
|
|
</div>
|
|
{:else}
|
|
{#snippet pagination()}
|
|
<Pagination.Root
|
|
count={result.count}
|
|
page={data.page}
|
|
perPage={data.pageSize}
|
|
onPageChange={(page) => {
|
|
const params = new URLSearchParams()
|
|
params.set("q", data.query)
|
|
params.set("page", page.toString())
|
|
|
|
goto(`/search?${params}`)
|
|
}}
|
|
let:pages
|
|
>
|
|
<div class="flex items-center space-x-1">
|
|
<Pagination.PrevButton
|
|
class="hover:enabled:bg-card-hover inline-flex size-8 items-center justify-center rounded transition disabled:opacity-50"
|
|
>
|
|
<ChevronLeft />
|
|
</Pagination.PrevButton>
|
|
{#each pages as page (page.key)}
|
|
{#if page.type === "ellipsis"}
|
|
<div class="px-2">...</div>
|
|
{:else}
|
|
<Pagination.Page
|
|
{page}
|
|
class="hover:bg-card-hover hover:text-heading data-[selected]:bg-primary-bg data-[selected]:text-primary-fg inline-flex size-8 items-center justify-center rounded font-bold transition"
|
|
>
|
|
{page.value}
|
|
</Pagination.Page>
|
|
{/if}
|
|
{/each}
|
|
<Pagination.NextButton
|
|
class="hover:enabled:bg-card-hover inline-flex size-8 items-center justify-center rounded transition disabled:opacity-50"
|
|
>
|
|
<ChevronRight />
|
|
</Pagination.NextButton>
|
|
</div>
|
|
</Pagination.Root>
|
|
{/snippet}
|
|
|
|
<div class="my-8 flex flex-col sm:flex-row sm:items-center sm:justify-between">
|
|
<h1 class="font-bold">{result.count} {result.count > 1 ? "results" : "result"}</h1>
|
|
<div class="mx-auto mt-8 sm:m-0">
|
|
{@render pagination()}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-8 space-y-4">
|
|
{#each result.data as pkg}
|
|
{@const [scope, name] = pkg.name.split("/")}
|
|
|
|
{#snippet timeAndVersion()}
|
|
<time datetime={pkg.published_at}>
|
|
{#if displayDates}
|
|
{formatDistanceToNow(new Date(pkg.published_at), { addSuffix: true })}
|
|
{:else}
|
|
...
|
|
{/if}
|
|
</time>
|
|
<span>{" · "}</span>
|
|
<span class="truncate">v{pkg.version}</span>
|
|
{/snippet}
|
|
|
|
<article
|
|
class="bg-card hover:bg-card-hover relative overflow-hidden rounded px-5 py-4 transition"
|
|
>
|
|
<div class="mb-1 flex items-center justify-between">
|
|
<h3 class="truncate text-xl font-semibold">
|
|
<a
|
|
href={`/packages/${pkg.name}`}
|
|
class="after:absolute after:inset-0 after:content-['']"
|
|
>
|
|
<span class="text-heading">{scope}/</span><span class="text-light">{name}</span>
|
|
</a>
|
|
</h3>
|
|
<div
|
|
class="text-heading hidden text-sm font-semibold sm:block"
|
|
class:invisible={!displayDates}
|
|
>
|
|
{@render timeAndVersion()}
|
|
</div>
|
|
</div>
|
|
|
|
<p class="mb-3 h-[1lh] overflow-hidden truncate text-sm">{pkg.description}</p>
|
|
|
|
<div
|
|
class={`text-primary text-sm font-bold ${displayDates ? "" : "invisible sm:visible"}`}
|
|
>
|
|
{pkg.targets.map((target) => TARGET_KIND_DISPLAY_NAMES[target.kind]).join(", ")}
|
|
<span class="sm:hidden">
|
|
<span>{" · "}</span>
|
|
{@render timeAndVersion()}
|
|
</span>
|
|
</div>
|
|
</article>
|
|
{/each}
|
|
</div>
|
|
|
|
<div class="mx-auto my-8 max-w-min">
|
|
{@render pagination()}
|
|
</div>
|
|
{/if}
|
|
{:catch error}
|
|
<div class="mx-auto max-w-screen-xl px-4 py-32 text-center">
|
|
<h1 class="text-heading mb-1 text-4xl font-bold">Error</h1>
|
|
<p class="text-lg">{error.message}</p>
|
|
</div>
|
|
{/await}
|
|
</div>
|