mirror of
https://github.com/ThaUnknown/miru.git
synced 2026-04-19 07:32:11 +00:00
fix: dont allow back navigation on setup
fix: togglegroup navigation in settings fix: combobox navigation fix: schedule skeletonloader on android fix: disable autofocus on modals fix: episodelist overflowing fix: mobile player buttons not working on android
This commit is contained in:
parent
39aed8afb8
commit
0ed81a4c07
13 changed files with 56 additions and 42 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ui",
|
||||
"version": "6.4.52",
|
||||
"version": "6.4.53",
|
||||
"license": "BUSL-1.1",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.15.5",
|
||||
|
|
|
|||
|
|
@ -56,7 +56,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 -mb-3 pb-3' use:dragScroll>
|
||||
<div class='overflow-y-auto pt-3 -ml-14 pl-14 -mr-3 pr-3 pointer-events-none -mb-3 pb-3' use:dragScroll>
|
||||
<div class='grid grid-cols-1 sm:grid-cols-[repeat(auto-fit,minmax(500px,1fr))] place-items-center gap-x-4 gap-y-7 justify-center align-middle pointer-events-auto'>
|
||||
{#each getPage(currentPage, episodeList) as { episode, image, title, summary, airingAt, airdate, filler, length } (episode)}
|
||||
{@const watched = _progress >= episode && !completed}
|
||||
|
|
|
|||
|
|
@ -162,7 +162,9 @@
|
|||
</script>
|
||||
|
||||
<Dialog.Root bind:open onOpenChange={close} portal='#episodeListTarget'>
|
||||
<Dialog.Content class='bg-black h-full lg:border-x-4 border-b-0 max-w-5xl w-full max-h-[calc(100%-1rem)] mt-2 p-0 items-center flex lg:rounded-t-xl overflow-hidden z-[100]'>
|
||||
<Dialog.Content class='bg-black h-full lg:border-x-4 border-b-0 max-w-5xl w-full max-h-[calc(100%-1rem)] mt-2 p-0 items-center flex-col flex lg:rounded-t-xl overflow-hidden z-[100]'>
|
||||
<!-- this hacky thing is required for dialog root focus trap... pitiful -->
|
||||
<div class='size-0' tabindex='0' />
|
||||
{#if $searchStore}
|
||||
<div class='absolute top-0 left-0 w-full h-full max-h-28 overflow-hidden'>
|
||||
<Banner media={$searchStore.media} class='object-cover w-full h-full absolute bottom-[0.5px] left-0 -z-10' />
|
||||
|
|
@ -173,7 +175,6 @@
|
|||
<div class='font-weight-bold text-2xl font-bold text-ellipsis text-nowrap overflow-hidden pb-2'>{title($searchStore.media) ?? ''}</div>
|
||||
<div class='flex items-center relative scale-parent'>
|
||||
<Input
|
||||
autofocus={false}
|
||||
class='pl-9 bg-background select:bg-accent select:text-accent-foreground shadow-sm no-scale placeholder:opacity-50'
|
||||
placeholder='Filter by text, or paste a magnet link or torrent file to specify a torrent manually'
|
||||
bind:value={inputText} />
|
||||
|
|
@ -197,7 +198,7 @@
|
|||
Auto Select Torrent
|
||||
</ProgressButton>
|
||||
</div>
|
||||
<div class='h-full overflow-y-auto px-4 sm:px-6 pt-2' role='menu' tabindex='-1' on:keydown={stopAnimation} on:pointerenter={stopAnimation} on:pointermove={stopAnimation} use:dragScroll>
|
||||
<div class='h-full overflow-y-auto px-4 sm:px-6 pt-2' role='menu' tabindex='-1' on:keydown={stopAnimation} on:focusin={stopAnimation} on:pointerenter={stopAnimation} on:pointermove={stopAnimation} use:dragScroll>
|
||||
{#await Promise.all([searchResult, $downloaded])}
|
||||
{#each Array.from({ length: 12 }) as _, i (i)}
|
||||
<div class='p-3 h-[104px] flex cursor-pointer mb-2 relative rounded-md overflow-hidden border border-border flex-col justify-between'>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
import { Button } from '$lib/components/ui/button'
|
||||
import * as Command from '$lib/components/ui/command'
|
||||
import * as Popover from '$lib/components/ui/popover'
|
||||
import { inputType } from '$lib/modules/navigate'
|
||||
import { inputType, navigate } from '$lib/modules/navigate'
|
||||
import { cn } from '$lib/utils.js'
|
||||
|
||||
export let items: readonly value[] = []
|
||||
|
|
@ -84,8 +84,10 @@
|
|||
</Button>
|
||||
</Popover.Trigger>
|
||||
<Popover.Content class={cn('p-0 border-0 z-[1000]')} sameWidth={true}>
|
||||
<Command.Root>
|
||||
<Command.Input {placeholder} class='h-9 placeholder:opacity-50' />
|
||||
<Command.Root onKeydown={navigate}>
|
||||
<!-- this hacky thing is required for dialog root focus trap... pitiful -->
|
||||
<div class='h-0 w-full' tabindex='0' />
|
||||
<Command.Input {placeholder} autofocus={false} class='h-9 placeholder:opacity-50' />
|
||||
<Command.Empty>No results found.</Command.Empty>
|
||||
{#if $inputType === 'dpad'}
|
||||
<Command.Group class='shrink-0' alwaysRender={true}>
|
||||
|
|
|
|||
|
|
@ -1,25 +1,30 @@
|
|||
<script lang='ts'>
|
||||
import { Command as CommandPrimitive } from 'cmdk-sv'
|
||||
|
||||
import { keywrap } from '$lib/modules/navigate'
|
||||
import { cn } from '$lib/utils.js'
|
||||
|
||||
type $$Props = CommandPrimitive.ItemProps
|
||||
|
||||
export let asChild = false
|
||||
|
||||
let className: $$Props['class'] = ''
|
||||
export { className as class }
|
||||
|
||||
function click (e: KeyboardEvent) {
|
||||
const target = e.target as HTMLElement
|
||||
target.click()
|
||||
}
|
||||
</script>
|
||||
|
||||
<CommandPrimitive.Item
|
||||
{asChild}
|
||||
class={cn(
|
||||
'aria-selected:bg-accent aria-selected:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
asChild={true}
|
||||
let:action
|
||||
let:attrs
|
||||
>
|
||||
<slot {action} {attrs} />
|
||||
<div {...$$restProps} on:keydown={keywrap(click)} use:action {...attrs} tabindex={0} class={cn(
|
||||
'select:bg-accent select:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className
|
||||
)}>
|
||||
<slot {action} {attrs} />
|
||||
</div>
|
||||
</CommandPrimitive.Item>
|
||||
|
|
|
|||
|
|
@ -829,7 +829,7 @@
|
|||
{/if}
|
||||
{#if !SUPPORTS.isAndroidTV}
|
||||
<div class='mobile:flex hidden gap-10 absolute items-center transition-opacity select:opacity-100' class:opacity-0={immersed || seeking}>
|
||||
<Button class='p-3 size-10 pointer-events-auto rounded-[50%] bg-black/20' variant='ghost' disabled={!prev}>
|
||||
<Button class='p-3 size-10 pointer-events-auto rounded-[50%] bg-black/20' variant='ghost' disabled={!prev} on:click={() => prev?.()}>
|
||||
<SkipBack fill='currentColor' strokeWidth='1' />
|
||||
</Button>
|
||||
<Button class='p-2.5 size-12 pointer-events-auto rounded-[50%] bg-black/20' variant='ghost' on:click={playPause}>
|
||||
|
|
@ -839,7 +839,7 @@
|
|||
<Pause fill='currentColor' strokeWidth='1' />
|
||||
{/if}
|
||||
</Button>
|
||||
<Button class='p-3 size-10 pointer-events-auto rounded-[50%] bg-black/20' variant='ghost' disabled={!next}>
|
||||
<Button class='p-3 size-10 pointer-events-auto rounded-[50%] bg-black/20' variant='ghost' disabled={!next} on:click={() => next?.()}>
|
||||
<SkipForward fill='currentColor' strokeWidth='1' />
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -871,7 +871,7 @@
|
|||
<a class='text-white text-lg font-normal leading-none line-clamp-1 hover:text-neutral-300 hover:underline' href='/app/anime/{mediaInfo.media.id}' data-up='#player-options-button-top'>{mediaInfo.session.title}</a>
|
||||
<Sheet.Root portal={wrapper} bind:open={episodeListOpen}>
|
||||
<Sheet.Trigger id='episode-list-button' data-down='#player-seekbar' class='text-[rgba(217,217,217,0.6)] hover:text-neutral-500 text-sm leading-none font-light line-clamp-1 text-left hover:underline'>{mediaInfo.session.description}</Sheet.Trigger>
|
||||
<Sheet.Content class='w-full sm:w-[550px] p-3 sm:p-6 max-w-full sm:max-w-full h-full overflow-y-scroll flex flex-col pb-0 shrink-0 gap-0 bg-black justify-between'>
|
||||
<Sheet.Content class='w-full sm:w-[550px] p-3 sm:p-6 max-w-full sm:max-w-full h-full overflow-y-scroll flex flex-col pb-0 shrink-0 gap-0 bg-black justify-between overflow-x-clip'>
|
||||
{#if mediaInfo.media}
|
||||
{#await Promise.all([episodes(mediaInfo.media.id), client.single(mediaInfo.media.id)]) then [eps, media]}
|
||||
{#if media.data?.Media}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
className
|
||||
)}
|
||||
{value}
|
||||
tabindex={0}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
|
|
|
|||
|
|
@ -276,14 +276,16 @@ function focusElement (element?: HTMLElement | null) {
|
|||
|
||||
// hacky, but make sure keybinds system loads first so it can prevent this from running
|
||||
|
||||
document.addEventListener('keydown', e => {
|
||||
document.addEventListener('keydown', navigate)
|
||||
|
||||
export function navigate (e: KeyboardEvent) {
|
||||
if (e.key in DirectionKeyMap) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
inputType.value = 'dpad'
|
||||
navigateDPad(DirectionKeyMap[e.key as 'ArrowDown' | 'ArrowUp' | 'ArrowLeft' | 'ArrowRight'])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function dragScroll (node: HTMLElement) {
|
||||
let x = 0
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@
|
|||
</script>
|
||||
|
||||
<div class='min-w-0 -ml-14 pl-14 grow items-center flex flex-col h-full overflow-y-auto z-10 pointer-events-none pb-10' use:dragScroll on:scroll={handleScroll} bind:this={container}>
|
||||
<div class='gap-6 w-full pt-4 md:pt-32 flex flex-col items-center justify-center max-w-[1600px] px-3 xl:px-14 pointer-events-auto overflow-x-clip'>
|
||||
<div class='gap-6 w-full pt-4 md:pt-32 flex flex-col items-center justify-center max-w-[1600px] px-3 xl:px-14 pointer-events-auto'>
|
||||
<div class='flex flex-col md:flex-row w-full items-center md:items-end gap-5 pt-12'>
|
||||
<Dialog.Root portal='#root'>
|
||||
<Dialog.Trigger class='shrink-0 w-[180px] h-[256px] rounded overflow-hidden relative group focus-visible:ring-1 focus-visible:ring-ring select:scale-[1.02] transition-transform duration-200'>
|
||||
|
|
@ -128,7 +128,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='flex gap-2 items-center md:justify-start md:self-start'>
|
||||
<div class='flex gap-2 items-center justify-center md:justify-start md:self-start w-full overflow-x-clip [&>*]:flex-shrink-0'>
|
||||
<div class='flex md:mr-3 w-full min-[380px]:w-[180px]'>
|
||||
<PlayButton size='default' {media} class='rounded-r-none w-full' />
|
||||
<EntryEditor {media} />
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@
|
|||
{#each dayList as { date, number } (date)}
|
||||
{@const sameMonth = isSameMonth(now, date)}
|
||||
<div>
|
||||
<div class='flex flex-col text-xs py-3 h-48' class:opacity-30={!sameMonth}>
|
||||
<div class='flex flex-col text-xs py-3 h-24 lg:h-48' class:opacity-30={!sameMonth}>
|
||||
<div class={cn('w-6 h-6 flex items-center justify-center font-bold mx-3', isToday(date) && 'bg-[rgb(61,180,242)] rounded-full')}>
|
||||
{number}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
import { Switch } from '$lib/components/ui/switch'
|
||||
import * as ToggleGroup from '$lib/components/ui/toggle-group'
|
||||
import native from '$lib/modules/native'
|
||||
import { navigate } from '$lib/modules/navigate'
|
||||
import { settings, languageCodes, subtitleResolutions, SUPPORTS } from '$lib/modules/settings'
|
||||
|
||||
async function selectPlayer () {
|
||||
|
|
@ -29,22 +30,24 @@
|
|||
</SettingCard>
|
||||
|
||||
<SettingCard class='md:flex-col md:items-start' title='Subtitle Dialogue Style Overrides' description={'Selectively override the default dialogue style for subtitles. This will not change the style of typesetting [Fancy 3D Signs and Songs].\n\nWarning: the heuristic used for deciding when to override the style is rather rough, and enabling this option can lead to incorrectly rendered subtitles.'}>
|
||||
<ToggleGroup.Root type='single' class='grid sm:grid-cols-2 gap-3' bind:value={$settings.subtitleStyle}>
|
||||
<ToggleGroup.Item value='none' class='h-auto aspect-video text-4xl px-0 relative'>
|
||||
<div class='absolute top-4 text-xl font-bold'>None</div>🚫
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Item value='gandhisans' class='h-auto px-0 relative'>
|
||||
<div class='absolute top-4 text-xl font-bold'>Gandhi Sans Bold</div>
|
||||
<img src='/gandhisans.png' class='w-full' />
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Item value='notosans' class='h-auto px-0 relative'>
|
||||
<div class='absolute top-4 text-xl font-bold'>Noto Sans Bold</div>
|
||||
<img src='/notosans.png' class='w-full' />
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Item value='roboto' class='h-auto px-0 relative'>
|
||||
<div class='absolute top-4 text-xl font-bold'>Roboto Bold</div>
|
||||
<img src='/roboto.png' class='w-full' />
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Root type='single' bind:value={$settings.subtitleStyle} asChild let:builder>
|
||||
<div class='grid sm:grid-cols-2 gap-3' use:builder.action {...builder} on:keydown|capture|stopPropagation={navigate}>
|
||||
<ToggleGroup.Item value='none' class='h-auto aspect-video text-4xl px-0 relative'>
|
||||
<div class='absolute top-4 text-xl font-bold'>None</div>🚫
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Item value='gandhisans' class='h-auto px-0 relative'>
|
||||
<div class='absolute top-4 text-xl font-bold'>Gandhi Sans Bold</div>
|
||||
<img src='/gandhisans.png' class='w-full' />
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Item value='notosans' class='h-auto px-0 relative'>
|
||||
<div class='absolute top-4 text-xl font-bold'>Noto Sans Bold</div>
|
||||
<img src='/notosans.png' class='w-full' />
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Item value='roboto' class='h-auto px-0 relative'>
|
||||
<div class='absolute top-4 text-xl font-bold'>Roboto Bold</div>
|
||||
<img src='/roboto.png' class='w-full' />
|
||||
</ToggleGroup.Item>
|
||||
</div>
|
||||
</ToggleGroup.Root>
|
||||
</SettingCard>
|
||||
|
||||
|
|
|
|||
|
|
@ -23,5 +23,5 @@
|
|||
I agree to the <a use:click={() => native.openURL(`${WEB_URL}/terms`)} class='text-primary underline'>Terms of Service</a> and <a use:click={() => native.openURL(`${WEB_URL}/privacy`)} class='text-primary underline'>Privacy Policy</a>
|
||||
</Label>
|
||||
</div>
|
||||
<Button class='text-lg font-bold shrink-0' disabled={!checked} size='lg' href={checked ? './storage' : undefined}>Start Setup</Button>
|
||||
<Button class='text-lg font-bold shrink-0' disabled={!checked} size='lg' href={checked ? './storage' : undefined} data-sveltekit-replacestate>Start Setup</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
function checkNext () {
|
||||
if (step === 2) localStorage.setItem('setup-finished', SETUP_VERSION.toString())
|
||||
goto(`../${NEXT[step]}`)
|
||||
goto(`../${NEXT[step]}`, { replaceState: true })
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue