infinite-agents-public/src_enhanced/ui_enhanced_7.html

1079 lines
38 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tab Navigation Enhanced</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: #f8f9fa;
color: #1a1a1a;
line-height: 1.6;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
main {
width: 100%;
max-width: 1200px;
background: white;
border-radius: 16px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
overflow: hidden;
}
h1 {
text-align: center;
padding: 40px 20px 20px;
font-size: 2rem;
font-weight: 600;
color: #1a1a1a;
}
.component-showcase {
padding: 20px;
}
/* Tab Container */
.tab-container {
margin-bottom: 40px;
background: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
overflow: hidden;
}
/* Tab Header */
.tab-header {
position: relative;
background: #f8f9fa;
border-bottom: 1px solid #e5e7eb;
overflow: hidden;
}
/* Tab List */
.tab-list {
display: flex;
position: relative;
overflow-x: auto;
scroll-behavior: smooth;
scrollbar-width: none;
-ms-overflow-style: none;
}
.tab-list::-webkit-scrollbar {
display: none;
}
/* Scroll Indicators */
.scroll-indicator {
position: absolute;
top: 0;
bottom: 0;
width: 40px;
pointer-events: none;
z-index: 10;
opacity: 0;
transition: opacity 0.3s ease;
}
.scroll-indicator.left {
left: 0;
background: linear-gradient(to right, rgba(248, 249, 250, 1), rgba(248, 249, 250, 0));
}
.scroll-indicator.right {
right: 0;
background: linear-gradient(to left, rgba(248, 249, 250, 1), rgba(248, 249, 250, 0));
}
.scroll-indicator.visible {
opacity: 1;
}
/* Tab Items */
.tab-item {
position: relative;
padding: 16px 24px;
border: none;
background: none;
color: #6b7280;
font-size: 14px;
font-weight: 500;
cursor: pointer;
white-space: nowrap;
transition: color 0.2s ease, background-color 0.2s ease;
display: flex;
align-items: center;
gap: 8px;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
.tab-item:hover {
color: #374151;
background-color: rgba(0, 0, 0, 0.04);
}
.tab-item:focus {
outline: none;
box-shadow: inset 0 0 0 2px #3b82f6;
border-radius: 4px;
}
.tab-item.active {
color: #3b82f6;
}
.tab-item.loading {
color: #9ca3af;
pointer-events: none;
}
.tab-item.dragging {
opacity: 0.5;
cursor: grabbing;
}
/* Close Button */
.tab-close {
margin-left: 8px;
padding: 2px;
background: none;
border: none;
color: #9ca3af;
cursor: pointer;
border-radius: 4px;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
}
.tab-close:hover {
color: #ef4444;
background: rgba(239, 68, 68, 0.1);
}
.tab-close svg {
width: 14px;
height: 14px;
}
/* Active Tab Indicator */
.tab-indicator {
position: absolute;
bottom: 0;
height: 3px;
background: #3b82f6;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
border-radius: 3px 3px 0 0;
}
/* Tab Content */
.tab-content {
position: relative;
min-height: 300px;
overflow: hidden;
}
.tab-panel {
position: absolute;
top: 0;
left: 0;
right: 0;
padding: 32px;
opacity: 0;
transform: translateX(20px);
transition: opacity 0.3s ease, transform 0.3s ease;
pointer-events: none;
}
.tab-panel.active {
opacity: 1;
transform: translateX(0);
pointer-events: auto;
position: relative;
}
.tab-panel.fade-out {
transform: translateX(-20px);
}
/* Loading State */
.tab-loading {
display: flex;
align-items: center;
justify-content: center;
padding: 60px;
}
.spinner {
width: 32px;
height: 32px;
border: 3px solid #e5e7eb;
border-top-color: #3b82f6;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Vertical Tabs */
.tab-container.vertical {
display: flex;
}
.tab-container.vertical .tab-header {
width: 200px;
border-bottom: none;
border-right: 1px solid #e5e7eb;
}
.tab-container.vertical .tab-list {
flex-direction: column;
overflow-x: hidden;
overflow-y: auto;
}
.tab-container.vertical .tab-indicator {
width: 3px;
height: auto;
right: 0;
left: auto;
bottom: auto;
}
.tab-container.vertical .tab-content {
flex: 1;
}
/* Mobile Responsive */
@media (max-width: 640px) {
.tab-item {
padding: 12px 16px;
font-size: 13px;
}
.tab-panel {
padding: 24px 16px;
}
.tab-container.vertical {
flex-direction: column;
}
.tab-container.vertical .tab-header {
width: 100%;
border-right: none;
border-bottom: 1px solid #e5e7eb;
}
.tab-container.vertical .tab-list {
flex-direction: row;
}
.tab-container.vertical .tab-indicator {
width: auto;
height: 3px;
right: auto;
bottom: 0;
}
}
/* Touch Support */
.tab-list.touching {
scroll-behavior: auto;
}
/* Dropdown for Mobile */
.tab-dropdown {
display: none;
position: relative;
margin: 20px;
}
.tab-dropdown-toggle {
width: 100%;
padding: 12px 16px;
background: white;
border: 1px solid #e5e7eb;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
color: #374151;
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
transition: all 0.2s ease;
}
.tab-dropdown-toggle:hover {
border-color: #3b82f6;
}
.tab-dropdown-menu {
position: absolute;
top: 100%;
left: 0;
right: 0;
margin-top: 4px;
background: white;
border: 1px solid #e5e7eb;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
opacity: 0;
transform: translateY(-8px);
pointer-events: none;
transition: all 0.2s ease;
max-height: 300px;
overflow-y: auto;
z-index: 100;
}
.tab-dropdown.open .tab-dropdown-menu {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}
.tab-dropdown-item {
padding: 12px 16px;
cursor: pointer;
transition: background-color 0.2s ease;
font-size: 14px;
color: #374151;
}
.tab-dropdown-item:hover {
background-color: #f3f4f6;
}
.tab-dropdown-item.active {
color: #3b82f6;
background-color: #eff6ff;
}
@media (max-width: 640px) {
.tab-header {
display: none;
}
.tab-dropdown {
display: block;
}
}
/* Additional Demo Styles */
.demo-controls {
display: flex;
gap: 12px;
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.demo-button {
padding: 8px 16px;
background: white;
border: 1px solid #e5e7eb;
border-radius: 6px;
font-size: 14px;
color: #374151;
cursor: pointer;
transition: all 0.2s ease;
}
.demo-button:hover {
border-color: #3b82f6;
color: #3b82f6;
}
.demo-button:active {
transform: translateY(1px);
}
/* Accessibility */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* Dark Mode Support */
@media (prefers-color-scheme: dark) {
body {
background: #0f0f0f;
color: #e5e7eb;
}
main {
background: #1a1a1a;
}
.tab-container {
background: #1a1a1a;
}
.tab-header {
background: #0f0f0f;
border-color: #2a2a2a;
}
.tab-item {
color: #9ca3af;
}
.tab-item:hover {
color: #e5e7eb;
background-color: rgba(255, 255, 255, 0.05);
}
.tab-item.active {
color: #60a5fa;
}
.tab-indicator {
background: #60a5fa;
}
.scroll-indicator.left {
background: linear-gradient(to right, rgba(15, 15, 15, 1), rgba(15, 15, 15, 0));
}
.scroll-indicator.right {
background: linear-gradient(to left, rgba(15, 15, 15, 1), rgba(15, 15, 15, 0));
}
}
</style>
</head>
<body>
<main>
<h1>Tab Navigation - Enhanced</h1>
<div class="component-showcase">
<!-- Demo Controls -->
<div class="demo-controls">
<button class="demo-button" onclick="addTab()">Add Tab</button>
<button class="demo-button" onclick="toggleOrientation()">Toggle Orientation</button>
<button class="demo-button" onclick="toggleCloseable()">Toggle Closeable</button>
<button class="demo-button" onclick="simulateLoading()">Simulate Loading</button>
</div>
<!-- Horizontal Tabs Example -->
<div class="tab-container" id="mainTabs">
<div class="tab-header">
<div class="scroll-indicator left"></div>
<div class="scroll-indicator right"></div>
<div class="tab-list" role="tablist" aria-label="Main navigation">
<button class="tab-item active" role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1" tabindex="0">
Dashboard
</button>
<button class="tab-item" role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2" tabindex="-1">
Analytics
</button>
<button class="tab-item" role="tab" aria-selected="false" aria-controls="panel-3" id="tab-3" tabindex="-1">
Reports
</button>
<button class="tab-item" role="tab" aria-selected="false" aria-controls="panel-4" id="tab-4" tabindex="-1">
Settings
</button>
<button class="tab-item" role="tab" aria-selected="false" aria-controls="panel-5" id="tab-5" tabindex="-1">
Team Members
</button>
<button class="tab-item" role="tab" aria-selected="false" aria-controls="panel-6" id="tab-6" tabindex="-1">
Integrations
</button>
</div>
<div class="tab-indicator"></div>
</div>
<!-- Mobile Dropdown -->
<div class="tab-dropdown" id="tabDropdown">
<button class="tab-dropdown-toggle" onclick="toggleDropdown()">
<span>Dashboard</span>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
<div class="tab-dropdown-menu">
<div class="tab-dropdown-item active" data-tab="tab-1">Dashboard</div>
<div class="tab-dropdown-item" data-tab="tab-2">Analytics</div>
<div class="tab-dropdown-item" data-tab="tab-3">Reports</div>
<div class="tab-dropdown-item" data-tab="tab-4">Settings</div>
<div class="tab-dropdown-item" data-tab="tab-5">Team Members</div>
<div class="tab-dropdown-item" data-tab="tab-6">Integrations</div>
</div>
</div>
<div class="tab-content">
<div class="tab-panel active" role="tabpanel" id="panel-1" aria-labelledby="tab-1">
<h2>Dashboard Overview</h2>
<p>Welcome to your enhanced dashboard. This tab navigation system features smooth transitions, keyboard navigation, and mobile responsiveness.</p>
<p>Try using the arrow keys to navigate between tabs, or swipe on mobile devices. The active tab indicator smoothly animates to show your current position.</p>
</div>
<div class="tab-panel" role="tabpanel" id="panel-2" aria-labelledby="tab-2">
<h2>Analytics Dashboard</h2>
<p>View comprehensive analytics with real-time data updates. The tab system supports lazy loading for performance optimization.</p>
<p>Notice how the content smoothly fades in when switching tabs, providing a polished user experience.</p>
</div>
<div class="tab-panel" role="tabpanel" id="panel-3" aria-labelledby="tab-3">
<h2>Reports Center</h2>
<p>Generate and view detailed reports. The tab system includes overflow handling with scroll indicators when there are many tabs.</p>
<p>On smaller screens, tabs automatically convert to a dropdown menu for better mobile usability.</p>
</div>
<div class="tab-panel" role="tabpanel" id="panel-4" aria-labelledby="tab-4">
<h2>Settings & Preferences</h2>
<p>Customize your experience with comprehensive settings. Tabs can be made closeable and support drag-and-drop reordering.</p>
<p>The system respects user preferences like reduced motion and dark mode for accessibility.</p>
</div>
<div class="tab-panel" role="tabpanel" id="panel-5" aria-labelledby="tab-5">
<h2>Team Management</h2>
<p>Manage your team members and permissions. The enhanced tab system supports both horizontal and vertical orientations.</p>
<p>Keyboard shortcuts include Home/End keys to jump to first/last tab for power users.</p>
</div>
<div class="tab-panel" role="tabpanel" id="panel-6" aria-labelledby="tab-6">
<h2>Integration Hub</h2>
<p>Connect with third-party services seamlessly. Loading states are elegantly handled for async content.</p>
<p>The tab system is fully accessible with proper ARIA attributes and screen reader support.</p>
</div>
</div>
</div>
</div>
</main>
<script>
// Tab System Class
class EnhancedTabs {
constructor(container) {
this.container = container;
this.tabList = container.querySelector('.tab-list');
this.tabs = container.querySelectorAll('.tab-item');
this.panels = container.querySelectorAll('.tab-panel');
this.indicator = container.querySelector('.tab-indicator');
this.dropdown = container.querySelector('.tab-dropdown');
this.dropdownToggle = container.querySelector('.tab-dropdown-toggle span');
this.dropdownItems = container.querySelectorAll('.tab-dropdown-item');
this.activeTab = container.querySelector('.tab-item.active');
this.isVertical = container.classList.contains('vertical');
this.closeable = false;
this.touchStartX = null;
this.touchStartY = null;
this.isDragging = false;
this.draggedTab = null;
this.init();
}
init() {
this.setupEventListeners();
this.updateIndicator();
this.checkScrollIndicators();
this.setupKeyboardNavigation();
this.setupTouchSupport();
this.setupDragAndDrop();
this.setupDropdownListeners();
}
setupEventListeners() {
this.tabs.forEach(tab => {
tab.addEventListener('click', (e) => this.handleTabClick(e));
});
this.tabList.addEventListener('scroll', () => this.checkScrollIndicators());
window.addEventListener('resize', () => {
this.updateIndicator();
this.checkScrollIndicators();
});
// Intersection Observer for lazy loading
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && entry.target.dataset.lazyLoad) {
this.loadTabContent(entry.target);
}
});
});
this.panels.forEach(panel => observer.observe(panel));
}
setupDropdownListeners() {
if (!this.dropdown) return;
this.dropdownItems.forEach(item => {
item.addEventListener('click', () => {
const tabId = item.dataset.tab;
const tab = document.getElementById(tabId);
if (tab) {
this.activateTab(tab);
this.updateDropdown(tab);
this.closeDropdown();
}
});
});
// Close dropdown on outside click
document.addEventListener('click', (e) => {
if (!this.dropdown.contains(e.target)) {
this.closeDropdown();
}
});
}
handleTabClick(e) {
const tab = e.target.closest('.tab-item');
if (tab && !tab.classList.contains('loading')) {
this.activateTab(tab);
}
}
activateTab(tab) {
if (tab === this.activeTab) return;
// Update tabs
this.tabs.forEach(t => {
t.classList.remove('active');
t.setAttribute('aria-selected', 'false');
t.setAttribute('tabindex', '-1');
});
tab.classList.add('active');
tab.setAttribute('aria-selected', 'true');
tab.setAttribute('tabindex', '0');
// Update panels with fade effect
const oldPanel = this.container.querySelector('.tab-panel.active');
const newPanelId = tab.getAttribute('aria-controls');
const newPanel = document.getElementById(newPanelId);
if (oldPanel && newPanel) {
oldPanel.classList.add('fade-out');
setTimeout(() => {
oldPanel.classList.remove('active', 'fade-out');
newPanel.classList.add('active');
}, 150);
}
this.activeTab = tab;
this.updateIndicator();
this.updateDropdown(tab);
// Focus management
if (!this.isDragging) {
tab.focus();
}
// Emit custom event
this.container.dispatchEvent(new CustomEvent('tabchange', {
detail: { tab, panel: newPanel }
}));
}
updateIndicator() {
if (!this.activeTab || !this.indicator) return;
const tabRect = this.activeTab.getBoundingClientRect();
const containerRect = this.tabList.getBoundingClientRect();
if (this.isVertical) {
const top = tabRect.top - containerRect.top + this.tabList.scrollTop;
this.indicator.style.transform = `translateY(${top}px)`;
this.indicator.style.height = `${tabRect.height}px`;
} else {
const left = tabRect.left - containerRect.left + this.tabList.scrollLeft;
this.indicator.style.transform = `translateX(${left}px)`;
this.indicator.style.width = `${tabRect.width}px`;
}
}
updateDropdown(tab) {
if (!this.dropdownToggle) return;
const text = tab.textContent.trim();
this.dropdownToggle.textContent = text;
this.dropdownItems.forEach(item => {
if (item.dataset.tab === tab.id) {
item.classList.add('active');
} else {
item.classList.remove('active');
}
});
}
checkScrollIndicators() {
if (!this.tabList) return;
const scrollLeft = this.container.querySelector('.scroll-indicator.left');
const scrollRight = this.container.querySelector('.scroll-indicator.right');
if (!scrollLeft || !scrollRight) return;
const { scrollLeft: scroll, scrollWidth, clientWidth } = this.tabList;
scrollLeft.classList.toggle('visible', scroll > 0);
scrollRight.classList.toggle('visible', scroll < scrollWidth - clientWidth - 1);
}
setupKeyboardNavigation() {
this.tabList.addEventListener('keydown', (e) => {
const currentTab = document.activeElement;
if (!currentTab.classList.contains('tab-item')) return;
let nextTab;
const tabs = Array.from(this.tabs);
const currentIndex = tabs.indexOf(currentTab);
switch (e.key) {
case 'ArrowLeft':
case 'ArrowUp':
e.preventDefault();
nextTab = tabs[currentIndex - 1] || tabs[tabs.length - 1];
break;
case 'ArrowRight':
case 'ArrowDown':
e.preventDefault();
nextTab = tabs[currentIndex + 1] || tabs[0];
break;
case 'Home':
e.preventDefault();
nextTab = tabs[0];
break;
case 'End':
e.preventDefault();
nextTab = tabs[tabs.length - 1];
break;
case 'Enter':
case ' ':
e.preventDefault();
this.activateTab(currentTab);
return;
}
if (nextTab) {
nextTab.focus();
this.ensureTabVisible(nextTab);
}
});
}
ensureTabVisible(tab) {
const tabRect = tab.getBoundingClientRect();
const listRect = this.tabList.getBoundingClientRect();
if (tabRect.left < listRect.left) {
this.tabList.scrollLeft -= listRect.left - tabRect.left + 20;
} else if (tabRect.right > listRect.right) {
this.tabList.scrollLeft += tabRect.right - listRect.right + 20;
}
}
setupTouchSupport() {
let touchStartX = 0;
let touchStartY = 0;
let touchStartScrollLeft = 0;
this.tabList.addEventListener('touchstart', (e) => {
touchStartX = e.touches[0].clientX;
touchStartY = e.touches[0].clientY;
touchStartScrollLeft = this.tabList.scrollLeft;
this.tabList.classList.add('touching');
}, { passive: true });
this.tabList.addEventListener('touchmove', (e) => {
const touchEndX = e.touches[0].clientX;
const touchEndY = e.touches[0].clientY;
const diffX = touchStartX - touchEndX;
const diffY = Math.abs(touchStartY - touchEndY);
// Only scroll horizontally if the horizontal movement is greater than vertical
if (Math.abs(diffX) > diffY && !this.isVertical) {
this.tabList.scrollLeft = touchStartScrollLeft + diffX;
}
}, { passive: true });
this.tabList.addEventListener('touchend', () => {
this.tabList.classList.remove('touching');
this.checkScrollIndicators();
}, { passive: true });
// Swipe between tabs on content area
let contentTouchStartX = 0;
const contentArea = this.container.querySelector('.tab-content');
contentArea.addEventListener('touchstart', (e) => {
contentTouchStartX = e.touches[0].clientX;
}, { passive: true });
contentArea.addEventListener('touchend', (e) => {
const touchEndX = e.changedTouches[0].clientX;
const diffX = contentTouchStartX - touchEndX;
if (Math.abs(diffX) > 50) { // Minimum swipe distance
const tabs = Array.from(this.tabs);
const currentIndex = tabs.indexOf(this.activeTab);
if (diffX > 0 && currentIndex < tabs.length - 1) {
// Swipe left - next tab
this.activateTab(tabs[currentIndex + 1]);
} else if (diffX < 0 && currentIndex > 0) {
// Swipe right - previous tab
this.activateTab(tabs[currentIndex - 1]);
}
}
}, { passive: true });
}
setupDragAndDrop() {
this.tabs.forEach(tab => {
tab.draggable = true;
tab.addEventListener('dragstart', (e) => {
this.isDragging = true;
this.draggedTab = tab;
tab.classList.add('dragging');
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', tab.innerHTML);
});
tab.addEventListener('dragend', () => {
this.isDragging = false;
tab.classList.remove('dragging');
this.draggedTab = null;
this.updateIndicator();
});
tab.addEventListener('dragover', (e) => {
e.preventDefault();
const afterElement = this.getDragAfterElement(e.clientX, e.clientY);
if (afterElement == null) {
this.tabList.appendChild(this.draggedTab);
} else {
this.tabList.insertBefore(this.draggedTab, afterElement);
}
});
});
}
getDragAfterElement(x, y) {
const draggableElements = [...this.tabList.querySelectorAll('.tab-item:not(.dragging)')];
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect();
const offset = this.isVertical ?
y - box.top - box.height / 2 :
x - box.left - box.width / 2;
if (offset < 0 && offset > closest.offset) {
return { offset: offset, element: child };
} else {
return closest;
}
}, { offset: Number.NEGATIVE_INFINITY }).element;
}
addCloseButtons() {
this.tabs.forEach((tab, index) => {
// Don't add close button to first tab
if (index === 0 || tab.querySelector('.tab-close')) return;
const closeBtn = document.createElement('button');
closeBtn.className = 'tab-close';
closeBtn.innerHTML = `
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
`;
closeBtn.onclick = (e) => {
e.stopPropagation();
this.closeTab(tab);
};
tab.appendChild(closeBtn);
});
}
removeCloseButtons() {
this.tabs.forEach(tab => {
const closeBtn = tab.querySelector('.tab-close');
if (closeBtn) closeBtn.remove();
});
}
closeTab(tab) {
const panel = document.getElementById(tab.getAttribute('aria-controls'));
const tabs = Array.from(this.tabs);
const currentIndex = tabs.indexOf(tab);
// Animate out
tab.style.width = tab.offsetWidth + 'px';
tab.style.transition = 'width 0.3s ease, opacity 0.3s ease, margin 0.3s ease';
requestAnimationFrame(() => {
tab.style.width = '0';
tab.style.opacity = '0';
tab.style.marginLeft = '0';
tab.style.marginRight = '0';
tab.style.padding = '0';
});
setTimeout(() => {
// If closing active tab, activate adjacent tab
if (tab === this.activeTab) {
const nextTab = tabs[currentIndex + 1] || tabs[currentIndex - 1];
if (nextTab) this.activateTab(nextTab);
}
tab.remove();
panel.remove();
this.updateIndicator();
this.checkScrollIndicators();
}, 300);
}
async loadTabContent(panel) {
const tab = document.getElementById(panel.getAttribute('aria-labelledby'));
tab.classList.add('loading');
// Simulate async content loading
const loadingDiv = document.createElement('div');
loadingDiv.className = 'tab-loading';
loadingDiv.innerHTML = '<div class="spinner"></div>';
panel.appendChild(loadingDiv);
await new Promise(resolve => setTimeout(resolve, 1500));
tab.classList.remove('loading');
loadingDiv.remove();
delete panel.dataset.lazyLoad;
}
closeDropdown() {
if (this.dropdown) {
this.dropdown.classList.remove('open');
}
}
}
// Initialize tabs
const tabSystem = new EnhancedTabs(document.getElementById('mainTabs'));
// Demo functions
let tabCounter = 7;
function addTab() {
const tabList = document.querySelector('.tab-list');
const tabContent = document.querySelector('.tab-content');
const dropdown = document.querySelector('.tab-dropdown-menu');
// Create new tab
const newTab = document.createElement('button');
newTab.className = 'tab-item';
newTab.role = 'tab';
newTab.id = `tab-${tabCounter}`;
newTab.setAttribute('aria-selected', 'false');
newTab.setAttribute('aria-controls', `panel-${tabCounter}`);
newTab.setAttribute('tabindex', '-1');
newTab.textContent = `New Tab ${tabCounter - 6}`;
// Create new panel
const newPanel = document.createElement('div');
newPanel.className = 'tab-panel';
newPanel.role = 'tabpanel';
newPanel.id = `panel-${tabCounter}`;
newPanel.setAttribute('aria-labelledby', `tab-${tabCounter}`);
newPanel.innerHTML = `
<h2>New Tab ${tabCounter - 6}</h2>
<p>This is dynamically added content. The tab system handles new tabs seamlessly with smooth animations.</p>
`;
// Create dropdown item
if (dropdown) {
const dropdownItem = document.createElement('div');
dropdownItem.className = 'tab-dropdown-item';
dropdownItem.dataset.tab = `tab-${tabCounter}`;
dropdownItem.textContent = `New Tab ${tabCounter - 6}`;
dropdown.appendChild(dropdownItem);
}
// Animate in
newTab.style.opacity = '0';
newTab.style.transform = 'translateY(-10px)';
tabList.appendChild(newTab);
tabContent.appendChild(newPanel);
requestAnimationFrame(() => {
newTab.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
newTab.style.opacity = '1';
newTab.style.transform = 'translateY(0)';
});
tabCounter++;
// Reinitialize tab system
setTimeout(() => {
const container = document.getElementById('mainTabs');
window.tabSystem = new EnhancedTabs(container);
window.tabSystem.activateTab(newTab);
window.tabSystem.ensureTabVisible(newTab);
if (window.tabSystem.closeable) {
window.tabSystem.addCloseButtons();
}
}, 100);
}
function toggleOrientation() {
const container = document.getElementById('mainTabs');
container.classList.toggle('vertical');
window.tabSystem = new EnhancedTabs(container);
}
function toggleCloseable() {
window.tabSystem.closeable = !window.tabSystem.closeable;
if (window.tabSystem.closeable) {
window.tabSystem.addCloseButtons();
} else {
window.tabSystem.removeCloseButtons();
}
}
function simulateLoading() {
const activePanel = document.querySelector('.tab-panel.active');
activePanel.dataset.lazyLoad = 'true';
window.tabSystem.loadTabContent(activePanel);
}
function toggleDropdown() {
const dropdown = document.getElementById('tabDropdown');
dropdown.classList.toggle('open');
}
// Global reference
window.tabSystem = tabSystem;
</script>
</body>
</html>