mirror of
https://github.com/aleksilassila/reiverr.git
synced 2026-04-22 00:35:12 +02:00
Added "Dynamic modals"; fixed title modals not allowing child modals
This commit is contained in:
54
src/lib/components/Modal/DynamicModal.svelte
Normal file
54
src/lib/components/Modal/DynamicModal.svelte
Normal file
@@ -0,0 +1,54 @@
|
||||
<script lang="ts">
|
||||
import classNames from 'classnames';
|
||||
import { modalStack } from './Modal';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { onDestroy } from 'svelte';
|
||||
|
||||
function handleShortcuts(event: KeyboardEvent) {
|
||||
if (event.key === 'Escape' && $modalStack.top) {
|
||||
modalStack.close($modalStack.top.id);
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
modalStack.reset();
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={handleShortcuts} />
|
||||
|
||||
<svelte:head>
|
||||
{#if $modalStack.top}
|
||||
<style>
|
||||
body {
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
{/if}
|
||||
</svelte:head>
|
||||
|
||||
{#each $modalStack.stack || [] as modal (modal.id)}
|
||||
{@const hidden = $modalStack.top?.group === modal.group && $modalStack.top?.id !== modal.id}
|
||||
|
||||
{#if modal.group === modal.id}
|
||||
<div
|
||||
class="fixed inset-0 bg-stone-950 bg-opacity-80 z-20"
|
||||
transition:fade={{ duration: 150 }}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class={classNames(
|
||||
'fixed inset-0 justify-center items-center z-20 overflow-hidden flex transition-opacity reltaive',
|
||||
{
|
||||
'opacity-0': hidden
|
||||
}
|
||||
)}
|
||||
on:click|self={() => modalStack.close(modal.id)}
|
||||
transition:fade|global={{ duration: 100 }}
|
||||
>
|
||||
<svelte:component this={modal.component} {...modal.props} modalId={modal.id} />
|
||||
</div>
|
||||
{/each}
|
||||
@@ -1,40 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { modalStack } from './Modal';
|
||||
|
||||
// export let visible = false;
|
||||
export let close: () => void;
|
||||
export let id: Symbol;
|
||||
|
||||
function handleShortcuts(event: KeyboardEvent) {
|
||||
if (event.key === 'Escape' && $modalStack.top === id) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
modalStack.push(id);
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={handleShortcuts} />
|
||||
<svelte:head>
|
||||
{#if $modalStack.top}
|
||||
<style>
|
||||
body {
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
{/if}
|
||||
</svelte:head>
|
||||
|
||||
{#if $modalStack.top === id}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="fixed inset-0 justify-center items-center z-20 overflow-hidden flex"
|
||||
on:click|self={close}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
@@ -1,56 +1,71 @@
|
||||
import type { TitleType } from '$lib/types';
|
||||
import { writable } from 'svelte/store';
|
||||
import TitlePageModal from '../TitlePageLayout/TitlePageModal.svelte';
|
||||
|
||||
function createModalStack() {
|
||||
const store = writable<{ stack: Symbol[]; top: Symbol | undefined }>({
|
||||
type ModalItem = {
|
||||
id: Symbol;
|
||||
group: Symbol;
|
||||
component: ConstructorOfATypedSvelteComponent;
|
||||
props: Record<string, any>;
|
||||
};
|
||||
function createDynamicModalStack() {
|
||||
const store = writable<{ stack: ModalItem[]; top: ModalItem | undefined }>({
|
||||
stack: [],
|
||||
top: undefined
|
||||
});
|
||||
|
||||
store.subscribe(console.log);
|
||||
|
||||
function close(symbol: Symbol) {
|
||||
store.update((s) => {
|
||||
s.stack = s.stack.filter((i) => i.id !== symbol);
|
||||
s.top = s.stack[s.stack.length - 1];
|
||||
return s;
|
||||
});
|
||||
}
|
||||
|
||||
function closeGroup(group: Symbol) {
|
||||
store.update((s) => {
|
||||
s.stack = s.stack.filter((i) => i.group !== group);
|
||||
s.top = s.stack[s.stack.length - 1];
|
||||
return s;
|
||||
});
|
||||
}
|
||||
|
||||
function create(
|
||||
component: ConstructorOfATypedSvelteComponent,
|
||||
props: Record<string, any>,
|
||||
group: Symbol | undefined = undefined
|
||||
) {
|
||||
const id = Symbol();
|
||||
const item = { id, component, props, group: group || id };
|
||||
store.update((s) => {
|
||||
s.stack.push(item);
|
||||
s.top = item;
|
||||
return s;
|
||||
});
|
||||
return id;
|
||||
}
|
||||
|
||||
function reset() {
|
||||
store.set({ stack: [], top: undefined });
|
||||
}
|
||||
|
||||
return {
|
||||
...store,
|
||||
push: (symbol: Symbol) => {
|
||||
store.update((s) => {
|
||||
if (s.stack.includes(symbol)) {
|
||||
return s;
|
||||
}
|
||||
|
||||
s.stack.push(symbol);
|
||||
s.top = symbol;
|
||||
return s;
|
||||
});
|
||||
},
|
||||
remove: (symbol: Symbol) => {
|
||||
store.update((s) => {
|
||||
s.stack = s.stack.filter((x) => x !== symbol);
|
||||
s.top = s.stack[s.stack.length - 1];
|
||||
return s;
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const modalStack = createModalStack();
|
||||
|
||||
export type ModalProps = ReturnType<typeof createModalProps>;
|
||||
|
||||
export function createModalProps(onClose: () => void, onBack?: () => void) {
|
||||
const id = Symbol();
|
||||
|
||||
function close() {
|
||||
onClose(); // ORDER MATTERS HERE
|
||||
modalStack.remove(id);
|
||||
}
|
||||
|
||||
function back() {
|
||||
onBack?.();
|
||||
modalStack.remove(id);
|
||||
}
|
||||
|
||||
return {
|
||||
create,
|
||||
close,
|
||||
back: onBack ? back : undefined,
|
||||
id
|
||||
closeGroup,
|
||||
reset
|
||||
};
|
||||
}
|
||||
|
||||
export const modalStack = createDynamicModalStack();
|
||||
|
||||
let lastTitleModal: Symbol | undefined = undefined;
|
||||
export function openTitleModal(tmdbId: number, type: TitleType) {
|
||||
if (lastTitleModal) {
|
||||
modalStack.close(lastTitleModal);
|
||||
}
|
||||
lastTitleModal = modalStack.create(TitlePageModal, { tmdbId, type });
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { fade } from 'svelte/transition';
|
||||
import { modalStack } from './Modal';
|
||||
</script>
|
||||
|
||||
{#if $modalStack.top}
|
||||
<div
|
||||
class="fixed inset-0 bg-stone-900 bg-opacity-50 z-[19] overflow-hidden"
|
||||
transition:fade|global={{ duration: 100 }}
|
||||
/>
|
||||
{/if}
|
||||
Reference in New Issue
Block a user