{
  "name": "HoverCard",
  "category": "Overlay",
  "subcategory": null,
  "description": "Popover that opens on hover for rich previews.",
  "thumbnail": "/preview-cards/hover-card.png",
  "hasDocsPage": true,
  "nugetPackage": "Lumeo",
  "files": [
    "UI/HoverCard/HoverCard.razor",
    "UI/HoverCard/HoverCardContent.razor",
    "UI/HoverCard/HoverCardTrigger.razor"
  ],
  "dependencies": [],
  "packageDependencies": [],
  "cssVars": [
    "--color-border",
    "--color-popover",
    "--color-popover-foreground",
    "--radius"
  ],
  "api": {
    "name": "HoverCard",
    "category": "Overlay",
    "subcategory": null,
    "description": "Popover that opens on hover for rich previews.",
    "nugetPackage": "Lumeo",
    "files": [
      "UI/HoverCard/HoverCard.razor",
      "UI/HoverCard/HoverCardContent.razor",
      "UI/HoverCard/HoverCardTrigger.razor"
    ],
    "namespace": "Lumeo",
    "inheritsFrom": null,
    "implements": [
      "IAsyncDisposable"
    ],
    "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": "OpenDelay",
        "type": "int",
        "default": "200",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "CloseDelay",
        "type": "int",
        "default": "300",
        "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": "HoverCardContext",
        "signature": "HoverCardContext(bool IsOpen, EventCallback RequestOpen, EventCallback RequestClose, EventCallback RequestTogglePin, string WrapperId)",
        "description": null
      }
    ],
    "cssVars": [
      "--color-border",
      "--color-popover",
      "--color-popover-foreground",
      "--radius"
    ],
    "examples": [
      {
        "title": "Default",
        "code": "<HoverCard>\n    <HoverCardTrigger>\n        <Button Variant=\"Button.ButtonVariant.Link\">@@blazor</Button>\n    </HoverCardTrigger>\n    <HoverCardContent>\n        <Stack Gap=\"1\">\n            <Heading Level=\"4\" Size=\"sm\" Weight=\"semibold\">@@blazor</Heading>\n            <Lumeo.Text Size=\"sm\" Color=\"muted\">\n                A free and open-source web framework.\n            </Lumeo.Text>\n            <Lumeo.Text As=\"span\" Size=\"xs\" Color=\"muted\">Joined December 2018</Lumeo.Text>\n        </Stack>\n    </HoverCardContent>\n</HoverCard>"
      },
      {
        "title": "Alignment",
        "code": "<HoverCard>\n    <HoverCardTrigger>\n        <Button Variant=\"Button.ButtonVariant.Outline\">Start</Button>\n    </HoverCardTrigger>\n    <HoverCardContent Align=\"HoverCardContent.HoverCardAlign.Start\">\n        <Lumeo.Text Size=\"sm\">Aligned to start.</Lumeo.Text>\n    </HoverCardContent>\n</HoverCard>\n\n<HoverCard>\n    <HoverCardTrigger>\n        <Button Variant=\"Button.ButtonVariant.Outline\">End</Button>\n    </HoverCardTrigger>\n    <HoverCardContent Align=\"HoverCardContent.HoverCardAlign.End\">\n        <Lumeo.Text Size=\"sm\">Aligned to end.</Lumeo.Text>\n    </HoverCardContent>\n</HoverCard>"
      },
      {
        "title": "With Arrow",
        "code": "<HoverCard>\n    <HoverCardTrigger><Button Variant=\"Button.ButtonVariant.Outline\">Hover me</Button></HoverCardTrigger>\n    <HoverCardContent ShowArrow=\"true\">\n        <Stack Gap=\"1\">\n            <Heading Level=\"4\" Size=\"sm\" Weight=\"semibold\">With arrow</Heading>\n            <Lumeo.Text Size=\"sm\" Color=\"muted\">A small pointer ties the card to its trigger.</Lumeo.Text>\n        </Stack>\n    </HoverCardContent>\n</HoverCard>"
      },
      {
        "title": "User Profile",
        "code": "<HoverCard>\n    <HoverCardTrigger>\n        <span class=\"font-medium underline cursor-pointer\">Jane Smith</span>\n    </HoverCardTrigger>\n    <HoverCardContent>\n        <Stack Gap=\"2\">\n            <Heading Level=\"4\" Size=\"sm\" Weight=\"semibold\">Jane Smith</Heading>\n            <Lumeo.Text Size=\"sm\" Color=\"muted\">Senior Developer at Acme Inc.</Lumeo.Text>\n            <Flex Align=\"center\" Gap=\"2\">\n                <Lumeo.Text As=\"span\" Size=\"xs\" Color=\"muted\">42 repositories</Lumeo.Text>\n                <Lumeo.Text As=\"span\" Size=\"xs\" Color=\"muted\">128 followers</Lumeo.Text>\n            </Flex>\n        </Stack>\n    </HoverCardContent>\n</HoverCard>"
      }
    ],
    "subComponents": {
      "HoverCardContent": {
        "componentName": "HoverCardContent",
        "fileName": "HoverCardContent.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [
          "IAsyncDisposable"
        ],
        "parameters": [
          {
            "name": "Context",
            "type": "HoverCard.HoverCardContext",
            "default": "default!",
            "description": null,
            "isCascading": true,
            "captureUnmatched": false
          },
          {
            "name": "ChildContent",
            "type": "RenderFragment?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Align",
            "type": "HoverCardAlign",
            "default": "HoverCardAlign.Center",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Side",
            "type": "HoverCardSide",
            "default": "HoverCardSide.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": "HoverCardAlign",
            "values": [
              "Start",
              "Center",
              "End"
            ],
            "description": null
          },
          {
            "name": "HoverCardSide",
            "values": [
              "Top",
              "Bottom"
            ],
            "description": null
          }
        ],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "HoverCardTrigger": {
        "componentName": "HoverCardTrigger",
        "fileName": "HoverCardTrigger.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Context",
            "type": "HoverCard.HoverCardContext",
            "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": "hover-card",
  "docsUrl": "https://lumeo.nativ.sh/components/hover-card",
  "sourceUrl": [
    {
      "file": "UI/HoverCard/HoverCard.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/HoverCard/HoverCard.razor"
    },
    {
      "file": "UI/HoverCard/HoverCardContent.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/HoverCard/HoverCardContent.razor"
    },
    {
      "file": "UI/HoverCard/HoverCardTrigger.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/HoverCard/HoverCardTrigger.razor"
    }
  ],
  "source": [
    {
      "path": "UI/HoverCard/HoverCard.razor",
      "content": "@namespace Lumeo\n@implements IAsyncDisposable\n@inject Lumeo.Services.ComponentInteropService Interop\n\n@{\n    var context = new HoverCardContext(\n        Open || _pinned,\n        EventCallback.Factory.Create(this, RequestOpen),\n        EventCallback.Factory.Create(this, RequestClose),\n        EventCallback.Factory.Create(this, TogglePin),\n        _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] public int OpenDelay { get; set; } = 200;\n    [Parameter] public int CloseDelay { get; set; } = 300;\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n\n    private readonly string _wrapperId = $\"hovercard-{Guid.NewGuid():N}\";\n    private readonly DelayedDispatch _dispatch = new();\n    // rc.44 — tap-pin path for touch devices. The trigger's @onclick toggles\n    // _pinned; click-outside auto-unpins via the global mousedown/touchstart\n    // listener in components.js.\n    private bool _pinned;\n    private bool _clickOutsideRegistered;\n\n    private void RequestOpen()\n    {\n        _dispatch.Cancel();\n        if (Open) return;\n        _dispatch.Schedule(OpenDelay, () => InvokeAsync(async () =>\n        {\n            Open = true;\n            await OpenChanged.InvokeAsync(true);\n            StateHasChanged();\n        }));\n    }\n\n    private void RequestClose()\n    {\n        // If the card is pinned (touch), hover-leave must NOT close it.\n        if (_pinned) return;\n        _dispatch.Cancel();\n        if (!Open) return;\n        _dispatch.Schedule(CloseDelay, () => InvokeAsync(async () =>\n        {\n            Open = false;\n            await OpenChanged.InvokeAsync(false);\n            StateHasChanged();\n        }));\n    }\n\n    private async Task TogglePin()\n    {\n        _pinned = !_pinned;\n        if (_pinned)\n        {\n            // Stop any in-flight close-delay so the tap reliably keeps it open.\n            _dispatch.Cancel();\n            if (!_clickOutsideRegistered)\n            {\n                await Interop.RegisterClickOutside(_wrapperId, null, OnClickOutside);\n                _clickOutsideRegistered = true;\n            }\n        }\n        else\n        {\n            // Optional auto-collapse on unpin if the user wasn't hovering.\n            if (Open)\n            {\n                Open = false;\n                await OpenChanged.InvokeAsync(false);\n            }\n        }\n        StateHasChanged();\n    }\n\n    private Task OnClickOutside()\n    {\n        if (_pinned)\n        {\n            _pinned = false;\n            if (Open)\n            {\n                Open = false;\n                _ = OpenChanged.InvokeAsync(false);\n            }\n            StateHasChanged();\n        }\n        return Task.CompletedTask;\n    }\n\n    public async ValueTask DisposeAsync()\n    {\n        _dispatch.Dispose();\n        if (_clickOutsideRegistered)\n        {\n            try { await Interop.UnregisterClickOutside(_wrapperId); }\n            catch (Microsoft.JSInterop.JSDisconnectedException) { }\n            _clickOutsideRegistered = false;\n        }\n    }\n\n    public record HoverCardContext(bool IsOpen, EventCallback RequestOpen, EventCallback RequestClose, EventCallback RequestTogglePin, string WrapperId);\n}\n"
    },
    {
      "path": "UI/HoverCard/HoverCardContent.razor",
      "content": "@namespace Lumeo\n@implements IAsyncDisposable\n\n@if (Context.IsOpen)\n{\n    <div id=\"@_contentId\" class=\"@CssClass\" @onmouseenter=\"() => Context.RequestOpen.InvokeAsync()\" @onmouseleave=\"() => Context.RequestClose.InvokeAsync()\" @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 HoverCard.HoverCardContext Context { get; set; } = default!;\n    [Parameter] public RenderFragment? ChildContent { get; set; }\n    [Parameter] public HoverCardAlign Align { get; set; } = HoverCardAlign.Center;\n    [Parameter] public HoverCardSide Side { get; set; } = HoverCardSide.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 = $\"hovercard-content-{Guid.NewGuid():N}\";\n    private bool _positioned;\n\n    private string CssClass => string.Join(\" \", new[] { \"fixed z-50 w-64 rounded-md border border-border/60 bg-popover p-4 text-popover-foreground shadow-lg outline-none\", ShowArrow ? \"overflow-visible\" : \"\", \"animate-fade-in\", Class }.Where(s => !string.IsNullOrWhiteSpace(s)));\n\n    private string ArrowClasses => Side switch\n    {\n        HoverCardSide.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        HoverCardSide.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        _ => \"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    private string AlignStr => Align switch\n    {\n        HoverCardAlign.Start => \"start\",\n        HoverCardAlign.Center => \"center\",\n        HoverCardAlign.End => \"end\",\n        _ => \"center\"\n    };\n\n    private string SideStr => Side switch\n    {\n        HoverCardSide.Top => \"top\",\n        HoverCardSide.Bottom => \"bottom\",\n        _ => \"bottom\"\n    };\n\n    protected override async Task OnAfterRenderAsync(bool firstRender)\n    {\n        if (Context.IsOpen && !_positioned)\n        {\n            await Interop.PositionFixed(_contentId, Context.WrapperId, AlignStr, false, SideStr);\n            _positioned = true;\n        }\n        else if (!Context.IsOpen && _positioned)\n        {\n            try { await Interop.UnpositionFixed(_contentId); } catch (JSDisconnectedException) { }\n            _positioned = false;\n        }\n    }\n\n    public async ValueTask DisposeAsync()\n    {\n        if (_positioned)\n        {\n            try { await Interop.UnpositionFixed(_contentId); } catch (JSDisconnectedException) { }\n            _positioned = false;\n        }\n    }\n\n    public enum HoverCardAlign { Start, Center, End }\n    public enum HoverCardSide { Top, Bottom }\n}\n"
    },
    {
      "path": "UI/HoverCard/HoverCardTrigger.razor",
      "content": "@namespace Lumeo\n\n<div class=\"@CssClass\"\n     aria-expanded=\"@Context.IsOpen\"\n     @onmouseenter=\"() => Context.RequestOpen.InvokeAsync()\"\n     @onmouseleave=\"() => Context.RequestClose.InvokeAsync()\"\n     @onclick=\"() => Context.RequestTogglePin.InvokeAsync()\"\n     @attributes=\"AdditionalAttributes\">\n    @ChildContent\n</div>\n\n@code {\n    [CascadingParameter] public HoverCard.HoverCardContext 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"
    }
  ],
  "slots": [
    {
      "name": "ChildContent",
      "owner": "HoverCard",
      "type": "RenderFragment?",
      "description": null
    },
    {
      "name": "ChildContent",
      "owner": "HoverCardContent",
      "type": "RenderFragment?",
      "description": null
    },
    {
      "name": "ChildContent",
      "owner": "HoverCardTrigger",
      "type": "RenderFragment?",
      "description": null
    }
  ],
  "serviceDependencies": [
    {
      "service": "ComponentInteropService",
      "namespace": "Lumeo.Services"
    },
    {
      "service": "ComponentInteropService",
      "namespace": ""
    }
  ],
  "cascadingDependencies": [
    {
      "name": "Context",
      "type": "HoverCard.HoverCardContext",
      "required": true
    }
  ],
  "relatedComponents": [
    {
      "name": "HoverCardContent",
      "reason": "sub-component"
    },
    {
      "name": "HoverCardTrigger",
      "reason": "sub-component"
    }
  ],
  "keyboardInteractions": [],
  "tests": [
    "tests/Lumeo.Tests/Components/HoverCard/HoverCardTests.cs"
  ],
  "mdSummary": "# HoverCard\n\nPopover that opens on hover for rich previews.\n\n**Package:** `Lumeo` · **Category:** Overlay\n**Docs:** https://lumeo.nativ.sh/components/hover-card\n\n## Parameters\n| Owner | Name | Type | Default | Description |\n|---|---|---|---|---|\n| HoverCard | ChildContent | `RenderFragment?` | `` |  |\n| HoverCard | Open | `bool` | `` |  |\n| HoverCard | OpenChanged | `EventCallback<bool>` | `` |  |\n| HoverCard | IsOpen | `bool` | `` |  |\n| HoverCard | IsOpenChanged | `EventCallback<bool>` | `` |  |\n| HoverCard | OpenDelay | `int` | `200` |  |\n| HoverCard | CloseDelay | `int` | `300` |  |\n| HoverCard | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| HoverCardContent | Context | `HoverCard.HoverCardContext` | `default!` |  |\n| HoverCardContent | ChildContent | `RenderFragment?` | `` |  |\n| HoverCardContent | Align | `HoverCardAlign` | `HoverCardAlign.Center` |  |\n| HoverCardContent | Side | `HoverCardSide` | `HoverCardSide.Bottom` |  |\n| HoverCardContent | ShowArrow | `bool` | `` |  |\n| HoverCardContent | Class | `string?` | `` |  |\n| HoverCardContent | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| HoverCardTrigger | Context | `HoverCard.HoverCardContext` | `default!` |  |\n| HoverCardTrigger | ChildContent | `RenderFragment?` | `` |  |\n| HoverCardTrigger | Class | `string?` | `` |  |\n| HoverCardTrigger | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n\n## Slots\n- `ChildContent` (HoverCard) — RenderFragment slot\n- `ChildContent` (HoverCardContent) — RenderFragment slot\n- `ChildContent` (HoverCardTrigger) — RenderFragment slot\n\n## Events\n- `OpenChanged` (`EventCallback<bool>`)\n- `IsOpenChanged` (`EventCallback<bool>`)\n\n## Sub-components\n- `HoverCardContent`, `HoverCardTrigger`\n\n## Cascading values it expects\n- `Context` (`HoverCard.HoverCardContext`)\n\n## Services it injects\n- `ComponentInteropService` (Lumeo.Services)\n- `ComponentInteropService` ()\n\n## Keyboard\n—\n\n## Example\n```razor\n<HoverCard />\n```\n\n## CSS variables\n`--color-border`, `--color-popover`, `--color-popover-foreground`, `--radius`\n\n## Files\n- `UI/HoverCard/HoverCard.razor`\n- `UI/HoverCard/HoverCardContent.razor`\n- `UI/HoverCard/HoverCardTrigger.razor`\n"
}