{
  "name": "InlineEdit",
  "source": "src/components/InlineEdit.tsx",
  "propsType": "InlineEditProps",
  "description": "InlineEdit — a value that becomes a field where it stands: an agent's\npurpose under its name, a row's label. Edit, change, Save or Cancel.\n\nTHE VALUE IS TEXT, AND EDIT IS A BUTTON. In view mode the value is plain\ntext, read as text; beside it is one tab stop, \"Edit purpose\". Clicking the\ntext also opens the editor, for a pointer. A value that is itself a button\nwould be read as \"Edit purpose, button\" with the purpose lost.\n\nFOCUS GOES IN, THEN BACK. Opening the editor focuses the field with its\ntext selected; Save and Cancel return focus to the Edit button, not to the\ntop of the page. Escape cancels from anywhere in the editor, and goes no\nfurther — a dialog it sits in does not close as well.\n\nNOTHING TYPED IS LOST. An invalid value, or a save that fails, keeps the\neditor open with the message as the field's error and what was typed still\nin it. While a save runs the field is read-only and Save says \"Saving…\";\nneither is disabled, so focus stays where it was.\n\nSAVED IS SAID. A status region, always mounted, announces \"Purpose saved\" —\nthe editor closing is not, on its own, news to a screen reader.",
  "import": "import { InlineEdit } from 'ionbase-ui';",
  "status": "stable",
  "summary": "A value that becomes a field where it stands — an agent's purpose under its name, a row's label. The value is text with an Edit button beside it; editing shows the field with Save and Cancel. Enter saves, Escape cancels, focus goes in and comes back, and a failed or invalid save keeps what was typed.",
  "useWhen": [
    "one value on a page that is read far more often than it is changed — a name, a description, a label",
    "changing it should not take the user to a settings page or a dialog"
  ],
  "useInstead": [
    {
      "when": "several values are changed and saved together",
      "use": "Input",
      "why": "a form with one Save is clearer than several editors each saving on their own"
    },
    {
      "when": "the change needs a warning or a second look — deleting, transferring ownership",
      "use": "Modal",
      "why": "an inline Save is one keystroke; a consequential change deserves a confirmation"
    },
    {
      "when": "a switch that applies at once",
      "use": "Toggle",
      "why": "there is nothing to type, so there is nothing to save or cancel"
    }
  ],
  "composition": {
    "order": [
      "InlineEdit"
    ],
    "note": "`label` is required: it names the field and the Edit button. Pass `value` and `onSave` to keep the value yourself; return a promise from `onSave` and throw to report a failure. `validate` runs before `onSave`. `placeholder` stands in for an empty value — \"Add a purpose\". `isMultiline` makes it a Textarea: Enter adds a line and ⌘/Ctrl+Enter saves.",
    "example": "<InlineEdit label=\"Purpose\" editLabel=\"Edit purpose\" value={purpose} onSave={savePurpose} validate={(v) => (v.trim() ? undefined : 'Say what the agent is for.')} />"
  },
  "variants": {
    "size": {
      "md": {
        "use": "the default — a medium field, beside body text"
      },
      "sm": {
        "use": "a small field, in a dense row or a table cell"
      }
    }
  },
  "a11y": {
    "requires": [
      "`label` — the name of the value, translated",
      "`editLabel` when \"Edit\" plus the label does not read naturally — \"Edit purpose\", not \"Edit Purpose\""
    ],
    "guarantees": [
      "the value is plain text, read as text; the Edit button is one tab stop beside it",
      "opening the editor focuses the field with its text selected",
      "Save and Cancel return focus to the Edit button",
      "Escape cancels from the field or either button, and does not reach a dialog around it",
      "an invalid value or a failed save keeps the editor open, with the message as the field's error and what was typed kept",
      "while saving, the field is read-only and Save says Saving… — nothing is disabled, so focus stays put",
      "a save is announced in a status region that is always mounted"
    ],
    "notes": [
      "saving an unchanged value just closes the editor, without calling `onSave`",
      "nothing happens on blur: leaving the field does not save or throw away what was typed",
      "the value inherits the text around it — a page description, a table cell — so it looks like the text it replaces; `size` is the field's"
    ]
  },
  "antiPatterns": [
    {
      "dont": "<button onClick={edit}>{purpose}</button>",
      "do": "<InlineEdit label=\"Purpose\" value={purpose} onSave={save} />",
      "why": "a value that is itself the button is announced as a button, and a sighted user has no sign it can be edited"
    },
    {
      "dont": "saving on blur",
      "do": "Save and Cancel, with Enter and Escape",
      "why": "tabbing away to check something commits a half-typed value"
    }
  ],
  "stylesheet": "src/styles/inline-edit.css",
  "tokens": [
    "--spacing-4",
    "--spacing-8",
    "--text-secondary"
  ],
  "props": {
    "label": {
      "type": "string",
      "required": true,
      "origin": "own",
      "description": "What the value is — \"Purpose\", \"Agent name\". Required: it names the\nfield while editing, and the edit button is \"Edit\" plus it."
    },
    "value": {
      "type": "string | undefined",
      "required": false,
      "origin": "own",
      "description": "The saved value, when the caller keeps it."
    },
    "defaultValue": {
      "type": "string | undefined",
      "required": false,
      "origin": "own",
      "description": "The starting value, when the component keeps it."
    },
    "onSave": {
      "type": "((value: string) => void | Promise<void>) | undefined",
      "required": false,
      "origin": "own",
      "description": "Called with the new value on Save. Return a promise to show \"Saving…\"\nwhile it runs; if it rejects, the editor stays open with the error's\nmessage, and what was typed is kept."
    },
    "onCancel": {
      "type": "(() => void) | undefined",
      "required": false,
      "origin": "own",
      "description": "Called when editing is cancelled — Cancel, or Escape."
    },
    "validate": {
      "type": "((value: string) => string | undefined | null) | undefined",
      "required": false,
      "origin": "own",
      "description": "Checked on Save, before `onSave`. Return the message to show, or nothing\nwhen the value is fine."
    },
    "placeholder": {
      "type": "string | undefined",
      "required": false,
      "origin": "own",
      "description": "Shown, in secondary text, when there is no value — \"Add a purpose\"."
    },
    "isMultiline": {
      "type": "boolean | undefined",
      "required": false,
      "origin": "own",
      "description": "A Textarea instead of an Input. Enter adds a line; ⌘/Ctrl+Enter saves."
    },
    "size": {
      "type": "InlineEditSize | undefined",
      "required": false,
      "origin": "own",
      "description": "The field's size: `sm` in a dense row, `md` beside body text. The value\nitself inherits the text around it.",
      "values": [
        "sm",
        "md"
      ]
    },
    "isReadOnly": {
      "type": "boolean | undefined",
      "required": false,
      "origin": "own",
      "description": "Shows the value with no way to edit it."
    },
    "editLabel": {
      "type": "string | undefined",
      "required": false,
      "origin": "own",
      "description": "The edit button's name. Defaults to \"Edit\" and the label."
    },
    "saveLabel": {
      "type": "string | undefined",
      "required": false,
      "origin": "own"
    },
    "savingLabel": {
      "type": "string | undefined",
      "required": false,
      "origin": "own"
    },
    "cancelLabel": {
      "type": "string | undefined",
      "required": false,
      "origin": "own"
    },
    "savedMessage": {
      "type": "string | undefined",
      "required": false,
      "origin": "own",
      "description": "Announced after a save. Defaults to the label and \"saved\"."
    },
    "errorFallback": {
      "type": "string | undefined",
      "required": false,
      "origin": "own",
      "description": "Shown when `onSave` rejects without a message of its own."
    }
  },
  "propCounts": {
    "own": 16,
    "aria": 0,
    "dom": 275,
    "other": 0
  }
}
