{
  "name": "Toolbar",
  "category": "Navigation",
  "subcategory": null,
  "description": "Horizontal toolbar container with separator, spacer, and group sub-components.",
  "thumbnail": "/preview-cards/toolbar.png",
  "hasDocsPage": true,
  "nugetPackage": "Lumeo",
  "files": [
    "UI/Toolbar/Toolbar.razor",
    "UI/Toolbar/ToolbarGroup.razor",
    "UI/Toolbar/ToolbarSeparator.razor",
    "UI/Toolbar/ToolbarSpacer.razor"
  ],
  "dependencies": [
    "button",
    "dropdown-menu"
  ],
  "packageDependencies": [
    "Blazicons.Lucide"
  ],
  "cssVars": [
    "--color-border",
    "--color-card",
    "--radius"
  ],
  "api": {
    "name": "Toolbar",
    "category": "Navigation",
    "subcategory": null,
    "description": "Horizontal toolbar container with separator, spacer, and group sub-components.",
    "nugetPackage": "Lumeo",
    "files": [
      "UI/Toolbar/Toolbar.razor",
      "UI/Toolbar/ToolbarGroup.razor",
      "UI/Toolbar/ToolbarSeparator.razor",
      "UI/Toolbar/ToolbarSpacer.razor"
    ],
    "namespace": "Lumeo",
    "inheritsFrom": null,
    "implements": [
      "IAsyncDisposable"
    ],
    "parameters": [
      {
        "name": "ChildContent",
        "type": "RenderFragment?",
        "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
      },
      {
        "name": "Overflow",
        "type": "bool",
        "default": null,
        "description": "When true the toolbar collapses items that don't fit into a trailing \"⋯\" dropdown. Use VisibleCount to control how many items are shown inline, or leave it at -1 (the default) to let the toolbar auto-measure via ResizeObserver.",
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "VisibleCount",
        "type": "int",
        "default": "-1",
        "description": "Number of leading items to show inline when Overflow is true. -1 (default) — auto-measure mode: a ResizeObserver measures the container width on every resize and computes the maximum number of items that fit before a \"⋯\" trigger button is needed. No consumer configuration required. Positive integer — manual mode: exactly that many items are shown inline and the rest collapse into the \"⋯\" dropdown regardless of available space.",
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "OverflowLabel",
        "type": "string?",
        "default": null,
        "description": "aria-label for the \"⋯\" overflow button. Default: \"More\".",
        "isCascading": false,
        "captureUnmatched": false
      }
    ],
    "events": [],
    "enums": [
      {
        "name": "ToolbarRenderSlot",
        "values": [
          "Visible",
          "Dropdown"
        ],
        "description": "Slot that a child item should render for in overflow mode."
      }
    ],
    "records": [
      {
        "name": "ToolbarOverflowContext",
        "signature": "ToolbarOverflowContext(ToolbarRenderSlot Slot, int VisibleCount, int TotalCount)",
        "description": "Cascaded by Toolbar to all descendant items when overflow mode is active. Items use this to decide whether to render normally (Visible slot) or as a dropdown row (Dropdown slot), and whether to hide themselves when they fall past the VisibleCount boundary."
      }
    ],
    "cssVars": [
      "--color-border",
      "--color-card",
      "--radius"
    ],
    "examples": [
      {
        "title": "Basic Toolbar",
        "code": "<Toolbar>\n    <Button Variant=\"Button.ButtonVariant.Ghost\" Size=\"Button.ButtonSize.Sm\">\n        <Blazicon Svg=\"Lucide.Bold\" class=\"h-4 w-4\" />\n    </Button>\n    <Button Variant=\"Button.ButtonVariant.Ghost\" Size=\"Button.ButtonSize.Sm\">\n        <Blazicon Svg=\"Lucide.Italic\" class=\"h-4 w-4\" />\n    </Button>\n    <ToolbarSeparator />\n    <Button Variant=\"Button.ButtonVariant.Ghost\" Size=\"Button.ButtonSize.Sm\">\n        <Blazicon Svg=\"Lucide.AlignLeft\" class=\"h-4 w-4\" />\n    </Button>\n</Toolbar>"
      },
      {
        "title": "With Spacer and Groups",
        "code": "<Toolbar>\n    <ToolbarGroup>\n        <Button Variant=\"Button.ButtonVariant.Ghost\" Size=\"Button.ButtonSize.Sm\">Undo</Button>\n        <Button Variant=\"Button.ButtonVariant.Ghost\" Size=\"Button.ButtonSize.Sm\">Redo</Button>\n    </ToolbarGroup>\n    <ToolbarSeparator />\n    <ToolbarGroup>\n        <Button Variant=\"Button.ButtonVariant.Ghost\" Size=\"Button.ButtonSize.Sm\">Bold</Button>\n        <Button Variant=\"Button.ButtonVariant.Ghost\" Size=\"Button.ButtonSize.Sm\">Italic</Button>\n    </ToolbarGroup>\n    <ToolbarSpacer />\n    <Button Variant=\"Button.ButtonVariant.Ghost\" Size=\"Button.ButtonSize.Sm\">Settings</Button>\n</Toolbar>"
      },
      {
        "title": "Overflow Menu",
        "code": "<!-- VisibleCount=3: first 3 items inline, rest collapse into ⋯ menu -->\n<Toolbar Overflow=\"true\" VisibleCount=\"3\">\n    <Button Variant=\"Button.ButtonVariant.Ghost\" Size=\"Button.ButtonSize.Sm\">Bold</Button>\n    <Button Variant=\"Button.ButtonVariant.Ghost\" Size=\"Button.ButtonSize.Sm\">Italic</Button>\n    <Button Variant=\"Button.ButtonVariant.Ghost\" Size=\"Button.ButtonSize.Sm\">Underline</Button>\n    <ToolbarSeparator />\n    <Button Variant=\"Button.ButtonVariant.Ghost\" Size=\"Button.ButtonSize.Sm\">Left</Button>\n    <Button Variant=\"Button.ButtonVariant.Ghost\" Size=\"Button.ButtonSize.Sm\">Center</Button>\n</Toolbar>"
      },
      {
        "title": "With Input",
        "code": "<Toolbar>\n    <Input Placeholder=\"Search...\" Class=\"h-7 text-sm w-48\" />\n    <ToolbarSeparator />\n    <Button Variant=\"Button.ButtonVariant.Ghost\" Size=\"Button.ButtonSize.Sm\">Filter</Button>\n    <ToolbarSpacer />\n    <Button Size=\"Button.ButtonSize.Sm\">Export</Button>\n</Toolbar>"
      }
    ],
    "subComponents": {
      "ToolbarGroup": {
        "componentName": "ToolbarGroup",
        "fileName": "ToolbarGroup.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "ChildContent",
            "type": "RenderFragment?",
            "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
      },
      "ToolbarSeparator": {
        "componentName": "ToolbarSeparator",
        "fileName": "ToolbarSeparator.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "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
      },
      "ToolbarSpacer": {
        "componentName": "ToolbarSpacer",
        "fileName": "ToolbarSpacer.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "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
      }
    },
    "parseFailed": false,
    "parseError": null
  },
  "$schema": "https://lumeo.nativ.sh/registry-component-schema.json",
  "key": "toolbar",
  "docsUrl": "https://lumeo.nativ.sh/components/toolbar",
  "sourceUrl": [
    {
      "file": "UI/Toolbar/Toolbar.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/Toolbar/Toolbar.razor"
    },
    {
      "file": "UI/Toolbar/ToolbarGroup.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/Toolbar/ToolbarGroup.razor"
    },
    {
      "file": "UI/Toolbar/ToolbarSeparator.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/Toolbar/ToolbarSeparator.razor"
    },
    {
      "file": "UI/Toolbar/ToolbarSpacer.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/Toolbar/ToolbarSpacer.razor"
    }
  ],
  "source": [
    {
      "path": "UI/Toolbar/Toolbar.razor",
      "content": "@namespace Lumeo\n@inject IJSRuntime JS\n@implements IAsyncDisposable\n\n@if (!Overflow)\n{\n    <div role=\"toolbar\"\n         aria-orientation=\"horizontal\"\n         tabindex=\"0\"\n         class=\"@CssClass\"\n         @attributes=\"AdditionalAttributes\">\n        @ChildContent\n    </div>\n}\nelse\n{\n    <div role=\"toolbar\"\n         aria-orientation=\"horizontal\"\n         class=\"@CssClassOverflow\"\n         id=\"@_toolbarId\"\n         @ref=\"_toolbarRef\"\n         @attributes=\"AdditionalAttributes\">\n        <CascadingValue Value=\"_overflowCtx\" IsFixed=\"false\">\n            @ChildContent\n        </CascadingValue>\n\n        @if (_overflowCtx.VisibleCount >= 0 && _overflowCtx.VisibleCount < _overflowCtx.TotalCount)\n        {\n            <div class=\"shrink-0\" data-toolbar-overflow-trigger>\n                <DropdownMenu @bind-Open=\"_overflowOpen\">\n                    <DropdownMenuTrigger>\n                        <Button Variant=\"Button.ButtonVariant.Ghost\"\n                                Size=\"Button.ButtonSize.Sm\"\n                                aria-label=\"@(OverflowLabel ?? \"More\")\">\n                            <Blazicon Svg=\"Lucide.Ellipsis\" class=\"h-4 w-4\" />\n                        </Button>\n                    </DropdownMenuTrigger>\n                    <DropdownMenuContent Align=\"DropdownMenuContent.DropdownMenuAlign.End\">\n                        <CascadingValue Value=\"_dropdownCtx\" IsFixed=\"false\">\n                            @ChildContent\n                        </CascadingValue>\n                    </DropdownMenuContent>\n                </DropdownMenu>\n            </div>\n        }\n    </div>\n}\n\n@code {\n    [Parameter] public RenderFragment? ChildContent { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n\n    /// <summary>\n    /// When true the toolbar collapses items that don't fit into a trailing \"⋯\" dropdown.\n    /// Use <see cref=\"VisibleCount\"/> to control how many items are shown inline, or leave\n    /// it at -1 (the default) to let the toolbar auto-measure via <c>ResizeObserver</c>.\n    /// </summary>\n    [Parameter] public bool Overflow { get; set; }\n\n    /// <summary>\n    /// Number of leading items to show inline when <see cref=\"Overflow\"/> is true.\n    /// <list type=\"bullet\">\n    ///   <item><description>\n    ///     <b>-1 (default)</b> — auto-measure mode: a <c>ResizeObserver</c> measures the\n    ///     container width on every resize and computes the maximum number of items that fit\n    ///     before a \"⋯\" trigger button is needed. No consumer configuration required.\n    ///   </description></item>\n    ///   <item><description>\n    ///     <b>Positive integer</b> — manual mode: exactly that many items are shown inline\n    ///     and the rest collapse into the \"⋯\" dropdown regardless of available space.\n    ///   </description></item>\n    /// </list>\n    /// </summary>\n    [Parameter] public int VisibleCount { get; set; } = -1;\n\n    /// <summary>aria-label for the \"⋯\" overflow button. Default: \"More\".</summary>\n    [Parameter] public string? OverflowLabel { get; set; }\n\n    // ── State ─────────────────────────────────────────────────────────────────\n\n    private bool _overflowOpen;\n    private ElementReference _toolbarRef;\n    // rc.42: per-instance DOM id so the JS observer can `document.getElementById`\n    // the toolbar without needing the ElementReference internal-id dance. Unique\n    // per component instance so multiple Toolbars on a page don't share state.\n    private readonly string _toolbarId = $\"lumeo-toolbar-{Guid.NewGuid():N}\";\n\n    // Auto-measurement (VisibleCount == -1 + Overflow == true)\n    private IJSObjectReference? _toolbarModule;\n    private bool _isObservingOverflow;\n    private DotNetObjectReference<Toolbar>? _selfRef;\n    private int _autoVisibleCount = -1; // -1 = not yet measured\n\n    private ToolbarOverflowContext _overflowCtx = new(ToolbarRenderSlot.Visible, -1, 0);\n    private ToolbarOverflowContext _dropdownCtx = new(ToolbarRenderSlot.Dropdown, -1, 0);\n\n    // ── Lifecycle ─────────────────────────────────────────────────────────────\n\n    protected override void OnParametersSet()\n    {\n        if (!Overflow) return;\n\n        // Effective visible count: prefer the explicit consumer value if > -1;\n        // otherwise fall back to the JS-measured _autoVisibleCount.\n        var effective = VisibleCount > -1 ? VisibleCount : _autoVisibleCount;\n\n        _overflowCtx = new ToolbarOverflowContext(ToolbarRenderSlot.Visible, effective, int.MaxValue);\n        _dropdownCtx = new ToolbarOverflowContext(ToolbarRenderSlot.Dropdown, effective, int.MaxValue);\n    }\n\n    protected override async Task OnAfterRenderAsync(bool firstRender)\n    {\n        if (!firstRender) return;\n        if (!Overflow || VisibleCount != -1) return; // manual mode — nothing to wire up\n\n        try\n        {\n            _toolbarModule ??= await JS.InvokeAsync<IJSObjectReference>(\n                \"import\", \"./_content/Lumeo/js/toolbar.js\");\n\n            _selfRef ??= DotNetObjectReference.Create(this);\n\n            // rc.42: pass the DOM id (string) instead of the ElementReference —\n            // observeToolbarOverflow does document.getElementById(elementId).\n            // Previously this passed `_toolbarRef` which the JS module didn't\n            // know how to convert to a DOM Element, AND the function itself\n            // didn't exist — so the whole feature was a runtime no-op.\n            await _toolbarModule.InvokeVoidAsync(\n                \"observeToolbarOverflow\", _toolbarId, _selfRef, nameof(OnOverflowMeasured));\n            _isObservingOverflow = true;\n        }\n        catch (JSDisconnectedException) { }\n        catch (ObjectDisposedException) { }\n    }\n\n    /// <summary>\n    /// Called by JS <c>observeToolbarOverflow</c> each time the fitting count changes.\n    /// Only triggers a re-render when the measured value actually changes.\n    /// </summary>\n    [JSInvokable]\n    public Task OnOverflowMeasured(int fittingCount, int totalCount)\n    {\n        if (fittingCount == _autoVisibleCount) return Task.CompletedTask; // no change — skip re-render\n\n        _autoVisibleCount = fittingCount;\n\n        _overflowCtx = new ToolbarOverflowContext(ToolbarRenderSlot.Visible, fittingCount, totalCount);\n        _dropdownCtx = new ToolbarOverflowContext(ToolbarRenderSlot.Dropdown, fittingCount, totalCount);\n\n        StateHasChanged();\n        return Task.CompletedTask;\n    }\n\n    // ── Disposal ──────────────────────────────────────────────────────────────\n\n    public async ValueTask DisposeAsync()\n    {\n        try\n        {\n            if (_isObservingOverflow && _toolbarModule is not null)\n            {\n                // rc.42: dispose by elementId — see observe site above for why.\n                await _toolbarModule.InvokeVoidAsync(\"disposeToolbarOverflow\", _toolbarId);\n            }\n\n            if (_toolbarModule is not null)\n                await _toolbarModule.DisposeAsync();\n        }\n        catch (JSDisconnectedException) { }\n        catch (ObjectDisposedException) { }\n        finally\n        {\n            _selfRef?.Dispose();\n        }\n    }\n\n    // ── CSS ──────────────────────────────────────────────────────────────────\n    private string CssClass =>\n        $\"flex items-center gap-1 p-1 bg-card border border-border/40 rounded-lg {Class}\".Trim();\n\n    private string CssClassOverflow =>\n        $\"flex items-center gap-1 p-1 bg-card border border-border/40 rounded-lg overflow-hidden {Class}\".Trim();\n\n    // ── Context record ────────────────────────────────────────────────────────\n\n    /// <summary>Slot that a child item should render for in overflow mode.</summary>\n    public enum ToolbarRenderSlot { Visible, Dropdown }\n\n    /// <summary>\n    /// Cascaded by <see cref=\"Toolbar\"/> to all descendant items when overflow mode is active.\n    /// Items use this to decide whether to render normally (Visible slot) or as a dropdown row\n    /// (Dropdown slot), and whether to hide themselves when they fall past the VisibleCount boundary.\n    /// </summary>\n    public record ToolbarOverflowContext(ToolbarRenderSlot Slot, int VisibleCount, int TotalCount);\n}\n"
    },
    {
      "path": "UI/Toolbar/ToolbarGroup.razor",
      "content": "@namespace Lumeo\n\n<div class=\"@CssClass\" @attributes=\"AdditionalAttributes\">\n    @ChildContent\n</div>\n\n@code {\n    [Parameter] public RenderFragment? ChildContent { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n\n    private string CssClass => $\"flex items-center gap-0.5 {Class}\".Trim();\n}\n"
    },
    {
      "path": "UI/Toolbar/ToolbarSeparator.razor",
      "content": "@namespace Lumeo\n\n<div class=\"@CssClass\" role=\"separator\" aria-orientation=\"vertical\" @attributes=\"AdditionalAttributes\"></div>\n\n@code {\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n\n    private string CssClass => $\"w-px h-6 bg-border/40 mx-1 shrink-0 {Class}\".Trim();\n}\n"
    },
    {
      "path": "UI/Toolbar/ToolbarSpacer.razor",
      "content": "@namespace Lumeo\n\n<div class=\"@CssClass\" @attributes=\"AdditionalAttributes\"></div>\n\n@code {\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n\n    private string CssClass => $\"flex-1 {Class}\".Trim();\n}\n"
    }
  ],
  "slots": [
    {
      "name": "ChildContent",
      "owner": "Toolbar",
      "type": "RenderFragment?",
      "description": null
    },
    {
      "name": "ChildContent",
      "owner": "ToolbarGroup",
      "type": "RenderFragment?",
      "description": null
    }
  ],
  "serviceDependencies": [
    {
      "service": "IJSRuntime",
      "namespace": ""
    }
  ],
  "cascadingDependencies": [],
  "relatedComponents": [
    {
      "name": "ToolbarGroup",
      "reason": "sub-component"
    },
    {
      "name": "ToolbarSeparator",
      "reason": "sub-component"
    },
    {
      "name": "ToolbarSpacer",
      "reason": "sub-component"
    },
    {
      "name": "DropdownMenu",
      "reason": "used internally"
    },
    {
      "name": "Button",
      "reason": "used internally"
    }
  ],
  "keyboardInteractions": [],
  "tests": [
    "tests/Lumeo.Tests/Components/Gantt/GanttTests.cs",
    "tests/Lumeo.Tests/Components/RichTextEditor/RichTextEditorTests.cs",
    "tests/Lumeo.Tests/Components/Scheduler/SchedulerTests.cs",
    "tests/Lumeo.Tests/Components/Toolbar/ToolbarTests.cs"
  ],
  "mdSummary": "# Toolbar\n\nHorizontal toolbar container with separator, spacer, and group sub-components.\n\n**Package:** `Lumeo` · **Category:** Navigation\n**Docs:** https://lumeo.nativ.sh/components/toolbar\n\n## Parameters\n| Owner | Name | Type | Default | Description |\n|---|---|---|---|---|\n| Toolbar | ChildContent | `RenderFragment?` | `` |  |\n| Toolbar | Class | `string?` | `` |  |\n| Toolbar | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| Toolbar | Overflow | `bool` | `` | When true the toolbar collapses items that don't fit into a trailing \"⋯\" dropdown. Use VisibleCount to control how many items are shown inline, or leave it at -1 (the default) to let the toolbar auto-measure via ResizeObserver. |\n| Toolbar | VisibleCount | `int` | `-1` | Number of leading items to show inline when Overflow is true. -1 (default) — auto-measure mode: a ResizeObserver measures the container width on every resize and computes the maximum number of items that fit before a \"⋯\" trigger button is needed. No consumer configuration required. Positive integer — manual mode: exactly that many items are shown inline and the rest collapse into the \"⋯\" dropdown regardless of available space. |\n| Toolbar | OverflowLabel | `string?` | `` | aria-label for the \"⋯\" overflow button. Default: \"More\". |\n| ToolbarGroup | ChildContent | `RenderFragment?` | `` |  |\n| ToolbarGroup | Class | `string?` | `` |  |\n| ToolbarGroup | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| ToolbarSeparator | Class | `string?` | `` |  |\n| ToolbarSeparator | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| ToolbarSpacer | Class | `string?` | `` |  |\n| ToolbarSpacer | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n\n## Slots\n- `ChildContent` (Toolbar) — RenderFragment slot\n- `ChildContent` (ToolbarGroup) — RenderFragment slot\n\n## Events\n- (none)\n\n## Sub-components\n- `ToolbarGroup`, `ToolbarSeparator`, `ToolbarSpacer`\n\n## Cascading values it expects\n- (none)\n\n## Services it injects\n- `IJSRuntime` ()\n\n## Keyboard\n—\n\n## Example\n```razor\n<Toolbar />\n```\n\n## CSS variables\n`--color-border`, `--color-card`, `--radius`\n\n## Files\n- `UI/Toolbar/Toolbar.razor`\n- `UI/Toolbar/ToolbarGroup.razor`\n- `UI/Toolbar/ToolbarSeparator.razor`\n- `UI/Toolbar/ToolbarSpacer.razor`\n"
}