mirror of
https://github.com/revanced/revanced-website.git
synced 2025-05-25 11:02:17 +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 {
|
||||
--font-family: 'Manrope', sans-serif;
|
||||
|
||||
--text-one: hsl(206, 100%, 94%);
|
||||
--surface-one: hsl(206, 100%, 94%);
|
||||
|
||||
--primary: hsl(206, 100%, 81%);
|
||||
|
||||
--secondary: hsl(208, 75%, 82%);
|
||||
|
||||
--tertiary: hsla(205, 91%, 69%, 0.15);
|
||||
|
||||
--background-one: hsl(252, 10%, 11%);
|
||||
--surface-two: hsl(252, 10%, 11%);
|
||||
|
||||
--surface-three: hsl(210, 14%, 17%);
|
||||
|
||||
--surface-four: hsl(212, 19%, 19%);
|
||||
--text-two: hsl(212, 19%, 19%);
|
||||
|
||||
--border: hsl(221, 17%, 26%);
|
||||
--surface-five: hsl(221, 17%, 26%);
|
||||
|
||||
--text-three: hsl(226, 48%, 18%);
|
||||
|
||||
--text-four: 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),
|
||||
0px 2px 4px -1px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
type: ButtonType;
|
||||
icon?: typeof import('~icons/mdi').default;
|
||||
iconColor?: string;
|
||||
color?: string;
|
||||
label?: string;
|
||||
children?: Snippet;
|
||||
};
|
||||
@ -40,6 +41,7 @@
|
||||
label = '',
|
||||
onclick = () => {},
|
||||
iconColor = 'currentColor',
|
||||
color = 'currentColor',
|
||||
target = '_self'
|
||||
}: Props = $props();
|
||||
|
||||
@ -47,6 +49,14 @@
|
||||
const navBarButtonSelected = type === 'navbar' && href && new URL(href, page.url.href).pathname === page.url.pathname;
|
||||
</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}
|
||||
<a
|
||||
{href}
|
||||
@ -54,21 +64,11 @@
|
||||
class={`button-${type}${navBarButtonSelected ? ' selected' : ''}`}
|
||||
aria-label={label}
|
||||
>
|
||||
{#if Icon}
|
||||
<Icon color={iconColor} />
|
||||
{/if}
|
||||
{#if type === 'navbar'}
|
||||
<span>{@render children?.()}</span>
|
||||
{:else}
|
||||
{@render children?.()}
|
||||
{/if}
|
||||
{@render content()}
|
||||
</a>
|
||||
{:else}
|
||||
<button {onclick} class={`button-${type}`} aria-label={label}>
|
||||
{#if Icon}
|
||||
<Icon color={iconColor} />
|
||||
{/if}
|
||||
{@render children?.()}
|
||||
{@render content()}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
@ -114,6 +114,11 @@
|
||||
letter-spacing: 0.01rem;
|
||||
}
|
||||
|
||||
.button-text:hover .content {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: currentColor;
|
||||
}
|
||||
|
||||
button:not(.button-navbar):hover,
|
||||
a:not(.button-navbar):hover {
|
||||
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