Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
File size: 4,287 Bytes
3742813 5bb39a7 3742813 5bb39a7 3742813 5bb39a7 3742813 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | ---
interface Props {
title?: string;
class?: string;
[key: string]: any;
}
const { title, class: className, ...props } = Astro.props;
const wrapperClass = ["tabs", className].filter(Boolean).join(" ");
---
<div class={wrapperClass} {...props}>
<div class="tabs__nav" role="tablist">
{title && <span class="tabs__title">{title}</span>}
</div>
<div class="tabs__panels">
<slot />
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.tabs').forEach((tabsEl) => {
const nav = tabsEl.querySelector('.tabs__nav') as HTMLElement;
const panels = Array.from(tabsEl.querySelectorAll(':scope > .tabs__panels > .tab-panel')) as HTMLElement[];
if (!nav || panels.length === 0) return;
panels.forEach((panel, i) => {
const title = panel.dataset.tabTitle || `Tab ${i + 1}`;
const btn = document.createElement('button');
btn.className = 'tabs__btn';
btn.role = 'tab';
btn.textContent = title;
btn.setAttribute('aria-selected', 'false');
btn.addEventListener('click', () => activate(i));
nav.appendChild(btn);
});
const buttons = Array.from(nav.querySelectorAll('.tabs__btn')) as HTMLButtonElement[];
function activate(index: number) {
buttons.forEach((b, i) => {
const active = i === index;
b.classList.toggle('tabs__btn--active', active);
b.setAttribute('aria-selected', String(active));
});
panels.forEach((p, i) => {
p.hidden = i !== index;
});
}
// Keyboard navigation
nav.addEventListener('keydown', (e) => {
const current = buttons.findIndex(b => b === document.activeElement);
if (current < 0) return;
let next = current;
if (e.key === 'ArrowRight') next = (current + 1) % buttons.length;
else if (e.key === 'ArrowLeft') next = (current - 1 + buttons.length) % buttons.length;
else if (e.key === 'Home') next = 0;
else if (e.key === 'End') next = buttons.length - 1;
else return;
e.preventDefault();
buttons[next].focus();
activate(next);
});
activate(0);
});
});
</script>
<style>
.tabs {
margin: 0 0 var(--spacing-4);
border: 1px solid var(--border-color);
border-radius: var(--table-border-radius);
background: var(--surface-bg);
}
.tabs__nav {
display: flex;
flex-wrap: wrap;
gap: 0;
border-bottom: 1px solid var(--border-color);
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.tabs__nav::-webkit-scrollbar {
display: none;
}
.tabs__title {
padding: var(--spacing-2) var(--spacing-3);
border-right: 1px solid var(--border-color);
font-size: 0.8em;
font-weight: 700;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.05em;
white-space: nowrap;
display: flex;
align-items: center;
user-select: none;
}
.tabs__nav :global(.tabs__btn) {
flex: 1 1 0;
padding: var(--spacing-2) var(--spacing-3);
text-align: center;
border: none !important;
border-bottom: 2px solid transparent !important;
border-radius: 0 !important;
background: none !important;
font: inherit;
font-size: 0.9em;
font-weight: 600;
color: var(--text-muted) !important;
cursor: pointer;
white-space: nowrap;
margin-bottom: -1px;
transition: color 150ms ease, border-color 150ms ease;
filter: none !important;
}
.tabs__nav :global(.tabs__btn:hover) {
color: var(--text-color) !important;
filter: none !important;
}
.tabs__nav :global(.tabs__btn--active) {
color: var(--primary-color) !important;
border-bottom-color: var(--primary-color) !important;
}
.tabs__nav :global(.tabs__btn:focus-visible) {
outline: 2px solid var(--primary-color);
outline-offset: -2px;
border-radius: 2px;
}
.tabs__panels :global(.tab-panel) {
padding: 8px;
}
.tabs__panels :global(.tab-panel > *:first-child) {
margin-top: 0 !important;
}
.tabs__panels :global(.tab-panel > *:last-child) {
margin-bottom: 0 !important;
padding-bottom: 0 !important;
}
</style>
|