{
  "name": "Popover",
  "category": "Overlay",
  "subcategory": null,
  "description": "Positionable floating panel with anchor and arrow.",
  "thumbnail": "/preview-cards/popover.png",
  "hasDocsPage": true,
  "nugetPackage": "Lumeo",
  "files": [
    "UI/Popover/Popover.razor",
    "UI/Popover/PopoverContent.razor",
    "UI/Popover/PopoverTrigger.razor"
  ],
  "dependencies": [],
  "packageDependencies": [],
  "cssVars": [
    "--color-border",
    "--color-popover",
    "--color-popover-foreground",
    "--radius"
  ],
  "api": {
    "name": "Popover",
    "category": "Overlay",
    "subcategory": null,
    "description": "Positionable floating panel with anchor and arrow.",
    "nugetPackage": "Lumeo",
    "files": [
      "UI/Popover/Popover.razor",
      "UI/Popover/PopoverContent.razor",
      "UI/Popover/PopoverTrigger.razor"
    ],
    "namespace": "Lumeo",
    "inheritsFrom": null,
    "implements": [],
    "parameters": [
      {
        "name": "ChildContent",
        "type": "RenderFragment?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Open",
        "type": "bool",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "OpenChanged",
        "type": "EventCallback<bool>",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "IsOpen",
        "type": "bool",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "IsOpenChanged",
        "type": "EventCallback<bool>",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "AdditionalAttributes",
        "type": "Dictionary<string, object>?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": true
      }
    ],
    "events": [
      {
        "name": "OpenChanged",
        "type": "EventCallback<bool>",
        "description": null
      },
      {
        "name": "IsOpenChanged",
        "type": "EventCallback<bool>",
        "description": null
      }
    ],
    "enums": [],
    "records": [
      {
        "name": "PopoverContext",
        "signature": "PopoverContext(bool IsOpen, EventCallback<bool> SetOpen, string WrapperId)",
        "description": null
      }
    ],
    "cssVars": [
      "--color-border",
      "--color-popover",
      "--color-popover-foreground",
      "--radius"
    ],
    "examples": [
      {
        "title": "Default",
        "code": "<Popover>\n    <PopoverTrigger>\n        <Button Variant=\"Button.ButtonVariant.Outline\">Open Popover</Button>\n    </PopoverTrigger>\n    <PopoverContent>\n        <Stack Gap=\"4\">\n            <Stack Gap=\"2\">\n                <Heading Level=\"4\" Class=\"text-base font-medium leading-none\">Dimensions</Heading>\n                <Lumeo.Text Size=\"sm\" Color=\"muted\">Set the dimensions for the layer.</Lumeo.Text>\n            </Stack>\n            <Grid Columns=\"3\" Gap=\"4\" Class=\"items-center\">\n                <Label>Width</Label>\n                <Input Class=\"col-span-2 h-8\" Value=\"100%\" />\n                <Label>Height</Label>\n                <Input Class=\"col-span-2 h-8\" Value=\"25px\" />\n            </Grid>\n        </Stack>\n    </PopoverContent>\n</Popover>"
      },
      {
        "title": "Alignment",
        "code": "<Popover>\n    <PopoverTrigger>\n        <Button Variant=\"Button.ButtonVariant.Outline\">Start</Button>\n    </PopoverTrigger>\n    <PopoverContent Align=\"PopoverContent.PopoverAlign.Start\">\n        <Lumeo.Text Size=\"sm\">Aligned to start.</Lumeo.Text>\n    </PopoverContent>\n</Popover>\n\n<Popover>\n    <PopoverTrigger>\n        <Button Variant=\"Button.ButtonVariant.Outline\">End</Button>\n    </PopoverTrigger>\n    <PopoverContent Align=\"PopoverContent.PopoverAlign.End\">\n        <Lumeo.Text Size=\"sm\">Aligned to end.</Lumeo.Text>\n    </PopoverContent>\n</Popover>"
      },
      {
        "title": "With Arrow",
        "code": "<Popover>\n    <PopoverTrigger><Button Variant=\"Button.ButtonVariant.Outline\">Top</Button></PopoverTrigger>\n    <PopoverContent Side=\"PopoverContent.PopoverSide.Top\" ShowArrow=\"true\">\n        <Lumeo.Text Size=\"sm\">Popover on top with an arrow pointing at the trigger.</Lumeo.Text>\n    </PopoverContent>\n</Popover>"
      },
      {
        "title": "Placement",
        "code": "<Popover>\n    <PopoverTrigger><Button Variant=\"Button.ButtonVariant.Outline\">Top</Button></PopoverTrigger>\n    <PopoverContent Side=\"PopoverContent.PopoverSide.Top\">\n        <Lumeo.Text Size=\"sm\">Popover on top</Lumeo.Text>\n    </PopoverContent>\n</Popover>\n<Popover>\n    <PopoverTrigger><Button Variant=\"Button.ButtonVariant.Outline\">Bottom</Button></PopoverTrigger>\n    <PopoverContent Side=\"PopoverContent.PopoverSide.Bottom\">\n        <Lumeo.Text Size=\"sm\">Popover on bottom</Lumeo.Text>\n    </PopoverContent>\n</Popover>\n<Popover>\n    <PopoverTrigger><Button Variant=\"Button.ButtonVariant.Outline\">Left</Button></PopoverTrigger>\n    <PopoverContent Side=\"PopoverContent.PopoverSide.Left\">\n        <Lumeo.Text Size=\"sm\">Popover on left</Lumeo.Text>\n    </PopoverContent>\n</Popover>\n<Popover>\n    <PopoverTrigger><Button Variant=\"Button.ButtonVariant.Outline\">Right</Button></PopoverTrigger>\n    <PopoverContent Side=\"PopoverContent.PopoverSide.Right\">\n        <Lumeo.Text Size=\"sm\">Popover on right</Lumeo.Text>\n    </PopoverContent>\n</Popover>"
      }
    ],
    "subComponents": {
      "PopoverContent": {
        "componentName": "PopoverContent",
        "fileName": "PopoverContent.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [
          "IAsyncDisposable"
        ],
        "parameters": [
          {
            "name": "Context",
            "type": "Popover.PopoverContext",
            "default": "default!",
            "description": null,
            "isCascading": true,
            "captureUnmatched": false
          },
          {
            "name": "ChildContent",
            "type": "RenderFragment?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Align",
            "type": "PopoverAlign",
            "default": "PopoverAlign.Center",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Side",
            "type": "PopoverSide",
            "default": "PopoverSide.Bottom",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowArrow",
            "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": [
          {
            "name": "PopoverAlign",
            "values": [
              "Start",
              "Center",
              "End"
            ],
            "description": null
          },
          {
            "name": "PopoverSide",
            "values": [
              "Top",
              "Bottom",
              "Left",
              "Right"
            ],
            "description": null
          }
        ],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "PopoverTrigger": {
        "componentName": "PopoverTrigger",
        "fileName": "PopoverTrigger.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Context",
            "type": "Popover.PopoverContext",
            "default": "default!",
            "description": null,
            "isCascading": true,
            "captureUnmatched": false
          },
          {
            "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
      }
    },
    "parseFailed": false,
    "parseError": null
  },
  "$schema": "https://lumeo.nativ.sh/registry-component-schema.json",
  "key": "popover",
  "docsUrl": "https://lumeo.nativ.sh/components/popover",
  "sourceUrl": [
    {
      "file": "UI/Popover/Popover.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/Popover/Popover.razor"
    },
    {
      "file": "UI/Popover/PopoverContent.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/Popover/PopoverContent.razor"
    },
    {
      "file": "UI/Popover/PopoverTrigger.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/Popover/PopoverTrigger.razor"
    }
  ],
  "source": [
    {
      "path": "UI/Popover/Popover.razor",
      "content": "@namespace Lumeo\n\n@{\n    var context = new PopoverContext(Open, EventCallback.Factory.Create<bool>(this, SetOpen), _wrapperId);\n}\n\n<CascadingValue Value=\"context\" IsFixed=\"false\">\n    <div id=\"@_wrapperId\" class=\"relative inline-block\" @attributes=\"AdditionalAttributes\">\n        @ChildContent\n    </div>\n</CascadingValue>\n\n@code {\n    [Parameter] public RenderFragment? ChildContent { get; set; }\n    [Parameter] public bool Open { get; set; }\n    [Parameter] public EventCallback<bool> OpenChanged { get; set; }\n\n    [Obsolete(\"Use Open instead. IsOpen will be removed in a future release.\")]\n    [Parameter] public bool IsOpen { get => Open; set => Open = value; }\n\n    [Obsolete(\"Use OpenChanged instead. IsOpenChanged will be removed in a future release.\")]\n    [Parameter] public EventCallback<bool> IsOpenChanged { get => OpenChanged; set => OpenChanged = value; }\n\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n\n    private readonly string _wrapperId = $\"popover-{Guid.NewGuid():N}\";\n\n    private async Task SetOpen(bool value)\n    {\n        Open = value;\n        await OpenChanged.InvokeAsync(value);\n    }\n\n    public record PopoverContext(bool IsOpen, EventCallback<bool> SetOpen, string WrapperId);\n}\n"
    },
    {
      "path": "UI/Popover/PopoverContent.razor",
      "content": "@namespace Lumeo\n@implements IAsyncDisposable\n\n@if (Context.IsOpen)\n{\n    <div id=\"@_contentId\" class=\"@CssClass\" @onkeydown=\"HandleKeyDown\" tabindex=\"-1\" @attributes=\"AdditionalAttributes\">\n        @ChildContent\n        @if (ShowArrow)\n        {\n            <div class=\"@ArrowClasses\"></div>\n        }\n    </div>\n}\n\n@inject ComponentInteropService Interop\n\n@code {\n    [CascadingParameter] public Popover.PopoverContext Context { get; set; } = default!;\n    [Parameter] public RenderFragment? ChildContent { get; set; }\n    [Parameter] public PopoverAlign Align { get; set; } = PopoverAlign.Center;\n    [Parameter] public PopoverSide Side { get; set; } = PopoverSide.Bottom;\n    [Parameter] public bool ShowArrow { 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 _contentId = $\"popover-content-{Guid.NewGuid():N}\";\n    private bool _registered;\n\n    private string CssClass => string.Join(\" \", new[] { \"fixed z-50 w-72 rounded-md border border-border/60 bg-popover p-4 text-popover-foreground shadow-lg outline-none\", ShowArrow ? \"overflow-visible\" : \"overflow-hidden\", \"animate-fade-in\", Class }.Where(s => !string.IsNullOrWhiteSpace(s)));\n\n    private string AlignStr => Align switch\n    {\n        PopoverAlign.Start => \"start\",\n        PopoverAlign.Center => \"center\",\n        PopoverAlign.End => \"end\",\n        _ => \"center\"\n    };\n\n    private string SideStr => Side switch\n    {\n        PopoverSide.Top => \"top\",\n        PopoverSide.Left => \"left\",\n        PopoverSide.Right => \"right\",\n        _ => \"bottom\"\n    };\n\n    // Rotated square so the arrow inherits both the popover surface colour AND a matching border\n    // on the two sides that face out — looks sharper than the hollow-triangle trick and lines the\n    // arrow up with the popover corner radius visually.\n    private string ArrowClasses => Side switch\n    {\n        PopoverSide.Top => \"absolute left-1/2 top-full -translate-x-1/2 -translate-y-1/2 rotate-45 h-2.5 w-2.5 bg-popover border-b border-r border-border/60\",\n        PopoverSide.Bottom => \"absolute left-1/2 bottom-full -translate-x-1/2 translate-y-1/2 rotate-45 h-2.5 w-2.5 bg-popover border-t border-l border-border/60\",\n        PopoverSide.Left => \"absolute top-1/2 left-full -translate-y-1/2 -translate-x-1/2 rotate-45 h-2.5 w-2.5 bg-popover border-t border-r border-border/60\",\n        PopoverSide.Right => \"absolute top-1/2 right-full -translate-y-1/2 translate-x-1/2 rotate-45 h-2.5 w-2.5 bg-popover border-b border-l border-border/60\",\n        _ => \"absolute left-1/2 bottom-full -translate-x-1/2 translate-y-1/2 rotate-45 h-2.5 w-2.5 bg-popover border-t border-l border-border/60\"\n    };\n\n    protected override async Task OnAfterRenderAsync(bool firstRender)\n    {\n        if (Context.IsOpen && !_registered)\n        {\n            await Interop.PositionFixed(_contentId, Context.WrapperId, AlignStr, false, SideStr);\n            await Interop.RegisterClickOutside(_contentId, Context.WrapperId, async () =>\n            {\n                await Context.SetOpen.InvokeAsync(false);\n                await InvokeAsync(StateHasChanged);\n            });\n            _registered = true;\n        }\n        else if (!Context.IsOpen && _registered)\n        {\n            await Cleanup();\n        }\n    }\n\n    private async Task HandleKeyDown(KeyboardEventArgs e)\n    {\n        if (e.Key == \"Escape\") await Context.SetOpen.InvokeAsync(false);\n    }\n\n    private async Task Cleanup()\n    {\n        try { await Interop.UnpositionFixed(_contentId); } catch (JSDisconnectedException) { }\n        try { await Interop.UnregisterClickOutside(_contentId); } catch (JSDisconnectedException) { }\n        _registered = false;\n    }\n\n    public async ValueTask DisposeAsync()\n    {\n        if (_registered) await Cleanup();\n    }\n\n    public enum PopoverAlign { Start, Center, End }\n    public enum PopoverSide { Top, Bottom, Left, Right }\n}\n"
    },
    {
      "path": "UI/Popover/PopoverTrigger.razor",
      "content": "@namespace Lumeo\n\n<div class=\"@CssClass\" role=\"button\" aria-expanded=\"@Context.IsOpen\" @onclick=\"Toggle\" @attributes=\"AdditionalAttributes\">\n    @ChildContent\n</div>\n\n@code {\n    [CascadingParameter] public Popover.PopoverContext Context { get; set; } = default!;\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 => $\"inline-flex {Class}\".Trim();\n\n    private async Task Toggle() => await Context.SetOpen.InvokeAsync(!Context.IsOpen);\n}\n"
    }
  ],
  "slots": [
    {
      "name": "ChildContent",
      "owner": "Popover",
      "type": "RenderFragment?",
      "description": null
    },
    {
      "name": "ChildContent",
      "owner": "PopoverContent",
      "type": "RenderFragment?",
      "description": null
    },
    {
      "name": "ChildContent",
      "owner": "PopoverTrigger",
      "type": "RenderFragment?",
      "description": null
    }
  ],
  "serviceDependencies": [
    {
      "service": "ComponentInteropService",
      "namespace": ""
    }
  ],
  "cascadingDependencies": [
    {
      "name": "Context",
      "type": "Popover.PopoverContext",
      "required": true
    }
  ],
  "relatedComponents": [
    {
      "name": "PopoverContent",
      "reason": "sub-component"
    },
    {
      "name": "PopoverTrigger",
      "reason": "sub-component"
    }
  ],
  "keyboardInteractions": [
    {
      "key": "Escape",
      "action": "HandleKeyDown handles Escape in PopoverContent.razor"
    }
  ],
  "tests": [
    "tests/Lumeo.Tests/Components/DatePicker/DatePickerTests.cs",
    "tests/Lumeo.Tests/Components/Popover/PopoverTests.cs"
  ],
  "mdSummary": "# Popover\n\nPositionable floating panel with anchor and arrow.\n\n**Package:** `Lumeo` · **Category:** Overlay\n**Docs:** https://lumeo.nativ.sh/components/popover\n\n## Parameters\n| Owner | Name | Type | Default | Description |\n|---|---|---|---|---|\n| Popover | ChildContent | `RenderFragment?` | `` |  |\n| Popover | Open | `bool` | `` |  |\n| Popover | OpenChanged | `EventCallback<bool>` | `` |  |\n| Popover | IsOpen | `bool` | `` |  |\n| Popover | IsOpenChanged | `EventCallback<bool>` | `` |  |\n| Popover | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| PopoverContent | Context | `Popover.PopoverContext` | `default!` |  |\n| PopoverContent | ChildContent | `RenderFragment?` | `` |  |\n| PopoverContent | Align | `PopoverAlign` | `PopoverAlign.Center` |  |\n| PopoverContent | Side | `PopoverSide` | `PopoverSide.Bottom` |  |\n| PopoverContent | ShowArrow | `bool` | `` |  |\n| PopoverContent | Class | `string?` | `` |  |\n| PopoverContent | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| PopoverTrigger | Context | `Popover.PopoverContext` | `default!` |  |\n| PopoverTrigger | ChildContent | `RenderFragment?` | `` |  |\n| PopoverTrigger | Class | `string?` | `` |  |\n| PopoverTrigger | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n\n## Slots\n- `ChildContent` (Popover) — RenderFragment slot\n- `ChildContent` (PopoverContent) — RenderFragment slot\n- `ChildContent` (PopoverTrigger) — RenderFragment slot\n\n## Events\n- `OpenChanged` (`EventCallback<bool>`)\n- `IsOpenChanged` (`EventCallback<bool>`)\n\n## Sub-components\n- `PopoverContent`, `PopoverTrigger`\n\n## Cascading values it expects\n- `Context` (`Popover.PopoverContext`)\n\n## Services it injects\n- `ComponentInteropService` ()\n\n## Keyboard\n- `Escape` — HandleKeyDown handles Escape in PopoverContent.razor\n\n## Example\n```razor\n<Popover />\n```\n\n## CSS variables\n`--color-border`, `--color-popover`, `--color-popover-foreground`, `--radius`\n\n## Files\n- `UI/Popover/Popover.razor`\n- `UI/Popover/PopoverContent.razor`\n- `UI/Popover/PopoverTrigger.razor`\n"
}