- Update to @tabler/icons-svelte-runes for a more 'Svente 5' experience. - Adjust error handling in load functions, plain error thrown for handling in the error handler. - Fix the warning regarding a click handler attached to an <img> on the About page - Re-organise component directory - Remove unused font declarations from global.css Features: - Add service-worker caching of FilterData API response to allow for filtering to work fully offline -
144 lines
2.8 KiB
Svelte
144 lines
2.8 KiB
Svelte
<script lang="ts">
|
|
import { slide } from 'svelte/transition';
|
|
import type { ApiStationsBoard } from '@owlboard/owlboard-ts';
|
|
import { IconAlertOctagonFilled, IconChevronDownFilled } from '@tabler/icons-svelte-runes';
|
|
|
|
let { messages = [] }: { messages: ApiStationsBoard.BoardMsgs[] } = $props();
|
|
let isOpen = $state(false);
|
|
</script>
|
|
|
|
{#if messages.length > 0}
|
|
<aside class="alert-card">
|
|
<button onclick={() => (isOpen = !isOpen)} class="trigger" class:active={isOpen}>
|
|
<span class="warning-icon">
|
|
<IconAlertOctagonFilled />
|
|
</span>
|
|
<span class="summary">
|
|
{messages.length}
|
|
{messages.length === 1 ? 'active alert' : 'active alerts'}
|
|
</span>
|
|
<span class="chevron" class:rotated={isOpen}><IconChevronDownFilled /></span>
|
|
</button>
|
|
|
|
{#if isOpen}
|
|
<div transition:slide={{ duration: 450 }} class="content">
|
|
{#each messages as msg}
|
|
<div class="message-item">
|
|
<p class="message-text">
|
|
{msg.t}
|
|
{#if msg.l && msg.lt}
|
|
<a href={msg.l} target="_blank" rel="noopener noreferrer" class="alert-link">
|
|
{msg.lt}
|
|
</a>
|
|
{/if}
|
|
</p>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
</aside>
|
|
{/if}
|
|
|
|
<style>
|
|
.alert-card {
|
|
margin: 0;
|
|
border-radius: 8px;
|
|
background: transparent;
|
|
position: relative;
|
|
z-index: 10;
|
|
margin-bottom: 0;
|
|
width: 95%;
|
|
max-width: 750px;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
|
|
.alert-card:active {
|
|
filter: brightness(1.2);
|
|
}
|
|
|
|
.trigger {
|
|
width: 100%;
|
|
display: flex;
|
|
border-radius: 8px;
|
|
align-items: center;
|
|
padding: 0.1rem 1.05rem;
|
|
background: var(--alert-orange);
|
|
color: white;
|
|
border: none;
|
|
cursor: pointer;
|
|
text-align: left;
|
|
position: relative;
|
|
z-index: 2;
|
|
height: 40px;
|
|
transition: all 0.65s 0.2s;
|
|
}
|
|
|
|
.trigger.active {
|
|
border-radius: 8px 8px 0 0;
|
|
transition: all 0.01s 0s;
|
|
}
|
|
|
|
.warning-icon {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-left: 0;
|
|
margin-top: 0;
|
|
margin-bottom: 0;
|
|
margin-right: 1rem;
|
|
padding: 0;
|
|
}
|
|
|
|
.summary {
|
|
flex: 1;
|
|
font-family: 'URW Gothic', sans-serif;
|
|
letter-spacing: 0.05em;
|
|
font-weight: 600;
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.chevron {
|
|
transition: transform 0.5s ease;
|
|
display: inline-flex;
|
|
justify-content: center;
|
|
}
|
|
|
|
.chevron.rotated {
|
|
transform: rotateX(180deg);
|
|
}
|
|
|
|
.content {
|
|
padding: 1rem;
|
|
position: absolute;
|
|
top: 40px;
|
|
border-radius: 0 0 8px 8px;
|
|
left: 0;
|
|
right: 0;
|
|
background: var(--alert-orange);
|
|
filter: brightness(1.2);
|
|
color: var(--color-title);
|
|
font-family: 'URW Gothic', sans-serif;
|
|
font-size: 1rem;
|
|
font-weight: 600;
|
|
line-height: 1.2;
|
|
z-index: 1;
|
|
}
|
|
|
|
.message-item {
|
|
margin: 10px;
|
|
padding-bottom: 0.75rem;
|
|
}
|
|
|
|
.message-item p {
|
|
margin: 0;
|
|
}
|
|
|
|
.message-item a {
|
|
color: #f2ff00;
|
|
}
|
|
|
|
.message-item:last-child {
|
|
padding-bottom: 0;
|
|
}
|
|
</style>
|