Skip to content

Input widget registry

At a glance

  • Inputs maps every Input enum value to a component — that's the whole registry.
  • 3 of the 12 inputs (TextSelector, CellSelector, TextSelectorAdd) read from the file being viewed via file-viewer's useFileInteraction.
  • Text-like inputs debounce commits 250ms through useDebouncedDraft.
  • Individual input components aren't exported — this page is their only documentation.

When a node's configuration panel needs to render a field, it doesn't pick a component directly — it looks up an Input enum value (defined in packages/types, re-exported from here) in Inputs, a Record<Input, (props: InputProps<any>) => JSX.Element> that node-inputs builds. The individual components below aren't exported from the package themselves — only the Inputs map and the useDebouncedDraft hook are — so this page is the only place their behavior is documented.

The two families

Most of the twelve inputs are thin wrappers around a Mantine control, plain controlled components that call handleChange on every change: Checkbox, NumberInput, SingleSelect, RadioSelector, MultiSelect, MultiSelectCreatable, and LiteralView (a disabled, read-only TextInput — for showing a computed value the user can't edit).

The other family exists specifically to let a user point at the file being mapped instead of typing a value by hand: TextSelector, CellSelector, and TextSelectorAdd. Each renders a clipboard-style icon button that, on click, calls useFileInteraction from file-viewer to read whatever the user currently has selected in the file view (getPath() for a text/element path, getSelectedCellLocaion() for a spreadsheet cell), and writes that into the field via handleChange. TextSelectorAdd is the array variant of TextSelector — clicking the icon appends a new path to the existing array instead of replacing a single value, switching between a plain Clipboard icon (list empty) and a ClipboardPlus icon (list non-empty) to signal which mode it's in. This is the concrete mechanism behind the node-inputs → file-viewer dependency noted in the overview.

SingleSelectCreatable and MultiSelectCreatable are a third, smaller variation: like their non-creatable counterparts, but the user can type a value not in the selection list and have it added as a new option (tracked in local useState, not persisted anywhere else). Both run new values through replaceEscapedCharacter from utils before storing them.

Unconditional escaping

The inline TODO comments in both files flag this as a known rough edge: // TODO: this escape assumes the user actually wants to escape the character, make this escape optional. There's currently no way for a user to opt out.

Debounced commits

TextInput, TextSelector, and CellSelector all route their value through useDebouncedDraft rather than calling handleChange on every keystroke directly. It keeps a local draft state for instant UI feedback, and only calls the real onCommit (i.e. handleChange) 250ms after the user stops typing (@mantine/hooks' useDebouncedValue). A lastSyncedRef guards both directions of sync — an upstream value change (paste, reset, loading a different node) overwrites the local draft, but only if it actually differs from what was last synced, so the debounce timer doesn't fight an echo of its own committed value.

InputProps

The common prop contract components in the first family use is InputProps<T>value: T, handleChange: (value: T) => void, selection (for the choice-based inputs), and an optional placeholder. It's defined locally in node-inputs/src/types/index.ts, re-exporting Input from types alongside it.

Not everyone uses the shared type

RadioSelector and SingleSelect / SingleSelectCreatable don't actually use InputProps<T> — each declares its own local props interface with the same shape by convention, rather than importing it. If you change the shared contract, TypeScript won't catch a drift between it and these three.