/** * **YaiTabs 1.0 - Advanced Tab Component System** * * **Key Features:** * - **Performance**: Single event listener per container, scales infinitely * - **Infinite Nesting**: Tested to 55+ levels without performance degradation * - **Auto-Disambiguation**: Intelligent ID conflict resolution for nested components * - **WCAG 2.1 AA Compliance**: Full accessibility with dynamic ARIA management * - **Dynamic Content Loading**: Fetch-based content with comprehensive delay controls * - **Hash Routing**: URL-based navigation with browser history integration * - **Animation System**: 9 smooth CSS-based transition behaviors * - **Remote Control**: Content buttons can control parent tab navigation * - **Framework Agnostic**: Works with React, Vue, Angular, or Vanilla JS * * **Performance Metrics:** * - Initialization: < 100ms for complex nested structures * - Interaction Response: < 16ms (60fps guaranteed) * - Memory Footprint: ~460 LOC, minimal runtime overhead * * @author YaiJS Team - Advanced component architecture * @license MIT * @see https://github.com/yaijs/yai/tree/main/tabs */ import { HookContext, LifecycleCallbacks, YaiCore, YaiCoreConfig } from '../yai-core.js'; /** * **Animation Behaviors** * * Smooth CSS-based transitions optimized for performance and accessibility. * All animations use transform + opacity for hardware acceleration. */ export type AnimationBehavior = /** Smooth opacity transition (default) */ | 'fade' /** Slide animation from bottom to top */ | 'slide-up' /** Slide animation from top to bottom */ | 'slide-down' /** Slide animation from right to left */ | 'slide-left' /** Slide animation from left to right */ | 'slide-right' /** Scale-based zoom transition */ | 'zoom' /** 3D flip transition effect */ | 'flip' /** Blur transition with opacity */ | 'blur' /** No animation, instant switch */ | 'instant'; /** * 🧭 **Navigation Positioning Options** * * Controls where the tab navigation appears relative to content. * Automatically handles CSS flexbox layout and ARIA orientation. */ export type NavigationPosition = /** Navigation at top (default, horizontal orientation) */ | 'top' /** Navigation on left (vertical orientation) */ | 'left' /** Navigation on right (vertical orientation) */ | 'right' /** Navigation at bottom (horizontal orientation) */ | 'bottom'; /** * 🏷️ **Tab Container HTML Attributes** * * Data attributes for the main tab container element. * * @example * ```html *
* ``` */ export interface TabContainerAttributes { /** Main identifier for tab components (required) */ 'data-yai-tabs'?: string; /** Animation behavior for tab transitions */ 'data-behavior'?: AnimationBehavior; /** Navigation positioning */ 'data-nav'?: NavigationPosition; /** Visual theme identifier */ 'data-theme'?: string; /** Browser history handling mode */ 'data-history-mode'?: 'replace' | 'push'; /** URL hash parameter key for routing */ 'data-ref-path'?: string; } /** * πŸŽ›οΈ **Tab Button HTML Attributes** * * Data attributes for tab navigation buttons. * * @example * ```html * * ``` */ export interface TabButtonAttributes { /** Button action type (required for buttons) */ 'data-tab-action'?: 'open'; /** Target panel ID (matches data-tab value) */ 'data-open'?: string; /** Marks as default/initial active tab */ 'data-default'?: boolean; /** Dynamic content URL for fetch-based loading */ 'data-url'?: string; /** Append content instead of replacing (default: false) */ 'data-append'?: string; /** Pre-fetch delay in milliseconds */ 'data-delay'?: string; /** Post-fetch delay in milliseconds */ 'data-post-delay'?: string; /** Minimum loading time (prevents flicker) */ 'data-min-loading'?: string; /** Always reload content (bypass cache) */ 'data-url-refresh'?: boolean; /** Marker set after content is loaded (internal use) */ 'data-url-loaded'?: boolean; /** Text to restore after loading completes */ 'data-restore-text'?: string; } /** * πŸ“„ **Tab Content Panel HTML Attributes** * * Data attributes for tab content containers. * * @example * ```html *
* Tab content here... *
* ``` */ export interface TabContentAttributes { /** Panel identifier (matches data-open value) */ 'data-tab'?: string; /** Remove default padding from content */ 'data-spaceless'?: boolean; } /** * βš™οΈ **YaiTabs Configuration Interface** * * Comprehensive configuration options extending YaiCore functionality * with tab-specific features and intelligent defaults. */ export interface YaiTabsConfig extends YaiCoreConfig { /** Default animation behavior when data-behavior is not specified */ behavior?: AnimationBehavior; /** Default navigation position */ navigation?: NavigationPosition; /** Default tab to open (by ID) */ defaultTab?: string; /** Automatically focus first container's active tab on init */ autoFocus?: boolean; /** Enable comprehensive ARIA accessibility setup */ autoAccessibility?: boolean; /** Enable URL hash-based routing */ hashRouting?: boolean; /** Enable remote control from content buttons */ remoteControl?: boolean; /** Animation duration in milliseconds */ animationDuration?: number; /** CSS class name for loading state */ loadingClass?: string; /** CSS class name for active state */ activeClass?: string; /** CSS class name for hidden state */ hiddenClass?: string; /** Prefix for auto-generated disambiguation IDs */ disambiguationPrefix?: string; /** Automatically resolve ID conflicts in nested components */ autoDisambiguate?: boolean; /** Allow closing active tab by clicking it again */ closable?: boolean; } /** * 🎣 **Tab-Specific Hook Context** * * Extended context object passed to tab lifecycle hooks with * tab-specific metadata and references. */ export interface TabHookContext extends HookContext { /** Current tab identifier */ tabId?: string; /** Tab button element */ tabButton?: Element; /** Tab content panel element */ tabContent?: Element; /** Target container for the operation */ targetContainer?: Element; /** Content URL being loaded */ url?: string; /** Animation behavior being used */ animationBehavior?: AnimationBehavior; } /** * πŸ”„ **Tab Lifecycle Callbacks** * * Tab-specific lifecycle hooks extending base functionality. * Currently uses base callbacks - can be extended for tab-specific events. */ export interface TabLifecycleCallbacks extends LifecycleCallbacks { // Base callbacks are sufficient for current implementation // Future tab-specific callbacks can be added here: // beforeTabChange?: (context: TabHookContext) => void | Promise; // afterTabChange?: (context: TabHookContext) => void | Promise; } /** * πŸ—οΈ **YaiTabs Class - Advanced Tab Component** * * Advanced tab system built on YEH's architecture. * Provides enterprise-grade functionality with infinite scalability. * * **🎯 Core Architecture:** * - Single event listener per container using event delegation * - Lazy component activation for optimal performance * - Automatic ID disambiguation for conflict resolution * - Hook-based extensibility for custom behavior * - Dynamic content loading with comprehensive delay management * * **πŸ“– Usage Examples:** * * @example * **Basic Usage:** * ```typescript * // Initialize with default configuration * const tabs = new YaiTabs(); * * // Custom configuration * const tabs = new YaiTabs({ * behavior: 'slide-up', * autoFocus: true, * autoDisambiguate: true * }); * ``` * * @example * **Hook-Based Customization:** * ```typescript * const tabs = new YaiTabs() * .hook('setLoading', ({ target, container }) => { * target?.classList.add('loading'); * target?.setAttribute('aria-busy', 'true'); * }) * .hook('removeLoading', ({ target, container }) => { * target?.classList.remove('loading'); * target?.setAttribute('aria-busy', 'false'); * }) * .hook('contentReady', ({ content, target }) => { * content?.classList.add('fade-in'); * if (target?.dataset.restoreText) { * target.textContent = target.dataset.restoreText; * } * }); * ``` * * @example * **HTML Structure:** * ```html * *
* * * * * *
*
*

Static content for tab 1

*
*
* *
*
*

Static content for tab 3

* * *
* *
*
Nested content 1
*
Nested content 2
*
*
*
*
*
* ``` * * @example * **Dynamic Content with Delays:** * ```html * * ``` * * @example * **Hash Routing Setup:** * ```html *
* * * *
*
Dashboard Content
*
Settings Content
*
*
* * ``` * * **🎯 Performance Features:** * - **Lazy Component Activation**: Nested components activate only when needed * - **DOM Caching**: Intelligent query caching reduces DOM traversal * - **Event Delegation**: Single listener handles all tab interactions * - **Memory Management**: Automatic cleanup prevents memory leaks * * **β™Ώ Accessibility Features:** * - **WCAG 2.1 AA Compliant**: Full keyboard navigation and screen reader support * - **Dynamic ARIA**: Automatic role, state, and property management * - **Keyboard Navigation**: Arrow keys, Home/End, Enter/Space, Escape * - **Focus Management**: Automatic focus restoration after dynamic content loading * - **Unique IDs**: Container-scoped ID generation prevents conflicts */ export declare class YaiTabs extends YaiCore { protected config: Required; protected routeMap: Map; /** * πŸ—οΈ **Create YaiTabs Instance** * * Initialize a new tab component with optional configuration. * Automatically sets up event delegation, lazy components, and accessibility. * * @param config - Optional configuration object */ constructor(config?: YaiTabsConfig); /** * πŸš€ **Initialize Component System** * * Core initialization method that: * - Discovers and processes all tab containers * - Initializes default tabs and accessibility * - Processes hash routing if enabled */ init(): void; /** * πŸ” **Initialize All Tab Containers** * * Discovers and initializes all tab containers in the specified root element. * Handles both root-level and nested components with proper lazy activation. * * @param rootElement - Root element to search for tab containers (default: document) */ initializeAllContainers(rootElement?: Document): void; /** * πŸ“‚ **Open Tab** * * Core method to open a tab with full animation and lifecycle support. * Handles dynamic content loading, accessibility updates, and event broadcasting. * * @param target - Button element that triggered the action * @param event - Original event object (null for programmatic calls) * @param container - Tab container element * @param isDefaultInitialization - Whether this is initial default tab opening * * @example * ```typescript * // Programmatic tab opening * const button = document.querySelector('[data-open="tab2"]'); * const container = document.querySelector('[data-yai-tabs]'); * tabs.openTab(button, null, container); * ``` */ openTab(target: Element, event: Event | null, container: Element, isDefaultInitialization?: boolean): void; /** * πŸ” **Check if Closable** * * Determine if tabs in a container are closable by checking the data-closable attribute. * Falls back to global config.closable if attribute is not set. * * @param container - Tab container element to check * @returns True if tabs are closable, false otherwise * @example * ```typescript * // Per-container override via data attribute *
...
* * // Check closability * if (tabs.getIsClosable(container)) { * // Show close buttons * } * ``` */ getIsClosable(container: Element): boolean; /** * ❌ **Close Tab** * * Close the currently active tab if closable option is enabled. * Handles cleanup and state management for proper tab closure. * * @param target - Button element for the tab to close * @param event - Original event object * @param container - Tab container element */ closeTab(target: Element, event: Event | null, container: Element): void; /** * πŸ”„ **Toggle Loading State** * * Manage loading states for tab buttons and containers with visual feedback. * Called automatically during content loading operations. * * @param container - Container element to update * @param isLoading - Whether to show (true) or hide (false) loading state * @param target - Button element that triggered loading */ toggleLoading(container: Element, isLoading?: boolean, target?: Element | null): void; /** * πŸ–±οΈ **Handle Click Events** * * Main click event handler using YEH's delegation system. * Routes clicks to appropriate action handlers based on data attributes. * * @param event - Click event object * @param target - Element that was clicked * @param container - Container element for the interaction */ handleClick(event: Event, target: Element, container: Element): void; /** * ⌨️ **Handle Keyboard Events** * * Comprehensive keyboard navigation handler supporting: * - Arrow keys for tab navigation * - Home/End for first/last tab * - Enter/Space for activation * - Escape for parent navigation * * @param event - Keyboard event object * @param target - Element with focus * @param container - Container element for navigation */ handleKeydown(event: KeyboardEvent, target: Element, container: Element): void; /** * πŸ”— **Handle Hash Change Events** * * Process URL hash changes for browser-based navigation. * Syncs tab state with URL parameters for bookmarkable tabs. */ handleHashchange(): void; // === Internal Processing Methods === /** * 🏷️ **Mark Root Containers** * * Mark root containers with data-root attribute and optionally apply lazy optimization. * Always sets data-root on root containers, and conditionally makes nested components lazy * based on config.lazyNestedComponents setting. */ protected _markRootContainers(): void; /** * ⚑ **Activate Lazy Components** * * Restore data-yai-tabs attributes to lazy components after root registration. * Enables proper component discovery while maintaining performance. */ protected _activateLazyComponents(): void; /** * πŸ”€ **Auto-Disambiguate IDs** * * Automatically resolve ID conflicts between nested components. * Generates unique IDs while preserving original references for routing. * * @param scope - Document scope to process (default: document) */ protected _autoDisambiguateIds(scope?: Document): void; /** * 🏭 **Process Root Container** * * Full initialization for root-level containers including: * - Event listener registration * - Accessibility setup * - Default tab activation * - Nesting level calculation * * @param data - Container processing data object */ protected _processContainer(data: any): void; /** * 🌿 **Process Nested Container** * * Lightweight initialization for nested containers that rely on * parent event delegation for optimal performance. * * @param data - Container processing data object */ protected _processNestedContainer(data: any): void; /** * 🧭 **Update ARIA Orientation** * * Dynamically detect and set ARIA orientation based on actual CSS layout. * Called during user interaction for guaranteed accuracy. * * @param container - Container to update orientation for */ protected _updateAriaOrientation(container: Element): void; /** * β™Ώ **Update ARIA States** * * Update ARIA attributes for all tab buttons and panels in a container. * Maintains accessibility compliance during tab state changes. * * @param container - Container to update ARIA states for */ protected _updateAriaStates(container: Element): void; /** * 🧹 **Cleanup Stale Active States** * * Remove active states from elements that should no longer be active. * Prevents state conflicts during rapid interactions. */ protected _cleanupStaleActiveStates(): void; /** * πŸ™ˆ **Cleanup Hidden Panels** * * Remove focusable elements from hidden tab panels to improve * keyboard navigation and accessibility. */ protected _cleanupHiddenPanels(): void; /** * β™Ώ **Setup Complete Accessibility** * * Configure comprehensive ARIA attributes for WCAG 2.1 AA compliance. * Sets up roles, states, properties, and keyboard navigation. * * @param data - Container data with accessibility metadata */ protected _setupCompleteAccessibility(data: any): void; /** * 🚫 **Remove Active States** * * Remove active classes and ARIA states from specified elements. * Supports custom selector arrays for flexible state management. * * @param target - Target element context * @param container - Container to process * @param selectors - CSS selectors for elements to deactivate */ protected _removeActive(target: Element, container: Element, selectors?: string[]): void; /** * 🎯 **Initialize Nested Defaults** * * Discover and activate default tabs in newly loaded content. * Handles nested component initialization after dynamic loading. * * @param content - Content container to process */ protected _initializeNestedDefaults(content: Element): void; /** * πŸ”„ **Post-Process Content** * * Final processing after content loading including: * - Nested component discovery * - Default tab activation * - Event system integration * * @param container - Container that received new content */ protected _postProcessContent(container: Element): void; /** * 🎯 **Activate Default Tabs** * * Find and activate default tabs in a container. * Handles both data-default attributes and configuration-based defaults. * * @param container - Container to process for default tabs */ protected _activateDefaultTabs(container: Element): void; /** * πŸ’Ύ **Preserve Navigation State** * * Store current navigation state for restoration after dynamic loading. * Maintains user context during content updates. * * @param containerOrPanel - Container or panel to preserve state for */ protected _preserveNavigationState(containerOrPanel: Element): void; /** * ↩️ **Restore Navigation State** * * Restore previously saved navigation state after content loading. * Ensures user context is maintained across dynamic updates. * * @param content - Content that was dynamically loaded */ protected _restoreNavigationState(content: Element): void; /** * 🧹 **Cleanup Sibling Branch Parameters** * * Clean up hash routing parameters for sibling containers * to prevent cross-contamination in complex nested structures. * * @param container - Container to clean up parameters for */ protected _cleanupSiblingBranchParameters(container: Element): void; /** * πŸ”— **Cleanup Nested Hash Entries** * * Remove hash routing entries for nested components that are * no longer active or relevant. * * @param parentContainer - Parent container to clean entries for */ protected _cleanupNestedHashEntries(parentContainer: Element): void; /** * πŸ“ **Preserve Content Height** * * Maintain container height during transitions to prevent layout shift. * Essential for smooth animations and user experience. * * @param container - Container to preserve height for */ protected _preserveContentHeight(container: Element): void; /** * πŸ‘€ **Manage Focus for Hidden Elements** * * Remove tabindex from elements in hidden panels to improve * keyboard navigation flow and accessibility. * * @param container - Container to manage focus for */ protected _manageFocusForHiddenElements(container: Element): void; // === Utility Methods === /** * πŸ‘οΈ **Check Element Visibility** * * Determine if an element is visible in the current viewport. * Used for optimization and accessibility decisions. * * @param element - Element to check visibility for * @returns True if element is visible */ protected isElementVisible(element: Element): boolean; /** * πŸ“¦ **Check Container Visibility** * * Determine if a tab container is currently visible. * Used for initialization and performance optimizations. * * @param container - Container to check visibility for * @returns True if container is visible */ protected isContainerVisible(container: Element): boolean; /** * 🏷️ **Mark Root Container** * * Add or remove root container markers for component hierarchy tracking. * Essential for proper event delegation and performance optimization. * * @param element - Element to mark/unmark * @param add - Whether to add (true) or remove (false) marker * @param marker - Custom marker attribute name */ protected _markRootContainer(element: Element, add?: boolean, marker?: string | null): void; /** * 🎣 **Hook Management (Extended)** * * Register lifecycle hooks with tab-specific context support. * Extends base hook system with tab-aware functionality. * * @param hookName - Name of the lifecycle hook * @param callback - Callback function to register * @returns this for method chaining * * @example * ```typescript * tabs.hook('contentReady', ({ content, tabId, container }) => { * console.log(`Tab ${tabId} content ready in container:`, container); * content?.classList.add('loaded'); * }); * ``` */ hook(hookName: keyof TabLifecycleCallbacks, callback: TabLifecycleCallbacks[keyof TabLifecycleCallbacks]): this; // === Static Utility Methods === /** * πŸ”— **Reconstruct URL from Reference Path** * * Static utility to build URL hash from a target reference path. * Reconstructs the complete navigation state including all parent levels. * * @param targetRef - Reference path of the target container (data-ref-path value) * @param targetValue - Optional explicit value for the target level * @param containerElement - Root element to search within (default: document) * @returns Reconstructed hash URL string (e.g., "#main-tabs=2&nested-tabs=1") * * @example * ```typescript * // Reconstruct URL for a specific tab state * const url = YaiTabs.reconstructUrlFromRef('nested-tabs', '3'); * // Returns: "#main-tabs=2&nested-tabs=3" (includes parent state) * * // Get current active state * const currentUrl = YaiTabs.reconstructUrlFromRef('nested-tabs'); * // Returns URL reflecting currently active tabs * ``` */ static reconstructUrlFromRef(targetRef: string, targetValue?: string | Element, containerElement?: Element | Document): string; /** * πŸ—ΊοΈ **Get Reference Path Data** * * Static utility to retrieve the full hierarchical path for a reference. * Returns ancestor chain from root to target for proper URL reconstruction. * * @param targetRef - Reference path to look up * @param scope - Root element to search within (default: document) * @returns Object containing full path array and any errors * * @example * ```typescript * const pathData = YaiTabs.getRefPath('nested-tabs'); * // Returns: { fullPath: ['main-tabs', 'nested-tabs'], error: null } * * // Handle errors * const result = YaiTabs.getRefPath('invalid-ref'); * if (result.error) { * console.error('Reference not found:', result.error); * } * ``` */ static getRefPath(targetRef: string, scope?: Element | Document): { fullPath: string[]; error: string | null; }; } /** * πŸ“€ **Module Exports** */ // Export main class as default export default YaiTabs; // Re-export base types for convenience export { HookContext, LifecycleCallbacks, YaiCore, YaiCoreConfig } from './yai-core.js'; /** * 🎯 **Quick Start Guide** * * 1. **Basic Setup:** * ```html *
* *
*
Content 1
*
Content 2
*
*
* ``` * * 2. **JavaScript Initialization:** * ```javascript * import YaiTabs from './yai-tabs.js'; * const tabs = new YaiTabs(); * ``` * * 3. **Custom Configuration:** * ```javascript * const tabs = new YaiTabs({ * behavior: 'slide-up', * autoFocus: true, * autoDisambiguate: true * }); * ``` * * **πŸ”§ Advanced Features:** * - **Infinite Nesting**: Tab containers can be nested infinitely * - **Dynamic Content**: Use data-url for fetch-based content loading * - **Hash Routing**: Add data-ref-path for URL-based navigation * - **Custom Hooks**: Use .hook() method for lifecycle customization * - **Accessibility**: Full WCAG 2.1 AA compliance with keyboard navigation * * **⚑ Performance Tips:** * - Use data-spaceless to remove default padding * - Leverage data-delay and data-min-loading for smooth UX * - Enable autoDisambiguate for conflict-free nested IDs */