mirror of
https://github.com/revanced/revanced-website.git
synced 2025-05-25 19:12:12 +02:00
feat: add <Banner />
component
This commit is contained in:
parent
bee1c8ccfe
commit
f3c3660159
27
src/app.css
27
src/app.css
@ -2,27 +2,44 @@
|
|||||||
|
|
||||||
:root {
|
:root {
|
||||||
--font-family: 'Manrope', sans-serif;
|
--font-family: 'Manrope', sans-serif;
|
||||||
|
|
||||||
--text-one: hsl(206, 100%, 94%);
|
--text-one: hsl(206, 100%, 94%);
|
||||||
--surface-one: hsl(206, 100%, 94%);
|
--surface-one: hsl(206, 100%, 94%);
|
||||||
|
|
||||||
--primary: hsl(206, 100%, 81%);
|
--primary: hsl(206, 100%, 81%);
|
||||||
|
|
||||||
--secondary: hsl(208, 75%, 82%);
|
--secondary: hsl(208, 75%, 82%);
|
||||||
|
|
||||||
--tertiary: hsla(205, 91%, 69%, 0.15);
|
--tertiary: hsla(205, 91%, 69%, 0.15);
|
||||||
|
|
||||||
--background-one: hsl(252, 10%, 11%);
|
--background-one: hsl(252, 10%, 11%);
|
||||||
--surface-two: hsl(252, 10%, 11%);
|
--surface-two: hsl(252, 10%, 11%);
|
||||||
|
|
||||||
--surface-three: hsl(210, 14%, 17%);
|
--surface-three: hsl(210, 14%, 17%);
|
||||||
|
|
||||||
--surface-four: hsl(212, 19%, 19%);
|
--surface-four: hsl(212, 19%, 19%);
|
||||||
--text-two: hsl(212, 19%, 19%);
|
--text-two: hsl(212, 19%, 19%);
|
||||||
|
|
||||||
--border: hsl(221, 17%, 26%);
|
--border: hsl(221, 17%, 26%);
|
||||||
--surface-five: hsl(221, 17%, 26%);
|
--surface-five: hsl(221, 17%, 26%);
|
||||||
|
|
||||||
--text-three: hsl(226, 48%, 18%);
|
--text-three: hsl(226, 48%, 18%);
|
||||||
|
|
||||||
--text-four: hsl(208, 30%, 75%);
|
--text-four: hsl(208, 30%, 75%);
|
||||||
--surface-six: hsl(208, 30%, 75%);
|
--surface-six: hsl(208, 30%, 75%);
|
||||||
--surface-seven: hsl(230, 9%, 13%);
|
|
||||||
--surface-eight: hsl(240, 9%, 13.5%);
|
|
||||||
--surface-nine: hsl(230, 9.5%, 17.5%);
|
|
||||||
--red-one: hsl(333, 84%, 62%);
|
|
||||||
|
|
||||||
--bezier-one: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
--surface-seven: hsl(230, 9%, 13%);
|
||||||
|
|
||||||
|
--surface-eight: hsl(240, 9%, 13.5%);
|
||||||
|
|
||||||
|
--surface-nine: hsl(230, 9.5%, 17.5%);
|
||||||
|
|
||||||
|
--red-one: hsl(333, 84%, 62%);
|
||||||
|
--red-two: hsl(357, 74%, 60%);
|
||||||
|
|
||||||
|
--yellow-one: hsl(59, 100%, 72%);
|
||||||
|
|
||||||
|
--bezier-one: ease-out;
|
||||||
--drop-shadow-one: 0px 4px 5px 0px rgba(0, 0, 0, 0.14), 0px 1px 10px 0px rgba(0, 0, 0, 0.12),
|
--drop-shadow-one: 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);
|
0px 2px 4px -1px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
type: ButtonType;
|
type: ButtonType;
|
||||||
icon?: typeof import('~icons/mdi').default;
|
icon?: typeof import('~icons/mdi').default;
|
||||||
iconColor?: string;
|
iconColor?: string;
|
||||||
|
color?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
};
|
};
|
||||||
@ -40,6 +41,7 @@
|
|||||||
label = '',
|
label = '',
|
||||||
onclick = () => {},
|
onclick = () => {},
|
||||||
iconColor = 'currentColor',
|
iconColor = 'currentColor',
|
||||||
|
color = 'currentColor',
|
||||||
target = '_self'
|
target = '_self'
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
@ -47,6 +49,14 @@
|
|||||||
const navBarButtonSelected = type === 'navbar' && href && new URL(href, page.url.href).pathname === page.url.pathname;
|
const navBarButtonSelected = type === 'navbar' && href && new URL(href, page.url.href).pathname === page.url.pathname;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- reusable snippet to remove duplicate code -->
|
||||||
|
{#snippet content()}
|
||||||
|
{#if Icon}
|
||||||
|
<Icon color={iconColor} />
|
||||||
|
{/if}
|
||||||
|
<span class="content" style="color: {color};">{@render children?.()}</span>
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
{#if href}
|
{#if href}
|
||||||
<a
|
<a
|
||||||
{href}
|
{href}
|
||||||
@ -54,21 +64,11 @@
|
|||||||
class={`button-${type}${navBarButtonSelected ? ' selected' : ''}`}
|
class={`button-${type}${navBarButtonSelected ? ' selected' : ''}`}
|
||||||
aria-label={label}
|
aria-label={label}
|
||||||
>
|
>
|
||||||
{#if Icon}
|
{@render content()}
|
||||||
<Icon color={iconColor} />
|
|
||||||
{/if}
|
|
||||||
{#if type === 'navbar'}
|
|
||||||
<span>{@render children?.()}</span>
|
|
||||||
{:else}
|
|
||||||
{@render children?.()}
|
|
||||||
{/if}
|
|
||||||
</a>
|
</a>
|
||||||
{:else}
|
{:else}
|
||||||
<button {onclick} class={`button-${type}`} aria-label={label}>
|
<button {onclick} class={`button-${type}`} aria-label={label}>
|
||||||
{#if Icon}
|
{@render content()}
|
||||||
<Icon color={iconColor} />
|
|
||||||
{/if}
|
|
||||||
{@render children?.()}
|
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
@ -114,6 +114,11 @@
|
|||||||
letter-spacing: 0.01rem;
|
letter-spacing: 0.01rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button-text:hover .content {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
button:not(.button-navbar):hover,
|
button:not(.button-navbar):hover,
|
||||||
a:not(.button-navbar):hover {
|
a:not(.button-navbar):hover {
|
||||||
filter: brightness(85%);
|
filter: brightness(85%);
|
||||||
|
148
src/lib/components/organisms/Banner.svelte
Normal file
148
src/lib/components/organisms/Banner.svelte
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Info from '~icons/mdi/information-outline';
|
||||||
|
import Warning from '~icons/mdi/alert-outline';
|
||||||
|
import Caution from '~icons/mdi/alert-circle-outline';
|
||||||
|
import Close from '~icons/mdi/close';
|
||||||
|
import { slide } from 'svelte/transition';
|
||||||
|
|
||||||
|
import type { Snippet } from 'svelte';
|
||||||
|
import Button from '$components/atoms/Button.svelte';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: Snippet;
|
||||||
|
level: 'info' | 'warning' | 'caution';
|
||||||
|
permanent?: boolean;
|
||||||
|
onDismiss?: () => void;
|
||||||
|
};
|
||||||
|
let { children, level = 'info', permanent = false, onDismiss }: Props = $props();
|
||||||
|
|
||||||
|
const icons = { info: Info, warning: Warning, caution: Caution };
|
||||||
|
const Icon = icons[level];
|
||||||
|
const dismissButtonColor = level === 'info' ? 'var(--text-one)' : '#000';
|
||||||
|
|
||||||
|
let closed = $state(!permanent);
|
||||||
|
|
||||||
|
if (!permanent)
|
||||||
|
$effect(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
closed = false;
|
||||||
|
}, 1); // trigger the in transition
|
||||||
|
});
|
||||||
|
|
||||||
|
const dismissBanner = () => {
|
||||||
|
closed = true;
|
||||||
|
onDismiss?.();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if permanent}
|
||||||
|
<div class="banner-container permanent">
|
||||||
|
<div class="banner {level}">
|
||||||
|
<div class="banner-text">
|
||||||
|
<Icon width={24} height={24} />
|
||||||
|
{@render children()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else if !closed}
|
||||||
|
<div class="banner-container" transition:slide={{ duration: 300, axis: 'y' }}>
|
||||||
|
<div class="banner {level}">
|
||||||
|
<div class="banner-text">
|
||||||
|
<Icon width={32} height={32} />
|
||||||
|
{@render children()}
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
icon={Close}
|
||||||
|
onclick={dismissBanner}
|
||||||
|
iconColor={dismissButtonColor}
|
||||||
|
color={dismissButtonColor}
|
||||||
|
>
|
||||||
|
Dismiss
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.banner-container,
|
||||||
|
.banner-container *,
|
||||||
|
.banner-container :global(*) {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-text :global(a) {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-text :global(a:hover) {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
/* optional: to prevent content spill during transition */
|
||||||
|
}
|
||||||
|
|
||||||
|
.permanent {
|
||||||
|
font-size: 0.87rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner {
|
||||||
|
margin: 0;
|
||||||
|
padding: 1.5rem 1.7rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1.3rem;
|
||||||
|
margin: 0.7rem 1rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permanent > .banner {
|
||||||
|
padding: 0.5rem 0.7rem;
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-text {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex: 1;
|
||||||
|
gap: 0.55rem;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner.info {
|
||||||
|
background-color: var(--surface-four);
|
||||||
|
color: var(--text-one);
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner.warning {
|
||||||
|
background-color: var(--yellow-one);
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner.caution {
|
||||||
|
background-color: var(--red-two);
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
|
.banner {
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 1.1rem 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner > :global(button) {
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,18 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Banner from '$components/organisms/Banner.svelte';
|
||||||
|
import Button from '$components/atoms/Button.svelte';
|
||||||
|
import api from '$api';
|
||||||
|
|
||||||
|
const statusUrl = 'https://status.revanced.app/'; // TODO: replace with env variable
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await api.general.ping() then apiUp}
|
||||||
|
{#if !apiUp}
|
||||||
|
<Banner level="caution">
|
||||||
|
The API is currently unresponsive and some services may not work correctly.
|
||||||
|
{#if statusUrl}
|
||||||
|
Check the <Button type="text" href={statusUrl}>status page</Button> for updates.
|
||||||
|
{/if}
|
||||||
|
</Banner>
|
||||||
|
{/if}
|
||||||
|
{/await}
|
Loading…
x
Reference in New Issue
Block a user