refactor(api): Remove deprecated api-tabs component

Remove unused tabs component and partials:
- Delete assets/js/components/api-tabs.ts
- Delete layouts/partials/api/tabs.html
- Delete layouts/partials/api/tab-panels.html
- Remove ApiTabs from main.js component registry

The new architecture renders content directly without tabs.
worktree-2025-12-30T19-16-55
Jason Stirnaman 2025-12-12 13:14:41 -06:00
parent b1a05aaa64
commit 4c5d99d8cf
4 changed files with 0 additions and 187 deletions

View File

@ -1,144 +0,0 @@
/**
* API Tabs Component
*
* Handles tab switching for API reference documentation.
* Uses data-tab and data-tab-panel attributes for explicit panel targeting,
* unlike the generic tabs which use positional indexing.
*
* Features:
* - Explicit panel targeting via data-tab-panel
* - Deep linking via URL hash
* - Browser back/forward navigation support
* - Custom event dispatch for TOC updates
*
* Usage:
* <div class="api-tabs-wrapper" data-component="api-tabs">
* <div class="api-tabs-nav">
* <a href="#operations" data-tab="operations" class="is-active">Operations</a>
* <a href="#auth" data-tab="auth">Authentication</a>
* </div>
* </div>
* <div class="api-tab-panels">
* <section data-tab-panel="operations">...</section>
* <section data-tab-panel="auth" style="display:none">...</section>
* </div>
*/
interface ComponentOptions {
component: HTMLElement;
}
/**
* Find the panels container (sibling element after tabs)
*/
function findPanelsContainer(tabsWrapper: HTMLElement): HTMLElement | null {
let sibling = tabsWrapper.nextElementSibling;
while (sibling) {
if (sibling.classList.contains('api-tab-panels')) {
return sibling as HTMLElement;
}
sibling = sibling.nextElementSibling;
}
return null;
}
/**
* Switch to a specific tab
*/
function switchTab(
tabsWrapper: HTMLElement,
panelsContainer: HTMLElement,
tabId: string,
updateHash = true
): void {
// Update active tab
const tabs = tabsWrapper.querySelectorAll<HTMLAnchorElement>('[data-tab]');
tabs.forEach((tab) => {
if (tab.dataset.tab === tabId) {
tab.classList.add('is-active');
} else {
tab.classList.remove('is-active');
}
});
// Update visible panel
const panels =
panelsContainer.querySelectorAll<HTMLElement>('[data-tab-panel]');
panels.forEach((panel) => {
if (panel.dataset.tabPanel === tabId) {
panel.style.display = 'block';
} else {
panel.style.display = 'none';
}
});
// Update URL hash without scrolling
if (updateHash) {
history.replaceState(null, '', '#' + tabId);
}
// Dispatch custom event for TOC update
document.dispatchEvent(
new CustomEvent('api-tab-change', { detail: { tab: tabId } })
);
}
/**
* Get tab ID from URL hash
*/
function getTabFromHash(): string | null {
const hash = window.location.hash.substring(1);
return hash || null;
}
/**
* Initialize API Tabs component
*/
export default function ApiTabs({ component }: ComponentOptions): void {
const panelsContainer = findPanelsContainer(component);
if (!panelsContainer) {
console.warn('[API Tabs] No .api-tab-panels container found');
return;
}
const tabs = component.querySelectorAll<HTMLAnchorElement>('[data-tab]');
if (tabs.length === 0) {
console.warn('[API Tabs] No tabs found with data-tab attribute');
return;
}
// Handle tab clicks
tabs.forEach((tab) => {
tab.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation(); // Prevent other tab handlers from firing
const tabId = tab.dataset.tab;
if (tabId) {
switchTab(component, panelsContainer, tabId);
}
});
});
// Handle deep linking via URL hash on load
const hashTab = getTabFromHash();
if (hashTab) {
const matchingTab = component.querySelector(`[data-tab="${hashTab}"]`);
if (matchingTab) {
switchTab(component, panelsContainer, hashTab, false);
}
}
// Handle browser back/forward navigation
window.addEventListener('hashchange', () => {
const newTabId = getTabFromHash();
if (newTabId) {
const matchingTab = component.querySelector(`[data-tab="${newTabId}"]`);
if (matchingTab) {
switchTab(component, panelsContainer, newTabId, false);
}
}
});
}

View File

@ -47,7 +47,6 @@ import { SidebarToggle } from './sidebar-toggle.js';
import Theme from './theme.js';
import ThemeSwitch from './theme-switch.js';
import ApiRapiDoc from './components/api-rapidoc.ts';
import ApiTabs from './components/api-tabs.ts';
import ApiToc from './components/api-toc.ts';
import RapiDocMini from './components/rapidoc-mini.ts';
@ -82,7 +81,6 @@ const componentRegistry = {
theme: Theme,
'theme-switch': ThemeSwitch,
'api-rapidoc': ApiRapiDoc,
'api-tabs': ApiTabs,
'api-toc': ApiToc,
'rapidoc-mini': RapiDocMini,
};

View File

@ -1,31 +0,0 @@
{{/*
API Reference Tab Panels (DEPRECATED)
This partial is kept for backward compatibility.
The new architecture renders content directly in layouts:
- layouts/api/list.html: Tag pages with operations list
- layouts/api/single.html: Individual operation pages with RapiDoc
For conceptual pages (isConceptual: true), renders tag description content.
For operational pages, renders the API documentation via RapiDoc.
*/}}
{{ $isConceptual := .Params.isConceptual | default false }}
{{ if $isConceptual }}
{{/* Conceptual Page - Display tag description content only */}}
<section class="api-conceptual-content">
{{ with .Content }}
{{ . }}
{{ else }}
{{ with .Params.tagDescription }}
{{ . | markdownify }}
{{ end }}
{{ end }}
</section>
{{ else }}
{{/* Operations Page - RapiDoc renderer */}}
<section class="api-operations-panel">
{{ partial "api/rapidoc.html" . }}
</section>
{{ end }}

View File

@ -1,10 +0,0 @@
{{/*
API Reference Page Tabs (DEPRECATED)
This partial is kept for backward compatibility but renders nothing.
The new architecture uses:
- Tag pages (list.html): Display operations list directly
- Operation pages (single.html): Display RapiDoc for single operation
*/}}
{{/* No tabs rendered - using simplified layout */}}