For AI agents: the complete documentation index is available at https://a3s-lab.github.io/UI/en/llms.txt, the full documentation bundle is available at https://a3s-lab.github.io/UI/en/llms-full.txt, and this page is available as Markdown at https://a3s-lab.github.io/UI/en/components/form-system/data-grids.md.

Editable data grids

A data grid presents an array of objects as comparable rows and columns while the controlled value remains a plain JSON array. Order lines, routing rules, inspection records, and node parameter mappings fit this layout.

实时示例批量粘贴 · 向下填充 · 排序筛选 · 对话框草稿
分流规则
逐行确认条件与目标;选择多行后可以批量删除。
行操作
分流规则第 1 行customer.level = vip等于销售处理
分流规则第 2 行execution.status等于人工复核
分流规则第 3 行message.category包含支持处理
受控值
{
  "routes": [
    {
      "id": "vip",
      "condition": "customer.level = vip",
      "operator": "equals",
      "destination": "sales"
    },
    {
      "id": "failed",
      "condition": "execution.status",
      "operator": "equals",
      "destination": "review"
    },
    {
      "id": "support",
      "condition": "message.category",
      "operator": "contains",
      "destination": "support"
    }
  ]
}

The example enables bulk paste, fill down, cross-column filtering, single-column sorting, dialog draft editing, and multi-row selection. Sorting and filtering change only the current view. Paste, fill, and row drafts update value once after validation. Canceling or pressing Escape produces no controlled-value change.

UI node properties

A data grid is a repeater bound to an array of objects, with direct child fields forming its columns.

PropertyTypeRequiredDescription
idstringYesNode identifier unique within the document
kind"repeater"YesData grids always use a repeater node
layout"data-grid"YesEnables the table and narrow-container row-card layout
schemaPathJSON PointerYesPoints to an object-array Schema
childrenstring[]YesDirect field columns; at least one
itemKeystringNoPoints to a required string identifier in each row object
dataGridUiDataGridDefinitionNoEditing, view, and bulk-operation policy; omission preserves inline editing

Read-only state is controlled by the FormRenderer readOnly property. Editing, selection, and bulk operations are disabled when read-only, while sorting and filtering remain available.

interface UiDataGridDefinition {
  editMode?: 'inline' | 'dialog';
  selection?: 'none' | 'multiple';
  sorting?: 'none' | 'single';
  filtering?: 'none' | 'search';
  paste?: 'none' | 'append';
  fill?: 'none' | 'down';
  virtualization?: {
    mode: 'rows';
    viewportHeight?: number;
    overscan?: number;
  };
}
dataGrid propertyOptionsDefaultRuntime behavior
editMode"inline" | "dialog""inline"Return cell changes immediately, or validate a complete row draft and submit it once
selection"none" | "multiple""none"Provide local multi-selection, select-all for the current view, and confirmed deletion
sorting"none" | "single""none"Provide local single-column ascending, descending, and original-order states
filtering"none" | "search""none"Search keywords across formatted text in visible columns
paste"none" | "append""none"Parse TSV data and append rows after whole-grid validation
fill"none" | "down""none"Copy one column from the first visible selected row to the remaining visible selected rows
virtualizationUiDataGridVirtualizationDefinitionDisabledRender only the current row window inside a fixed-height scroll region; requires editMode: "dialog"

Virtualization properties:

PropertyTypeDefaultConstraint
mode"rows"NoneRequired; windows the object-array rows
viewportHeightnumber480Integer from 240 through 960, in CSS pixels
overscannumber6Integer from 2 through 24; number of rows pre-rendered before and after the viewport

Every property is an explicit switch. Existing documents with layout: "data-grid" need no migration. Omitted features remain disabled, and editMode still defaults to "inline".

Structure and Schema

const document: FormDocument = {
  kind: 'a3s.form',
  apiVersion: 'a3s.dev/form/v1alpha1',
  revision: 1,
  metadata: { title: 'Routing rules', locale: 'en-US' },
  schema: {
    $schema: 'https://json-schema.org/draft/2020-12/schema',
    type: 'object',
    properties: {
      routes: {
        type: 'array',
        minItems: 1,
        maxItems: 500,
        items: {
          type: 'object',
          properties: {
            id: { type: 'string' },
            condition: { type: 'string', minLength: 1 },
            destination: { type: 'string', minLength: 1 },
          },
          required: ['id', 'condition', 'destination'],
          additionalProperties: false,
        },
      },
    },
    required: ['routes'],
    additionalProperties: false,
  },
  ui: {
    root: 'root',
    nodes: [
      { id: 'root', kind: 'root', children: ['routes'] },
      {
        id: 'routes',
        kind: 'repeater',
        layout: 'data-grid',
        label: 'Routing rules',
        schemaPath: '/properties/routes',
        itemKey: 'id',
        dataGrid: {
          editMode: 'dialog',
          selection: 'multiple',
          sorting: 'single',
          filtering: 'search',
          paste: 'append',
          fill: 'down',
          virtualization: {
            mode: 'rows',
            viewportHeight: 420,
          },
        },
        children: ['condition', 'destination'],
      },
      {
        id: 'condition',
        kind: 'field',
        label: 'Condition value',
        schemaPath: '/properties/routes/items/properties/condition',
        width: 6,
      },
      {
        id: 'destination',
        kind: 'field',
        label: 'Target branch',
        schemaPath: '/properties/routes/items/properties/destination',
        width: 6,
      },
    ],
  },
};

The compiler rejects these structures:

  • A data-grid that is not a repeater.
  • A Schema that is not an array of objects.
  • No direct field columns, or a direct child that is not a field.
  • dataGrid on another layout.
  • Unsupported values for editMode, selection, sorting, filtering, paste, fill, or virtualization.mode.
  • fill: "down" without selection: "multiple".
  • Virtual scrolling without editMode: "dialog".
  • A viewportHeight or overscan value that is not an integer in the allowed range.

A direct column can still bind to a nested property, such as /properties/routes/items/properties/when/properties/equals.

Editing modes

Inline editing

editMode: "inline" renders real fields directly in cells. Every field change immediately returns the complete array. Use it when there are few columns, inputs are short, and horizontal continuous entry matters.

Dialog draft editing

editMode: "dialog" shows field summaries in the table and opens a guarded row-level task through Edit:

  1. Clone the current row on open without changing the host value.
  2. Continue using real field components, row rules, and row data sources in the dialog.
  3. Run synchronous Schema and rule validation before saving, keep errors on their fields, and focus the first error.
  4. Return the complete array once after a successful save.
  5. Discard the draft on cancel, close, or Escape.
  6. If the host replaces the same row while it is being edited, prevent overwriting and require reopening the latest value.

New rows also use the draft flow. When itemKey points to a required string property, the runtime gives new rows a business identifier separate from UI state; it does not rewrite existing rows.

Host asynchronous validation continues after the row enters the controlled value, following field-blur, wizard-step, or final-submit protocols. The dialog itself does not store remote drafts.

Multi-row selection and deletion

selection: "multiple" adds row checkboxes, select all, and bulk deletion. The selection set uses stable runtime row identities. It is not written to form values and is never submitted or serialized.

Bulk deletion has two steps: first show the number of rows to remove, then confirm submission. The result always respects minItems; if deletion would cross the lower bound, the button is disabled and shows the number of rows that must remain. Moving a row preserves selection, while host removal automatically expires that row's selection.

Persistence, permission checks, and remote operations remain host-managed. Form Renderer only produces a new controlled array.

Bulk paste

paste: "append" adds “Paste rows” to the grid toolbar. The dialog does not request system clipboard permission; it accepts only text the user deliberately pastes. Input, parsing preview, and error state do not change controlled values.

The dialog lists column order explicitly. Each tab-separated cell maps in order to an editable column. hidden, calculated, matrix-single, and matrix-multiple do not become paste columns. A direct UI child field may bind a nested property; parsed values are written to the row object by relative path.

TSV parsing rules:

  • Support LF and CRLF line endings.
  • Double-quoted cells can contain tabs and newlines.
  • A double quote inside a cell is written as "".
  • Ignore rows in which every cell is empty.
  • Accept at most 1,000 rows and 256 KiB per operation, without exceeding the Schema's maxItems.
  • A row cannot contain more cells than there are editable columns.

The Schema type determines cell conversion:

Schema typeInput rule
string or undeclaredPreserve the original text
numberFinite number
integerInteger
booleantrue/false, 1/0, yes/no, on/off, 是/否
objectJSON object
arrayJSON array
nullnull

enum is checked after type conversion. An empty non-string cell preserves the row Schema's default value; an empty string cell writes an empty string. Automatic itemKey identities and object defaults still come from the repeater's creation logic.

After “Append data” is selected, parsed rows merge with the existing array and run through complete Schema, rule, and computation evaluation. If any row fails, the first error is shown and the array remains unchanged. If all pass, the fully evaluated array is returned once.

Fill down

fill: "down" depends on selection: "multiple". Designer enables multi-row selection when fill down is enabled and disables fill down when multi-row selection is disabled. The compiler rejects imported documents that do not satisfy this dependency.

Operation order:

  1. Select at least two currently visible rows.
  2. Open “Fill down.”
  3. Choose an editable column.
  4. Confirm the source value and target row count.
  5. Write the complete array once after validation passes.

The current sort and filter results determine visible order. The first visible selected row is the source; all remaining visible selected rows are targets. Selected rows hidden by a filter are not changed. Nested object and array values use structural cloning. If the source property is absent, the same path is removed from each target row.

The dialog snapshots source and target rows when opened. If the host replaces, removes, or changes any related row before confirmation, fill stops on a conflict and never overwrites the current controlled value with an old snapshot. The fill result then runs whole-grid Schema, rule, and computation evaluation; any error cancels the entire write.

Sorting and filtering

sorting: "single" gives each column heading a three-state sort: ascending, descending, and original order. Numbers use numeric comparison; text uses the current locale's natural order; empty values always sort last. Narrow containers replace hidden table headings with a sort selector synchronized to the column contract.

Sorting affects only the current Renderer and never reorders or returns the controlled array. Row edits, deletion, and error paths remain bound to original array indices. Move-up and move-down buttons are disabled while sorting or filtering is active; true array order can be changed only after restoring the complete original view.

filtering: "search" matches keywords across formatted text in all visible columns without reading undisplayed properties. Every keyword must match the same row. Matching is case-insensitive and applies Unicode normalization. Result counts and the empty-results recovery action use localized copy.

Select all after filtering selects only currently visible rows. Previously selected rows hidden by a filter remain selected. “Add a row” clears the filter first so the new row is not hidden immediately. Form validation errors also restore the complete view, allowing the error summary to locate and open the relevant row.

When inline editing and sorting are both enabled, the currently focused row retains its position until blur to prevent it jumping during input. It is re-sorted using the latest value after blur. Read-only forms retain sorting and filtering while editing, selection, and row actions stay disabled.

Row virtualization

virtualization.mode: "rows" constrains the grid to a fixed-height scroll region and mounts only the current window plus buffered rows before and after it. Sorting, filtering, select all, bulk deletion, paste, fill, Schema validation, and controlled values still process the complete array; the virtual window does not narrow the data range.

500 行实时示例动态行高 · 固定表头 · 视口窗口 · 完整受控数组
分流规则
逐行确认条件与目标;选择多行后可以批量删除。
表格共 500 行。聚焦此区域后,可滚动或使用 Home、End、Page Up、Page Down 加载对应行。
行操作
分流规则第 1 行payload.segment.001等于销售处理
分流规则第 2 行payload.segment.002包含支持处理
分流规则第 3 行payload.segment.003存在人工复核
分流规则第 4 行payload.segment.004等于销售处理
分流规则第 5 行payload.segment.005包含支持处理
分流规则第 6 行payload.segment.006存在人工复核
分流规则第 7 行payload.segment.007等于销售处理
分流规则第 8 行payload.segment.008包含支持处理
分流规则第 9 行payload.segment.009存在人工复核
分流规则第 10 行payload.segment.010等于销售处理
分流规则第 11 行payload.segment.011包含支持处理
分流规则第 12 行payload.segment.012存在人工复核
分流规则第 13 行payload.segment.013等于销售处理
分流规则第 14 行payload.segment.014包含支持处理
受控值摘要
{
  "rows": 500,
  "first": {
    "id": "route-001",
    "condition": "payload.segment.001",
    "operator": "equals",
    "destination": "sales"
  },
  "last": {
    "id": "route-500",
    "condition": "payload.segment.500",
    "operator": "contains",
    "destination": "support"
  }
}

The example holds a controlled array of 500 rows. Its initial page output includes only enough rows to cover the 420px viewport and buffer. Scrolling replaces the window using measured row heights. The desktop table and narrow-container row cards share the same window calculation.

Virtualization requires dialog editing. Inline inputs are never unmounted by scrolling out of the window, and row drafts do not depend on the virtual row's mount lifecycle. Designer switches to dialog editing automatically when virtual scrolling is enabled and removes virtualization when switching back to inline editing. The compiler enforces the same constraints on imported documents.

Runtime behavior:

  • Initial server rendering uses a deterministic row-height estimate. After client mount, ResizeObserver updates actual heights for desktop rows and mobile cards.
  • Column headings remain sticky inside the desktop scroll region. Mobile continues to use row cards with complete field labels.
  • aria-rowcount describes the complete result count, mounted rows use global aria-rowindex values, and spacer regions do not enter the accessible row model.
  • The scroll region is keyboard-focusable. Home, End, Page Up, and Page Down move to the beginning, end, or neighboring viewport, while static instructions tell assistive technology the full row count and available keys.
  • When the error summary targets an unmounted row, the runtime first scrolls to and mounts it, then opens its row dialog and focuses the error field.
  • Filter and sort changes reset the window to the beginning of the results so it cannot retain a scroll position beyond the new result range.
  • New rows and bulk appends still produce a complete controlled array. If the current filter allows a target row to appear, the grid scrolls to its new position automatically.

viewportHeight controls the embedding panel's vertical footprint. A compact settings panel typically uses 320–480px; full-page data entry can use 560–720px. overscan changes only the scroll buffer and should not grow with the total array length.

Designer

The “Data grid” preset creates Name, Quantity, and Notes columns. The properties panel provides:

  • Switching between card rows and a data grid.
  • Switching between inline and dialog draft editing.
  • A virtual-scrolling switch and scroll-region height.
  • A multi-row selection switch.
  • A bulk-paste switch.
  • A fill-down switch.
  • A single-column sorting switch.
  • A cross-column filtering switch.
  • Renaming, replacing the component, reordering, duplicating, and removing standard field columns.

Switching back to card rows removes the dataGrid policy so grid-only configuration is not left on another layout. Preview uses the real Renderer, with the same value shape as an embedded host.

Responsive behavior and accessibility

Desktop retains a native table, column headings, row headings with scope="row", and explicit row-action names. Virtual grids add complete aria-rowcount and global aria-rowindex values. Row editing, paste, and fill use native <dialog> elements that trap focus while open, focus the message on error, and restore focus to the trigger when closed. Paste preview uses a semantic table; column order, input labels, source value, and target count all have explicit names.

At Form container widths of 520px or less, each row becomes a card with field labels. The decision uses the embedding container—not the browser viewport—so sidebar, node-panel, and dialog hosts behave consistently. Dialog fields follow 12-column width settings and collapse to one column in narrow containers.

Current boundaries

The current implementation covers inline editing, dialog drafts, single-column sorting, cross-column filtering, multi-row selection, confirmed deletion, TSV append, fill down, dynamic-row-height virtualization, stable row identities, and item-count boundaries. Reading or exporting CSV files, authorization, and remote batch processing are host capabilities and cannot be enabled implicitly through undeclared properties.

Questionnaire matrices with shared options use matrix-single or matrix-multiple, not the data-grid layout.