diff --git a/web/src/components/dialog/DialogHolder.svelte b/web/src/components/dialog/DialogHolder.svelte index b5496d65..7d6ef1dd 100644 --- a/web/src/components/dialog/DialogHolder.svelte +++ b/web/src/components/dialog/DialogHolder.svelte @@ -1,14 +1,17 @@ <script lang="ts"> import SmallDialog from "./SmallDialog.svelte"; import dialogs from "$lib/dialogs"; + + $: dialogVisible = $dialogs.length > 0; </script> -<div id="dialog-holder" aria-hidden="true"> +<div id="dialog-holder" class:visible={dialogVisible}> {#each $dialogs as dialog} {#if dialog.type === "small"} <SmallDialog id={dialog.id} title={dialog.title} + meowbalt={dialog.meowbalt} bodyText={dialog.bodyText} bodySubText={dialog.bodySubText} buttons={dialog.buttons} @@ -21,8 +24,25 @@ #dialog-holder { position: absolute; padding-top: env(safe-area-inset-bottom); - height: calc(100%); + height: 100%; width: 100%; - pointer-events: none; + + display: flex; + justify-content: center; + align-items: center; + z-index: 99; + + visibility: hidden; + backdrop-filter: blur(7px); + -webkit-backdrop-filter: blur(7px); + } + + #dialog-holder.visible { + visibility: visible; + } + + :global([data-reduce-transparency="true"]) #dialog-holder { + backdrop-filter: none !important; + -webkit-backdrop-filter: none !important; } </style> diff --git a/web/src/components/dialog/SmallDialog.svelte b/web/src/components/dialog/SmallDialog.svelte index 8ee30862..3ca087a1 100644 --- a/web/src/components/dialog/SmallDialog.svelte +++ b/web/src/components/dialog/SmallDialog.svelte @@ -2,7 +2,10 @@ import { killDialog } from "$lib/dialogs"; import type { DialogButton } from "$lib/types/dialog"; + import MeowbaltError from "$components/meowbalt/MeowbaltError.svelte"; + export let id: string; + export let meowbalt: string = ""; export let title: string = ""; export let bodyText: string = ""; export let bodySubText: string = ""; @@ -10,46 +13,227 @@ let dialogParent: HTMLDialogElement; + let closing = false; + const close = () => { if (dialogParent) { - dialogParent.close(); - killDialog(); + closing = true; + setTimeout(() => { + dialogParent.close(); + killDialog(); + }, 150) } - } + }; $: if (dialogParent) { dialogParent.showModal(); } </script> -<dialog id="dialog-{id}" bind:this={dialogParent} class="small-dialog"> - <div class="popup-header"> - <h2>{title}</h2> - </div> - <div class="popup-body"> - {bodyText} - {#if bodySubText} - <div class="subtext">{bodySubText}</div> +<dialog id="dialog-{id}" bind:this={dialogParent} class:closing> + <div class="dialog-body small-dialog" class:meowbalt-visible={meowbalt}> + {#if meowbalt === "error"} + <div class="meowbalt-container"> + <MeowbaltError /> + </div> {/if} - </div> - <div class="popup-buttons"> - {#each buttons as button} - <button - on:click={ - (async() => { + <div class="popup-header"> + {#if title} + <h2>{title}</h2> + {/if} + </div> + <div class="popup-body"> + {#if bodyText} + <div class="body-text" tabindex="-1">{bodyText}</div> + {/if} + {#if bodySubText} + <div class="subtext">{bodySubText}</div> + {/if} + </div> + <div class="popup-buttons"> + {#each buttons as button} + <button + class="button popup-button" + class:active={button.main} + on:click={async () => { await button.action(); close(); - }) - } - > - {button.text} - </button> - {/each} + }} + > + {button.text} + </button> + {/each} + </div> </div> + + <div + id="dialog-backdrop" + aria-hidden="true" + on:click={() => close()} + ></div> </dialog> <style> + dialog { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + background: none; + + max-height: 100%; + max-width: 100%; + height: 100%; + width: 100%; + margin: 0; + padding: 0; + border: none; + pointer-events: all; + + inset-inline-start: unset; + inset-inline-end: unset; + + overflow: hidden; + } + + dialog:modal { + inset-block-start: 0; + inset-block-end: 0; + } + + dialog:modal::backdrop { + display: none; + } + .small-dialog { - max-width: 375px; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: var(--padding); + max-width: 340px; + width: 340px; + background: var(--popup-bg); + box-shadow: 0 0 0 2px var(--popup-stroke) inset, + 0 0 60px 10px var(--popup-bg); + padding: 18px; + margin: var(--padding); + border-radius: 29px; + animation: modal-in 0.3s; + position: relative; + will-change: transform; + } + + .small-dialog.meowbalt-visible { + padding-top: 45px; + } + + .meowbalt-container { + position: absolute; + top: -110px; + } + + .closing .small-dialog { + animation: modal-out 0.15s; + opacity: 0; + } + + .body-text { + font-size: 14.5px; + font-weight: 500; + line-height: 1.7; + color: var(--gray); + } + + .body-text:focus-visible { + box-shadow: none !important; + } + + .popup-buttons { + display: flex; + flex-direction: row; + width: 100%; + gap: calc(var(--padding) / 2) + } + + .popup-button { + width: 100%; + height: 40px; + } + + #dialog-backdrop { + --backdrop-opacity: 0.4; + background-color: var(--popup-backdrop); + position: absolute; + height: 100%; + width: 100%; + z-index: -1; + opacity: var(--backdrop-opacity); + animation: backdrop-in 0.15s; + backdrop-filter: blur(7px); + } + + :global([data-reduce-transparency="true"]) #dialog-backdrop { + --backdrop-opacity: 0.5; + } + + .closing #dialog-backdrop { + opacity: 0; + animation: backdrop-out 0.15s; + } + + @keyframes modal-in { + from { + transform: scale(0.8); + opacity: 0; + } + 50% { + transform: scale(1.005); + opacity: 1; + } + 100% { + transform: scale(1); + opacity: 1; + } + } + + @keyframes modal-out { + from { + opacity: 1; + } + to { + opacity: 0; + transform: scale(0.9); + visibility: hidden; + } + } + + @keyframes backdrop-in { + from { + opacity: 0; + } + to { + opacity: var(--backdrop-opacity); + } + } + + @keyframes backdrop-out { + from { + opacity: var(--backdrop-opacity); + } + to { + opacity: 0; + } + } + + @media screen and (max-width: 535px) { + dialog { + align-items: end; + } + + .small-dialog { + margin-bottom: calc(var(--padding) + env(safe-area-inset-bottom)); + box-shadow: 0 0 0 2px var(--popup-stroke) inset; + } } </style> diff --git a/web/src/components/meowbalt/MeowbaltError.svelte b/web/src/components/meowbalt/MeowbaltError.svelte new file mode 100644 index 00000000..34ec570e --- /dev/null +++ b/web/src/components/meowbalt/MeowbaltError.svelte @@ -0,0 +1,19 @@ +<script lang="ts"> + import { t } from "$lib/i18n/translations"; +</script> + +<img + id="meowbalt-error" + src="/meowbalt/error.png" + height="160" + alt={$t("a11y.meowbalt.error")} +/> + +<style> + #meowbalt-error { + display: block; + margin: 0; + object-fit: cover; + margin-left: 25px; + } +</style> diff --git a/web/src/components/meowbalt/MeowbaltLoaf.svelte b/web/src/components/meowbalt/MeowbaltLoaf.svelte index 908b1ec8..3e8533e3 100644 --- a/web/src/components/meowbalt/MeowbaltLoaf.svelte +++ b/web/src/components/meowbalt/MeowbaltLoaf.svelte @@ -6,7 +6,6 @@ id="meowbalt-loaf" src="/meowbalt/smile.png" height="152" - width="141" alt={$t("a11y.meowbalt.smile")} /> diff --git a/web/src/components/save/buttons/DownloadButton.svelte b/web/src/components/save/buttons/DownloadButton.svelte index 718a9041..5201b3bc 100644 --- a/web/src/components/save/buttons/DownloadButton.svelte +++ b/web/src/components/save/buttons/DownloadButton.svelte @@ -18,11 +18,13 @@ let defaultErrorPopup = { id: "save-error", type: "small", + meowbalt: "error", title: "", bodySubText: "", buttons: [{ text: $t("general.gotit"), color: "gray", + main: true, action: () => {}, }] } diff --git a/web/src/lib/types/dialog.ts b/web/src/lib/types/dialog.ts index b473acd1..20bea678 100644 --- a/web/src/lib/types/dialog.ts +++ b/web/src/lib/types/dialog.ts @@ -1,14 +1,16 @@ export type DialogButton = { text: string, color: string, + main: boolean, action: () => unknown | Promise<unknown> } export type DialogInfo = { id: string, type: "small", + meowbalt: "error", title: string, bodyText: string, bodySubText: string, - buttons: DialogButton[] + buttons: DialogButton[], } diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index 01f025fe..4af2d963 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -66,10 +66,14 @@ --button: #f4f4f4; --button-hover: #e8e8e8; --button-hover-transparent: rgba(0, 0, 0, 0.06); - --button-stroke: rgba(0, 0, 0, 0.05); + --button-stroke: rgba(0, 0, 0, 0.06); --button-text: #282828; --button-box-shadow: 0 0 0 1.5px var(--button-stroke) inset; + --popup-bg: #f1f1f1; + --popup-backdrop: var(--primary); + --popup-stroke: rgba(0, 0, 0, 0.08); + --sidebar-bg: #000000; --sidebar-highlight: #ffffff; --sidebar-hover: rgba(255, 255, 255, 0.1); @@ -120,6 +124,10 @@ --button-text: #e1e1e1; --button-box-shadow: 0 0 0 1.5px var(--button-stroke) inset; + --popup-bg: #191919; + --popup-backdrop: var(--primary); + --popup-stroke: rgba(255, 255, 255, 0.08); + --sidebar-bg: #101010; --sidebar-highlight: #f2f2f2; @@ -319,15 +327,6 @@ font-size: 11px; } - :global(dialog) { - max-height: 100%; - max-width: 100%; - padding: var(--padding); - border-radius: var(--border-radius); - border: none; - pointer-events: all; - } - :global(.subtext) { font-size: 12.5px; font-weight: 500; diff --git a/web/static/meowbalt/checking.png b/web/static/meowbalt/checking.png new file mode 100644 index 00000000..0d6a51c2 Binary files /dev/null and b/web/static/meowbalt/checking.png differ diff --git a/web/static/meowbalt/error.png b/web/static/meowbalt/error.png new file mode 100644 index 00000000..f20c1d63 Binary files /dev/null and b/web/static/meowbalt/error.png differ diff --git a/web/static/meowbalt/think.png b/web/static/meowbalt/think.png new file mode 100644 index 00000000..2b06f0cc Binary files /dev/null and b/web/static/meowbalt/think.png differ