{
  "name": "InplaceEditor",
  "category": "Forms",
  "subcategory": "Specialized",
  "description": "Click-to-edit text/number field that swaps in an input.",
  "thumbnail": "/preview-cards/inplace-editor.png",
  "hasDocsPage": true,
  "nugetPackage": "Lumeo",
  "files": [
    "UI/InplaceEditor/InplaceEditor.razor"
  ],
  "dependencies": [],
  "packageDependencies": [
    "Blazicons.Lucide"
  ],
  "cssVars": [
    "--color-background",
    "--color-border",
    "--color-destructive",
    "--color-foreground",
    "--color-muted",
    "--color-muted-foreground",
    "--color-primary",
    "--color-primary-foreground",
    "--color-ring",
    "--radius"
  ],
  "api": {
    "name": "InplaceEditor",
    "category": "Forms",
    "subcategory": "Specialized",
    "description": "Click-to-edit text/number field that swaps in an input.",
    "nugetPackage": "Lumeo",
    "files": [
      "UI/InplaceEditor/InplaceEditor.razor"
    ],
    "namespace": "Lumeo",
    "inheritsFrom": null,
    "implements": [],
    "parameters": [
      {
        "name": "FormField",
        "type": "FormField.FormFieldContext?",
        "default": null,
        "description": null,
        "isCascading": true,
        "captureUnmatched": false
      },
      {
        "name": "Value",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "ValueChanged",
        "type": "EventCallback<string?>",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "ChildContent",
        "type": "RenderFragment?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Placeholder",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "EditMode",
        "type": "InplaceEditMode",
        "default": "InplaceEditMode.Text",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "SelectOptions",
        "type": "List<string>?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "SaveOnBlur",
        "type": "bool",
        "default": "true",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "SaveOnEnter",
        "type": "bool",
        "default": "true",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "ShowButtons",
        "type": "bool",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Disabled",
        "type": "bool",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "OnSave",
        "type": "EventCallback<string>",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "OnCancel",
        "type": "EventCallback",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Required",
        "type": "bool",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Invalid",
        "type": "bool",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "ErrorText",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "HelperText",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Label",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Name",
        "type": "string?",
        "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": "ValueChanged",
        "type": "EventCallback<string?>",
        "description": null
      },
      {
        "name": "OnSave",
        "type": "EventCallback<string>",
        "description": null
      },
      {
        "name": "OnCancel",
        "type": "EventCallback",
        "description": null
      }
    ],
    "enums": [
      {
        "name": "InplaceEditMode",
        "values": [
          "Text",
          "Textarea",
          "Select"
        ],
        "description": null
      }
    ],
    "records": [],
    "cssVars": [
      "--color-background",
      "--color-border",
      "--color-destructive",
      "--color-foreground",
      "--color-muted",
      "--color-muted-foreground",
      "--color-primary",
      "--color-primary-foreground",
      "--color-ring",
      "--radius"
    ],
    "examples": [
      {
        "title": "Basic Inplace Edit",
        "code": "<InplaceEditor @bind-Value=\"_name\" Placeholder=\"Click to edit...\" />\n\n@code {\n    private string? _name = \"John Doe\";\n}"
      },
      {
        "title": "With Save / Cancel Buttons",
        "code": "<InplaceEditor @bind-Value=\"_title\" ShowButtons=\"true\" Placeholder=\"Click to edit title...\" />"
      },
      {
        "title": "Textarea Mode",
        "code": "<InplaceEditor @bind-Value=\"_bio\"\n               EditMode=\"InplaceEditor.InplaceEditMode.Textarea\"\n               Placeholder=\"Click to add a bio...\" />"
      },
      {
        "title": "Select Mode",
        "code": "<InplaceEditor @bind-Value=\"_status\"\n               EditMode=\"InplaceEditor.InplaceEditMode.Select\"\n               SelectOptions=\"@_options\" />\n\n@code {\n    private string? _status = \"Active\";\n    private List<string> _options = new() { \"Active\", \"Inactive\", \"Pending\" };\n}"
      }
    ],
    "subComponents": {},
    "parseFailed": false,
    "parseError": null
  },
  "$schema": "https://lumeo.nativ.sh/registry-component-schema.json",
  "key": "inplace-editor",
  "docsUrl": "https://lumeo.nativ.sh/components/inplace-editor",
  "sourceUrl": [
    {
      "file": "UI/InplaceEditor/InplaceEditor.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/InplaceEditor/InplaceEditor.razor"
    }
  ],
  "source": [
    {
      "path": "UI/InplaceEditor/InplaceEditor.razor",
      "content": "@namespace Lumeo\n\n@if (Label is not null && !InsideFormField)\n{\n    <label class=\"block text-sm font-medium text-foreground mb-1.5\">\n        @Label\n        @if (Required)\n        {\n            <span class=\"text-destructive ml-0.5\">*</span>\n        }\n    </label>\n}\n\n<div class=\"@CssClass\" @attributes=\"AdditionalAttributes\">\n    @if (_editing && !Disabled)\n    {\n        <div class=\"flex items-start gap-2 w-full\">\n            <div class=\"flex-1 min-w-0\">\n                @if (EditMode == InplaceEditMode.Textarea)\n                {\n                    <textarea class=\"@EditInputClass\"\n                              rows=\"3\"\n                              value=\"@_editValue\"\n                              name=\"@(Name ?? FormField?.Name)\"\n                              aria-required=\"@(Required ? \"true\" : \"false\")\"\n                              aria-invalid=\"@(EffectiveInvalid ? \"true\" : \"false\")\"\n                              @oninput=\"OnInput\"\n                              @onkeydown=\"OnKeyDown\"\n                              @onblur=\"OnBlur\" />\n                }\n                else if (EditMode == InplaceEditMode.Select && SelectOptions is not null)\n                {\n                    <select class=\"w-full rounded border border-border bg-background px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-ring\"\n                            value=\"@_editValue\"\n                            name=\"@(Name ?? FormField?.Name)\"\n                            aria-required=\"@(Required ? \"true\" : \"false\")\"\n                            aria-invalid=\"@(EffectiveInvalid ? \"true\" : \"false\")\"\n                            @onchange=\"OnSelectChange\"\n                            @onblur=\"OnBlur\">\n                        @foreach (var option in SelectOptions)\n                        {\n                            <option value=\"@option\">@option</option>\n                        }\n                    </select>\n                }\n                else\n                {\n                    <input type=\"text\"\n                           class=\"@EditInputClass\"\n                           value=\"@_editValue\"\n                           name=\"@(Name ?? FormField?.Name)\"\n                           aria-required=\"@(Required ? \"true\" : \"false\")\"\n                           aria-invalid=\"@(EffectiveInvalid ? \"true\" : \"false\")\"\n                           @oninput=\"OnInput\"\n                           @onkeydown=\"OnKeyDown\"\n                           @onblur=\"OnBlur\" />\n                }\n            </div>\n\n            @if (ShowButtons)\n            {\n                <div class=\"flex gap-1 shrink-0\">\n                    <button type=\"button\"\n                            class=\"inline-flex items-center justify-center rounded p-1 text-xs bg-primary text-primary-foreground hover:bg-primary/90 transition-colors\"\n                            @onclick=\"Save\" @onclick:stopPropagation=\"true\">\n                        <Blazicon Svg=\"Lucide.Check\" class=\"h-3 w-3\" />\n                    </button>\n                    <button type=\"button\"\n                            class=\"inline-flex items-center justify-center rounded p-1 text-xs bg-muted text-muted-foreground hover:bg-muted/70 transition-colors\"\n                            @onclick=\"Cancel\" @onclick:stopPropagation=\"true\">\n                        <Blazicon Svg=\"Lucide.X\" class=\"h-3 w-3\" />\n                    </button>\n                </div>\n            }\n        </div>\n    }\n    else\n    {\n        <div class=\"@DisplayClass\" @onclick=\"StartEditing\" aria-required=\"@(Required ? \"true\" : \"false\")\" aria-invalid=\"@(EffectiveInvalid ? \"true\" : \"false\")\">\n            <div class=\"flex-1 min-w-0\">\n                @if (ChildContent is not null)\n                {\n                    @ChildContent\n                }\n                else if (!string.IsNullOrEmpty(Value))\n                {\n                    <span class=\"text-sm\">@Value</span>\n                }\n                else if (!string.IsNullOrEmpty(Placeholder))\n                {\n                    <span class=\"text-sm text-muted-foreground/60 italic\">@Placeholder</span>\n                }\n            </div>\n            @if (!Disabled)\n            {\n                <span class=\"ml-2 shrink-0 opacity-0 group-hover:opacity-100 transition-opacity text-muted-foreground\">\n                    <Blazicon Svg=\"Lucide.Pencil\" class=\"h-3.5 w-3.5\" />\n                </span>\n            }\n        </div>\n    }\n</div>\n\n@if (!InsideFormField)\n{\n    @if (EffectiveInvalid && ErrorText is not null)\n    {\n        <p class=\"text-xs text-destructive mt-1.5\">@ErrorText</p>\n    }\n    else if (HelperText is not null)\n    {\n        <p class=\"text-xs text-muted-foreground mt-1.5\">@HelperText</p>\n    }\n}\n\n@code {\n    [CascadingParameter] private FormField.FormFieldContext? FormField { get; set; }\n\n    [Parameter] public string? Value { get; set; }\n    [Parameter] public EventCallback<string?> ValueChanged { get; set; }\n    [Parameter] public RenderFragment? ChildContent { get; set; }\n    [Parameter] public string? Placeholder { get; set; }\n    [Parameter] public InplaceEditMode EditMode { get; set; } = InplaceEditMode.Text;\n    [Parameter] public List<string>? SelectOptions { get; set; }\n    [Parameter] public bool SaveOnBlur { get; set; } = true;\n    [Parameter] public bool SaveOnEnter { get; set; } = true;\n    [Parameter] public bool ShowButtons { get; set; }\n    [Parameter] public bool Disabled { get; set; }\n    [Parameter] public EventCallback<string> OnSave { get; set; }\n    [Parameter] public EventCallback OnCancel { get; set; }\n    [Parameter] public bool Required { get; set; }\n    [Parameter] public bool Invalid { get; set; }\n    [Parameter] public string? ErrorText { get; set; }\n    [Parameter] public string? HelperText { get; set; }\n    [Parameter] public string? Label { get; set; }\n    [Parameter] public string? Name { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n\n    private bool EffectiveInvalid => Invalid || FormField?.HasError == true;\n    private bool InsideFormField => FormField is not null;\n\n    private bool _editing;\n    private string? _editValue;\n\n    private string CssClass => $\"group w-full {Class}\".Trim();\n    private string EditInputClass => $\"w-full rounded border {(EffectiveInvalid ? \"border-destructive focus:ring-destructive/20\" : \"border-border focus:ring-ring\")} bg-background px-2 py-1 text-sm focus:outline-none focus:ring-1\";\n\n    private string DisplayClass => string.Join(\" \", new[] { \"flex items-center w-full rounded px-2 py-1 cursor-pointer hover:bg-muted/30 transition-colors min-h-8\", Disabled ? \"cursor-default hover:bg-transparent opacity-50 pointer-events-none\" : null }.Where(s => !string.IsNullOrWhiteSpace(s)));\n\n    public enum InplaceEditMode { Text, Textarea, Select }\n\n    private void StartEditing()\n    {\n        if (Disabled) return;\n        _editValue = Value;\n        _editing = true;\n    }\n\n    private void OnInput(ChangeEventArgs e)\n    {\n        _editValue = e.Value?.ToString();\n    }\n\n    private void OnSelectChange(ChangeEventArgs e)\n    {\n        _editValue = e.Value?.ToString();\n    }\n\n    private async Task OnKeyDown(KeyboardEventArgs e)\n    {\n        if (e.Key == \"Escape\")\n        {\n            await Cancel();\n        }\n        else if (e.Key == \"Enter\" && SaveOnEnter && EditMode == InplaceEditMode.Text)\n        {\n            await Save();\n        }\n    }\n\n    private async Task OnBlur()\n    {\n        if (SaveOnBlur && _editing && !ShowButtons)\n        {\n            await Save();\n        }\n    }\n\n    private async Task Save()\n    {\n        _editing = false;\n        var saved = _editValue ?? \"\";\n        await ValueChanged.InvokeAsync(saved);\n        await OnSave.InvokeAsync(saved);\n    }\n\n    private async Task Cancel()\n    {\n        _editing = false;\n        _editValue = Value;\n        await OnCancel.InvokeAsync();\n    }\n}\n"
    }
  ],
  "slots": [
    {
      "name": "ChildContent",
      "owner": "InplaceEditor",
      "type": "RenderFragment?",
      "description": null
    }
  ],
  "serviceDependencies": [],
  "cascadingDependencies": [
    {
      "name": "FormField",
      "type": "FormField.FormFieldContext?",
      "required": false
    }
  ],
  "relatedComponents": [],
  "keyboardInteractions": [
    {
      "key": "Escape",
      "action": "OnKeyDown handles Escape in InplaceEditor.razor"
    },
    {
      "key": "Enter",
      "action": "OnKeyDown handles Enter in InplaceEditor.razor"
    }
  ],
  "tests": [
    "tests/Lumeo.Tests/Components/InplaceEditor/InplaceEditorTests.cs"
  ],
  "mdSummary": "# InplaceEditor\n\nClick-to-edit text/number field that swaps in an input.\n\n**Package:** `Lumeo` · **Category:** Forms/Specialized\n**Docs:** https://lumeo.nativ.sh/components/inplace-editor\n\n## Parameters\n| Owner | Name | Type | Default | Description |\n|---|---|---|---|---|\n| InplaceEditor | FormField | `FormField.FormFieldContext?` | `` |  |\n| InplaceEditor | Value | `string?` | `` |  |\n| InplaceEditor | ValueChanged | `EventCallback<string?>` | `` |  |\n| InplaceEditor | ChildContent | `RenderFragment?` | `` |  |\n| InplaceEditor | Placeholder | `string?` | `` |  |\n| InplaceEditor | EditMode | `InplaceEditMode` | `InplaceEditMode.Text` |  |\n| InplaceEditor | SelectOptions | `List<string>?` | `` |  |\n| InplaceEditor | SaveOnBlur | `bool` | `true` |  |\n| InplaceEditor | SaveOnEnter | `bool` | `true` |  |\n| InplaceEditor | ShowButtons | `bool` | `` |  |\n| InplaceEditor | Disabled | `bool` | `` |  |\n| InplaceEditor | OnSave | `EventCallback<string>` | `` |  |\n| InplaceEditor | OnCancel | `EventCallback` | `` |  |\n| InplaceEditor | Required | `bool` | `` |  |\n| InplaceEditor | Invalid | `bool` | `` |  |\n| InplaceEditor | ErrorText | `string?` | `` |  |\n| InplaceEditor | HelperText | `string?` | `` |  |\n| InplaceEditor | Label | `string?` | `` |  |\n| InplaceEditor | Name | `string?` | `` |  |\n| InplaceEditor | Class | `string?` | `` |  |\n| InplaceEditor | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n\n## Slots\n- `ChildContent` (InplaceEditor) — RenderFragment slot\n\n## Events\n- `ValueChanged` (`EventCallback<string?>`)\n- `OnSave` (`EventCallback<string>`)\n- `OnCancel` (`EventCallback`)\n\n## Sub-components\n- (none)\n\n## Cascading values it expects\n- `FormField` (`FormField.FormFieldContext?`)\n\n## Services it injects\n- (none)\n\n## Keyboard\n- `Escape` — OnKeyDown handles Escape in InplaceEditor.razor\n- `Enter` — OnKeyDown handles Enter in InplaceEditor.razor\n\n## Example\n```razor\n<InplaceEditor />\n```\n\n## CSS variables\n`--color-background`, `--color-border`, `--color-destructive`, `--color-foreground`, `--color-muted`, `--color-muted-foreground`, `--color-primary`, `--color-primary-foreground`, `--color-ring`, `--radius`\n\n## Files\n- `UI/InplaceEditor/InplaceEditor.razor`\n"
}