feat: keyboard navigation

This commit is contained in:
ThaUnknown 2023-01-06 15:16:24 +01:00
parent 63d088710d
commit 005a6718f4
13 changed files with 197 additions and 132 deletions

View file

@ -1,6 +1,6 @@
{
"name": "Miru",
"version": "3.5.4",
"version": "3.5.5",
"author": "ThaUnknown_ <ThaUnknown@users.noreply.github.com>",
"description": "Stream anime torrents, real-time with no waiting for downloads.",
"main": "src/index.js",

View file

@ -4,6 +4,19 @@ const { Client } = require('discord-rpc')
const log = require('electron-log')
const { autoUpdater } = require('electron-updater')
const flags = [
['enable-gpu-rasterization'],
['enable-zero-copy'],
['ignore-gpu-blocklist'],
['enable-hardware-overlays', 'single-fullscreen,single-on-top,underlay'],
['enable-features', 'EnableDrDc,CanvasOopRasterization,BackForwardCache:TimeToLiveInBackForwardCacheInSeconds/300/should_ignore_blocklists/true/enable_same_site/true,ThrottleDisplayNoneAndVisibilityHiddenCrossOriginIframes,UseSkiaRenderer,WebAssemblyLazyCompilationEnableDrDc,CanvasOopRasterization,BackForwardCache:TimeToLiveInBackForwardCacheInSeconds/300/should_ignore_blocklists/true/enable_same_site/true,ThrottleDisplayNoneAndVisibilityHiddenCrossOriginIframes,UseSkiaRenderer,WebAssemblyLazyCompilation'],
['force_high_performance_gpu'],
['disable-features', 'Vulkan']
]
for (const [flag, value] of flags) {
app.commandLine.appendSwitch(flag, value)
}
if (process.defaultApp) {
if (process.argv.length >= 2) {
app.setAsDefaultProtocolClient('miru', process.execPath, [path.resolve(process.argv[1])])
@ -255,12 +268,7 @@ discord.on('ready', async () => {
discord.subscribe('ACTIVITY_JOIN')
discord.subscribe('ACTIVITY_SPECTATE')
})
discord.on('ACTIVITY_JOIN_REQUEST', console.log)
discord.on('ACTIVITY_SPECTATE', console.log)
discord.on('ACTIVITY_JOIN', (args) => {
console.log('ACTIVITY_JOIN')
console.log(args)
console.log('------')
BrowserWindow.getAllWindows()[0]?.send('w2glink', args.secret)
})

View file

@ -1,36 +1,37 @@
<script context='module'>
import { setContext } from 'svelte'
import { writable } from 'svelte/store'
import { alRequest } from '@/modules/anilist.js'
import { setContext } from 'svelte'
import { writable } from 'svelte/store'
import { alRequest } from '@/modules/anilist.js'
export const page = writable('home')
export const view = writable(null)
export async function handleAnime (anime) {
view.set(null)
view.set((await alRequest({ method: 'SearchIDSingle', id: anime })).data.Media)
}
window.IPC.on('open-anime', handleAnime)
export const page = writable('home')
export const view = writable(null)
export async function handleAnime (anime) {
view.set(null)
view.set((await alRequest({ method: 'SearchIDSingle', id: anime })).data.Media)
}
window.IPC.on('open-anime', handleAnime)
</script>
<script>
import Sidebar from './lib/Sidebar.svelte'
import Router from './lib/Router.svelte'
import ViewAnime from './lib/ViewAnime/ViewAnime.svelte'
import ViewTrailer from './lib/ViewAnime/ViewTrailer.svelte'
import RSSView from './lib/RSSView.svelte'
import Menubar from './lib/Menubar.svelte'
import Toasts from './lib/Toasts.svelte'
import 'quartermoon/css/quartermoon-variables.css'
import NyaaBlock from './lib/NyaaBlock.svelte'
import Sidebar from './lib/Sidebar.svelte'
import Router from './lib/Router.svelte'
import ViewAnime from './lib/ViewAnime/ViewAnime.svelte'
import ViewTrailer from './lib/ViewAnime/ViewTrailer.svelte'
import RSSView from './lib/RSSView.svelte'
import Menubar from './lib/Menubar.svelte'
import Toasts from './lib/Toasts.svelte'
import 'quartermoon/css/quartermoon-variables.css'
import NyaaBlock from './lib/NyaaBlock.svelte'
import { wrapEnter } from '@/modules/util.js'
setContext('view', view)
setContext('view', view)
const sidebar = writable(true)
setContext('sidebar', sidebar)
const sidebar = writable(true)
setContext('sidebar', sidebar)
setContext('gallery', writable(null))
setContext('gallery', writable(null))
setContext('trailer', writable(null))
setContext('trailer', writable(null))
</script>
<Toasts />
@ -39,7 +40,10 @@ setContext('trailer', writable(null))
<RSSView />
<div class='page-wrapper with-navbar with-sidebar with-transitions' data-sidebar-type='overlayed-sm-and-down' data-sidebar-hidden={$sidebar || null}>
<div class='sticky-alerts' />
<div class='sidebar-overlay' on:click={() => ($sidebar = !$sidebar)} />
<div class='sidebar-overlay'
on:click={() => ($sidebar = !$sidebar)} on:keydown={wrapEnter(() => ($sidebar = !$sidebar))}
tabindex='0'
role='button' />
<NyaaBlock />
<Menubar />
<Sidebar bind:page={$page} />
@ -82,6 +86,10 @@ setContext('trailer', writable(null))
filter: invert(0.84);
padding-top: 2rem;
}
:global(*:focus-visible){
outline: none;
box-shadow: var(--dm-button-primary-box-shadow-focus) !important;
}
:global(.root) {
animation: 0.3s ease 0s 1 root-load-in;

View file

@ -1,5 +1,5 @@
<script>
import { countdown } from '@/modules/util.js'
import { countdown, wrapEnter } from '@/modules/util.js'
import { getContext } from 'svelte'
export let cards = new Promise(() => {})
const view = getContext('view')
@ -7,6 +7,7 @@
$view = media
}
export let length = 5
export let tabable = false
</script>
{#await cards}
@ -27,7 +28,7 @@
{#if typeof card === 'string'}
<div class='day-row font-size-24 font-weight-bold h-50 d-flex align-items-end'>{card}</div>
{:else if !card.media}
<div class='card m-0 p-0' on:click={card.onclick}>
<div class='card m-0 p-0' on:click={card.onclick} on:keydown={wrapEnter(card.onclick)} tabindex={tabable ? 0 : null} role='button'>
<div class='row h-full'>
<div class='col-4 skeloader' />
<div class='col-8 bg-very-dark px-15 py-10'>
@ -38,7 +39,12 @@
</div>
</div>
{:else}
<div class='card m-0 p-0' on:click={card.onclick || (() => viewMedia(card.media))} style:--color={card.media.coverImage.color || '#1890ff'} title={card.parseObject?.file_name}>
<div class='card m-0 p-0'
on:click={card.onclick || (() => viewMedia(card.media))}
on:keydown={wrapEnter(card.onclick || (() => viewMedia(card.media)))}
tabindex={tabable ? 0 : null} role='button'
style:--color={card.media.coverImage.color || '#1890ff'}
title={card.parseObject?.file_name}>
<div class='row h-full'>
<div class='col-4'>
<img loading='lazy' src={card.media.coverImage.extraLarge || ''} alt='cover' class='cover-img w-full h-full' />

View file

@ -1,20 +1,20 @@
<script>
import Cards from './Cards.svelte'
import Cards from './Cards.svelte'
export let media
$: update(media)
let loading = true
async function update (med) {
loading = true
const index = med.length - 1
await med[index]
if (med[index] === media[media.length - 1]) loading = false
}
export let media
$: update(media)
let loading = true
async function update (med) {
loading = true
const index = med.length - 1
await med[index]
if (med[index] === media[media.length - 1]) loading = false
}
</script>
<div class='gallery browse' class:loading>
{#each media as cards, i (i)}
<Cards {cards} length={4} />
<Cards {cards} length={4} tabable={true} />
{/each}
</div>

View file

@ -72,7 +72,7 @@
bind:this={searchTextInput}
autofocus
type='search'
class='form-control bg-dark border-left-0 shadow-none text-capitalize'
class='form-control bg-dark border-left-0 text-capitalize'
autocomplete='off'
bind:value={search.search}
data-option='search'
@ -84,27 +84,29 @@
<div class='material-icons mr-10 font-size-30'>theater_comedy</div>
Genre
</div>
<select class='form-control bg-dark shadow-lg' required bind:value={search.genre}>
<option value selected disabled hidden>Any</option>
<option value='Action'>Action</option>
<option value='Adventure'>Adventure</option>
<option value='Comedy'>Comedy</option>
<option value='Drama'>Drama</option>
<option value='Ecchi'>Ecchi</option>
<option value='Fantasy'>Fantasy</option>
<option value='Horror'>Horror</option>
<option value='Mahou Shoujo'>Mahou Shoujo</option>
<option value='Mecha'>Mecha</option>
<option value='Music'>Music</option>
<option value='Mystery'>Mystery</option>
<option value='Psychological'>Psychological</option>
<option value='Romance'>Romance</option>
<option value='Sci-Fi'>Sci-Fi</option>
<option value='Slice of Life'>Slice of Life</option>
<option value='Sports'>Sports</option>
<option value='Supernatural'>Supernatural</option>
<option value='Thriller'>Thriller</option>
</select>
<div class='shadow-lg'>
<select class='form-control bg-dark' required bind:value={search.genre}>
<option value selected disabled hidden>Any</option>
<option value='Action'>Action</option>
<option value='Adventure'>Adventure</option>
<option value='Comedy'>Comedy</option>
<option value='Drama'>Drama</option>
<option value='Ecchi'>Ecchi</option>
<option value='Fantasy'>Fantasy</option>
<option value='Horror'>Horror</option>
<option value='Mahou Shoujo'>Mahou Shoujo</option>
<option value='Mecha'>Mecha</option>
<option value='Music'>Music</option>
<option value='Mystery'>Mystery</option>
<option value='Psychological'>Psychological</option>
<option value='Romance'>Romance</option>
<option value='Sci-Fi'>Sci-Fi</option>
<option value='Slice of Life'>Slice of Life</option>
<option value='Sports'>Sports</option>
<option value='Supernatural'>Supernatural</option>
<option value='Thriller'>Thriller</option>
</select>
</div>
</div>
<div class='col-lg col-4 p-10 d-flex flex-column justify-content-end'>
<div class='pb-10 font-size-24 font-weight-semi-bold d-flex'>
@ -112,14 +114,14 @@
Season
</div>
<div class='input-group shadow-lg'>
<select class='form-control bg-dark shadow-none border-right-dark' required bind:value={search.season}>
<select class='form-control bg-dark border-right-dark' required bind:value={search.season}>
<option value selected disabled hidden>Any</option>
<option value='WINTER'>Winter</option>
<option value='SPRING'>Spring</option>
<option value='SUMMER'>Summer</option>
<option value='FALL'>Fall</option>
</select>
<input type='number' placeholder='Any' min='1940' max='2100' list='search-year' class='form-control bg-dark shadow-none' bind:value={search.year} />
<input type='number' placeholder='Any' min='1940' max='2100' list='search-year' class='form-control bg-dark' bind:value={search.year} />
<datalist id='search-year'>
{#each Array(new Date().getFullYear() - 1940 + 2) as _, i}
{@const year = new Date().getFullYear() + 2 - i}
@ -133,54 +135,64 @@
<div class='material-icons mr-10 font-size-30'>monitor</div>
Format
</div>
<select class='form-control bg-dark shadow-lg' required bind:value={search.format}>
<option value selected disabled hidden>Any</option>
<option value='TV'>TV Show</option>
<option value='MOVIE'>Movie</option>
<option value='TV_SHORT'>TV Short</option>
<option value='OVA'>OVA</option>
<option value='ONA'>ONA</option>
</select>
<div class='shadow-lg'>
<select class='form-control bg-dark' required bind:value={search.format}>
<option value selected disabled hidden>Any</option>
<option value='TV'>TV Show</option>
<option value='MOVIE'>Movie</option>
<option value='TV_SHORT'>TV Short</option>
<option value='OVA'>OVA</option>
<option value='ONA'>ONA</option>
</select>
</div>
</div>
<div class='col p-10 d-flex flex-column justify-content-end'>
<div class='pb-10 font-size-24 font-weight-semi-bold d-flex'>
<div class='material-icons mr-10 font-size-30'>live_tv</div>
Status
</div>
<select class='form-control bg-dark shadow-lg' required bind:value={search.status}>
<option value selected disabled hidden>Any</option>
<option value='RELEASING'>Airing</option>
<option value='FINISHED'>Finished</option>
<option value='NOT_YET_RELEASED'>Not Yet Aired</option>
<option value='CANCELLED'>Cancelled</option>
</select>
<div class='shadow-lg'>
<select class='form-control bg-dark' required bind:value={search.status}>
<option value selected disabled hidden>Any</option>
<option value='RELEASING'>Airing</option>
<option value='FINISHED'>Finished</option>
<option value='NOT_YET_RELEASED'>Not Yet Aired</option>
<option value='CANCELLED'>Cancelled</option>
</select>
</div>
</div>
<div class='col p-10 d-flex flex-column justify-content-end'>
<div class='pb-10 font-size-24 font-weight-semi-bold d-flex'>
<div class='material-icons mr-10 font-size-30'>sort</div>
Sort
</div>
<select class='form-control bg-dark shadow-lg' required bind:value={search.sort}>
<option value selected disabled hidden>Name</option>
<option value='START_DATE_DESC'>Release Date</option>
<option value='SCORE_DESC'>Score</option>
<option value='POPULARITY_DESC'>Popularity</option>
<option value='TRENDING_DESC'>Trending</option>
<option value='UPDATED_TIME_DESC' disabled hidden>Updated Date</option>
</select>
<div class='shadow-lg'>
<select class='form-control bg-dark' required bind:value={search.sort}>
<option value selected disabled hidden>Name</option>
<option value='START_DATE_DESC'>Release Date</option>
<option value='SCORE_DESC'>Score</option>
<option value='POPULARITY_DESC'>Popularity</option>
<option value='TRENDING_DESC'>Trending</option>
<option value='UPDATED_TIME_DESC' disabled hidden>Updated Date</option>
</select>
</div>
</div>
<input type='file' class='d-none' id='search-image' accept='image/*' on:input={handleFile} />
<div class='col-auto p-10 d-flex'>
<button class='btn bg-dark material-icons font-size-18 px-5 align-self-end shadow-lg border-0' type='button'>
<label for='search-image' class='pointer'>
image
</label>
</button>
<div class='shadow-lg align-self-end'>
<button class='btn bg-dark material-icons font-size-18 px-5 align-self-end border-0' type='button'>
<label for='search-image' class='pointer'>
image
</label>
</button>
</div>
</div>
<div class='col-auto p-10 d-flex'>
<button class='btn bg-dark material-icons font-size-18 px-5 align-self-end shadow-lg border-0' type='button' on:click={searchClear} class:text-primary={!!current}>
delete
</button>
<div class='shadow-lg align-self-end'>
<button class='btn bg-dark material-icons font-size-18 px-5 align-self-end border-0' type='button' on:click={searchClear} class:text-primary={!!current}>
delete
</button>
</div>
</div>
</div>
@ -200,7 +212,7 @@
.bg-dark::-webkit-inner-spin-button {
filter: invert(0.942);
}
:global(input:invalid) {
input:not(:focus):invalid {
box-shadow: 0 0 0 0.2rem var(--danger-color) !important;
}
select.form-control:invalid {

View file

@ -1,10 +1,14 @@
<script>
import { wrapEnter } from '@/modules/util.js'
import Cards from './Cards.svelte'
export let opts
const cards = opts.preview()
</script>
<span class='d-flex px-20 align-items-end pointer text-decoration-none text-muted' on:click={opts.onclick}>
<span class='d-flex px-20 align-items-end pointer text-decoration-none text-muted'
on:click={opts.onclick} on:keydown={wrapEnter(opts.onclick)}
tabindex='0'
role='button'>
<div class='pl-10 font-size-24 font-weight-semi-bold'>{opts.title}</div>
<div class='pr-10 ml-auto font-size-12'>View More</div>
</span>

View file

@ -1,5 +1,5 @@
<script context='module'>
import { DOMPARSER } from '@/modules/util.js'
import { DOMPARSER, wrapEnter } from '@/modules/util.js'
import { set } from './Settings.svelte'
import { addToast } from './Toasts.svelte'
import { alRequest } from '@/modules/anilist.js'
@ -299,13 +299,15 @@
add(entry.link)
table = null
}
let modal
$: table && modal?.focus()
</script>
<div class='modal' class:show={table} id='viewAnime' on:keydown={checkClose} tabindex='-1'>
<div class='modal' class:show={table} id='viewAnime' on:keydown={checkClose} tabindex='-1' bind:this={modal}>
{#if table}
<div class='modal-dialog p-20' role='document' on:click|self={close}>
<div class='modal-dialog p-20' role='document' on:click|self={close} on:keydown|self={wrapEnter(close)}>
<div class='modal-content w-auto'>
<button class='close pointer' type='button' on:click={close}> &times; </button>
<button class='close pointer z-30 top-20 right-0' type='button' on:click={close}> &times; </button>
<table class='table table-hover'>
<thead>
<tr>
@ -320,7 +322,7 @@
</thead>
<tbody class='results pointer'>
{#each table as row, index}
<tr class:text-secondary={row.best} on:click={() => play(row)}>
<tr class:text-secondary={row.best} on:click={() => play(row)} on:keydown={wrapEnter(() => play(row))} tabindex='0' role='button'>
<th>{index + 1}</th>
<td>{row.title}</td>
<td>{row.size}</td>

View file

@ -4,6 +4,7 @@
import { media } from './Player/MediaHandler.svelte'
import { platformMap } from './Settings.svelte'
import { addToast } from './Toasts.svelte'
import { wrapEnter } from '@/modules/util.js'
const sidebar = getContext('sidebar')
const view = getContext('view')
const gallery = getContext('gallery')
@ -95,7 +96,10 @@
data-toggle='tooltip'
data-placement='right'
data-title={text}
tabindex='0'
role='button'
on:click={click}
on:keydown={wrapEnter(click)}
class:mt-auto={i === links.length - 2}>
<span class='text-nowrap d-flex align-items-center' class:justify-content-between={i === 0}>
{#if image}

View file

@ -104,14 +104,14 @@
<div class='col-md-4 d-flex justify-content-end flex-column'>
<div class='d-flex flex-column flex-wrap'>
<button
class='btn btn-primary d-flex align-items-center font-weight-bold font-size-24 h-50 mb-5 shadow-lg'
class='btn btn-primary d-flex align-items-center font-weight-bold font-size-24 h-50 mb-5'
type='button'
on:click={() => play(media)}>
<span class='material-icons mr-10 font-size-24 w-30'> play_arrow </span>
<span>{getPlayText(media)}</span>
</button>
{#if alToken}
<button class='btn d-flex align-items-center mb-5 font-weight-bold font-size-16 btn-primary shadow-lg' on:click={toggleStatus}>
<button class='btn d-flex align-items-center mb-5 font-weight-bold font-size-16 btn-primary' on:click={toggleStatus}>
<span class='material-icons mr-10 font-size-18 w-30'> {(media.mediaListEntry?.status in toggleStatusMap) ? 'remove' : 'add'} </span>
{getStatusText(media)}
</button>
@ -135,17 +135,17 @@
</div>
{/if}
{#if media.trailer}
<button class='btn d-flex align-items-center mb-5 font-weight-bold font-size-16 shadow-lg' on:click={() => viewTrailer(media)}>
<button class='btn d-flex align-items-center mb-5 font-weight-bold font-size-16' on:click={() => viewTrailer(media)}>
<span class='material-icons mr-15 font-size-18 w-30'> live_tv </span>
Trailer
</button>
{/if}
<div class='d-flex mb-5 w-full'>
<button class='btn flex-fill font-weight-bold font-size-16 shadow-lg d-flex align-items-center' on:click={() => { openInBrowser(`https://anilist.co/anime/${media.id}`) }}>
<button class='btn flex-fill font-weight-bold font-size-16 d-flex align-items-center' on:click={() => { openInBrowser(`https://anilist.co/anime/${media.id}`) }}>
<span class='material-icons mr-15 font-size-18 w-30'> open_in_new </span>
Open
</button>
<button class='btn flex-fill font-weight-bold font-size-16 ml-5 shadow-lg d-flex align-items-center' on:click={() => { copyToClipboard(`<miru://anime/${media.id}>`) }}>
<button class='btn flex-fill font-weight-bold font-size-16 ml-5 d-flex align-items-center' on:click={() => { copyToClipboard(`<miru://anime/${media.id}>`) }}>
<span class='material-icons mr-15 font-size-18 w-30'> share </span>
Share
</button>

View file

@ -2,6 +2,7 @@
import { playAnime } from '../RSSView.svelte'
import { alRequest } from '@/modules/anilist.js'
import { getMediaMaxEp } from '@/modules/anime.js'
import { wrapEnter } from '@/modules/util.js'
import { getContext } from 'svelte'
import Details from './Details.svelte'
import Following from './Following.svelte'
@ -26,7 +27,7 @@
<div class='modal modal-full' class:show={media} on:keydown={checkClose} tabindex='-1' bind:this={modal}>
{#if media}
<div class='h-full modal-content bg-very-dark p-0 overflow-y-auto'>
<button class='close pointer z-30 bg-dark shadow-lg top-20 right-0' type='button' on:click={close}> &times; </button>
<button class='close pointer z-30 bg-dark top-20 right-0' type='button' on:click={close}> &times; </button>
<div class='h-md-half w-full position-relative z-20'>
<div class='h-full w-full position-absolute bg-dark-light banner' style:--bannerurl={`url('${media.bannerImage || ''}')`} />
<div class='d-flex h-full top w-full'>
@ -89,7 +90,11 @@
{media.description?.replace(/<[^>]*>/g, '') || ''}
</div>
<ToggleList list={media.relations?.edges?.filter(({ node }) => node.type === 'ANIME')} let:item title='Relations'>
<div class='w-150 mx-15 mb-10 rel pointer' on:click={async () => { $view = null; $view = (await alRequest({ method: 'SearchIDSingle', id: item.node.id })).data.Media }}>
<div class='w-150 mx-15 mb-10 rel pointer'
on:click={async () => { $view = null; $view = (await alRequest({ method: 'SearchIDSingle', id: item.node.id })).data.Media }}
on:keydown={wrapEnter(async () => { $view = null; $view = (await alRequest({ method: 'SearchIDSingle', id: item.node.id })).data.Media })}
tabindex='0' role='button'
>
<img loading='lazy' src={item.node.coverImage.medium || ''} alt='cover' class='cover-img w-full h-200 rel-img' />
<div class='pt-5'>{item.relationType.replace(/_/g, ' ').toLowerCase()}</div>
<h5 class='font-weight-bold text-white'>{item.node.title.userPreferred}</h5>
@ -110,7 +115,13 @@
on:click={() => {
playAnime(media, ep)
close()
}}>
}}
on:keydown={wrapEnter(() => {
playAnime(media, ep)
close()
})}
tabindex='0' role='button'
>
<td class='w-full font-weight-semi-bold'>Episode {ep}</td>
<td class='material-icons text-right h-full d-table-cell'>play_arrow</td>
</tr>
@ -119,15 +130,19 @@
</table>
{/if}
<ToggleList list={media.recommendations.edges.filter(edge => edge.node.mediaRecommendation)} let:item title='Recommendations'>
<div class='w-150 mx-15 mb-10 rel pointer' on:click={async () => { $view = null; $view = (await alRequest({ method: 'SearchIDSingle', id: item.node.mediaRecommendation.id })).data.Media }}>
<div class='w-150 mx-15 mb-10 rel pointer'
on:click={async () => { $view = null; $view = (await alRequest({ method: 'SearchIDSingle', id: item.node.mediaRecommendation.id })).data.Media }}
on:keydown={wrapEnter(async () => { $view = null; $view = (await alRequest({ method: 'SearchIDSingle', id: item.node.mediaRecommendation.id })).data.Media })}
tabindex='0' role='button'
>
<img loading='lazy' src={item.node.mediaRecommendation.coverImage.medium || ''} alt='cover' class='cover-img w-full h-200 rel-img' />
<h5 class='font-weight-bold text-white'>{item.node.mediaRecommendation.title.userPreferred}</h5>
</div>
</ToggleList>
</div>
<div class='col-md-3 px-sm-0 px-20'>
<Details {media}/>
<Following {media}/>
<Details {media} />
<Following {media} />
</div>
</div>
</div>

View file

@ -1,24 +1,24 @@
<script>
import { getContext } from 'svelte'
const url = getContext('trailer')
import { getContext } from 'svelte'
import { wrapEnter } from '@/modules/util.js'
let modal
function close () {
$url = null
}
function checkClose ({ keyCode }) {
if (keyCode === 27) close()
}
$: $url && modal?.focus()
const url = getContext('trailer')
let modal
function close () {
$url = null
}
function checkClose ({ keyCode }) {
if (keyCode === 27) close()
}
$: $url && modal?.focus()
</script>
<div class='modal' class:show={$url} on:keydown={checkClose} tabindex='-1' bind:this={modal}>
{#if $url}
<div class='modal-dialog' role='document' on:click|self={close}>
<div class='modal-dialog' role='document' on:click|self={close} on:keydown|self={wrapEnter(close)}>
<div class='modal-content w-three-quarter h-full bg-transparent d-flex justify-content-center flex-column'>
<button class='close pointer z-30 bg-dark shadow-lg top-20 right-0' type='button' on:click={close}>
×
</button>
<button class='close pointer z-30 top-20 right-0' type='button' on:click={close}> &times; </button>
<div class='trailer w-full position-relative'>
<iframe
id='trailerVideo'

View file

@ -85,3 +85,9 @@ export function throttle (cb, limit) {
}
}
}
export function wrapEnter (fn) {
return ({ key }) => {
if (key === 'Enter') fn()
}
}