v0.0.14
builders

Tabs builder

Builder class for creating accessible Tabs components. Handles focus management, keyboard interactions, and ARIA attributes.

Usage

<script>
    import { TabsBuilder } from 'svxui/builders/tabs';

    // Controlled value state
    let value = $state('Tab1');
    const options = ['Tab1', 'Tab2', 'Tab3'];

    // Initialize tabs
    const tabs = new TabsBuilder({
        get value() {
            return value;
        },
        set value(newValue) {
            value = newValue;
        },
        orientation: 'horizontal',
        activateOnFocus: true,
        loop: true
    });
</script>

<!-- Tabs container -->
<div class="tabs" {...tabs.rootAttrs}>
    <!-- Tabs triggers container -->
    <div class="triggers" {...tabs.triggerListAttrs}>
        {#each options as opt, i (i)}
            {@const trigger = tabs.getTrigger(opt)}
            <button class="trigger" class:active={trigger.active} {...trigger.attrs}>{opt}</button>
        {/each}
    </div>

    <!-- Tabs contents -->
    {#each options as opt, i (i)}
        {@const content = tabs.getContent(opt)}
        {#if content.active}
            <div class="content" {...content.attrs}>{opt}</div>
        {/if}
    {/each}
</div>

<style>
    .tabs {
        display: flex;
        flex-direction: column;
        gap: 5px;

        .triggers {
            display: flex;
            gap: 5px;

            .trigger.active {
                background-color: lightgray;
                color: black;
            }
        }

        .content {
            padding: 1rem;
        }
    }
</style>

Type Definition

TabsBuilder

export declare class TabsBuilder<Value> {
    /**
     * @param options - Tabs configuration options.
     *
     * **Lifecycle note:** `TabsBuilder` internally uses `SelectionState` which holds a reactive
     * `$effect.root()`. Call `destroy()` when the builder is instantiated outside a Svelte component
     * to avoid memory leaks.
     */
    constructor(options: TabsBuilderOptions<Value>);

    /**
     * Tabs instance id
     */
    get id(): string;

    /**
     * Tabs orientation
     */
    get orientation(): Orientation;

    /**
     * Tabs is globally disabled
     */
    get disabled(): boolean;

    /**
     * Loop or not keyboard navigation
     */
    get loop(): boolean;

    /**
     * Enable or not tab activation on focus
     */
    get activateOnFocus(): boolean;

    /**
     * Check if an tab item is active
     * @param value The tab item value to check
     */
    isActive: (value: Value) => boolean;

    /**
     * Activate a tab item
     * @param value The tab item value to activate
     */
    activate: (value: Value) => void;

    /**
     * Destroy internal reactive effects. Call this when the builder is instantiated
     * outside a Svelte component to avoid memory leaks.
     */
    destroy: () => void;

    /**
     * Tabs root attributes
     */
    get rootAttrs(): TabsRootAttributes;

    /**
     * Tabs triggers list attributes
     */
    get triggerListAttrs(): TabsItemTriggerListAttributes;

    /**
     * Build a tab item trigger part
     * @param value The tab item value
     * @param triggerOptions Optional per-trigger configuration overrides
     * @returns The tabs item trigger instance
     */
    getTrigger(value: Value, triggerOptions?: TabsItemTriggerOptions): TabsItemTrigger;

    /**
     * Build a tab item content part
     * @param value The tab item value
     * @param contentOptions Optional per-content configuration overrides
     * @returns The tabs item content instance
     */
    getContent(value: Value, contentOptions?: TabsItemContentOptions): TabsItemContent;
}

TabsBuilderOptions

import type { Orientation } from '$lib/shared.types.js';
import type { SelectionStateOptions } from '$lib/utilities/selection-state/index.js';

/**
 * Tabs state options
 */
export type TabsBuilderOptions<Value> = Omit<SelectionStateOptions<Value, false>, 'multiple'> & {
    /**
     * Tabs orientation
     */
    orientation?: Orientation;
    /**
     * Tabs must be disabled or not
     */
    disabled?: boolean;
    /**
     * Loop focus on keyboard navigation
     */
    loop?: boolean;
    /**
     * Activate item on focus
     */
    activateOnFocus?: boolean;
};

export type TabsRootAttributes = {
    readonly id: `tabs-root-${string}`;
    readonly 'data-disabled': '' | undefined;
    readonly 'data-orientation': Orientation;
};

export type TabsItemTriggerListAttributes = {
    readonly [x: string]: '' | Orientation | `tabs-list-${string}` | 'tablist' | undefined;
    readonly id: `tabs-list-${string}`;
    readonly 'data-disabled': '' | undefined;
    readonly 'data-orientation': Orientation;
    readonly role: 'tablist';
};

/**
 * Tabs item trigger part customization
 */
export type TabsItemTriggerOptions = {
    /**
     * Custom tab item triggert part id
     */
    id?: string;
    /**
     * Disable or not tab item
     */
    disabled?: boolean;
};

/**
 * Tabs item trigger part state
 */
export type TabsItemTrigger = {
    /**
     * Tab item is activated or not
     */
    readonly active: boolean;
    /**
     * Tab item is disabled or not
     */
    readonly disabled?: boolean;
    /**
     * Tab trigger part attributes
     */
    readonly attrs: TabsItemTriggerAttributes;
};

export type TabsItemTriggerAttributes = {
    readonly id: string;
    readonly tabindex: 0 | -1;
    readonly disabled: boolean;
    readonly 'data-state': 'active' | 'inactive';
    readonly 'data-orientation': Orientation;
    readonly 'data-disabled': '' | undefined;
    readonly role: 'tab';
    readonly 'aria-selected'?: boolean;
    readonly 'aria-controls': string | undefined;
    readonly onfocus: () => void;
    readonly onclick: (e: MouseEvent) => void;
    // readonly onkeydown: (e: KeyboardEvent) => void;
};

/**
 * Tabs item content part customization
 */
export type TabsItemContentOptions = {
    /**
     * Custom tab item content part id
     */
    id?: string;
    /**
     * Disable or not tab item
     */
    disabled?: boolean;
};

/**
 * Tab item content part state
 */
export type TabsItemContent = {
    /**
     * Tab item is activated or not
     */
    readonly active: boolean;
    /**
     * Tab item is disabled or not
     */
    readonly disabled?: boolean;
    /**
     * Tab content part attributes
     */
    readonly attrs: TabsItemContentAttributes;
};

export type TabsItemContentAttributes = {
    readonly id: string;
    readonly disabled: boolean;
    readonly tabindex: 0;
    readonly hidden: true | undefined;
    readonly 'data-state': 'active' | 'inactive';
    readonly 'data-orientation': Orientation;
    readonly 'data-disabled'?: '';
    readonly role: 'tabpanel';
    readonly 'aria-labelledby'?: string;
};