mirror of
https://github.com/ReVanced/revanced-website.git
synced 2026-01-27 12:51:03 +00:00
refactor: Rename Modals to Dialogs, Move Dialogs and Banners to layout
This commit is contained in:
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
25
src/lib/components/QRCode.svelte
Normal file
25
src/lib/components/QRCode.svelte
Normal 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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user