Skip to content

Latest commit

 

History

History

README.md

@object-ui/fields

The standard field library and registry for Object UI.

Features

  • 📚 Standard Fields - Implementation of all ObjectStack protocol fields (Text, Number, Date, Lookup, etc.)
  • 🔌 Plugin System - FieldRegistry allows registering custom renderers or overriding standard ones.
  • 🛠 Helpers - Utilities for schema mapping, validation, and expression evaluation.

Installation

npm install @object-ui/fields

Field Registry

The Field Registry is the core mechanism that allows decoupling view components from specific field implementations.

Registering a Custom Field

You can override standard fields or add new ones:

import { registerFieldRenderer } from '@object-ui/fields';
import { MyCustomColorPicker } from './MyCustomColorPicker';

// Register a new 'color' field type
registerFieldRenderer('color', MyCustomColorPicker);

Using Standard Fields

View components use getCellRenderer to resolve the correct component for a field type.

import { getCellRenderer } from '@object-ui/fields';

const MyGridCell = ({ field, value }) => {
  const Renderer = getCellRenderer(field.type);
  return <Renderer field={field} value={value} />;
};

Standard Field Types

Supported types out of the box:

  • Basic: text, textarea, number, boolean
  • Format: currency, percent
  • Date: date, datetime, time
  • Selection: select, lookup, master_detail
  • Contact: email, phone, url
  • Media: file, image
  • System: formula, summary, auto_number

Rendering form field widgets outside the form

The full widget surface is exported for consumers that render field widgets outside a record form (ADR-0059):

  • FORM_FIELD_TYPES — the frozen list of every type the form can render.
  • resolveFormWidgetType(type) — resolves any field-type spelling to its widget key (spec aliases like toggle/json/secret included; unknown types fall back to text, mirroring the form).
  • getLazyFieldWidget(type) — the widget wrapped in React.lazy (cached per type; render inside <Suspense>), sharing the same loaders registerField uses so nothing is bundled eagerly.

The app-shell ActionParamDialog uses these to render declared action params through the exact same widgets as the object form — with a drift test pinning param support ⊇ form support.

File uploads in line-item grids

GridField (the master-detail line-items grid) supports type: 'file' columns: the cell renders a compact upload button plus removable file chips (thumbnails for images) instead of degrading to a text input, so users can attach a receipt or photo per row without opening the row form (objectui#2360). Columns accept accept?: string[] and multiple?: boolean; uploads run through the same UploadProvider pipeline as the full-size FileField (the compact control is exported as FileCell). Auto-derived subform columns map file/image/ avatar fields to file columns instead of dropping them.

Multi-value selects

A select field declared multiple: true selects zero-or-more values (spec allows multiple on select). SelectField delegates to the multi-value chip picker (the same widget the multiselect type uses) and stores a string[]. Delegating inside SelectField — rather than at a type-resolution layer — means every surface that renders the select widget (the object form, the inline grid editor, and the app-shell ActionParamDialog) gets multi-select identically, with no per-surface drift. Both single- and multi-value selects resolve per-option visibleWhen cascading and dependsOn gating through the same useCascadingOptions hook, so the offered chips narrow (and now-invalid selections are pruned) exactly as the single dropdown does.

Cascading & role-gated select options

select, multiselect, radio, and checkboxes options support a per-option visibleWhen CEL predicate (offered only when TRUE, evaluated against the live record + current_user) and a field-level dependsOn. Together they drive dependent selects (country → province → city) and role-gated options with no bespoke matrix — the same primitives dependent lookups use. All four widgets resolve this through the shared useCascadingOptions hook, which wraps the pure resolveCascadingOptions helper in @object-ui/core (also used by the form renderer's inline pre-filter, so gating and filtering never drift). While a dependsOn parent is empty the control is gated; a parent change re-filters the list and clears a now-invalid value (scalar select / radio drop the value; multi-value multiselect / checkboxes prune just the offered-out entries). Client-side hiding is UX only — gate authorization-sensitive values on the server too. See content/docs/fields/select.mdx.

Links

License

MIT — see LICENSE.