{
  "name": "NumberTicker",
  "category": "Motion",
  "subcategory": null,
  "description": "Animated count-up from zero to target number.",
  "thumbnail": "/preview-cards/number-ticker.png",
  "hasDocsPage": true,
  "nugetPackage": "Lumeo.Motion",
  "files": [
    "UI/NumberTicker/NumberTicker.razor"
  ],
  "dependencies": [],
  "packageDependencies": [],
  "cssVars": [
    "--color-foreground"
  ],
  "api": {
    "name": "NumberTicker",
    "category": "Motion",
    "subcategory": null,
    "description": "Animated count-up from zero to target number.",
    "nugetPackage": "Lumeo.Motion",
    "files": [
      "UI/NumberTicker/NumberTicker.razor"
    ],
    "namespace": "Lumeo",
    "inheritsFrom": null,
    "implements": [
      "IAsyncDisposable"
    ],
    "parameters": [
      {
        "name": "Value",
        "type": "double",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "StartValue",
        "type": "double",
        "default": "0",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "DurationMs",
        "type": "int",
        "default": "1500",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Decimals",
        "type": "int",
        "default": "0",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Prefix",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Suffix",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "ThousandsSeparator",
        "type": "string",
        "default": "\",\"",
        "description": "Thousands group separator (default \",\"). Set to empty string to suppress.",
        "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": [],
    "cssVars": [
      "--color-foreground"
    ],
    "examples": [
      {
        "title": "Basic Count-Up",
        "code": "<NumberTicker Value=\"1000\" Class=\"text-4xl font-bold tabular-nums\" />\n<NumberTicker Value=\"99.9\" Decimals=\"1\" Class=\"text-4xl font-bold tabular-nums\" />"
      },
      {
        "title": "With Prefix and Suffix",
        "code": "<NumberTicker Value=\"48295\" Prefix=\"$\" Class=\"text-3xl font-semibold tabular-nums\" />\n<NumberTicker Value=\"94.7\" Decimals=\"1\" Suffix=\"%\" Class=\"text-3xl font-semibold tabular-nums\" />"
      },
      {
        "title": "Custom Duration",
        "code": "<NumberTicker Value=\"250\" DurationMs=\"500\" Class=\"text-2xl font-bold\" />\n<NumberTicker Value=\"250\" DurationMs=\"3000\" Class=\"text-2xl font-bold\" />"
      },
      {
        "title": "Custom Separator & Currency",
        "code": "<NumberTicker Value=\"1500000\" ThousandsSeparator=\".\" DurationMs=\"2000\" />\n<NumberTicker Value=\"9876543\" Prefix=\"$\" ThousandsSeparator=\",\" DurationMs=\"2000\" />\n<NumberTicker Value=\"99.9\" Decimals=\"1\" Suffix=\"%\" />"
      }
    ],
    "subComponents": {},
    "parseFailed": false,
    "parseError": null
  },
  "$schema": "https://lumeo.nativ.sh/registry-component-schema.json",
  "key": "number-ticker",
  "docsUrl": "https://lumeo.nativ.sh/components/number-ticker",
  "sourceUrl": [
    {
      "file": "UI/NumberTicker/NumberTicker.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Motion/UI/NumberTicker/NumberTicker.razor"
    }
  ],
  "source": [
    {
      "path": "UI/NumberTicker/NumberTicker.razor",
      "content": "@namespace Lumeo\n@implements IAsyncDisposable\n@inject ComponentInteropService Interop\n\n<span class=\"@CssClass\" @attributes=\"AdditionalAttributes\">\n    @if (!string.IsNullOrEmpty(Prefix))\n    {\n        <span>@Prefix</span>\n    }\n    <span id=\"@_elementId\">@InitialNumberText</span>\n    @if (!string.IsNullOrEmpty(Suffix))\n    {\n        <span>@Suffix</span>\n    }\n</span>\n\n@code {\n    [Parameter] public double Value { get; set; }\n    [Parameter] public double StartValue { get; set; } = 0;\n    [Parameter] public int DurationMs { get; set; } = 1500;\n    [Parameter] public int Decimals { get; set; } = 0;\n    [Parameter] public string? Prefix { get; set; }\n    [Parameter] public string? Suffix { get; set; }\n    /// <summary>Thousands group separator (default \",\"). Set to empty string to suppress.</summary>\n    [Parameter] public string ThousandsSeparator { 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 _elementId = $\"lumeo-ticker-{Guid.NewGuid():N}\";\n    private double _lastRenderedValue;\n    private bool _initialized;\n\n    private string CssClass => string.Join(\" \", new[] { \"tabular-nums inline-flex items-baseline\", Class }.Where(s => !string.IsNullOrWhiteSpace(s)));\n\n    private string InitialNumberText => StartValue.ToString(\"N\" + Decimals);\n\n    protected override async Task OnAfterRenderAsync(bool firstRender)\n    {\n        if (firstRender)\n        {\n            _lastRenderedValue = Value;\n            _initialized = true;\n            await Tick(StartValue, Value);\n        }\n        else if (_initialized && !AreClose(_lastRenderedValue, Value))\n        {\n            var from = _lastRenderedValue;\n            _lastRenderedValue = Value;\n            await Tick(from, Value);\n        }\n    }\n\n    private async Task Tick(double from, double to)\n    {\n        try\n        {\n            await Interop.MotionTickNumber(_elementId, from, to, DurationMs, Decimals, ThousandsSeparator);\n        }\n        catch (JSDisconnectedException) { }\n    }\n\n    private static bool AreClose(double a, double b) => Math.Abs(a - b) < 1e-9;\n\n    public async ValueTask DisposeAsync()\n    {\n        await Interop.MotionDisposeTicker(_elementId);\n    }\n}\n"
    }
  ],
  "slots": [],
  "serviceDependencies": [
    {
      "service": "ComponentInteropService",
      "namespace": ""
    }
  ],
  "cascadingDependencies": [],
  "relatedComponents": [],
  "keyboardInteractions": [],
  "tests": [
    "tests/Lumeo.Tests/Components/NumberTicker/NumberTickerTests.cs"
  ],
  "mdSummary": "# NumberTicker\n\nAnimated count-up from zero to target number.\n\n**Package:** `Lumeo.Motion` · **Category:** Motion\n**Docs:** https://lumeo.nativ.sh/components/number-ticker\n\n## Parameters\n| Owner | Name | Type | Default | Description |\n|---|---|---|---|---|\n| NumberTicker | Value | `double` | `` |  |\n| NumberTicker | StartValue | `double` | `0` |  |\n| NumberTicker | DurationMs | `int` | `1500` |  |\n| NumberTicker | Decimals | `int` | `0` |  |\n| NumberTicker | Prefix | `string?` | `` |  |\n| NumberTicker | Suffix | `string?` | `` |  |\n| NumberTicker | ThousandsSeparator | `string` | `\",\"` | Thousands group separator (default \",\"). Set to empty string to suppress. |\n| NumberTicker | Class | `string?` | `` |  |\n| NumberTicker | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n\n## Slots\n- (none)\n\n## Events\n- (none)\n\n## Sub-components\n- (none)\n\n## Cascading values it expects\n- (none)\n\n## Services it injects\n- `ComponentInteropService` ()\n\n## Keyboard\n—\n\n## Example\n```razor\n<NumberTicker />\n```\n\n## CSS variables\n`--color-foreground`\n\n## Files\n- `UI/NumberTicker/NumberTicker.razor`\n"
}