# Inline edit

A printed label that becomes a field when you press it. At rest it is plain printing, so a page of them reads as text; under the pointer an outline and a pencil say it can be changed. Pressed, it is let into the plate as the field, in the same type, so the words stay where they were. Enter prints the new words back and Escape puts the old ones back.

Kit part · Free · https://tactileui.dev/parts/inline-edit

- Element: `<tui-inline-edit>`
- Needs: `kit.css` and `kit.mjs` ([how to install the kit](https://tactileui.dev/docs/installation#the-kit))
- Categories: Inputs
- Finishes: Aluminium, Graphite, Oak, Walnut, Porcelain, Black, Daylight, Night, Paper, Phosphor, E-paper, AMOLED
- Volumes: Tactile, Quiet

## Variants

### At rest · plain printing

```html
<div class="tui-inline-edit"><button class="tui-inline-edit__print" type="button" tabindex="-1"><span class="tui-inline-edit__text">Northwind billing</span><svg class="tui-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 13h3l7-7-3-3-7 7z"/></svg></button></div>
```

### Hover · outline and pencil

```html
<div class="tui-inline-edit"><button class="tui-inline-edit__print is-hover" type="button" tabindex="-1"><span class="tui-inline-edit__text">Northwind billing</span><svg class="tui-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 13h3l7-7-3-3-7 7z"/></svg></button></div>
```

### Open · the field

```html
<div class="tui-inline-edit" data-editing><input class="tui-inline-edit__input" value="Northwind billing" aria-label="Project name" tabindex="-1"></div>
```

### Empty · prompt in its place

```html
<div class="tui-inline-edit"><button class="tui-inline-edit__print" type="button" tabindex="-1" aria-label="Add an owner"><span class="tui-inline-edit__text" data-empty="Add an owner"></span><svg class="tui-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 13h3l7-7-3-3-7 7z"/></svg></button></div>
```

### Required and empty · held open

```html
<div class="tui-inline-edit" data-editing><input class="tui-inline-edit__input" value="" aria-label="Project name" tabindex="-1" aria-invalid="true"></div>
```

### Long words are cut with an ellipsis

```html
<div style="width:200px"><div class="tui-inline-edit"><button class="tui-inline-edit__print" type="button" tabindex="-1"><span class="tui-inline-edit__text">Northwind billing and payouts for the European accounts</span><svg class="tui-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 13h3l7-7-3-3-7 7z"/></svg></button></div></div>
```

## Every state

- Rest
- Hover
- Focus
- Open
- Invalid
- Disabled

Hover, pressed and focus are forced with `.is-hover`, `.is-down` and `.is-focus`; checked and disabled are real.

## API

| Name | Kind | Values | What it does |
| - | - | - | - |
|  | element | with label, and required or empty if wanted | The label and its field. label names the field; required refuses an empty save; empty is the prompt shown when there are no words. |
| button.tui-inline-edit__print | element | holds .tui-inline-edit__text | The printed words, a real button. The script gives it its pencil and a name that includes the words. |
| input.tui-inline-edit__input | element | hidden at first | The real field. It posts with a form whether it is open or not; set its name and value. |
| value | property | string | The words as last saved; setting it reprints them. |
| editing | property | boolean | Whether the field is open. |
| tui-inline-edit | event | detail { value, previous } | Bubbles from the element when new words are saved; change fires from the input as ever. |
| inlineEditResolve(options) | function | { action, previous, draft, required, label } | What an edit comes to: saved, unchanged, cancelled or invalid, with the words said aloud. Pure. |
| Enter · Escape · Tab | key | in the field | Save and close; put the old words back and close; keep what was typed and move on. |
| Enter · Space | key | on the words | Open the field with its text selected. |

## Markup

```html
<tui-inline-edit label="Project name" required>
  <button class="tui-inline-edit__print" type="button">
    <span class="tui-inline-edit__text">Northwind billing</span>
  </button>
  <input class="tui-inline-edit__input" name="project" value="Northwind billing" hidden>
</tui-inline-edit>

<!-- A value that may be empty: say what shows in its place -->
<tui-inline-edit label="Owner" empty="Add an owner">
  <button class="tui-inline-edit__print" type="button"><span class="tui-inline-edit__text"></span></button>
  <input class="tui-inline-edit__input" name="owner" hidden>
</tui-inline-edit>
```

```js
import { inlineEditResolve } from '/kit/kit.mjs';

const title = document.querySelector('tui-inline-edit');
title.addEventListener('tui-inline-edit', (e) => {
  api.rename(e.detail.value);                  // saved; e.detail.previous is what it was
});

title.value;                                    // 'Northwind billing'
inlineEditResolve({ action: 'save', previous: 'A', draft: ' B ', label: 'Name' });
// { outcome: 'saved', value: 'B', done: true, say: 'Name saved: B' }
```

The demo on the page:

```html
<form class="tui-panel" style="width:min(100%,460px);display:grid;gap:16px;text-align:left" action="/parts/inline-edit" method="get" data-ie-form>
  <div class="tui-panel__head" style="margin-bottom:0"><span class="tui-label">Project</span><span class="tui-status"><span class="tui-lamp" data-on></span>Active</span></div>
  <div style="display:grid;gap:4px"><span class="tui-label" style="padding-left:2px">Name</span><tui-inline-edit label="Project name" required><button class="tui-inline-edit__print" type="button"><span class="tui-inline-edit__text">Northwind billing</span></button><input class="tui-inline-edit__input" name="project" value="Northwind billing" hidden></tui-inline-edit></div>
  <div style="display:grid;gap:4px"><span class="tui-label" style="padding-left:2px">Owner</span><tui-inline-edit label="Owner" empty="Add an owner"><button class="tui-inline-edit__print" type="button"><span class="tui-inline-edit__text"></span></button><input class="tui-inline-edit__input" name="owner" value="" hidden></tui-inline-edit></div>
  <div style="display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap"><button class="tui-key" type="submit">Post the form</button><output class="tui-muted" style="font-size:var(--tui-t-small)" data-ie-out aria-live="polite">Nothing saved yet</output></div>
</form>
```

## Usage notes

Press the project name, change it and press Enter: it prints back and the line under it says what was saved. Press it again and Escape puts the old name back. Press the owner and click away: moving away keeps what you typed. The name is required, so an empty save keeps the field open.

## Accessibility

- The words are a native `<button>` named by what they say and what they are for, “Northwind billing, edit project name”, so the visible words are the start of the name.
- The field has the same label, “Project name”, and opens with its text selected and the focus in it. A screen reader says it as an edit field with that name.
- Every outcome is said aloud by a polite status line: “Project name saved: Northwind payouts”, “Edit cancelled. Project name is Northwind billing”, “Project name cannot be empty”.
- Enter and Escape return the focus to the words; moving away with Tab or a click does not take it back, so it goes where the person was heading.
- Escape is not passed on, so a dialog around the field is not closed by the key that was cancelling an edit. Enter does nothing while an input method is composing.
- The pencil is always shown on a phone, where there is no hover to find it by, and the field is 48px tall there with a 16px text size. The state never relies on colour.

## How it’s built

The words and the field are the same height and the same type, with the same padding, so opening it moves nothing. At rest there is no cap at all: only the kit’s printing, with a hairline outline and a pencil on hover or focus. Open, the field is the field’s own well. The page keeps one real input for the whole life of the part, hidden when it is not being edited, so it posts, validates and resets like any other. The script is the small decision table in `inlineEditResolve` and the focus that follows it.

## Rules

Rename a thing where it is named: press the words, type, press Enter. Escape undoes it, and a screen reader is told what happened.

The full rules are in `inline-edit.rules.md` in the brain, which ships with the Kit.

## Related parts

- [Field](https://tactileui.dev/parts/field): Text entry in a recess, the label printed above it, and a hint that says how to fix an error.
- [Input group](https://tactileui.dev/parts/input-group): One recess that holds a field and everything that belongs to it. Words that are part of the value but are not typed, such as https:// or a currency, are printed on the floor of the well behind an engraved line; a glyph sits at the start; small keys sit at the end to clear the text, copy it, or show a password.
- [Scrub field](https://tactileui.dev/parts/scrub): A number you can type or drag, the way a pro tool’s value field works: an engraved ruler under the numerals slides beneath a fixed index as the value moves.
- [Text area](https://tactileui.dev/parts/textarea): Longer text in a deeper recess: notes, messages, descriptions. It grows by its corner handle.
