mirror of
https://github.com/ThaUnknown/miru.git
synced 2026-04-06 01:39:55 +00:00
feat: mobile sidebar
This commit is contained in:
parent
6616b9347e
commit
3846de3d9d
9 changed files with 110 additions and 75 deletions
|
|
@ -15,6 +15,8 @@
|
|||
let isFlying = false
|
||||
let timeout: number
|
||||
|
||||
// WE LOVE RACE CONDITIONS WOOOO YEAAH MY SANITY
|
||||
|
||||
async function start () {
|
||||
if (isAnimating) return
|
||||
isAnimating = true
|
||||
|
|
@ -52,13 +54,14 @@
|
|||
if (lockedState === 'locked' || visibilityState === 'hidden') return reset()
|
||||
if (idleState === 'active' && activityState === 'active') return reset()
|
||||
if ($page.url.pathname === '/app/player/') return reset()
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(start, 20_000)
|
||||
}
|
||||
|
||||
$: checkIdleState(idleState, lockedState, activityState, visibilityState)
|
||||
</script>
|
||||
|
||||
<svelte:document bind:visibilityState on:mouseleave={() => { if (!document.hasFocus()) activityState = 'inactive' }} on:mouseenter={() => { console.log('uwu'); activityState = 'active' }} />
|
||||
<svelte:document bind:visibilityState on:mouseleave={() => { if (!document.hasFocus()) activityState = 'inactive' }} on:mouseenter={() => { activityState = 'active' }} />
|
||||
<svelte:window on:focus={() => { activityState = 'active' }} on:blur={() => { activityState = 'inactive' }}
|
||||
on:pointermove={() => { idleState = 'active'; activityState = 'active' }} />
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@
|
|||
</script>
|
||||
|
||||
<Pagination count={episodeCount} {perPage} bind:currentPage let:pages let:hasNext let:hasPrev let:range let:setPage siblingCount={1}>
|
||||
<div class='overflow-y-auto pt-3 -mx-14 px-14 pointer-events-none'>
|
||||
<div class='overflow-y-auto pt-3 -mx-14 px-14 pointer-events-none -mb-3 pb-3'>
|
||||
<div class='grid grid-cols-1 sm:grid-cols-[repeat(auto-fit,minmax(500px,1fr))] place-items-center gap-x-10 gap-y-7 justify-center align-middle pointer-events-auto'>
|
||||
{#each getPage(currentPage) as { episode, image, title, summary, airingAt, airdate, filler, length } (episode)}
|
||||
{@const watched = _progress >= episode}
|
||||
|
|
|
|||
|
|
@ -52,5 +52,7 @@
|
|||
</Dialog.Header>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
<Switch bind:checked={$exopts[config.id].enabled} hideState={true} />
|
||||
{#if $exopts[config.id]}
|
||||
<Switch bind:checked={$exopts[config.id].enabled} hideState={true} />
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
$: isActive = matchPath(href, $page)
|
||||
</script>
|
||||
|
||||
<Button variant='ghost' {href} class={cn(className, 'px-2 w-full relative')} {...$$restProps}>
|
||||
<Button variant='ghost' {href} class={cn(className, 'px-2 w-10 relative')} {...$$restProps}>
|
||||
{#if isActive}
|
||||
<div class='bg-white absolute inset-0 rounded-md' in:send={{ key }} out:receive={{ key }} />
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import Root from './sidebar.svelte'
|
||||
|
||||
export {
|
||||
Root as Sidebar
|
||||
Root as Sidebar
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,71 +1,26 @@
|
|||
<script lang='ts'>
|
||||
import { Home, Search, Calendar, Users, MessagesSquare, Heart, Settings, LogIn } from 'lucide-svelte'
|
||||
import Hub from '$lib/components/icons/Hub.svelte'
|
||||
import SidebarButton from './SidebarButton.svelte'
|
||||
import { BannerImage } from '../banner'
|
||||
import native from '$lib/modules/native'
|
||||
import { isMobile } from '$lib/utils'
|
||||
import { Menu, X } from 'lucide-svelte'
|
||||
import { Button } from '../button'
|
||||
import client from '$lib/modules/auth/client'
|
||||
import * as Avatar from '$lib/components/ui/avatar'
|
||||
|
||||
const auth = client.hasAuth
|
||||
|
||||
$: hasAuth = $auth
|
||||
let open = false // 152 x 140
|
||||
</script>
|
||||
|
||||
<div class='w-14 p-2 flex flex-col z-10 shrink-0 bg-black'>
|
||||
<BannerImage class='absolute top-0 left-0 w-14 -z-10' />
|
||||
<img src='/logo_cropped.png' alt='logo' class='mb-3 cursor-pointer h-10 object-contain px-1' loading='lazy' decoding='async' />
|
||||
<SidebarButton class='mb-2' href='/app/home/'>
|
||||
<Home size={18} />
|
||||
</SidebarButton>
|
||||
<SidebarButton class='mb-2' href='/app/search/'>
|
||||
<Search size={18} />
|
||||
</SidebarButton>
|
||||
<SidebarButton class='mb-2' href='/app/schedule/'>
|
||||
<Calendar size={18} />
|
||||
</SidebarButton>
|
||||
<SidebarButton class='mb-2' href='/app/w2g/'>
|
||||
<Users size={18} />
|
||||
</SidebarButton>
|
||||
<SidebarButton class='mb-2' href='/app/chat/'>
|
||||
<MessagesSquare size={18} />
|
||||
</SidebarButton>
|
||||
<SidebarButton class='mb-2' href='/app/client/'>
|
||||
<Hub size={18} fill='currentColor' />
|
||||
</SidebarButton>
|
||||
<Button variant='ghost' on:click={() => native.openURL('https://github.com/sponsors/ThaUnknown/')} class='px-2 w-full relative mb-2 mt-auto select:!bg-transparent text-[#fa68b6] select:text-[#fa68b6]'>
|
||||
<Heart size={18} fill='currentColor' class='absolute' />
|
||||
<Heart size={18} fill='currentColor' class='donate' />
|
||||
</Button>
|
||||
<SidebarButton class='mb-2' href='/app/settings/'>
|
||||
<Settings size={18} />
|
||||
</SidebarButton>
|
||||
<SidebarButton href='/app/profile/'>
|
||||
{#if hasAuth}
|
||||
{@const viewer = client.profile()}
|
||||
<Avatar.Root class='size-6 rounded-md'>
|
||||
<Avatar.Image src={viewer?.avatar?.medium ?? ''} alt={viewer?.name} />
|
||||
<Avatar.Fallback>{viewer?.name}</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
{:else}
|
||||
<LogIn size={18} />
|
||||
{/if}
|
||||
</SidebarButton>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
:global(.donate) {
|
||||
filter: drop-shadow(0 0 1rem #fa68b6);
|
||||
animation: glow 1s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes glow {
|
||||
from {
|
||||
transform: translate3d(var(--tw-translate-x), var(--tw-translate-y), 0) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(1) scaleY(1);
|
||||
}
|
||||
to {
|
||||
transform: translate3d(var(--tw-translate-x), var(--tw-translate-y), 0) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(0.5) scaleY(0.5);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{#if $isMobile}
|
||||
<div class='shrink-0 z-50 bg-black absolute right-4 bottom-4 w-14 h-[52px] flex rounded-md items-end justify-end overflow-clip transition-[width,height]' class:w-[152px]={open} class:h-[140px]={open}>
|
||||
<div class='p-2 grid grid-cols-3 gap-2 shrink-0'>
|
||||
<slot />
|
||||
<Button variant='ghost' class='px-2 w-full relative' on:click={() => { open = !open }}>
|
||||
{#if open}
|
||||
<X size={18} fill='currentColor' />
|
||||
{:else}
|
||||
<Menu size={18} fill='currentColor' />
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class='w-14 p-2 flex flex-col z-10 shrink-0 bg-black gap-2'>
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
69
src/lib/components/ui/sidebar/sidebarlist.svelte
Normal file
69
src/lib/components/ui/sidebar/sidebarlist.svelte
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<script lang='ts'>
|
||||
import { Home, Search, Calendar, Users, MessagesSquare, Heart, Settings, LogIn } from 'lucide-svelte'
|
||||
import Hub from '$lib/components/icons/Hub.svelte'
|
||||
import SidebarButton from './SidebarButton.svelte'
|
||||
import { BannerImage } from '../banner'
|
||||
import native from '$lib/modules/native'
|
||||
import { Button } from '../button'
|
||||
import client from '$lib/modules/auth/client'
|
||||
import * as Avatar from '$lib/components/ui/avatar'
|
||||
|
||||
const auth = client.hasAuth
|
||||
|
||||
$: hasAuth = $auth
|
||||
</script>
|
||||
|
||||
<BannerImage class='absolute top-0 left-0 w-14 -z-10 hidden md:block' />
|
||||
<img src='/logo_cropped.png' alt='logo' class='mb-3 cursor-pointer h-10 object-contain px-1 hidden md:block' loading='lazy' decoding='async' />
|
||||
<SidebarButton href='/app/home/'>
|
||||
<Home size={18} />
|
||||
</SidebarButton>
|
||||
<SidebarButton href='/app/search/'>
|
||||
<Search size={18} />
|
||||
</SidebarButton>
|
||||
<SidebarButton href='/app/schedule/'>
|
||||
<Calendar size={18} />
|
||||
</SidebarButton>
|
||||
<SidebarButton href='/app/w2g/'>
|
||||
<Users size={18} />
|
||||
</SidebarButton>
|
||||
<SidebarButton href='/app/chat/'>
|
||||
<MessagesSquare size={18} />
|
||||
</SidebarButton>
|
||||
<SidebarButton href='/app/client/'>
|
||||
<Hub size={18} fill='currentColor' />
|
||||
</SidebarButton>
|
||||
<Button variant='ghost' on:click={() => native.openURL('https://github.com/sponsors/ThaUnknown/')} class='px-2 w-full relative mt-auto select:!bg-transparent text-[#fa68b6] select:text-[#fa68b6]'>
|
||||
<Heart size={18} fill='currentColor' class='absolute' />
|
||||
<Heart size={18} fill='currentColor' class='donate' />
|
||||
</Button>
|
||||
<SidebarButton href='/app/settings/'>
|
||||
<Settings size={18} />
|
||||
</SidebarButton>
|
||||
<SidebarButton href='/app/profile/' class='hidden md:block'>
|
||||
{#if hasAuth}
|
||||
{@const viewer = client.profile()}
|
||||
<Avatar.Root class='size-6 rounded-md'>
|
||||
<Avatar.Image src={viewer?.avatar?.medium ?? ''} alt={viewer?.name} />
|
||||
<Avatar.Fallback>{viewer?.name}</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
{:else}
|
||||
<LogIn size={18} />
|
||||
{/if}
|
||||
</SidebarButton>
|
||||
|
||||
<style>
|
||||
:global(.donate) {
|
||||
filter: drop-shadow(0 0 1rem #fa68b6);
|
||||
animation: glow 1s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes glow {
|
||||
from {
|
||||
transform: translate3d(var(--tw-translate-x), var(--tw-translate-y), 0) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(1) scaleY(1);
|
||||
}
|
||||
to {
|
||||
transform: translate3d(var(--tw-translate-x), var(--tw-translate-y), 0) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(0.5) scaleY(0.5);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -48,6 +48,10 @@ export function coverMedium (media: Pick<Media, 'trailer' | 'bannerImage' | 'cov
|
|||
return media.coverImage?.medium?.replace('/small/', '/medium/') ?? banner(media)
|
||||
}
|
||||
|
||||
export function coverSmall (media: Pick<Media, 'trailer' | 'bannerImage' | 'coverImage'>): string | undefined {
|
||||
return media.coverImage?.medium ?? banner(media)
|
||||
}
|
||||
|
||||
export function title (media: Pick<Media, 'title'>): string {
|
||||
return media.title?.userPreferred ?? 'TBA'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
<script lang='ts'>
|
||||
import { BannerImage } from '$lib/components/ui/banner'
|
||||
import { isMobile } from '$lib/utils'
|
||||
import { Sidebar } from '$lib/components/ui/sidebar'
|
||||
import SearchModal from '$lib/components/SearchModal.svelte'
|
||||
import Sidebarlist from '$lib/components/ui/sidebar/sidebarlist.svelte'
|
||||
</script>
|
||||
|
||||
<BannerImage class='absolute top-0 left-0' />
|
||||
<SearchModal />
|
||||
<div class='flex flex-row grow h-full overflow-hidden relative'>
|
||||
{#if !$isMobile}
|
||||
<Sidebar />
|
||||
{/if}
|
||||
|
||||
<Sidebar>
|
||||
<Sidebarlist />
|
||||
</Sidebar>
|
||||
|
||||
<slot />
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue