Files
svelte-mud/src/lib/components/SidebarTabs.svelte

133 lines
3.7 KiB
Svelte
Raw Normal View History

2025-04-22 19:55:19 +02:00
<script lang="ts">
import { createEventDispatcher } from 'svelte';
export let activeTab: 'profiles' | 'triggers' | 'settings' = 'profiles';
const dispatch = createEventDispatcher<{
tabChange: { tab: 'profiles' | 'triggers' | 'settings' }
}>();
// Handle keyboard navigation for sidebar tabs
function handleSidebarTabKeydown(event: KeyboardEvent) {
// Define the tab order
const tabs = ['profiles', 'triggers', 'settings'];
const currentIndex = tabs.indexOf(activeTab);
switch (event.key) {
case 'ArrowRight':
case 'ArrowDown':
// Move to next tab
event.preventDefault();
const nextIndex = (currentIndex + 1) % tabs.length;
dispatch('tabChange', { tab: tabs[nextIndex] as any });
document.getElementById(`tab-${tabs[nextIndex]}`)?.focus();
break;
case 'ArrowLeft':
case 'ArrowUp':
// Move to previous tab
event.preventDefault();
const prevIndex = (currentIndex - 1 + tabs.length) % tabs.length;
dispatch('tabChange', { tab: tabs[prevIndex] as any });
document.getElementById(`tab-${tabs[prevIndex]}`)?.focus();
break;
case 'Home':
// Move to first tab
event.preventDefault();
dispatch('tabChange', { tab: tabs[0] as any });
document.getElementById(`tab-${tabs[0]}`)?.focus();
break;
case 'End':
// Move to last tab
event.preventDefault();
dispatch('tabChange', { tab: tabs[tabs.length - 1] as any });
document.getElementById(`tab-${tabs[tabs.length - 1]}`)?.focus();
break;
case 'Enter':
case ' ':
// Activate current tab
event.preventDefault();
// The click handler will handle the tab change
break;
default:
// Let other keys function normally
break;
}
}
</script>
<div class="sidebar-tabs" role="tablist" aria-label="Sidebar tabs">
<button
class="sidebar-tab"
id="tab-profiles"
role="tab"
aria-controls="panel-profiles"
aria-selected={activeTab === 'profiles'}
class:active={activeTab === 'profiles'}
tabindex={activeTab === 'profiles' ? "0" : "-1"}
on:click={() => dispatch('tabChange', { tab: 'profiles' })}
on:keydown={handleSidebarTabKeydown}>
Profiles
</button>
<button
class="sidebar-tab"
id="tab-triggers"
role="tab"
aria-controls="panel-triggers"
aria-selected={activeTab === 'triggers'}
class:active={activeTab === 'triggers'}
tabindex={activeTab === 'triggers' ? "0" : "-1"}
on:click={() => dispatch('tabChange', { tab: 'triggers' })}
on:keydown={handleSidebarTabKeydown}>
Triggers
</button>
<button
class="sidebar-tab"
id="tab-settings"
role="tab"
aria-controls="panel-settings"
aria-selected={activeTab === 'settings'}
class:active={activeTab === 'settings'}
tabindex={activeTab === 'settings' ? "0" : "-1"}
on:click={() => dispatch('tabChange', { tab: 'settings' })}
on:keydown={handleSidebarTabKeydown}>
Settings
</button>
</div>
<style>
.sidebar-tabs {
display: flex;
border-bottom: 1px solid var(--color-border);
}
.sidebar-tab {
flex: 1;
padding: 0.5rem;
text-align: center;
background: none;
border: none;
cursor: pointer;
color: var(--color-text-muted);
}
.sidebar-tab.active {
color: var(--color-text);
border-bottom: 2px solid var(--color-primary);
}
.sidebar-tab:focus {
outline: 2px solid var(--color-primary);
outline-offset: -2px;
position: relative;
z-index: 2;
}
.sidebar-tab:focus:not(:focus-visible) {
outline: none;
}
</style>