[READ-ONLY] Mirror of https://github.com/flo-bit/skywatched. review movies and tv shows, based on at proto
skywatched.app
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}