Spaces:
Running
Running
| <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.id} class="w-6 h-6 rounded-lg object-cover" /> | |
| <p class="text-neutral-200 text-base font-medium">{value.id}</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> |