Skip to Content

Widget Matrix

Use this document for:

  • FieldType -> WidgetType compatibility
  • widget examples
  • widget-specific widgetProps

Related docs:

  • Fields: core Field props, conditions, filters, Field.onChange, value contracts
  • Relation fields: RelationTable, SelectTree, OneToMany, ManyToMany

FieldType -> WidgetType Matrix

FieldTypeDefault behaviorSupported WidgetType
Stringsingle-line inputURL, Email, Phone, Text, RichText, TemplateEditor, Markdown, Code, Color, yyyy-MM, MM-dd, CronEditor
MultiStringtag-style comma/enter input-
Integernumeric inputMonetary, Percentage, Slider
Longnumeric inputMonetary, Percentage, Slider
Doublenumeric inputMonetary, Percentage, Slider
BigDecimaldecimal string inputMonetary, Percentage, Slider
BooleanswitchCheckBox
Datedate pickerRelative
DateTimedatetime inputRelative
Timetime inputHH:mm:ss, HH:mm
Optionsingle selectRadio, StatusBar, StatusIcon
MultiOptioncheckbox groupCheckBox
ManyToOnereference selectSelectTree
OneToOnereference selectSelectTree
ManyToManyrelation table + picker dialogSelectTree, TagList
OneToManyrelation table-
Filefile uploadImage
MultiFilemulti-file uploadMultiImage
JSONCodeMirror JSON editorJsonTree
Filtersfilter builder-
Ordersorder builder-
DTOCodeMirror JSON editorJsonTree

String Widgets

Default String

<Field fieldName="name" />

URL, Email, Color

<Field fieldName="homepage" widgetType="URL" /> <Field fieldName="contactEmail" widgetType="Email" /> <Field fieldName="themeColor" widgetType="Color" />

Phone

International phone input. Stores a single E.164 string (e.g. "+6591234567"). The widget composes a country picker (flag + dial code) with a national-number input that formats as the user types via libphonenumber-js AsYouType. Country list, names, and dial codes are fetched from the backend endpoint GET /CountryRegion/listDialCodes via useDialCodes() so the supported countries and i18n labels stay server-driven; libphonenumber-js handles validation/parsing/formatting locally.

Paste-friendly: pasting a value that begins with + re-parses to switch the country picker and reformat the national digits. Read-only mode renders the value in international format (e.g. "+65 9123 4567").

<Field fieldName="workPhone" widgetType="Phone" />

Value contract: E.164 string ("+<dialCode><nationalDigits>"), or empty string when no number is entered. Schema-level validation should use phoneE164 / phoneE164Optional from @/utils/schema-validators.

Text

<Field fieldName="description" widgetType="Text" />

RichText

Tiptap-based WYSIWYG rich text editor. Stores and reads HTML strings.

Toolbar: bold, italic, underline, strikethrough, headings (H1–H4), bullet/ordered lists, indent/outdent, text alignment, link, image upload, table, horizontal rule, highlight, undo/redo.

Image handling:

  • Upload — toolbar image button opens a native file picker; the selected image is read as a base64 data URL and inserted inline. No external URL input.
  • Resize — click an image to select it, then drag any of the four corner handles to resize. Width is stored on the node; height scales proportionally via height: auto.
  • Serialization — images are stored in HTML with inline styles for portability: <img src="..." width="400" style="width: 400px; max-width: 100%; height: auto;">. Images without an explicit width get style="max-width: 100%; height: auto;".

Two-level lazy loading: read-only mode renders raw HTML without loading the editor; edit mode lazy-loads the full Tiptap editor.

<Field fieldName="content" widgetType="RichText" />

TemplateEditor

Tiptap-based template editor for designing email templates, document templates, and other content intended for backend rendering and PDF generation.

Storage format: HTML with data-tpl-* attributes for template-specific nodes. The editor round-trips between stored HTML and Tiptap JSON automatically via custom parseHTML / renderHTML rules.

Features:

  • Field placeholders — insert model fields as inline chips. HTML output: <span data-tpl-field="fieldPath" data-tpl-label="label">{{fieldPath}}</span>. The Insert Field picker (including related one-level paths and loop-table column choices) excludes reversedFields from @/types/BaseModel (e.g. id, tenantId, version, audit timestamps and user references), matching other flows that treat these as system-managed.
  • Custom variables — insert single-use variables as inline chips. HTML output: <span data-tpl-variable="employee_name" data-tpl-label="Employee Name" data-tpl-value-type="String" data-tpl-required="true">{{employee_name}}</span>
  • Signature slots — insert fixed-size inline signature placeholders that can live inside text flow. Multiple signature slots can be inserted on the same line, sit side by side, and leave room for typed spacing or text between slots. Default toolbar presets map to signing-party slots such as Sender and Receiver. HTML output: <span data-tpl-signature="Sender" data-tpl-label="Sender Signature"></span>
  • Relation field expansion — expand ManyToOne / OneToOne relations one level to insert nested paths (e.g. department.name)
  • Loop tables — insert OneToMany / ManyToMany relations as loop tables with selectable columns. HTML output: <table data-tpl-loop="relationField" data-tpl-model="RelatedModel"> with <th data-tpl-field="col"> headers
  • Toolbar actionsInsert Field, Insert Variable, and Insert Signature, each individually configurable via widget props
  • Two-level lazy loading — same as RichText: read-only renders HTML without Tiptap; edit mode lazy-loads the full editor
  • Saved-record previewDocumentTemplate RichText records can open /admin/document-template/[id]/preview in a new tab, use the left-side outline to jump between variables and signature slots, switch Preview As between All, Sender, and Receiver, and click highlighted placeholders in the document preview to fill custom variables and capture signatures locally
<Field fieldName="offerLetterTemplate" widgetType="TemplateEditor" widgetProps={{ modelName: "Employee" }} />
<Field fieldName="htmlTemplate" widgetType="TemplateEditor" widgetProps={{ modelName: "{{ modelName }}" }} />

TemplateEditor widget props:

PropTypeDefaultNotes
modelNamestring-Model whose fields are available for insertion. Supports static values like "Employee" and field references like "{{ modelName }}".
minHeightnumber | string320pxMinimum editor height.
enableInsertFieldbooleantrueWhether the toolbar shows Insert Field.
enableInsertVariablebooleantrueWhether the toolbar shows Insert Variable.
enableInsertSignaturebooleantrueWhether the toolbar shows Insert Signature.

If modelName is omitted, falls back to the field’s own metaField.modelName.

Custom variable schema:

AttributeTypeNotes
codestringRequired. Must start with a letter and then use only letters, numbers, or underscores. Unique within the template.
labelstringRequired display label for editor/read-only preview.
valueType"String" | "Date" | "DateTime" | "Boolean"Drives preview input type and formatting.
requiredbooleanRequired variables show validation feedback in the preview page until filled.

Custom variable behavior:

  • Insert Variable opens a dialog for code, label, valueType, and required
  • code is required and must be unique within the template
  • existing variable chips can be edited in the editor to update code, label, valueType, and required

Signature slot behavior:

  • Insert Signature opens a dropdown with Sender Signature and Receiver Signature
  • selecting Sender Signature inserts a signature slot with code="Sender"
  • selecting Receiver Signature inserts a signature slot with code="Receiver"
  • code is required and must be unique within the template
  • default slot size is 240 x 120; click a slot to select it and drag any corner handle to resize both width and height freely (min 120 x 60)
  • resized dimensions are stored on the node as data-tpl-width / data-tpl-height attributes and inline style; the signed image inside fills the slot via width: 100%; height: 100%; object-fit: contain
  • multiple signature slots can be inserted on the same line
  • adjacent signature slots can share the same row, with normal text or spacing inserted between them
  • existing signature slots can be edited in the editor to update their code and label
  • preview page supports hand-drawn signatures and local uploaded signature images

Role-aware preview and signing behavior:

  • /admin/document-template/[id]/preview includes a Preview As selector with All, Sender, and Receiver
  • Preview As = All keeps every signature slot editable for admin/template testing workflows
  • Preview As = Sender only allows the Sender signature slot to open the signature dialog; other signature slots render as locked/read-only
  • Preview As = Receiver only allows the Receiver signature slot to open the signature dialog; other signature slots render as locked/read-only
  • the preview outline mirrors this state by marking the active party slot as editable and the other slot as locked
  • /admin/signing-document/[id]/sign is the focused signing workspace for a single assigned slot and displays the resolved signing party label from SigningDocument.signSlotCode
  • the signing workspace only submits the assigned signSlotCode; if no slot is assigned, signing is blocked and the page shows an empty-state warning instead of the signature capture workflow
  • current preset role labels are Sender and Receiver; if additional parties are introduced later, update the shared signature preset definitions before extending template or signing flows

yyyy-MM

Month-year picker stored as a "yyyy-MM" string (e.g. "2024-03"). Renders a popover panel with year navigation (arrows) and a month grid. Clicking the year label switches to a year-grid page view; clicking a year returns to the month grid.

<Field fieldName="period" widgetType="yyyy-MM" /> <Field fieldName="reportPeriod" widgetType="yyyy-MM" widgetProps={{ min: "2020-01", max: "2030-12", defaultPanelYear: 2026 }} />

yyyy-MM widget props:

PropTypeDefaultNotes
minstring"1900-01"Lower bound, inclusive. Format: "yyyy-MM". Falls back to default if invalid.
maxstring"2100-12"Upper bound, inclusive.
clearablebooleantrueShow Clear button in the panel footer.
showQuickPickbooleantrueShow This month quick button (disabled when current month is out of [min, max]).
yearStepnumber1Year-grid stepping. 5 lays out 1900, 1905, 1910, … in the year view.
defaultPanelYearnumbercurrent yearFirst-open panel year when the field has no value. Clamped to [min.year, max.year].
yearsPerPagenumber12Year-grid page size. Common alternates: 16, 20.

Months outside [min, max] are rendered but disabled in the month grid; same for years outside the range in the year grid.

Value contract: string in "yyyy-MM" format, or undefined when cleared.

MM-dd

Month-day picker stored as a "MM-dd" string (e.g. "03-15"). Renders a popover panel with a day-grid calendar. Clicking the month label in the header switches to a month-grid view; clicking a month returns to the day grid for that month. February 29 is always selectable (year-independent field).

<Field fieldName="anniversary" widgetType="MM-dd" /> <Field fieldName="fiscalYearStart" widgetType="MM-dd" widgetProps={{ min: "01-01", max: "06-30", firstDayOfWeek: 1 }} />

MM-dd widget props:

PropTypeDefaultNotes
minstring"01-01"Lower bound, inclusive. Format: "MM-dd".
maxstring"12-31"Upper bound, inclusive. Cross-year ranges (min > max, e.g. "10-01""03-31") are not supported and fall back.
clearablebooleantrueShow Clear button.
showQuickPickbooleantrueShow Today quick button (disabled when today is out of [min, max]).
firstDayOfWeek0 | 1 | 2 | 3 | 4 | 5 | 60 (Sunday)Week start. Default 0 matches the international product positioning; set 1 for ISO/Monday, etc.
defaultPanelMonth1..12current monthFirst-open panel month when the field has no value. Clamped to [min.month, max.month].

Days and months outside [min, max] are rendered but disabled.

Value contract: string in "MM-dd" format, or undefined when cleared.

CronEditor

Visual cron expression editor. Stores a standard cron string (5-part or 6-part). Opens a popover panel with tabbed editors for each cron position (Seconds, Minutes, Hours, Day, Month, Week). Each tab supports four modes: Every (wildcard), Specific values (multi-select grid), Range (from–to), and Interval (every N starting at X). The panel shows a live expression preview and the next scheduled run times.

<Field fieldName="cronExpression" widgetType="CronEditor" />
<Field fieldName="schedule" widgetType="CronEditor" widgetProps={{ format: "5-part" }} />

CronEditor widget props:

PropTypeDefaultNotes
format"6-part" | "5-part""6-part"6-part includes seconds tab; 5-part hides it.
showPreviewbooleantrueShow next run times preview in the panel.
previewCountnumber5Number of next run times to display.

Value contract: string in standard cron format (e.g. "0 30 9 * * *" for 6-part, "30 9 * * *" for 5-part), or empty string when cleared.

Markdown

<Field fieldName="notes" widgetType="Markdown" widgetProps={{ mode: "split", minHeight: 360 }} />

Markdown widget props:

PropTypeDefaultNotes
mode"split" | "edit" | "preview""split"split shows editor + preview; edit editor only; preview preview only.
heightnumber | string-Fixed height for editor/preview panels.
minHeightnumber | string320pxMinimum panel height.
maxHeightnumber | string-Maximum panel height.
lineNumbersbooleantrueEditor gutter line numbers.
lineWrappingbooleantrueWrap long markdown lines.
tabSizenumber2Editor indentation size.
autoFocusbooleanfalseFocus editor after mount.

Editor toolbar (shown in edit and split modes):

  • Line count — displays total line count (visible when lineNumbers is true)
  • Search — opens CodeMirror search panel (also available via Ctrl/Cmd+F)
  • Copy — copies full editor content to clipboard

When the field is read-only and the value is empty (or whitespace only), the editor body shows CodeEditorEmptyState (shared/code-editor-empty-state.tsx) with UI caption styling instead of an empty CodeMirror; default copy: “No content to display.” Custom hosts (for example Studio preview dialogs) can pass emptyMessage on CodeEditorEmptyState when composing it directly.

Preview is rendered by react-markdown with remark-gfm enabled by default.

Code

<Field fieldName="script" widgetType="Code" widgetProps={{ language: "python", minHeight: 320, lineNumbers: true }} />

Code widget props:

PropTypeDefaultNotes
language"plain" | "java" | "html" | "json" | "markdown" | "python" | "sql" | "yaml" | "yml""plain"Syntax highlighting language.
heightnumber | string-Fixed editor height.
minHeightnumber | string240pxMinimum editor height.
maxHeightnumber | string-Maximum editor height.
lineNumbersbooleantrueEditor gutter line numbers.
lineWrappingbooleantrueWrap long lines.
tabSizenumber2Editor indentation size.
autoFocusbooleanfalseFocus editor after mount.
showDownloadbooleantrueToolbar Download control. Set false to hide. Hidden while the form is submitting (isSubmitting).
downloadFileNamestring-Suggested download file name. Defaults to a sanitized fieldName plus an extension inferred from language (for example script.sql).

Editor toolbar:

  • Line count — displays total line count (visible when lineNumbers is true)
  • Search — opens CodeMirror search panel (also available via Ctrl/Cmd+F)
  • Copy — copies full editor content to clipboard
  • Download — saves the current value as a text file in the browser (client-side Blob; no server call). Still available when the field is read-only.

When read-only and the value is empty (or whitespace only), the editor body shows CodeEditorEmptyState instead of an empty CodeMirror (same behavior and defaults as the Markdown widget section above).

MultiString

<Field fieldName="tags" />

Numeric Widgets

Monetary

<Field fieldName="amount" widgetType="Monetary" />

Percentage

<Field fieldName="ratio" widgetType="Percentage" />

Slider

<Field fieldName="score" widgetType="Slider" widgetProps={{ minValue: 0, maxValue: 100, step: 5 }} />

Slider widget props:

PropTypeDefaultNotes
minValuenumber0Minimum slider value.
maxValuenumber100Maximum slider value.
stepnumberinferred from field type / scaleStep size.

Boolean And Option Widgets

Option and MultiOption interactive widgets (OptionSelect, Radio, StatusBar, CheckBox) support client-side option filtering via Field.filters — the filter is matched on each option’s itemCode. Badge is display-only and unaffected. See Fields → filters.

CheckBox

<Field fieldName="active" widgetType="CheckBox" />

Radio

<Field fieldName="status" widgetType="Radio" />

Radio widget props:

PropTypeDefaultNotes
direction"horizontal" | "vertical""vertical"Radio item layout direction.

StatusBar

<Field fieldName="status" widgetType="StatusBar" />

StatusBar widget props:

PropTypeDefaultNotes
wrapbooleantrueAllow status items to wrap.

Read-only display (default)

Option / MultiOption fields automatically render as a read-only display when the surrounding context is read-only — no widgetType declaration needed.

  • Any selected option carrying itemTone → coloured StatusBadge.
  • No itemTone on any selected option → plain text (itemName, comma-separated for MultiOption).
  • Empty value → -.
// In a card body, board card, table cell, or read-only form: <Field fieldName="status" /> <Field fieldName="tags" />

StatusIcon

Compact icon-only indicator for Option fields. Use this in dense table cells, board cards, and status strips where a colored icon conveys the state more directly than a labeled badge.

<Field fieldName="deployStatus" widgetType="StatusIcon" />

Zero per-page config — colour and icon are read entirely from the option-set’s itemTone and itemIcon metadata. To change the visual for a status, edit the option-set, not the page.

StatusIcon widget props:

PropTypeDefaultNotes
iconClassNamestring-Extra className applied to the rendered icon.

For value-driven use outside a Field (e.g. when the value comes from sidecar data with no surrounding RecordContext), import the underlying primitive directly:

import { StatusIcon } from "@/components/fields/widgets/option/StatusIconWidget"; <StatusIcon value={lastDeployment.deployStatus} />

Option metadata → presentation

The OptionReference shape that drives all presentation:

{ itemCode: string; itemName: string; itemTone?: "Success" | "Warning" | "Error" | "Info" | "Neutral"; itemIcon?: string; // key into STATUS_ICON_REGISTRY }

Tone codes match the backend OptionItemTone enum (Title-Case). itemTone resolves to a colour preset:

itemToneText colourDefault icon
Successtext-emerald-600Check (CheckCircle2)
Warningtext-amber-600Alert (AlertCircle)
Errortext-destructiveX (XCircle)
Infotext-sky-600Info (Info)
Neutraltext-muted-foregroundPending (CircleDashed)

itemIcon keys match the backend OptionItemIcon enum and resolve through the frontend’s STATUS_ICON_REGISTRY. Current registry keys:

KeyComponentNotes
CheckCheckCircle2
XXCircle
BanBan
AlertAlertCircle
PausePauseCircle
InfoInfo
EyeEye
LoaderLoader2Animates by default (spin: true)
ClockClock
PendingCircleDashed
UndoUndo2
LockLock

Adding a new icon key requires (a) adding the code to the backend OptionItemIcon enum + the system option-set, (b) adding the entry to icon-registry.ts. Keep the set small — most cases are covered by the 5 tone-default icons.

Date And Time Widgets

HH:mm / HH:mm:ss

Time picker stored as "HH:mm" (e.g. "09:30") or "HH:mm:ss" (e.g. "09:30:15"). Renders a popover with a column-list panel — one column per unit (hours / minutes / [seconds]) where candidates are generated from the configured step. Selecting a cell commits the new value; cross-column boundary disabling enforces [min, max] exactly. Replaces the native <input type="time"> for cross-browser consistency.

<Field fieldName="startTime" widgetType="HH:mm" /> <Field fieldName="meetingTime" widgetType="HH:mm" widgetProps={{ min: "09:00", max: "18:00", minuteStep: 15, quickOptions: ["09:00", "10:30", "14:00", "16:30"], }} /> <Field fieldName="processStartTime" widgetType="HH:mm:ss" widgetProps={{ secondStep: 15, defaultTime: "00:00:00" }} />

HH:mm / HH:mm:ss widget props:

PropTypeDefaultNotes
minstring"00:00" / "00:00:00"Lower bound, inclusive. Format matches the widget’s own format string.
maxstring"23:59" / "23:59:59"Upper bound, inclusive.
clearablebooleantrueShow Clear button.
showQuickPickbooleantrueShow Now quick button (disabled when current time is out of [min, max]).
minuteStepnumber1Minute candidate granularity. Legal: 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60. 60 means only 00. Out-of-set values fall back to 1 with a dev-mode warn.
secondStepnumber1Second candidate granularity. Only used by HH:mm:ss; ignored for HH:mm. Same legal set as minuteStep; 60 means only 00.
defaultTimestring-First-open prefilled value when the field has no value. Snapped up to the step grid; falls back to min if it can’t fit in [min, max].
quickOptionsstring[]-Custom preset chips above the columns (e.g. ["09:00", "12:00", "18:00"]). Each option must be in [min, max] and aligned to the step grid; otherwise disabled.
use12HoursbooleanfalseType-only placeholder — not implemented in this version.

Footer behavior:

  • Apply (✓ icon, always shown): Confirms current panel state and closes the popover. If the field is empty, fills with min first.
  • Now (when showQuickPick=true): Sets the wall-clock time, subject to [min, max]. Does not close the popover, so the user can adjust further before Apply.
  • Clear (when clearable=true): Sets the field to empty. Does not close the popover.

Off-grid existing values (e.g. saved "09:23" with minuteStep=15) are preserved on the trigger button verbatim. The columns won’t show a “selected” highlight for off-grid components; picking from the grid snaps to the grid.

Value contract: string in the widget’s format, or undefined when cleared.

Relative

Renders a Date / DateTime value as a human-readable distance from now (e.g. "2 days ago", "in 3 hours"). The absolute timestamp is exposed via the title attribute as a hover tooltip so precise time stays accessible.

<Field fieldName="updatedTime" widgetType="Relative" /> <Field fieldName="dueDate" widgetType="Relative" widgetProps={{ strict: false }} />

Display-only widget. Never exposes an editor — even on editable fields. Apply only to readonly fields or read-only surfaces (cards, tables, read-only forms). On an editable field the value will appear as plain text and the user cannot change it.

Relative widget props:

PropTypeDefaultNotes
addSuffixbooleantrueInclude the "ago" / "in" modifier. Set false for raw distance like "5 minutes".
strictbooleantrueStrict unit ("2 days") vs natural phrasing ("about 2 days"). Strict avoids the "about" qualifier and uses a single unit.

The relative phrase is rendered once at mount; it does not auto-update with the wall clock. The hover tooltip with the absolute timestamp is the affordance for precision, which is sufficient for typical card / table surfaces. If you need ticking updates, drive a re-render from the parent component (e.g. an interval) — the widget will pick up the new “now” on each render.

For value-driven use outside a Field (e.g. when the value comes from sidecar data with no surrounding form context), import the underlying primitive directly:

import { RelativeTimeDisplay } from "@/components/fields/widgets/relative"; <RelativeTimeDisplay value={drift.lastCheckedTime} className="text-muted-foreground" />

Quick range filter (column header)

Date / DateTime columns expose a date-range preset panel inside the column-header filter popover, sitting alongside the standard operator + value form. Users pick a semantic range with one click; the precise operator path stays available for fine control.

Preset registry

Presets are tagged with a direction (past / future / neutral) so the panel can adapt to the field’s temporal nature. neutral presets always show; past / future show based on the panel’s mode.

SectionDirectionItems
QuickmixedToday (neutral), Yesterday (past), Tomorrow (future)
PastpastLast 7 / 15 / 30 / 60 / 90 days (rolling, includes today)
PeriodneutralThis week, This month, This year
FuturefutureNext 7 / 15 / 30 / 60 / 90 days (rolling, includes today, mirror)
UnaryIs set, Is not set (one-click, no value required)

filterDateRange — choosing past / future / both

Set filterDateRange on <Field /> inside ModelTable / RelationTable to control which directions surface in that column’s filter:

// Default (no prop) — past + neutral. Right for created/updated time, hire date, etc. <Field fieldName="createdTime" /> // Future + neutral — right for due dates, scheduled events, contract end. <Field fieldName="contractEndDate" filterDateRange="future" /> // Both — right for double-sided fields (birthday, anniversary). <Field fieldName="birthdayDate" filterDateRange="both" />

Concrete use case: surfacing employees whose birthday falls in the next 30 days is one click — <Field fieldName="birthdayDate" filterDateRange="both" /> then pick Next 30 days.

filterDateRange is a UI-only declaration (“how should this column surface filters”), not backend metadata. It mirrors filterBySource in spirit — a call-site decision that does not belong in the model definition. Has no effect outside table contexts.

Interaction

  • Clicking a preset auto-switches the operator to BETWEEN (or the matching unary), applies, and closes the popover. The operator dropdown does not need to be touched.
  • Editing the operator or start / end date manually clears the preset highlight and waits for the explicit Apply button.
  • Highlight is derived from the current (operator, value) against the registry. A “Today” filter saved yesterday opens highlighted as “Yesterday” the next day — reflecting current semantics rather than the original click.

Time zone & week

  • Ranges resolve in configs.timeZone (or browser tz when unset), via @date-fns/tz for DST-safe boundaries.
  • weekStartsOn defaults to Monday (1) and is overridable per call.

Rolling-window semantics

  • Both Last N days and Next N days include today. So Last 7 days = [today − 6, today] and Next 7 days = [today, today + 6], each spanning 7 calendar days.
  • “Strictly future N days” can be expressed by combining Tomorrow + Next N days if needed.

Persistence semantics

  • Presets are snapshotted to absolute yyyy-MM-dd start / end at click time. Saved views and shared URLs do not drift over time.
  • Backend contract is unchanged: presets are sent as standard BETWEEN / IS SET / IS NOT SET FilterCondition units; no new operator is introduced.

Single-source helpers live at src/components/views/table/utils/date-range-presets.ts:

  • resolvePresetRange(id, options?) — preset id → { start, end } Date pair
  • matchPreset(bounds, options?) — bounds → preset id or null (used for UI highlight)
  • filterPresetsByMode(presets, mode) — filter registry by "past" | "future" | "both"
  • DATE_RANGE_PRESETS — ordered registry for UI iteration; each entry carries direction

Relation Widgets

SelectTree

<Field fieldName="departmentId" widgetType="SelectTree" />

TagList

<Field fieldName="userIds" widgetType="TagList" tableView={UserTableView} />

See Relation Fields for relation-specific behavior, query rules, and RelationTable.

File And Image Widgets

Image

<Field fieldName="avatar" widgetType="Image" /> <Field fieldName="photo" widgetType="Image" widgetProps={{ display: "avatar", avatarSize: "xl", previewUrl: userInfo.photoUrl, crop: { enabled: true, aspect: 1, shape: "round" }, }} />

Image widget props:

PropTypeDefaultNotes
acceptstring"image/*"Native file input accept string.
aspectRationumber | string-Preview ratio only.
objectFit"cover" | "contain""cover"Preview image fit mode.
uploadTextstring"Upload image" or "Upload photo"Empty-state upload text.
helperTextstring-Widget-local helper text.
display"card" | "avatar""card"avatar is the compact profile-photo layout.
avatarSize"sm" | "md" | "lg" | "xl""lg"Only used when display="avatar".
previewUrlstring-Existing image URL when the field value is still a saved file id string.
crop.enabledbooleanfalseEnable crop flow before upload.
crop.aspectnumber1 in avatar mode; otherwise derived from aspectRatio, else 4 / 3Crop ratio. 1 means square.
crop.shape"rect" | "round""rect"Crop shape.
crop.zoombooleantrueEnable zoom control.
crop.minZoomnumber1Minimum zoom.
crop.maxZoomnumber3Maximum zoom.

MultiImage

<Field fieldName="photos" widgetType="MultiImage" />

MultiImage adds:

PropTypeDefaultNotes
maxCountnumber-Maximum number of uploaded images.
columns2 | 3 | 4 | 5 | 64Gallery grid columns.

Structured Value Widgets

JsonTree

<Field fieldName="config" widgetType="JsonTree" />

JSON editor widget props:

PropTypeDefaultNotes
heightnumber | string-Fixed editor height.
minHeightnumber | string240pxMinimum editor height.
maxHeightnumber | string-Maximum editor height.
lineNumbersbooleantrueEditor gutter line numbers.
lineWrappingbooleantrueWrap long lines.
tabSizenumber2Indentation size.
formatOnBlurbooleantrueReformat valid JSON on blur.
autoFocusbooleanfalseFocus editor after mount.

Filters

<Field fieldName="filters" />

Filters widget props:

PropTypeDefaultNotes
allowedFieldsstring[]-Whitelist searchable fields.
excludeFieldsstring[]-Hide fields from the builder.

Orders

<Field fieldName="orders" />

Orders widget props:

PropTypeDefaultNotes
allowedFieldsstring[]-Whitelist sortable fields.
excludeFieldsstring[]-Hide fields from builder.
Last updated on