Skip to content

Cell Editor Registry

The CellEditorRegistry determines which editor opens when a user activates a cell. Editors are registered with a matcher function and a priority — the first match wins.

EditorColumn TypeBehavior
InlineEditorfallback (all)Textarea overlay; handles Enter, Escape, Tab, scroll
DatePickerEditor'date'Calendar overlay; commits YYYY-MM-DD
DateTimeEditor'datetime'Calendar + time spinners; commits YYYY-MM-DDTHH:mm

The inline editor is the default fallback when no registered editor matches.

resolve(column, value) iterates registered entries from highest to lowest priority. The first matcher returning true provides the editor. If no match, the engine falls back to InlineEditor.

registry.resolve(column, cellValue)
→ entries sorted by priority (descending)
→ first matcher(column, value) === true → return editor
→ no match → null → InlineEditor fallback
engine.registerCellEditor(myEditor, 'currency', 10);

This registers myEditor to handle columns with type: 'currency' at priority 10.

For complex matching logic, access the registry directly:

engine.getCellEditorRegistry().register(
myEditor,
(column, value) => column.type === 'rating' && typeof value === 'number',
5
);

Higher priority values are checked first.

Custom editors implement the CellEditor interface:

interface CellEditor {
readonly id: string;
readonly isOpen: boolean;
readonly editingRow: number;
readonly editingCol: number;
open(context: CellEditorContext, commitFn: CellEditorCommit, closeFn): void;
close(reason: EditorCloseReason): void;
setTheme(theme: SpreadsheetTheme): void;
setLocale(locale: ResolvedLocale): void;
destroy(): void;
}

ResolvedLocale is Required<SpreadsheetLocale> — the result of merging a partial locale with English defaults via resolveLocale().

  1. Open: The engine calls editor.open(context, commitFn, closeFn). The editor renders its DOM into context.container.
  2. Value change: The editor manages its own internal state.
  3. Commit: Call commitFn(row, col, oldValue, newValue) to write the value. This executes a CellEditCommand via the command manager and emits a cellChange event.
  4. Close: Call closeFn(reason) or the engine calls editor.close(reason). The editor removes its DOM.
  5. Destroy: Called during engine teardown to release all resources.

The context object provides everything the editor needs:

FieldDescription
row, colCell coordinates
valueCurrent cell value
columnColumnDef for this cell
containerDOM element to render into
scrollContainerScrollable parent
layoutEngineFor cell rect calculations
scrollManagerScroll state
cellStoreCell data access
dataViewFiltered/sorted data view
themeCurrent theme
localeCurrent locale
mergeManagerMerged cell info
frozenRows, frozenColumnsFreeze pane counts

The reason parameter in close() tells the engine how to handle focus after closing:

'enter' | 'shift-enter' | 'tab' | 'shift-tab' | 'escape' | 'blur' | 'scroll' | 'programmatic'

  • InlineEditor: Creates a <textarea> positioned over the cell via LayoutEngine.getCellRect(), z-index 20. Auto-focuses and selects content on open. Scroll commits and closes (except for frozen corner cells).
  • Overlay editors (date, datetime): Create a <div role="dialog">, z-index 50. Positioned below the cell (flips above if needed). Outside click closes.

When engine.setTheme() or engine.setLocale() is called, the registry propagates the change to all registered editors via setTheme() / setLocale().