[READ-ONLY] Mirror of https://github.com/flo-bit/skywatched. review movies and tv shows, based on at proto skywatched.app
0

Configure Feed

Select the types of activity you want to include in your feed.

skywatched / src / lib / Components / UI / SearchCombobox.svelte
4.2 kB 145 lines
1<script lang="ts"> 2 import { rateMovieModal } from '$lib/state/modals.svelte'; 3 import { watchedItems } from '$lib/state/user.svelte'; 4 import type { Item } from '$lib/types'; 5 import { createCombobox, melt } from '@melt-ui/svelte'; 6 import { fly } from 'svelte/transition'; 7 8 const { 9 elements: { menu, input, option, label }, 10 states: { open, inputValue, touchedInput }, 11 helpers: { isSelected } 12 } = createCombobox({ 13 forceVisible: true 14 }); 15 16 let debounceTimer: ReturnType<typeof setTimeout>; 17 18 const debounce = (callback: () => void) => { 19 clearTimeout(debounceTimer); 20 debounceTimer = setTimeout(callback, 500); 21 }; 22 23 let results: Item[] = []; 24 25 let searching = false; 26 27 function onInput() { 28 if ($inputValue.length < 2) { 29 results = []; 30 } else { 31 searching = true; 32 debounce(async () => { 33 if ($inputValue.length < 2) return; 34 35 const response = await fetch(`/api/search?q=${$inputValue}`); 36 const data = await response.json(); 37 results = data; 38 39 searching = false; 40 }); 41 } 42 } 43</script> 44 45<div class="flex flex-col gap-1"> 46 <!-- svelte-ignore a11y-label-has-associated-control - $label contains the 'for' attribute --> 47 <!-- <label use:melt={$label}> 48 <span class="text-sm font-medium text-accent-900">Find a movie or show:</span> 49 </label> --> 50 51 <div class="relative"> 52 <input 53 use:melt={$input} 54 class="flex h-10 w-full items-center justify-between rounded-lg border-base-800 bg-black px-3 pr-12 text-base-50 55 placeholder:text-base-400 focus:border-none focus:outline-none focus:ring-2 focus:ring-accent-500" 56 placeholder="Find a movie or show" 57 oninput={onInput} 58 /> 59 <div class="absolute right-2 top-1/2 z-10 -translate-y-1/2 text-accent-500"> 60 {#if $open && $inputValue.length > 1} 61 <svg 62 xmlns="http://www.w3.org/2000/svg" 63 fill="none" 64 viewBox="0 0 24 24" 65 stroke-width="1.5" 66 stroke="currentColor" 67 class="size-4" 68 > 69 <path stroke-linecap="round" stroke-linejoin="round" d="m4.5 15.75 7.5-7.5 7.5 7.5" /> 70 </svg> 71 {:else} 72 <svg 73 xmlns="http://www.w3.org/2000/svg" 74 fill="none" 75 viewBox="0 0 24 24" 76 stroke-width="1.5" 77 stroke="currentColor" 78 class="size-4" 79 > 80 <path 81 stroke-linecap="round" 82 stroke-linejoin="round" 83 d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" 84 /> 85 </svg> 86 {/if} 87 </div> 88 </div> 89</div> 90{#if $open && $inputValue.length > 1} 91 <ul 92 class=" z-50 flex max-h-[300px] flex-col overflow-hidden rounded-lg" 93 use:melt={$menu} 94 transition:fly={{ duration: 150, y: -5 }} 95 > 96 <!-- svelte-ignore a11y-no-noninteractive-tabindex --> 97 <div 98 class="mx-2 flex max-h-full flex-col gap-0 divide-y divide-base-700/50 overflow-y-auto rounded-lg border border-base-700 bg-base-800 px-2 py-2 text-base-50" 99 tabindex="0" 100 > 101 {#each results as item, index (index)} 102 <li 103 use:melt={$option({ 104 value: item, 105 label: item.title 106 })} 107 class="relative w-full cursor-pointer scroll-my-2 px-2 py-2 data-[highlighted]:text-accent-200" 108 > 109 <button 110 onclick={() => { 111 rateMovieModal.selectedItem = { 112 ...item, 113 currentRating: watchedItems.getRating(item)?.rating ?? undefined, 114 currentReview: watchedItems.getRating(item)?.ratingText ?? undefined 115 }; 116 rateMovieModal.showModal = true; 117 }} 118 class="flex w-full items-center justify-start gap-2" 119 > 120 <div 121 class="relative z-20 aspect-[2/3] h-12 w-auto shrink-0 overflow-hidden rounded-md border border-base-800 bg-base-900/50" 122 > 123 <img 124 src="https://image.tmdb.org/t/p/w154{item.poster_path}" 125 alt="movie poster for {item.title}" 126 class="size-full object-cover object-center lg:size-full" 127 /> 128 </div> 129 <span class="text-left text-sm font-medium">{item.title}</span> 130 </button> 131 </li> 132 {:else} 133 <li 134 class="relative cursor-pointer rounded-md py-1 pl-4 pr-4 text-sm data-[highlighted]:text-accent-200" 135 > 136 {#if searching} 137 Searching... 138 {:else if $inputValue.length > 1} 139 No results found 140 {/if} 141 </li> 142 {/each} 143 </div> 144 </ul> 145{/if}