Skip to main content
Search
Search the documentation

Inline edit

Text that turns into a field in place - rename a project, a column, a file.

pnpm dlx shadcn-svelte@latest add https://coral.imlargo.dev/r/kit-inline-edit.json

Rename-in-place has more states than it looks: showing, editing, saving, rejected. The usual version swaps a span for an input on click and handles two of them: leaving the keyboard with no way in, the Escape key closing the dialog it sits in, and a failed rename throwing away what was typed.

Click, or focus and press F2.

What Coral adds

  • Reachable. The value is a real button: Tab reaches it, Enter and Space open it, and F2 does too, as in every file manager.
  • Enter saves, Escape cancels, blur saves. Escape stops there instead of also closing a dialog. saveOnBlur={false} makes leaving the field cancel instead.
  • Focus comes back. After Enter or Escape, focus returns to the value. After a click elsewhere it stays where the reader clicked.
  • Waits, and survives rejection. onsave can be async. Return false or throw and the field stays open with the text as typed, to be corrected rather than retyped.
  • Nothing to save, nothing saved. Unchanged text closes the field without calling onsave.

Saving to a server

Try «api-gateway».

While saving, the field is readonly rather than disabled: disabling a focused field blurs it, and that blur would read as the reader leaving: saving a second time, or cancelling a save already on its way.

Validation

required refuses empty text, validate refuses anything else, and sanitize (trimming, by default) runs before both. A refused edit marks the field aria-invalid and stays open. What to say about it is copy, and copy is the project's. Render the message next to the component.

Import

<script lang="ts">
	import InlineEdit from '#lib/components/coral/kit/inline-edit/inline-edit.svelte';
</script>

Props

Everything the shadcn input accepts goes to the field while editing: maxlength, aria-*, autocomplete. On top of that:

PropTypeDefaultDescription
valuestring''Bindable. Only changes once a save goes through.
editingbooleanfalseBindable. Set true to start an edit from a menu.
onsave(value: string) => unknown-Async-aware. false or throw keeps the field open.
onerror(error: unknown) => void-What onsave threw. The field stays open, text as typed.
oncancel() => void-Escape, or blur with saveOnBlur off.
validate(value: string) => boolean-false refuses the text.
sanitize(raw: string) => stringtrimCleans text before validating and saving.
requiredbooleanfalseRefuses empty text.
disabledbooleanfalseThe value still shows; the button no longer starts an edit.
saveOnBlurbooleantrueLeaving the field saves. Off, it cancels.
selectOnEditbooleantrueSelects the text when editing starts.
placeholderstring''Shown while empty, in both modes.
editLabel(value: string) => stringEdit {value}Accessible name of the value button.
classstring-Merged onto the value button and, while editing, the group.
inputClassstring-Merged onto the field.
displaySnippet<[{ value, placeholder }]>-Replaces what the value button shows.

Accessibility

The default editLabel includes the visible value, so the accessible name contains the text on screen. That's what WCAG 2.5.3 asks of a control a voice-control user will address by what it says. aria-busy is set while saving and aria-invalid after a refused edit.