Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
<script lang="ts"> | |
import { clickoutside } from '@svelte-put/clickoutside'; | |
import type { ModelCard } from "$lib/type"; | |
import Icon from "@iconify/svelte"; | |
import Item from "./Item.svelte"; | |
export let defaultModels: ModelCard[] = []; | |
export let onChange: (model: ModelCard | null) => void; | |
export let value: ModelCard | null = null; | |
let models: ModelCard[] = []; | |
let search: string = ""; | |
let open: boolean = false; | |
const handleSearch = (event: any) => { | |
search = event.target.value; | |
if (search.length >= 3) { | |
open = true; | |
onSearch(); | |
} else { | |
models = [] | |
} | |
} | |
const onSearch = async () => { | |
const request = await fetch(`/api/models?search=${search}`); | |
const response = await request.json(); | |
if (response?.cards && response?.cards.length > 0) { | |
models = response.cards; | |
} else { | |
models = []; | |
} | |
} | |
const handleClickOutside = (event: any) => { | |
if (event.target === event.currentTarget) { | |
open = false; | |
} | |
} | |
</script> | |
<!-- svelte-ignore a11y-click-events-have-key-events --> | |
<!-- svelte-ignore a11y-no-static-element-interactions --> | |
<div | |
class="w-full bg-neutral-900 rounded-xl border border-neutral-800 p-4 text-neutral-500 focus-within:border-neutral-700 transition-all duration-200 relative" | |
use:clickoutside on:clickoutside={() => open = false} | |
on:click={handleClickOutside} | |
> | |
{#if value} | |
<div class="flex items-center justify-between gap-4"> | |
<div class="flex items-center justify-start gap-3"> | |
<img src={value.image} alt={value.title} class="w-6 h-6 rounded-lg object-cover" /> | |
<p class="text-neutral-200 text-base font-medium">{value.title}</p> | |
</div> | |
<button on:click={() => onChange(null)}> | |
<Icon icon="maki:cross" class="w-4 h-4 text-neutral-500 transition-all duration-200 cursor-pointer" /> | |
</button> | |
</div> | |
{:else} | |
<div class="flex items-center justify-start gap-2 group"> | |
<Icon icon="lucide:search" class="w-5 h-5 text-neutral-500 group-focus-within:text-neutral-100 transition-all duration-200" /> | |
<input | |
value={search} | |
type="text" | |
class="w-full bg-transparent text-neutral-200 placeholder:text-neutral-500 outline-none" | |
placeholder="Filter by model name " | |
on:focus={() => open = true} | |
on:input={handleSearch} | |
/> | |
</div> | |
{/if} | |
<div | |
class="w-full absolute bottom-0 left-0 p-2 bg-neutral-900 rounded-xl translate-y-[calc(100%+12px)] border border-neutral-800 transition-all duration-200 z-10" | |
class:opacity-0="{!open}" | |
class:pointer-events-none="{!open}" | |
> | |
{#if search?.length >= 3} | |
{#if models?.length > 0} | |
{#each models as model} | |
<Item | |
model={model} | |
onClick={() => { | |
open = false; | |
onChange(model) | |
}} | |
/> | |
{/each} | |
{:else} | |
<div class="flex items-center justify-center flex-col gap-2 p-3"> | |
<Icon icon="bxs:sad" class="w-12 h-12 text-neutral-500 transition-all duration-200" /> | |
<p class="text-neutral-500 text-base">No models found</p> | |
</div> | |
{/if} | |
{:else} | |
{#each defaultModels as model} | |
<Item | |
model={model} | |
onClick={() => { | |
open = false; | |
onChange(model) | |
}} | |
/> | |
{/each} | |
{/if} | |
</div> | |
</div> |