Spaces:
Sleeping
Sleeping
File size: 3,767 Bytes
10b8070 33c382c 10b8070 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
<script lang="ts">
import { base } from "$app/paths";
import type { ToolLogoColor, ToolLogoIcon } from "$lib/types/Tool";
import { debounce } from "$lib/utils/debounce";
import { onMount } from "svelte";
import ToolLogo from "./ToolLogo.svelte";
import CarbonClose from "~icons/carbon/close";
interface ToolSuggestion {
_id: string;
displayName: string;
createdByName: string;
color: ToolLogoColor;
icon: ToolLogoIcon;
}
export let toolIds: string[] = [];
let selectedValues: ToolSuggestion[] = [];
onMount(async () => {
selectedValues = await Promise.all(
toolIds.map(async (id) => await fetch(`${base}/api/tools/${id}`).then((res) => res.json()))
);
await fetchSuggestions("");
});
let inputValue = "";
let maxValues = 3;
let suggestions: ToolSuggestion[] = [];
async function fetchSuggestions(query: string) {
suggestions = (await fetch(`${base}/api/tools/search?q=${query}`).then((res) =>
res.json()
)) satisfies ToolSuggestion[];
}
const debouncedFetch = debounce((query: string) => fetchSuggestions(query), 300);
function addValue(value: ToolSuggestion) {
if (selectedValues.length < maxValues && !selectedValues.includes(value)) {
selectedValues = [...selectedValues, value];
toolIds = [...toolIds, value._id];
inputValue = "";
suggestions = [];
}
}
function removeValue(id: ToolSuggestion["_id"]) {
selectedValues = selectedValues.filter((v) => v._id !== id);
toolIds = selectedValues.map((value) => value._id);
}
</script>
{#if selectedValues.length > 0}
<div class="flex flex-wrap items-center justify-center gap-2">
{#each selectedValues as value}
<div
class="flex items-center justify-center space-x-2 rounded border border-gray-300 bg-gray-200 px-2 py-1"
>
<ToolLogo color={value.color} icon={value.icon} size="sm" />
<div class="flex flex-col items-center justify-center py-1">
<a
href={`${base}/tools/${value._id}`}
target="_blank"
class="line-clamp-1 truncate font-semibold text-blue-600 hover:underline"
>{value.displayName}</a
>
{#if value.createdByName}
<p class="text-center text-xs text-gray-500">
Created by
<a class="underline" href="{base}/tools?user={value.createdByName}" target="_blank"
>{value.createdByName}</a
>
</p>
{:else}
<p class="text-center text-xs text-gray-500">Official HuggingChat tool</p>
{/if}
</div>
<button
on:click|stopPropagation|preventDefault={() => removeValue(value._id)}
class="text-lg text-gray-600"
>
<CarbonClose />
</button>
</div>
{/each}
</div>
{/if}
{#if selectedValues.length < maxValues}
<div class="group relative block">
<input
type="text"
bind:value={inputValue}
on:input={(ev) => {
inputValue = ev.currentTarget.value;
debouncedFetch(inputValue);
}}
disabled={selectedValues.length >= maxValues}
class="w-full rounded border border-gray-200 bg-gray-100 px-3 py-2"
class:opacity-50={selectedValues.length >= maxValues}
class:bg-gray-100={selectedValues.length >= maxValues}
placeholder="Type to search tools..."
/>
{#if suggestions.length > 0}
<div
class="invisible absolute z-10 mt-1 w-full rounded border border-gray-300 bg-white shadow-lg group-focus-within:visible"
>
{#each suggestions as suggestion}
<button
on:click|stopPropagation|preventDefault={() => addValue(suggestion)}
class="w-full cursor-pointer px-3 py-2 text-left hover:bg-blue-500 hover:text-white"
>
{suggestion.displayName}
{#if suggestion.createdByName}
<span class="text-xs text-gray-500"> by {suggestion.createdByName}</span>
{/if}
</button>
{/each}
</div>
{/if}
</div>
{/if}
|