mirror of
https://github.com/aleksilassila/reiverr.git
synced 2026-04-22 08:45:13 +02:00
Project Refactoring
This commit is contained in:
57
src/lib/components/Navbar/Navbar.svelte
Normal file
57
src/lib/components/Navbar/Navbar.svelte
Normal file
@@ -0,0 +1,57 @@
|
||||
<script lang="ts">
|
||||
import { MagnifyingGlass, Person } from 'radix-icons-svelte';
|
||||
import classNames from 'classnames';
|
||||
import { page } from '$app/stores';
|
||||
import TitleSearchModal from './TitleSearchModal.svelte';
|
||||
import IconButton from '../IconButton.svelte';
|
||||
|
||||
let y = 0;
|
||||
let transparent = true;
|
||||
let baseStyle = '';
|
||||
|
||||
let isSearchVisible = false;
|
||||
|
||||
function getLinkStyle(path: string) {
|
||||
return $page.url.pathname === path ? 'text-amber-200' : 'hover:text-zinc-50 cursor-pointer';
|
||||
}
|
||||
|
||||
$: {
|
||||
transparent = y <= 0;
|
||||
baseStyle = classNames(
|
||||
'fixed px-8 inset-x-0 grid grid-cols-[min-content_1fr_min-content] items-center z-10',
|
||||
'transition-all',
|
||||
{
|
||||
'bg-zinc-900 bg-opacity-50 backdrop-blur-2xl h-16': !transparent,
|
||||
'h-24': transparent
|
||||
}
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window bind:scrollY={y} />
|
||||
|
||||
<div class={baseStyle}>
|
||||
<a href="/" class="flex gap-2 items-center hover:text-inherit">
|
||||
<div class="rounded-full bg-amber-300 h-4 w-4" />
|
||||
<h1 class="font-display uppercase font-semibold tracking-wider text-xl">Reiverr</h1>
|
||||
</a>
|
||||
<div
|
||||
class="flex items-center justify-center gap-8 font-normal text-sm tracking-wider text-zinc-200"
|
||||
>
|
||||
<a href="/" class={$page && getLinkStyle('/')}>Home</a>
|
||||
<a href="/discover" class={$page && getLinkStyle('/discover')}>Discover</a>
|
||||
<a href="/library" class={$page && getLinkStyle('/library')}>Library</a>
|
||||
<a href="/sources" class={$page && getLinkStyle('/sources')}>Sources</a>
|
||||
<a href="/settings" class={$page && getLinkStyle('/settings')}>Settings</a>
|
||||
</div>
|
||||
<div class="flex gap-2 items-center">
|
||||
<IconButton on:click={() => (isSearchVisible = true)}>
|
||||
<MagnifyingGlass size={20} />
|
||||
</IconButton>
|
||||
<IconButton>
|
||||
<Person size={20} />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TitleSearchModal bind:visible={isSearchVisible} />
|
||||
87
src/lib/components/Navbar/TitleSearchModal.svelte
Normal file
87
src/lib/components/Navbar/TitleSearchModal.svelte
Normal file
@@ -0,0 +1,87 @@
|
||||
<script lang="ts">
|
||||
import Modal from '../Modal/Modal.svelte';
|
||||
import ModalContent from '../Modal/ModalContent.svelte';
|
||||
import { Cross1, Cross2, MagnifyingGlass } from 'radix-icons-svelte';
|
||||
import IconButton from '../IconButton.svelte';
|
||||
import { TmdbApi } from '$lib/apis/tmdbApi';
|
||||
import type { MultiSearchResponse } from '$lib/apis/tmdbApi';
|
||||
import { TMDB_IMAGES } from '$lib/constants';
|
||||
import ModalHeader from '../Modal/ModalHeader.svelte';
|
||||
export let visible = false;
|
||||
let searchValue = '';
|
||||
|
||||
export let close = () => {
|
||||
visible = false;
|
||||
searchValue = '';
|
||||
fetching = false;
|
||||
results = null;
|
||||
if (timeout) clearTimeout(timeout);
|
||||
timeout = undefined;
|
||||
};
|
||||
|
||||
let timeout;
|
||||
let fetching = false;
|
||||
let results: MultiSearchResponse['results'] | null = null;
|
||||
const searchTimeout = () => {
|
||||
if (timeout) clearTimeout(timeout);
|
||||
timeout = setTimeout(() => {
|
||||
searchMovie(searchValue);
|
||||
}, 700);
|
||||
};
|
||||
|
||||
const searchMovie = (query: string) => {
|
||||
fetching = true;
|
||||
TmdbApi.get<MultiSearchResponse>('/search/movie', {
|
||||
params: {
|
||||
query
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.data) results = res.data.results;
|
||||
})
|
||||
.finally(() => (fetching = false));
|
||||
};
|
||||
</script>
|
||||
|
||||
<Modal {visible} {close}>
|
||||
<ModalContent>
|
||||
<ModalHeader {close}>
|
||||
<MagnifyingGlass size="20" class="text-zinc-400" />
|
||||
<input
|
||||
bind:value={searchValue}
|
||||
on:input={searchTimeout}
|
||||
type="text"
|
||||
class="flex-1 bg-transparent font-light outline-none"
|
||||
placeholder="Search for Movies and Shows..."
|
||||
/>
|
||||
</ModalHeader>
|
||||
{#if !results || searchValue === ''}
|
||||
<div class="text-sm text-zinc-200 opacity-50 font-light p-4">No recent searches</div>
|
||||
{:else if results?.length === 0 && !fetching}
|
||||
<div class="text-sm text-zinc-200 opacity-50 font-light p-4">No search results</div>
|
||||
{:else}
|
||||
<div class="py-2">
|
||||
{#each results.filter((m) => m).slice(0, 5) as result}
|
||||
<div
|
||||
class="flex px-4 py-2 gap-4 hover:bg-highlight-dim cursor-pointer"
|
||||
on:click={() => window.open('/movie/' + result.id)}
|
||||
>
|
||||
<div
|
||||
style={"background-image: url('" + TMDB_IMAGES + result.poster_path + "');"}
|
||||
class="bg-center bg-cover w-16 h-24 rounded-sm"
|
||||
/>
|
||||
<div class="flex-1 flex flex-col gap-1">
|
||||
<div class="flex gap-2">
|
||||
<div class="font-normal tracking-wide">{result.original_title}</div>
|
||||
<div class="text-zinc-400">
|
||||
{new Date(result.release_date).getFullYear()}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-zinc-300 line-clamp-3">{result.overview}</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
Reference in New Issue
Block a user