{
  "name": "Tabs",
  "category": "Navigation",
  "subcategory": null,
  "description": "Tabbed content with keyboard nav and animated active indicator.",
  "thumbnail": "/preview-cards/tabs.png",
  "hasDocsPage": true,
  "nugetPackage": "Lumeo",
  "files": [
    "UI/Tabs/Tabs.razor",
    "UI/Tabs/TabsContent.razor",
    "UI/Tabs/TabsList.razor",
    "UI/Tabs/TabsTrigger.razor"
  ],
  "dependencies": [],
  "packageDependencies": [
    "Blazicons.Lucide"
  ],
  "cssVars": [
    "--color-background",
    "--color-border",
    "--color-card",
    "--color-foreground",
    "--color-muted",
    "--color-muted-foreground",
    "--color-primary",
    "--color-primary-foreground",
    "--color-ring",
    "--radius"
  ],
  "api": {
    "name": "Tabs",
    "category": "Navigation",
    "subcategory": null,
    "description": "Tabbed content with keyboard nav and animated active indicator.",
    "nugetPackage": "Lumeo",
    "files": [
      "UI/Tabs/Tabs.razor",
      "UI/Tabs/TabsContent.razor",
      "UI/Tabs/TabsList.razor",
      "UI/Tabs/TabsTrigger.razor"
    ],
    "namespace": "Lumeo",
    "inheritsFrom": null,
    "implements": [],
    "parameters": [
      {
        "name": "ChildContent",
        "type": "RenderFragment?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "ActiveValue",
        "type": "string",
        "default": "\"\"",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "ActiveValueChanged",
        "type": "EventCallback<string>",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Orientation",
        "type": "TabsOrientation",
        "default": "TabsOrientation.Horizontal",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Variant",
        "type": "TabsVariant",
        "default": "TabsVariant.Default",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "AnimatedIndicator",
        "type": "bool",
        "default": null,
        "description": "When true, renders a sliding underline that animates between active triggers inside each TabsList. Default is false (no underline, original behaviour).",
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "RenderMode",
        "type": "TabsRenderMode",
        "default": "TabsRenderMode.Active",
        "description": "Controls when each TabsContent panel is rendered into the DOM and whether its state survives a tab switch: Active (default) — only the active panel is in the DOM; switching away unmounts it (form inputs, scroll position and any component state are lost). Lightest footprint. Lazy — a panel is rendered the first time its tab becomes active, then kept in the DOM (hidden) so its state is preserved on subsequent switches. Like DevExpress OnDemand. Eager — every panel is rendered immediately; inactive ones are hidden. State always preserved, but the heaviest initial render. Like DevExpress Always. A single TabsContent can opt into eager rendering regardless of this setting via ForceMount=\"true\".",
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "SwipeEnabled",
        "type": "bool",
        "default": null,
        "description": "When true, a horizontal pointer drag (&gt;50 px horizontal, &lt;30 px vertical) on any TabsContent panel switches to the next or previous tab. Default is false (opt-in).",
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "SwipeWrap",
        "type": "bool",
        "default": null,
        "description": "When true, swiping past the last tab wraps around to the first (and vice-versa). Default is false (no wrap-around at boundaries).",
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Class",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "AdditionalAttributes",
        "type": "Dictionary<string, object>?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": true
      }
    ],
    "events": [
      {
        "name": "ActiveValueChanged",
        "type": "EventCallback<string>",
        "description": null
      }
    ],
    "enums": [
      {
        "name": "TabsOrientation",
        "values": [
          "Horizontal",
          "Vertical"
        ],
        "description": null
      },
      {
        "name": "TabsVariant",
        "values": [
          "Default",
          "Card",
          "Pill"
        ],
        "description": null
      },
      {
        "name": "TabsRenderMode",
        "values": [
          "Active",
          "Lazy",
          "Eager"
        ],
        "description": "Render strategy for TabsContent panels. See RenderMode for the semantics of each value."
      }
    ],
    "records": [
      {
        "name": "TabsContext",
        "signature": "TabsContext(\n        string ActiveValue,\n        EventCallback<string> SetValue,\n        TabsOrientation Orientation,\n        TabsVariant Variant,\n        Dictionary<string, (string TabId, string PanelId)> TabIds,\n        bool AnimatedIndicator,\n        TabsRenderMode RenderMode,\n        HashSet<string> ActivatedValues,\n        bool SwipeEnabled,\n        bool SwipeWrap,\n        Action<string> RegisterContent,\n        Func<string, Task> SwipeNavigate)",
        "description": null
      }
    ],
    "cssVars": [
      "--color-background",
      "--color-border",
      "--color-card",
      "--color-foreground",
      "--color-muted",
      "--color-muted-foreground",
      "--color-primary",
      "--color-primary-foreground",
      "--color-ring",
      "--radius"
    ],
    "examples": [
      {
        "title": "Default",
        "code": "<Tabs ActiveValue=\"account\">\n    <TabsList>\n        <TabsTrigger Value=\"account\">Account</TabsTrigger>\n        <TabsTrigger Value=\"password\">Password</TabsTrigger>\n    </TabsList>\n    <TabsContent Value=\"account\">\n        <Card>\n            <CardHeader>\n                <Heading Level=\"3\" Size=\"lg\" Class=\"leading-none tracking-tight\">Account</Heading>\n                <Lumeo.Text Size=\"sm\" Color=\"muted\">Make changes to your account here.</Lumeo.Text>\n            </CardHeader>\n            <CardContent>\n                <Stack Gap=\"4\">\n                    <FormField Label=\"Name\">\n                        <Input Value=\"Pedro Duarte\" />\n                    </FormField>\n                    <FormField Label=\"Username\">\n                        <Input Value=\"@peduarte\" />\n                    </FormField>\n                </Stack>\n            </CardContent>\n            <CardFooter>\n                <Button>Save changes</Button>\n            </CardFooter>\n        </Card>\n    </TabsContent>\n    <TabsContent Value=\"password\">\n        <Card>\n            <CardHeader>\n                <Heading Level=\"3\" Size=\"lg\" Class=\"leading-none tracking-tight\">Password</Heading>\n                <Lumeo.Text Size=\"sm\" Color=\"muted\">Change your password here.</Lumeo.Text>\n            </CardHeader>\n            <CardContent>\n                <Stack Gap=\"4\">\n                    <FormField Label=\"Current password\">\n                        <Input type=\"password\" />\n                    </FormField>\n                    <FormField Label=\"New password\">\n                        <Input type=\"password\" />\n                    </FormField>\n                </Stack>\n            </CardContent>\n            <CardFooter>\n                <Button>Save password</Button>\n            </CardFooter>\n        </Card>\n    </TabsContent>\n</Tabs>"
      },
      {
        "title": "Notifications",
        "code": "<Tabs ActiveValue=\"all\">\n    <TabsList>\n        <TabsTrigger Value=\"all\">All</TabsTrigger>\n        <TabsTrigger Value=\"unread\">Unread</TabsTrigger>\n        <TabsTrigger Value=\"archived\">Archived</TabsTrigger>\n    </TabsList>\n    <TabsContent Value=\"all\">\n        @foreach (var n in _notifications)\n        {\n            <Flex Gap=\"3\" Align=\"start\" Class=\"p-3 rounded-lg border\">\n                <span class=\"mt-1.5 h-2 w-2 rounded-full @(n.Unread ? \"bg-primary\" : \"bg-muted\")\"></span>\n                <Stack Gap=\"0\">\n                    <Lumeo.Text Size=\"sm\" Weight=\"medium\">@n.Title</Lumeo.Text>\n                    <Lumeo.Text Size=\"xs\" Color=\"muted\">@n.Time</Lumeo.Text>\n                </Stack>\n            </Flex>\n        }\n    </TabsContent>\n    <TabsContent Value=\"unread\">\n        @foreach (var n in _notifications.Where(x => x.Unread)) { /* same */ }\n    </TabsContent>\n    <TabsContent Value=\"archived\">\n        <Lumeo.Text Size=\"sm\" Color=\"muted\" Class=\"p-4 text-center\">No archived notifications.</Lumeo.Text>\n    </TabsContent>\n</Tabs>"
      },
      {
        "title": "Vertical Tabs",
        "code": "<Tabs Orientation=\"Tabs.TabsOrientation.Vertical\" @bind-ActiveValue=\"_verticalTab\" Class=\"flex gap-4\">\n    <TabsList>\n        <TabsTrigger Value=\"account\">Account</TabsTrigger>\n        <TabsTrigger Value=\"security\">Security</TabsTrigger>\n        <TabsTrigger Value=\"notifications\">Notifications</TabsTrigger>\n    </TabsList>\n    <div class=\"flex-1\">\n        <TabsContent Value=\"account\">\n            <Card>\n                <CardHeader><CardTitle>Account</CardTitle><CardDescription>Manage your account settings.</CardDescription></CardHeader>\n                <CardContent><Lumeo.Text Size=\"sm\" Color=\"muted\">Account settings content goes here.</Lumeo.Text></CardContent>\n            </Card>\n        </TabsContent>\n        <TabsContent Value=\"security\">\n            <Card>\n                <CardHeader><CardTitle>Security</CardTitle><CardDescription>Update your security preferences.</CardDescription></CardHeader>\n                <CardContent><Lumeo.Text Size=\"sm\" Color=\"muted\">Security settings content goes here.</Lumeo.Text></CardContent>\n            </Card>\n        </TabsContent>\n        <TabsContent Value=\"notifications\">\n            <Card>\n                <CardHeader><CardTitle>Notifications</CardTitle><CardDescription>Configure notification preferences.</CardDescription></CardHeader>\n                <CardContent><Lumeo.Text Size=\"sm\" Color=\"muted\">Notification settings content goes here.</Lumeo.Text></CardContent>\n            </Card>\n        </TabsContent>\n    </div>\n</Tabs>\n\n@code {\n    private string _verticalTab = \"account\";\n}"
      },
      {
        "title": "Simple",
        "code": "<Tabs ActiveValue=\"overview\">\n    <TabsList>\n        <TabsTrigger Value=\"overview\">Overview</TabsTrigger>\n        <TabsTrigger Value=\"analytics\">Analytics</TabsTrigger>\n        <TabsTrigger Value=\"reports\">Reports</TabsTrigger>\n    </TabsList>\n    <TabsContent Value=\"overview\">\n        <Lumeo.Text Size=\"sm\" Color=\"muted\" Class=\"p-4\">Overview content goes here.</Lumeo.Text>\n    </TabsContent>\n    <TabsContent Value=\"analytics\">\n        <Lumeo.Text Size=\"sm\" Color=\"muted\" Class=\"p-4\">Analytics content goes here.</Lumeo.Text>\n    </TabsContent>\n    <TabsContent Value=\"reports\">\n        <Lumeo.Text Size=\"sm\" Color=\"muted\" Class=\"p-4\">Reports content goes here.</Lumeo.Text>\n    </TabsContent>\n</Tabs>"
      }
    ],
    "subComponents": {
      "TabsContent": {
        "componentName": "TabsContent",
        "fileName": "TabsContent.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [
          "IAsyncDisposable"
        ],
        "parameters": [
          {
            "name": "Context",
            "type": "Tabs.TabsContext",
            "default": "default!",
            "description": null,
            "isCascading": true,
            "captureUnmatched": false
          },
          {
            "name": "ChildContent",
            "type": "RenderFragment?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Value",
            "type": "string",
            "default": "\"\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ForceMount",
            "type": "bool",
            "default": null,
            "description": "When true, this panel is always rendered into the DOM (hidden when inactive), regardless of the parent RenderMode. Use for panels whose state must survive every tab switch — e.g. a half-filled form — while leaving the rest of the tab set on the lighter default mode.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "TabsList": {
        "componentName": "TabsList",
        "fileName": "TabsList.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [
          "IAsyncDisposable"
        ],
        "parameters": [
          {
            "name": "Context",
            "type": "Tabs.TabsContext?",
            "default": null,
            "description": null,
            "isCascading": true,
            "captureUnmatched": false
          },
          {
            "name": "ChildContent",
            "type": "RenderFragment?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "TrailingContent",
            "type": "RenderFragment?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Scrollable",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "TabsTrigger": {
        "componentName": "TabsTrigger",
        "fileName": "TabsTrigger.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Context",
            "type": "Tabs.TabsContext",
            "default": "default!",
            "description": null,
            "isCascading": true,
            "captureUnmatched": false
          },
          {
            "name": "ChildContent",
            "type": "RenderFragment?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "IconContent",
            "type": "RenderFragment?",
            "default": null,
            "description": "Icon slot. Named IconContent to avoid shadowing the &lt;Icon&gt; component.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "TrailingContent",
            "type": "RenderFragment?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Value",
            "type": "string",
            "default": "\"\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "IsClosable",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Disabled",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OnClose",
            "type": "EventCallback",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          }
        ],
        "events": [
          {
            "name": "OnClose",
            "type": "EventCallback",
            "description": null
          }
        ],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      }
    },
    "parseFailed": false,
    "parseError": null
  },
  "$schema": "https://lumeo.nativ.sh/registry-component-schema.json",
  "key": "tabs",
  "docsUrl": "https://lumeo.nativ.sh/components/tabs",
  "sourceUrl": [
    {
      "file": "UI/Tabs/Tabs.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/Tabs/Tabs.razor"
    },
    {
      "file": "UI/Tabs/TabsContent.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/Tabs/TabsContent.razor"
    },
    {
      "file": "UI/Tabs/TabsList.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/Tabs/TabsList.razor"
    },
    {
      "file": "UI/Tabs/TabsTrigger.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/Tabs/TabsTrigger.razor"
    }
  ],
  "source": [
    {
      "path": "UI/Tabs/Tabs.razor",
      "content": "@namespace Lumeo\n\n@{\n    var context = new TabsContext(ActiveValue, EventCallback.Factory.Create<string>(this, SetValue), Orientation, Variant, _tabIds, AnimatedIndicator, RenderMode, _activatedValues, SwipeEnabled, SwipeWrap, RegisterContent, NavigateBySwipe);\n}\n\n<div class=\"@CssClass\" @attributes=\"AdditionalAttributes\">\n    <CascadingValue Value=\"context\" IsFixed=\"false\">\n        @ChildContent\n    </CascadingValue>\n</div>\n\n@code {\n    [Parameter] public RenderFragment? ChildContent { get; set; }\n    [Parameter] public string ActiveValue { get; set; } = \"\";\n    [Parameter] public EventCallback<string> ActiveValueChanged { get; set; }\n    [Parameter] public TabsOrientation Orientation { get; set; } = TabsOrientation.Horizontal;\n    [Parameter] public TabsVariant Variant { get; set; } = TabsVariant.Default;\n\n    /// <summary>\n    /// When true, renders a sliding underline that animates between active\n    /// triggers inside each <see cref=\"TabsList\"/>. Default is <c>false</c>\n    /// (no underline, original behaviour).\n    /// </summary>\n    [Parameter] public bool AnimatedIndicator { get; set; }\n\n    /// <summary>\n    /// Controls when each <see cref=\"TabsContent\"/> panel is rendered into\n    /// the DOM and whether its state survives a tab switch:\n    /// <list type=\"bullet\">\n    /// <item><see cref=\"TabsRenderMode.Active\"/> (default) — only the active\n    /// panel is in the DOM; switching away unmounts it (form inputs, scroll\n    /// position and any component state are lost). Lightest footprint.</item>\n    /// <item><see cref=\"TabsRenderMode.Lazy\"/> — a panel is rendered the\n    /// first time its tab becomes active, then kept in the DOM (hidden) so\n    /// its state is preserved on subsequent switches. Like DevExpress\n    /// <c>OnDemand</c>.</item>\n    /// <item><see cref=\"TabsRenderMode.Eager\"/> — every panel is rendered\n    /// immediately; inactive ones are hidden. State always preserved, but\n    /// the heaviest initial render. Like DevExpress <c>Always</c>.</item>\n    /// </list>\n    /// A single <see cref=\"TabsContent\"/> can opt into eager rendering\n    /// regardless of this setting via <c>ForceMount=\"true\"</c>.\n    /// </summary>\n    [Parameter] public TabsRenderMode RenderMode { get; set; } = TabsRenderMode.Active;\n\n    /// <summary>\n    /// When true, a horizontal pointer drag (&gt;50 px horizontal, &lt;30 px\n    /// vertical) on any <see cref=\"TabsContent\"/> panel switches to the\n    /// next or previous tab. Default is <c>false</c> (opt-in).\n    /// </summary>\n    [Parameter] public bool SwipeEnabled { get; set; }\n\n    /// <summary>\n    /// When true, swiping past the last tab wraps around to the first (and\n    /// vice-versa). Default is <c>false</c> (no wrap-around at boundaries).\n    /// </summary>\n    [Parameter] public bool SwipeWrap { get; set; }\n\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n\n    // Registry: value -> (tabId, panelId) — populated by TabsTrigger on first render\n    private readonly Dictionary<string, (string TabId, string PanelId)> _tabIds = new();\n    // Set of tab values that have ever been the active tab — drives Lazy mode.\n    private readonly HashSet<string> _activatedValues = new();\n    // Ordered list of registered TabsContent values — used for swipe navigation.\n    private readonly List<string> _contentOrder = new();\n\n    private string CssClass => Orientation == TabsOrientation.Vertical\n        ? $\"flex flex-row {Class}\".Trim()\n        : $\"{Class}\".Trim();\n\n    protected override void OnParametersSet()\n    {\n        // Track activations so Lazy mode knows which panels to keep mounted.\n        if (!string.IsNullOrEmpty(ActiveValue))\n            _activatedValues.Add(ActiveValue);\n    }\n\n    internal void RegisterContent(string value)\n    {\n        if (!_contentOrder.Contains(value))\n            _contentOrder.Add(value);\n    }\n\n    internal async Task NavigateBySwipe(string direction)\n    {\n        if (_contentOrder.Count == 0) return;\n        var idx = _contentOrder.IndexOf(ActiveValue);\n        if (idx < 0) return;\n\n        int next;\n        if (direction == \"next\")\n        {\n            next = idx + 1;\n            if (next >= _contentOrder.Count)\n            {\n                if (!SwipeWrap) return;\n                next = 0;\n            }\n        }\n        else // \"prev\"\n        {\n            next = idx - 1;\n            if (next < 0)\n            {\n                if (!SwipeWrap) return;\n                next = _contentOrder.Count - 1;\n            }\n        }\n\n        await SetValue(_contentOrder[next]);\n        StateHasChanged();\n    }\n\n    private async Task SetValue(string value)\n    {\n        ActiveValue = value;\n        if (!string.IsNullOrEmpty(value))\n            _activatedValues.Add(value);\n        await ActiveValueChanged.InvokeAsync(value);\n    }\n\n    public record TabsContext(\n        string ActiveValue,\n        EventCallback<string> SetValue,\n        TabsOrientation Orientation,\n        TabsVariant Variant,\n        Dictionary<string, (string TabId, string PanelId)> TabIds,\n        bool AnimatedIndicator,\n        TabsRenderMode RenderMode,\n        HashSet<string> ActivatedValues,\n        bool SwipeEnabled,\n        bool SwipeWrap,\n        Action<string> RegisterContent,\n        Func<string, Task> SwipeNavigate)\n    {\n        public TabsContext(string ActiveValue, EventCallback<string> SetValue, TabsOrientation Orientation = TabsOrientation.Horizontal, TabsVariant Variant = TabsVariant.Default)\n            : this(ActiveValue, SetValue, Orientation, Variant, new Dictionary<string, (string, string)>(), false, TabsRenderMode.Active, new HashSet<string>(), false, false, _ => { }, _ => Task.CompletedTask) { }\n    }\n\n    public enum TabsOrientation { Horizontal, Vertical }\n\n    public enum TabsVariant\n    {\n        Default,\n        Card,\n        Pill\n    }\n\n    /// <summary>Render strategy for <see cref=\"TabsContent\"/> panels. See\n    /// <see cref=\"Tabs.RenderMode\"/> for the semantics of each value.</summary>\n    public enum TabsRenderMode\n    {\n        /// <summary>Only the active panel is in the DOM; switching away unmounts it. Default.</summary>\n        Active,\n        /// <summary>Panels render on first activation and stay mounted (hidden) afterwards.</summary>\n        Lazy,\n        /// <summary>All panels render immediately; inactive ones are hidden.</summary>\n        Eager\n    }\n}\n"
    },
    {
      "path": "UI/Tabs/TabsContent.razor",
      "content": "@namespace Lumeo\n@inject Lumeo.Services.IComponentInteropService Interop\n@implements IAsyncDisposable\n\n@if (ShouldRender_)\n{\n    <div role=\"tabpanel\"\n         id=\"@PanelId\"\n         aria-labelledby=\"@TabId\"\n         tabindex=\"0\"\n         hidden=\"@(IsActive ? null : \"hidden\")\"\n         class=\"@CssClass\"\n         @attributes=\"AdditionalAttributes\">\n        @ChildContent\n    </div>\n}\n\n@code {\n    [CascadingParameter] public Tabs.TabsContext Context { get; set; } = default!;\n    [Parameter] public RenderFragment? ChildContent { get; set; }\n    [Parameter, EditorRequired] public string Value { get; set; } = \"\";\n\n    /// <summary>\n    /// When true, this panel is always rendered into the DOM (hidden when\n    /// inactive), regardless of the parent <see cref=\"Tabs.RenderMode\"/>.\n    /// Use for panels whose state must survive every tab switch — e.g. a\n    /// half-filled form — while leaving the rest of the tab set on the\n    /// lighter default mode.\n    /// </summary>\n    [Parameter] public bool ForceMount { get; set; }\n\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n\n    private bool IsActive => Context.ActiveValue == Value;\n    private bool _swipeAttached;\n\n    // Decide whether this panel's <div> exists in the DOM at all.\n    //   Active mode  → only when this tab is the active one\n    //   Lazy mode    → once this tab has ever been activated (or ForceMount)\n    //   Eager mode   → always (or ForceMount)\n    private bool ShouldRender_\n    {\n        get\n        {\n            if (ForceMount) return true;\n            return Context.RenderMode switch\n            {\n                Tabs.TabsRenderMode.Eager => true,\n                Tabs.TabsRenderMode.Lazy  => IsActive || Context.ActivatedValues.Contains(Value),\n                _                          => IsActive\n            };\n        }\n    }\n\n    private string PanelId => Context.TabIds.TryGetValue(Value, out var ids) ? ids.PanelId : $\"tabpanel-{Value}\";\n    private string TabId => Context.TabIds.TryGetValue(Value, out var ids) ? ids.TabId : $\"tab-{Value}\";\n\n    private string CssClass => Context.SwipeEnabled\n        ? $\"mt-2 focus-visible:outline-none touch-pan-y {Class}\".Trim()\n        : $\"mt-2 focus-visible:outline-none {Class}\".Trim();\n\n    protected override void OnInitialized()\n    {\n        // Register this panel's value in the parent's ordered list.\n        Context.RegisterContent(Value);\n    }\n\n    protected override async Task OnAfterRenderAsync(bool firstRender)\n    {\n        if (!Context.SwipeEnabled) return;\n\n        // Re-attach whenever this panel transitions from inactive → active.\n        // In Active render mode the <div> is removed from the DOM when the tab\n        // becomes inactive, so every time it reappears it is a fresh element\n        // with no JS listeners — we must re-register each time.\n        var shouldAttach = IsActive;\n        if (shouldAttach && !_swipeAttached)\n        {\n            _swipeAttached = true;\n            try\n            {\n                await Interop.RegisterTabSwipe(PanelId, Context.SwipeWrap, async direction =>\n                {\n                    // Navigate via the parent Tabs component's method through the context callback.\n                    // The context carries a delegate that resolves to Tabs.NavigateBySwipe.\n                    await Context.SwipeNavigate(direction);\n                });\n            }\n            catch (JSDisconnectedException) { _swipeAttached = false; }\n            catch (InvalidOperationException) { _swipeAttached = false; } // JS unavailable during prerender\n        }\n        else if (!shouldAttach && _swipeAttached)\n        {\n            _swipeAttached = false;\n            try { await Interop.UnregisterTabSwipe(PanelId); }\n            catch (JSDisconnectedException) { }\n        }\n    }\n\n    public async ValueTask DisposeAsync()\n    {\n        if (!_swipeAttached) return;\n        try\n        {\n            await Interop.UnregisterTabSwipe(PanelId);\n        }\n        catch (JSDisconnectedException) { }\n    }\n}\n"
    },
    {
      "path": "UI/Tabs/TabsList.razor",
      "content": "@namespace Lumeo\n@inject Lumeo.Services.ComponentInteropService Interop\n@implements IAsyncDisposable\n\n@if (Scrollable)\n{\n    <div class=\"overflow-hidden\">\n        <div id=\"@_listId\" role=\"tablist\" aria-orientation=\"@OrientationStr\" class=\"@CssClass overflow-x-auto scrollbar-none\" style=\"@IndicatorStyle\" data-sliding-pill=\"@(UsePillIndicator ? \"true\" : null)\" @attributes=\"AdditionalAttributes\">\n            @ChildContent\n            @if (TrailingContent is not null)\n            {\n                @TrailingContent\n            }\n            @if (RenderIndicator)\n            {\n                <span aria-hidden=\"true\" class=\"@IndicatorClass\" data-ready=\"@(_indicatorReady ? \"true\" : \"false\")\"></span>\n            }\n        </div>\n    </div>\n}\nelse\n{\n    <div id=\"@_listId\" role=\"tablist\" aria-orientation=\"@OrientationStr\" class=\"@CssClass\" style=\"@IndicatorStyle\" data-sliding-pill=\"@(UsePillIndicator ? \"true\" : null)\" @attributes=\"AdditionalAttributes\">\n        @ChildContent\n        @if (TrailingContent is not null)\n        {\n            @TrailingContent\n        }\n        @if (RenderIndicator)\n        {\n            <span aria-hidden=\"true\" class=\"@IndicatorClass\" data-ready=\"@(_indicatorReady ? \"true\" : \"false\")\"></span>\n        }\n    </div>\n}\n\n@code {\n    [CascadingParameter] public Tabs.TabsContext? Context { get; set; }\n\n    [Parameter] public RenderFragment? ChildContent { get; set; }\n    [Parameter] public RenderFragment? TrailingContent { get; set; }\n    [Parameter] public bool Scrollable { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n\n    private readonly string _listId = $\"tabslist-{Guid.NewGuid():N}\";\n    private double _indicatorX;\n    private double _indicatorW;\n    private bool _indicatorReady;\n    private string? _lastMeasuredValue;\n\n    private bool IsVertical => Context?.Orientation == Tabs.TabsOrientation.Vertical;\n    private string OrientationStr => IsVertical ? \"vertical\" : \"horizontal\";\n    private Tabs.TabsVariant CurrentVariant => Context?.Variant ?? Tabs.TabsVariant.Default;\n\n    private bool RenderIndicator => Context?.AnimatedIndicator == true && !IsVertical;\n\n    // Default and Pill variants render a background pill that slides; Card uses an underline.\n    private bool UsePillIndicator => RenderIndicator && CurrentVariant != Tabs.TabsVariant.Card;\n\n    private string IndicatorClass => UsePillIndicator ? \"lumeo-tabs-indicator\" : \"lumeo-tabs-underline\";\n\n    private string? IndicatorStyle => RenderIndicator\n        ? $\"--lumeo-tabs-indicator-x: {_indicatorX.ToString(System.Globalization.CultureInfo.InvariantCulture)}px; --lumeo-tabs-indicator-w: {_indicatorW.ToString(System.Globalization.CultureInfo.InvariantCulture)}px;\"\n        : null;\n\n    protected override async Task OnAfterRenderAsync(bool firstRender)\n    {\n        if (!RenderIndicator || Context is null) return;\n\n        var active = Context.ActiveValue;\n        if (string.IsNullOrEmpty(active)) return;\n        if (!Context.TabIds.TryGetValue(active, out var ids)) return;\n        if (_lastMeasuredValue == active && _indicatorReady) return;\n\n        ComponentInteropService.TabMeasurement? measurement;\n        try\n        {\n            measurement = await Interop.TabsMeasure(ids.TabId);\n        }\n        catch (JSDisconnectedException) { return; }\n        catch (InvalidOperationException) { return; } // thrown during prerender when JS is unavailable\n        if (measurement is null) return;\n\n        var changed = Math.Abs(measurement.X - _indicatorX) > 0.5\n                     || Math.Abs(measurement.Width - _indicatorW) > 0.5\n                     || !_indicatorReady;\n\n        _indicatorX = measurement.X;\n        _indicatorW = measurement.Width;\n        _indicatorReady = true;\n        _lastMeasuredValue = active;\n\n        if (changed)\n        {\n            StateHasChanged();\n        }\n    }\n\n    public ValueTask DisposeAsync()\n    {\n        // No persistent JS state to clean up — measure is a one-shot invocation.\n        return ValueTask.CompletedTask;\n    }\n\n    private string CssClass\n    {\n        get\n        {\n            var baseClasses = CurrentVariant switch\n            {\n                Tabs.TabsVariant.Card => IsVertical\n                    ? \"flex flex-col h-auto w-auto items-stretch justify-start text-muted-foreground\"\n                    : \"inline-flex h-10 items-center justify-center text-muted-foreground\",\n                Tabs.TabsVariant.Pill => IsVertical\n                    ? \"flex flex-col h-auto w-auto items-stretch justify-start gap-1 text-muted-foreground\"\n                    : \"inline-flex h-10 items-center justify-center gap-1 text-muted-foreground\",\n                _ => IsVertical\n                    ? \"flex flex-col h-auto w-auto items-stretch justify-start rounded-lg bg-muted p-1 text-muted-foreground\"\n                    : \"inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground\"\n            };\n            var indicatorWrapper = RenderIndicator ? \"relative lumeo-tabs-list\" : null;\n            return string.Join(\" \", new[] { baseClasses, indicatorWrapper, Class }.Where(s => !string.IsNullOrWhiteSpace(s)));\n        }\n    }\n}\n"
    },
    {
      "path": "UI/Tabs/TabsTrigger.razor",
      "content": "@namespace Lumeo\n\n<button type=\"button\"\n        id=\"@_tabId\"\n        role=\"tab\"\n        aria-selected=\"@IsActive.ToString().ToLower()\"\n        aria-disabled=\"@(Disabled ? \"true\" : null)\"\n        aria-controls=\"@_panelId\"\n        tabindex=\"@(Disabled ? -1 : IsActive ? 0 : -1)\"\n        class=\"@CssClass\"\n        @onclick=\"Activate\"\n        @onkeydown=\"HandleKeyDown\"\n        @onkeydown:preventDefault=\"@_preventKey\"\n        @attributes=\"AdditionalAttributes\">\n    @if (IconContent is not null)\n    {\n        <span class=\"inline-flex shrink-0\">@IconContent</span>\n    }\n    @ChildContent\n    @if (TrailingContent is not null)\n    {\n        <span class=\"inline-flex shrink-0\">@TrailingContent</span>\n    }\n    @if (IsClosable)\n    {\n        <span class=\"ml-1 inline-flex items-center justify-center rounded-sm cursor-pointer opacity-70 hover:opacity-100 transition-opacity h-11 w-11 -my-2 sm:h-4 sm:w-4 sm:my-0\"\n              @onclick=\"HandleClose\" @onclick:stopPropagation=\"true\"\n              role=\"button\" aria-label=\"Close tab\">\n            <Blazicon Svg=\"Lucide.X\" class=\"h-3 w-3\" />\n        </span>\n    }\n</button>\n\n@inject ComponentInteropService Interop\n@inject ThemeService Theme\n\n@code {\n    [CascadingParameter] public Tabs.TabsContext Context { get; set; } = default!;\n    [Parameter] public RenderFragment? ChildContent { get; set; }\n\n    /// <summary>Icon slot. Named <c>IconContent</c> to avoid shadowing the <c>&lt;Icon&gt;</c> component.</summary>\n    [Parameter] public RenderFragment? IconContent { get; set; }\n\n    [Parameter] public RenderFragment? TrailingContent { get; set; }\n    [Parameter, EditorRequired] public string Value { get; set; } = \"\";\n    [Parameter] public bool IsClosable { get; set; }\n    [Parameter] public bool Disabled { get; set; }\n    [Parameter] public EventCallback OnClose { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n\n    private readonly string _tabId = $\"tab-{Guid.NewGuid():N}\";\n    private readonly string _panelId = $\"tabpanel-{Guid.NewGuid():N}\";\n    private bool _preventKey;\n\n    private bool IsActive => Context.ActiveValue == Value;\n    private Tabs.TabsVariant CurrentVariant => Context.Variant;\n\n    protected override void OnInitialized()\n    {\n        Context.TabIds[Value] = (_tabId, _panelId);\n    }\n\n    private string CssClass\n    {\n        get\n        {\n            var disabledClasses = Disabled ? \"opacity-50 cursor-not-allowed pointer-events-none\" : \"\";\n            var variantClasses = CurrentVariant switch\n            {\n                Tabs.TabsVariant.Card => IsActive\n                    ? \"rounded-t-lg border border-b-0 border-border/40 bg-card text-foreground\"\n                    : \"text-muted-foreground hover:text-foreground\",\n                Tabs.TabsVariant.Pill => IsActive\n                    ? \"rounded-full bg-primary text-primary-foreground\"\n                    : \"text-muted-foreground hover:bg-muted/50 rounded-full\",\n                _ => IsActive\n                    ? \"bg-background text-foreground shadow\"\n                    : \"\"\n            };\n            return $\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium cursor-pointer transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed {variantClasses} {disabledClasses} {Class}\".Trim();\n        }\n    }\n\n    private async Task Activate()\n    {\n        if (Disabled) return;\n        await Context.SetValue.InvokeAsync(Value);\n    }\n\n    private async Task HandleClose()\n    {\n        await OnClose.InvokeAsync();\n    }\n\n    private async Task HandleKeyDown(KeyboardEventArgs e)\n    {\n        if (Disabled) return;\n\n        var isHorizontal = Context.Orientation == Tabs.TabsOrientation.Horizontal;\n        var isRtl = Theme.CurrentDirection == LayoutDirection.Rtl;\n        var keys = Context.TabIds.Keys.ToList();\n        var idx = keys.IndexOf(Value);\n\n        // In RTL, ArrowLeft/Right are swapped for horizontal tabs so they follow visual flow.\n        var (hBack, hForward) = (isHorizontal && isRtl) ? (\"ArrowRight\", \"ArrowLeft\") : (\"ArrowLeft\", \"ArrowRight\");\n\n        string? nextValue = null;\n        if (isHorizontal)\n        {\n            if (e.Key == hForward) nextValue = FindNextEnabledTab(keys, idx, 1);\n            else if (e.Key == hBack) nextValue = FindNextEnabledTab(keys, idx, -1);\n        }\n        else\n        {\n            if (e.Key == \"ArrowDown\") nextValue = FindNextEnabledTab(keys, idx, 1);\n            else if (e.Key == \"ArrowUp\") nextValue = FindNextEnabledTab(keys, idx, -1);\n        }\n        if (e.Key == \"Home\") nextValue = keys.Count > 0 ? keys[0] : null;\n        else if (e.Key == \"End\") nextValue = keys.Count > 0 ? keys[^1] : null;\n\n        _preventKey = e.Key is \"ArrowRight\" or \"ArrowLeft\" or \"ArrowDown\" or \"ArrowUp\" or \"Home\" or \"End\";\n\n        if (nextValue is not null && Context.TabIds.TryGetValue(nextValue, out var ids))\n        {\n            await Context.SetValue.InvokeAsync(nextValue);\n            await Interop.FocusElement(ids.TabId);\n        }\n    }\n\n    private string? FindNextEnabledTab(List<string> keys, int currentIdx, int direction)\n    {\n        if (keys.Count == 0) return null;\n        var nextIdx = currentIdx + direction;\n        if (nextIdx < 0) nextIdx = keys.Count - 1;\n        else if (nextIdx >= keys.Count) nextIdx = 0;\n        return keys[nextIdx];\n    }\n}\n"
    }
  ],
  "slots": [
    {
      "name": "ChildContent",
      "owner": "Tabs",
      "type": "RenderFragment?",
      "description": null
    },
    {
      "name": "ChildContent",
      "owner": "TabsContent",
      "type": "RenderFragment?",
      "description": null
    },
    {
      "name": "ChildContent",
      "owner": "TabsList",
      "type": "RenderFragment?",
      "description": null
    },
    {
      "name": "TrailingContent",
      "owner": "TabsList",
      "type": "RenderFragment?",
      "description": null
    },
    {
      "name": "ChildContent",
      "owner": "TabsTrigger",
      "type": "RenderFragment?",
      "description": null
    },
    {
      "name": "IconContent",
      "owner": "TabsTrigger",
      "type": "RenderFragment?",
      "description": "Icon slot. Named IconContent to avoid shadowing the &lt;Icon&gt; component."
    },
    {
      "name": "TrailingContent",
      "owner": "TabsTrigger",
      "type": "RenderFragment?",
      "description": null
    }
  ],
  "serviceDependencies": [
    {
      "service": "IComponentInteropService",
      "namespace": "Lumeo.Services"
    },
    {
      "service": "ComponentInteropService",
      "namespace": "Lumeo.Services"
    },
    {
      "service": "ComponentInteropService",
      "namespace": ""
    },
    {
      "service": "ThemeService",
      "namespace": ""
    }
  ],
  "cascadingDependencies": [
    {
      "name": "Context",
      "type": "Tabs.TabsContext",
      "required": true
    }
  ],
  "relatedComponents": [
    {
      "name": "TabsContent",
      "reason": "sub-component"
    },
    {
      "name": "TabsList",
      "reason": "sub-component"
    },
    {
      "name": "TabsTrigger",
      "reason": "sub-component"
    }
  ],
  "keyboardInteractions": [
    {
      "key": "ArrowDown",
      "action": "HandleKeyDown handles ArrowDown in TabsTrigger.razor"
    },
    {
      "key": "ArrowUp",
      "action": "HandleKeyDown handles ArrowUp in TabsTrigger.razor"
    },
    {
      "key": "Home",
      "action": "HandleKeyDown handles Home in TabsTrigger.razor"
    },
    {
      "key": "End",
      "action": "HandleKeyDown handles End in TabsTrigger.razor"
    }
  ],
  "tests": [
    "tests/Lumeo.Tests/Components/Tabs/TabsSwipeReregistrationTests.cs",
    "tests/Lumeo.Tests/Components/Tabs/TabsSwipeTests.cs"
  ],
  "mdSummary": "# Tabs\n\nTabbed content with keyboard nav and animated active indicator.\n\n**Package:** `Lumeo` · **Category:** Navigation\n**Docs:** https://lumeo.nativ.sh/components/tabs\n\n## Parameters\n| Owner | Name | Type | Default | Description |\n|---|---|---|---|---|\n| Tabs | ChildContent | `RenderFragment?` | `` |  |\n| Tabs | ActiveValue | `string` | `\"\"` |  |\n| Tabs | ActiveValueChanged | `EventCallback<string>` | `` |  |\n| Tabs | Orientation | `TabsOrientation` | `TabsOrientation.Horizontal` |  |\n| Tabs | Variant | `TabsVariant` | `TabsVariant.Default` |  |\n| Tabs | AnimatedIndicator | `bool` | `` | When true, renders a sliding underline that animates between active triggers inside each TabsList. Default is false (no underline, original behaviour). |\n| Tabs | RenderMode | `TabsRenderMode` | `TabsRenderMode.Active` | Controls when each TabsContent panel is rendered into the DOM and whether its state survives a tab switch: Active (default) — only the active panel is in the DOM; switching away unmounts it (form inputs, scroll position and any component state are lost). Lightest footprint. Lazy — a panel is rendered the first time its tab becomes active, then kept in the DOM (hidden) so its state is preserved on subsequent switches. Like DevExpress OnDemand. Eager — every panel is rendered immediately; inactive ones are hidden. State always preserved, but the heaviest initial render. Like DevExpress Always. A single TabsContent can opt into eager rendering regardless of this setting via ForceMount=\"true\". |\n| Tabs | SwipeEnabled | `bool` | `` | When true, a horizontal pointer drag (&gt;50 px horizontal, &lt;30 px vertical) on any TabsContent panel switches to the next or previous tab. Default is false (opt-in). |\n| Tabs | SwipeWrap | `bool` | `` | When true, swiping past the last tab wraps around to the first (and vice-versa). Default is false (no wrap-around at boundaries). |\n| Tabs | Class | `string?` | `` |  |\n| Tabs | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| TabsContent | Context | `Tabs.TabsContext` | `default!` |  |\n| TabsContent | ChildContent | `RenderFragment?` | `` |  |\n| TabsContent | Value | `string` | `\"\"` |  |\n| TabsContent | ForceMount | `bool` | `` | When true, this panel is always rendered into the DOM (hidden when inactive), regardless of the parent RenderMode. Use for panels whose state must survive every tab switch — e.g. a half-filled form — while leaving the rest of the tab set on the lighter default mode. |\n| TabsContent | Class | `string?` | `` |  |\n| TabsContent | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| TabsList | Context | `Tabs.TabsContext?` | `` |  |\n| TabsList | ChildContent | `RenderFragment?` | `` |  |\n| TabsList | TrailingContent | `RenderFragment?` | `` |  |\n| TabsList | Scrollable | `bool` | `` |  |\n| TabsList | Class | `string?` | `` |  |\n| TabsList | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| TabsTrigger | Context | `Tabs.TabsContext` | `default!` |  |\n| TabsTrigger | ChildContent | `RenderFragment?` | `` |  |\n| TabsTrigger | IconContent | `RenderFragment?` | `` | Icon slot. Named IconContent to avoid shadowing the &lt;Icon&gt; component. |\n| TabsTrigger | TrailingContent | `RenderFragment?` | `` |  |\n| TabsTrigger | Value | `string` | `\"\"` |  |\n| TabsTrigger | IsClosable | `bool` | `` |  |\n| TabsTrigger | Disabled | `bool` | `` |  |\n| TabsTrigger | OnClose | `EventCallback` | `` |  |\n| TabsTrigger | Class | `string?` | `` |  |\n| TabsTrigger | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n\n## Slots\n- `ChildContent` (Tabs) — RenderFragment slot\n- `ChildContent` (TabsContent) — RenderFragment slot\n- `ChildContent` (TabsList) — RenderFragment slot\n- `TrailingContent` (TabsList) — RenderFragment slot\n- `ChildContent` (TabsTrigger) — RenderFragment slot\n- `IconContent` (TabsTrigger) — Icon slot. Named IconContent to avoid shadowing the &lt;Icon&gt; component.\n- `TrailingContent` (TabsTrigger) — RenderFragment slot\n\n## Events\n- `ActiveValueChanged` (`EventCallback<string>`)\n\n## Sub-components\n- `TabsContent`, `TabsList`, `TabsTrigger`\n\n## Cascading values it expects\n- `Context` (`Tabs.TabsContext`)\n\n## Services it injects\n- `IComponentInteropService` (Lumeo.Services)\n- `ComponentInteropService` (Lumeo.Services)\n- `ComponentInteropService` ()\n- `ThemeService` ()\n\n## Keyboard\n- `ArrowDown` — HandleKeyDown handles ArrowDown in TabsTrigger.razor\n- `ArrowUp` — HandleKeyDown handles ArrowUp in TabsTrigger.razor\n- `Home` — HandleKeyDown handles Home in TabsTrigger.razor\n- `End` — HandleKeyDown handles End in TabsTrigger.razor\n\n## Example\n```razor\n<Tabs />\n```\n\n## CSS variables\n`--color-background`, `--color-border`, `--color-card`, `--color-foreground`, `--color-muted`, `--color-muted-foreground`, `--color-primary`, `--color-primary-foreground`, `--color-ring`, `--radius`\n\n## Files\n- `UI/Tabs/Tabs.razor`\n- `UI/Tabs/TabsContent.razor`\n- `UI/Tabs/TabsList.razor`\n- `UI/Tabs/TabsTrigger.razor`\n"
}