refactor: Rename Modals to Dialogs, Move Dialogs and Banners to layout

This commit is contained in:
Ushie
2025-06-14 04:58:38 +03:00
parent 32c0bbb3d4
commit b4eed9ac6a
23 changed files with 458 additions and 420 deletions

View File

@@ -1,105 +0,0 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import Close from 'svelte-material-icons/Close.svelte';
import ArrowRight from 'svelte-material-icons/ArrowRight.svelte';
import Button from './Button.svelte';
export let title: string;
export let description: string | undefined = undefined;
export let buttonText: string | undefined = undefined;
export let buttonOnClick: any | undefined = undefined;
export let level: 'info' | 'caution' = 'info';
export let permanent: boolean = false;
export let onDismiss: () => void = () => {};
const dispatch = createEventDispatcher();
let closed: boolean = false;
function getVariant(level: string): 'default' | 'onDangerBackground' {
return level === 'caution' ? 'onDangerBackground' : 'default';
}
const dismiss = () => {
if (onDismiss) onDismiss();
closed = true;
dispatch('dismissed');
};
</script>
{#if !closed}
<div class="banner {level}" class:permanent>
<div class="text">
<h1 id="title">{title}</h1>
<h2 id="description">{description}</h2>
</div>
<div class="actions">
{#if !permanent}
<Button type={'icon'} icon={Close} on:click={dismiss} />
{/if}
{#if buttonText && buttonOnClick}
<Button variant={getVariant(level)} on:click={buttonOnClick}>
{buttonText}
<ArrowRight />
</Button>
{/if}
</div>
</div>
{/if}
<style lang="scss">
#title {
line-height: 26px;
color: currentColor;
font-size: 20px;
}
#description {
line-height: 20px;
color: currentColor;
font-size: 14px;
}
.banner {
display: flex;
justify-content: space-between;
box-sizing: border-box;
gap: 1.3rem;
width: 100%;
margin: 0;
padding: 24px 40px;
border-radius: 0;
font-size: 0.87rem;
&.info {
background-color: var(--surface-four);
color: var(--text-one);
#description {
color: var(--text-four);
}
}
&.caution {
background-color: var(--red-three);
color: #601410;
}
@media (max-width: 767px) {
flex-direction: column;
padding: 1.1rem 1.3rem;
}
.text {
display: flex;
flex-direction: column;
flex: 1;
gap: 0.55rem;
word-wrap: break-word;
}
.actions {
display: flex;
justify-content: end;
gap: 1rem;
}
}
</style>

View File

@@ -1,185 +0,0 @@
<script lang="ts">
import { fade } from 'svelte/transition';
import { quadInOut } from 'svelte/easing';
import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
export let modalOpen = false;
export let fullscreen = false;
export let notDismissible = false;
let element: HTMLDialogElement;
let y = 0;
function parseScroll() {
y = element.scrollTop;
}
</script>
{#if modalOpen}
<div
class="overlay"
on:click={() => {
if (!notDismissible) modalOpen = !modalOpen;
}}
on:keypress={() => {
if (!notDismissible) modalOpen = !modalOpen;
}}
transition:fade={{ easing: quadInOut, duration: 150 }}
/>
<dialog
class="modal"
class:fullscreen
class:scrolled={y > 10}
aria-modal="true"
bind:this={element}
on:scroll={parseScroll}
transition:fade={{ easing: quadInOut, duration: 150 }}
>
<div class="top">
<div class="title" class:hasIcon={$$slots.icon}>
{#if fullscreen}
<button id="back-button" on:click={() => (modalOpen = !modalOpen)}>
<ArrowLeft size="24px" color="var(--surface-six)" />
</button>
{/if}
{#if $$slots.icon}
<slot name="icon" />
{/if}
{#if $$slots.title}
<h3>
<slot name="title" />
</h3>
{/if}
</div>
{#if $$slots.description}
<p>
<slot name="description" />
</p>
{/if}
<div class="slot"><slot /></div>
{#if $$slots.buttons}
<div class="buttons">
<slot name="buttons" />
</div>
{/if}
</div>
</dialog>
{/if}
<style>
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 6;
}
.top {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
}
.title {
display: flex;
align-items: center;
gap: 1rem;
width: 100%;
background-color: var(--surface-seven);
}
.buttons {
display: flex;
gap: 2rem;
justify-content: flex-end;
width: 100%;
}
#back-button {
cursor: pointer;
}
.hasIcon {
flex-direction: column;
}
.modal {
position: fixed;
width: min(85%, 425px);
max-height: 75%;
overflow-y: scroll;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: none;
border-radius: 26px;
background-color: var(--surface-seven);
display: flex;
gap: 5%;
white-space: normal;
display: flex;
flex-direction: column;
gap: 2px;
z-index: 7;
padding: 32px;
box-shadow:
0px 4px 5px 0px rgba(0, 0, 0, 0.14),
0px 1px 10px 0px rgba(0, 0, 0, 0.12),
0px 2px 4px -1px rgba(0, 0, 0, 0.2);
scrollbar-width: none;
-ms-overflow-style: none;
}
button {
padding: 0;
margin: 0;
border: none;
background-color: transparent;
display: flex;
align-items: center;
}
.fullscreen {
padding: 0;
max-height: 100%;
width: 100%;
border-radius: 0;
}
.fullscreen .slot {
padding: 0 32px 32px;
}
.fullscreen .title {
justify-content: flex-start;
position: sticky;
padding: 32px;
padding-bottom: 0.75rem;
top: 0;
left: 0;
}
.fullscreen.scrolled .title {
border-bottom: 1px solid var(--border);
}
.slot {
display: flex;
flex-direction: column;
align-content: center;
width: 100%;
}
.modal::-webkit-scrollbar {
display: none;
}
</style>

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import ImageModal from '$lib/components/ImageModal.svelte';
import ImageDialog from '$layout/Dialogs/ImageDialog.svelte';
export let images: string[];
export let columns: number = 3;
@@ -7,14 +7,14 @@
let selectedImage: { src: string; alt: string } | null = null;
function openModal(image: string, index: number) {
function openDialog(image: string, index: number) {
selectedImage = {
src: image,
alt: `Gallery image ${index + 1}`
};
}
function closeModal() {
function closeDialog() {
selectedImage = null;
}
</script>
@@ -28,8 +28,8 @@
src={image}
alt={`Gallery image ${i + 1}`}
loading="lazy"
on:click={() => openModal(image, i)}
on:keydown={(e) => e.key === 'Enter' && openModal(image, i)}
on:click={() => openDialog(image, i)}
on:keydown={(e) => e.key === 'Enter' && openDialog(image, i)}
tabindex="0"
/>
</div>
@@ -37,7 +37,7 @@
</div>
{#if selectedImage}
<ImageModal src={selectedImage.src} alt={selectedImage.alt} on:close={closeModal} />
<ImageDialog src={selectedImage.src} alt={selectedImage.alt} on:close={closeDialog} />
{/if}
<style>

View File

@@ -1,76 +0,0 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
export let src: string;
export let alt: string;
const dispatch = createEventDispatcher();
function closeModal() {
dispatch('close');
}
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Escape') closeModal();
}
</script>
<svelte:window on:keydown={handleKeydown} />
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="modal-overlay" on:click={closeModal} transition:fade={{ duration: 175 }}>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="modal-content" on:click|stopPropagation transition:fade={{ duration: 175 }}>
<button class="close-button" on:click={closeModal}>×</button>
<img {src} {alt} />
</div>
</div>
<style>
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.85);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
padding: 2rem;
box-sizing: border-box;
}
.modal-content {
position: relative;
max-width: 90vw;
max-height: 90vh;
}
img {
max-width: 100%;
max-height: 90vh;
object-fit: contain;
}
.close-button {
position: absolute;
top: -1rem;
right: -2rem;
background: none;
border: none;
color: white;
font-size: 2rem;
cursor: pointer;
padding: 0.5rem;
line-height: 1;
z-index: 1001;
}
.close-button:hover {
color: #ddd;
}
</style>

View File

@@ -0,0 +1,25 @@
<script lang="ts">
import { onMount } from 'svelte';
import QRious from 'qrious/dist/qrious';
export let codeValue: string;
export let squareSize: number = 150;
onMount(() => {
new QRious({
element: document.getElementById('qrcode'),
value: codeValue,
size: squareSize
});
});
</script>
<canvas id="qrcode" />
<style>
canvas {
border-radius: 0.5rem;
background-color: white;
padding: 0.25rem;
}
</style>

View File

@@ -7,7 +7,7 @@
viewBox="0 0 1440 500"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="none"
style="opacity: {visibility ? '100%' : '0'}"
style="opacity: {visibility ? '100%' : '0'}; height: {visibility ? '40vh' : '0px'}"
>
<path class="wave" />
</svg>
@@ -19,10 +19,9 @@
bottom: -1px;
z-index: -1;
width: 100%;
height: 40vh;
}
@media screen and (max-height: 780px) {
@media (max-height: 780px) {
svg {
opacity: 0 !important;
}