web/dialog: css for small dialog

- moved backdrop to each dialog
- dialog is now closable by clicking the backdrop
- added meowbalt to dialogs
- added more meowbalt assets & components
- added "main" boolean to indicate the main action button in a list of buttons
This commit is contained in:
wukko 2024-07-16 14:00:56 +06:00
parent 03bd995839
commit fa835d0010
No known key found for this signature in database
GPG key ID: 3E30B3F26C7B4AA2
10 changed files with 264 additions and 39 deletions

View file

@ -1,14 +1,17 @@
<script lang="ts"> <script lang="ts">
import SmallDialog from "./SmallDialog.svelte"; import SmallDialog from "./SmallDialog.svelte";
import dialogs from "$lib/dialogs"; import dialogs from "$lib/dialogs";
$: dialogVisible = $dialogs.length > 0;
</script> </script>
<div id="dialog-holder" aria-hidden="true"> <div id="dialog-holder" class:visible={dialogVisible}>
{#each $dialogs as dialog} {#each $dialogs as dialog}
{#if dialog.type === "small"} {#if dialog.type === "small"}
<SmallDialog <SmallDialog
id={dialog.id} id={dialog.id}
title={dialog.title} title={dialog.title}
meowbalt={dialog.meowbalt}
bodyText={dialog.bodyText} bodyText={dialog.bodyText}
bodySubText={dialog.bodySubText} bodySubText={dialog.bodySubText}
buttons={dialog.buttons} buttons={dialog.buttons}
@ -21,8 +24,25 @@
#dialog-holder { #dialog-holder {
position: absolute; position: absolute;
padding-top: env(safe-area-inset-bottom); padding-top: env(safe-area-inset-bottom);
height: calc(100%); height: 100%;
width: 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> </style>

View file

@ -2,7 +2,10 @@
import { killDialog } from "$lib/dialogs"; import { killDialog } from "$lib/dialogs";
import type { DialogButton } from "$lib/types/dialog"; import type { DialogButton } from "$lib/types/dialog";
import MeowbaltError from "$components/meowbalt/MeowbaltError.svelte";
export let id: string; export let id: string;
export let meowbalt: string = "";
export let title: string = ""; export let title: string = "";
export let bodyText: string = ""; export let bodyText: string = "";
export let bodySubText: string = ""; export let bodySubText: string = "";
@ -10,46 +13,227 @@
let dialogParent: HTMLDialogElement; let dialogParent: HTMLDialogElement;
let closing = false;
const close = () => { const close = () => {
if (dialogParent) { if (dialogParent) {
dialogParent.close(); closing = true;
killDialog(); setTimeout(() => {
dialogParent.close();
killDialog();
}, 150)
} }
} };
$: if (dialogParent) { $: if (dialogParent) {
dialogParent.showModal(); dialogParent.showModal();
} }
</script> </script>
<dialog id="dialog-{id}" bind:this={dialogParent} class="small-dialog"> <dialog id="dialog-{id}" bind:this={dialogParent} class:closing>
<div class="popup-header"> <div class="dialog-body small-dialog" class:meowbalt-visible={meowbalt}>
<h2>{title}</h2> {#if meowbalt === "error"}
</div> <div class="meowbalt-container">
<div class="popup-body"> <MeowbaltError />
{bodyText} </div>
{#if bodySubText}
<div class="subtext">{bodySubText}</div>
{/if} {/if}
</div> <div class="popup-header">
<div class="popup-buttons"> {#if title}
{#each buttons as button} <h2>{title}</h2>
<button {/if}
on:click={ </div>
(async() => { <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(); await button.action();
close(); close();
}) }}
} >
> {button.text}
{button.text} </button>
</button> {/each}
{/each} </div>
</div> </div>
<div
id="dialog-backdrop"
aria-hidden="true"
on:click={() => close()}
></div>
</dialog> </dialog>
<style> <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 { .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> </style>

View file

@ -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>

View file

@ -6,7 +6,6 @@
id="meowbalt-loaf" id="meowbalt-loaf"
src="/meowbalt/smile.png" src="/meowbalt/smile.png"
height="152" height="152"
width="141"
alt={$t("a11y.meowbalt.smile")} alt={$t("a11y.meowbalt.smile")}
/> />

View file

@ -18,11 +18,13 @@
let defaultErrorPopup = { let defaultErrorPopup = {
id: "save-error", id: "save-error",
type: "small", type: "small",
meowbalt: "error",
title: "", title: "",
bodySubText: "", bodySubText: "",
buttons: [{ buttons: [{
text: $t("general.gotit"), text: $t("general.gotit"),
color: "gray", color: "gray",
main: true,
action: () => {}, action: () => {},
}] }]
} }

View file

@ -1,14 +1,16 @@
export type DialogButton = { export type DialogButton = {
text: string, text: string,
color: string, color: string,
main: boolean,
action: () => unknown | Promise<unknown> action: () => unknown | Promise<unknown>
} }
export type DialogInfo = { export type DialogInfo = {
id: string, id: string,
type: "small", type: "small",
meowbalt: "error",
title: string, title: string,
bodyText: string, bodyText: string,
bodySubText: string, bodySubText: string,
buttons: DialogButton[] buttons: DialogButton[],
} }

View file

@ -66,10 +66,14 @@
--button: #f4f4f4; --button: #f4f4f4;
--button-hover: #e8e8e8; --button-hover: #e8e8e8;
--button-hover-transparent: rgba(0, 0, 0, 0.06); --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-text: #282828;
--button-box-shadow: 0 0 0 1.5px var(--button-stroke) inset; --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-bg: #000000;
--sidebar-highlight: #ffffff; --sidebar-highlight: #ffffff;
--sidebar-hover: rgba(255, 255, 255, 0.1); --sidebar-hover: rgba(255, 255, 255, 0.1);
@ -120,6 +124,10 @@
--button-text: #e1e1e1; --button-text: #e1e1e1;
--button-box-shadow: 0 0 0 1.5px var(--button-stroke) inset; --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-bg: #101010;
--sidebar-highlight: #f2f2f2; --sidebar-highlight: #f2f2f2;
@ -319,15 +327,6 @@
font-size: 11px; 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) { :global(.subtext) {
font-size: 12.5px; font-size: 12.5px;
font-weight: 500; font-weight: 500;

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB