Initial SvelteKit frontend port of windwiderstand.de
Full parity with Astro site: content rows, post/tag routes, pagination, event badges + OSM map, comments, Live-Search via /api/search-index, CMS image proxy, RSS, sitemap. Deploy: Dockerfile + docker-compose.prod.yml + Gitea Actions workflow to build + push to git.pm86.de registry and ssh-deploy to Contabo.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
<script lang="ts">
|
||||
/** @type {{ id: string; label: string }[]} */
|
||||
let {
|
||||
tabs = [],
|
||||
selectedId = $bindable(''),
|
||||
labelledBy = '',
|
||||
} = $props();
|
||||
|
||||
$effect(() => {
|
||||
if (tabs.length && !selectedId) selectedId = tabs[0].id;
|
||||
});
|
||||
|
||||
function selectTab(id: string) {
|
||||
selectedId = id;
|
||||
}
|
||||
|
||||
function handleKeydown(e: KeyboardEvent) {
|
||||
const idx = tabs.findIndex((t) => t.id === selectedId);
|
||||
if (e.key === 'ArrowRight' && idx < tabs.length - 1) {
|
||||
e.preventDefault();
|
||||
selectedId = tabs[idx + 1].id;
|
||||
} else if (e.key === 'ArrowLeft' && idx > 0) {
|
||||
e.preventDefault();
|
||||
selectedId = tabs[idx - 1].id;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
role="tablist"
|
||||
aria-label={labelledBy || 'Tabs'}
|
||||
class="flex h-12 border-b border-stein-200"
|
||||
onkeydown={handleKeydown}
|
||||
>
|
||||
{#each tabs as tab}
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-selected={selectedId === tab.id}
|
||||
aria-controls="panel-{tab.id}"
|
||||
id="tab-{tab.id}"
|
||||
class="border-b-2 px-4 text-sm font-medium transition-colors {selectedId === tab.id
|
||||
? 'border-wald-500 text-wald-600 font-semibold'
|
||||
: 'border-transparent text-stein-500 hover:text-stein-700'}"
|
||||
tabindex={selectedId === tab.id ? 0 : -1}
|
||||
onclick={() => selectTab(tab.id)}
|
||||
>
|
||||
{tab.label}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
Reference in New Issue
Block a user