Skip to content

@jsvision/datagrid / EditableDataGrid

Class: EditableDataGrid<T>

Documented in: Data Grid

Defined in: datagrid/src/grid.ts:400

An editable, self-drawing data grid over a typed column model and a GridDataSource. It is a Group, so a plain instance is not itself a focus target — focus its EditableDataGrid.rows body renderer to move the cursor and edit.

Example

ts
import { at, Group, createEventLoop, resolveCapabilities, signal } from '@jsvision/ui';
import { column, fromRows, EditableDataGrid } from '@jsvision/datagrid';

interface Row { id: number; name: string; }
const rows = signal<Row[]>([{ id: 1, name: 'Ada' }, { id: 2, name: 'Bo' }]);
const columns = [
  column({
    id: 'name', title: 'Name',
    value: (r: Row) => r.name,
    parse: (t) => t,                 // editable: parse + set present
    set: (r, v) => { r.name = v; },
  }),
];

const grid = at(
  new EditableDataGrid<Row>({
    columns,
    source: fromRows(rows, { rowKey: (r) => r.id }),
    onCommit: (c) => String(c.value).trim().length > 0, // veto an empty name
  }),
  0,
  0,
  20,
  6,
);

const root = new Group();
root.add(grid);
const caps = resolveCapabilities().profile;
const loop = createEventLoop({ width: 20, height: 6 }, { caps });
loop.mount(root);
loop.focusView(grid.rows); // focus the body: arrow keys move, F2/Enter/type edits, Enter commits

Extends

  • Group

Type Parameters

T

T

Constructors

Constructor

new EditableDataGrid<T>(opts): EditableDataGrid<T>

Defined in: datagrid/src/grid.ts:525

Parameters

opts

EditableDataGridOptions<T>

The columns, the source, optional zebra striping, and an optional onCommit veto sink.

Returns

EditableDataGrid<T>

Overrides

Group.constructor

Properties

background?

optional background?: keyof Theme

Defined in: ui/dist/view/group.d.ts:55

Optional background theme role filled before children compose, so overlap never leaks cells.

Inherited from

Group.background


bounds

bounds: Rect

Defined in: ui/dist/view/view.d.ts:61

Parent-relative integer rect; written by the layout pass — read it in draw/hit-testing.

Inherited from

Group.bounds


castsShadow

castsShadow: boolean

Defined in: ui/dist/view/view.d.ts:91

When true, the renderer paints a drop shadow on the cells just below and to the right of this view, in paint order (a later sibling's shadow falls over an earlier one). Default false. The Desktop sets it per window.

Inherited from

Group.castsShadow


centered

centered: boolean

Defined in: ui/dist/view/view.d.ts:98

When true, the layout pass recentres this view within its parent after layout — origin = (parent - self) / 2 on both axes. Intended for absolutely-placed views (a modal dialog, a message box) whose size is fixed and whose origin would otherwise be placed by the caller. Default false; Dialog sets it when centered.

Inherited from

Group.centered


children

readonly children: View[]

Defined in: ui/dist/view/group.d.ts:53

Ordered children; array order is paint order (back-to-front).

Inherited from

Group.children


focusable

focusable: boolean

Defined in: ui/dist/view/view.d.ts:104

Whether this view can receive keyboard focus. Effective focusability also requires the view to be visible and enabled with no hidden/disabled ancestor. Default false; the focus manager drives the state.focused flag.

Inherited from

Group.focusable


grabsFocus

grabsFocus: boolean

Defined in: ui/dist/view/view.d.ts:113

Whether a mouse-down that hits this view moves keyboard focus to it. Default true — the usual click-to-focus. Set false for a control that should act on a click without stealing focus from whatever is focused (e.g. a dialog Cancel button, or a toolbar/stepper button): the click still dispatches, but the previously-focused view keeps focus, so it never fires a focus-leave side effect such as a field's blur-validation. Independent of focusable — a grabsFocus: false view can still be reached by Tab and activated by Space.

Inherited from

Group.grabsFocus


i18n

readonly i18n: I18n

Defined in: datagrid/src/grid.ts:499

Translation service used by package-owned grid surfaces.


layout

readonly layout: Readonly<LayoutProps>

Defined in: ui/dist/view/view.d.ts:83

Layout props for this view (direction, size, padding, absolute placement, …) — read-only.

Change them with setLayout, which is the only writer. The field and every prop on it are closed, so neither view.layout = {…} nor view.layout.rect = {…} compiles, and neither does editing a solved rect a field at a time (view.layout.rect.x = 5). That is deliberate: a wholesale assignment silently drops every prop it omits and never reflows, and an in-place prop write reflows only if you remember to ask.

Read it freely — this is where a view's solved intent lives, and layout.rect is how an absolutely-placed view reports where it was put.

Inherited from

Group.layout


overlay

readonly overlay: Group

Defined in: datagrid/src/grid.ts:430

The absolute overlay host on top of the grid — the cell editor mounts into it while editing.


popupOverlay

readonly popupOverlay: Group

Defined in: datagrid/src/grid.ts:436

A second absolute overlay, above overlay, that hosts a filter popup opened from the header funnel. Kept separate from the editor overlay so a filter popup and an open cell editor never collide, and hit-transparent while empty (see EditorOverlay).


postProcess

postProcess: boolean

Defined in: ui/dist/view/view.d.ts:117

Take part in the post-process sweep (after the focused view sees the event).

Inherited from

Group.postProcess


preProcess

preProcess: boolean

Defined in: ui/dist/view/view.d.ts:115

Take part in the pre-process sweep (root→down, before the focused view sees the event).

Inherited from

Group.preProcess


state

readonly state: ViewState

Defined in: ui/dist/view/view.d.ts:70

Draw-against flags. The object reference is fixed; individual fields mutate (e.g. focused).

Writing visible or disabled changes only what the next paint would draw — it does not ask for that paint. Follow such a write with invalidate (or invalidateLayout, which a visibility flip needs, since layout omits hidden views). A development build warns when a write goes unaccounted for.

Inherited from

Group.state

Accessors

rows

Get Signature

get rows(): EditableGridRows<T>

Defined in: datagrid/src/grid.ts:406

The focusable body renderer — focus this (a plain Group is not a focus target). In a frozen grid this is the center (horizontally-scrolling) panel; every panel shares one row cursor, so focusing it and moving the cursor drives all panels together.

Returns

EditableGridRows<T>

Methods

accelerators()

accelerators(): readonly string[]

Defined in: ui/dist/view/view.d.ts:131

The Alt+hotkey accelerator characters (lowercase) this view claims in its focus scope, for duplicate-accelerator detection. The base returns none; accelerator-bearing widgets (Button/Label/CheckGroup/RadioGroup) override it to report their ~X~ hotkey(s).

Returns

readonly string[]

The claimed accelerator chars, or an empty list when the view claims none.

Inherited from

Group.accelerators


activeMessage()

activeMessage(): string | null

Defined in: datagrid/src/grid.ts:979

The active validation/veto message shown in the grid's message band, or null when there is none. Reactive — it follows the most recent blocked commit or row-gate message and clears once the offending cell/row is valid again.

Returns

string | null

The active message string, or null.


add()

add(child): void

Defined in: ui/dist/view/group.d.ts:72

Add a child, appending it on top (later in the array draws in front). If this group is already mounted, the child mounts immediately — its scope nested under this group's — and a reflow is scheduled for the new layout; otherwise the child mounts when this group itself mounts.

Parameters

child

View

The view to append.

Returns

void

Inherited from

Group.add


addDynamic()

addDynamic(build): void

Defined in: ui/dist/view/group.d.ts:104

Add children reactively from a Show/For accessor, so the set of children updates itself as signals change. When the group is mounted, an effect reads the accessor, mounts any newly produced views, unmounts any that disappeared (firing their onCleanup), and schedules a reflow on change. Pass a factory that builds the combinator inside itself, not an already-built accessor — this lets the group own and dispose the combinator's reactive nodes on unmount.

Parameters

build

DynamicBuilder

A factory that constructs the combinator, e.g. () => Show(cond, then) or () => For(each, key, render).

Returns

void

Example

ts
import { Group, View, signal, Show, type DrawContext } from '@jsvision/ui';

class Panel extends View {
  draw(ctx: DrawContext) {
    ctx.fill(' ', ctx.color('window'));
  }
}

const open = signal(false);
const group = new Group();
group.addDynamic(() => Show(() => open(), () => new Panel())); // Panel appears when `open` is true

Inherited from

Group.addDynamic


addSort()

addSort(columnId, dir?): void

Defined in: datagrid/src/grid.ts:1012

Add or update a secondary sort key (a Ctrl+click / the multi-key API). With an explicit dir, sets-or-appends that key. Without dir: a new column is appended ascending; an existing key cycles its direction in place (asc → desc → removed), keeping its priority. An unknown columnId is ignored.

Parameters

columnId

string

The column to add or update.

dir?

SortDir

Optional explicit direction; omit to append-ascending / cycle.

Returns

void


applyVariant()

applyVariant(variant): void

Defined in: datagrid/src/grid.ts:1457

Restore a layout GridVariant saved with saveVariant. Reproduces, in a fixed sequence, the column order, visibility, width overrides, freeze, sort, and filter. An id the grid no longer has is skipped (never thrown); a column the grid has that the variant does not name keeps its current state and is appended after the named columns. A filter on a column the variant hides is retained and reappears when the column is shown.

Parameters

variant

GridVariant

The variant to apply.

Returns

void

Example

ts
import { signal } from '@jsvision/ui';
import { column, fromRows, EditableDataGrid } from '@jsvision/datagrid';

interface Row { id: number; name: string }
const rows = signal<Row[]>([{ id: 1, name: 'Ada' }]);
const columns = [column({ id: 'name', title: 'Name', value: (r: Row) => r.name })];
const grid = new EditableDataGrid<Row>({ columns, source: fromRows(rows, { rowKey: (r) => r.id }) });

const mine = grid.saveVariant('mine'); // persist this somewhere…
grid.applyVariant(mine); // reproduces order / width / visibility / freeze / sort / filter

autoFitAll()

autoFitAll(): void

Defined in: datagrid/src/grid.ts:1507

Auto-fit every visible column (see EditableDataGrid.autoFitColumn).

Returns

void


autoFitColumn()

autoFitColumn(id): void

Defined in: datagrid/src/grid.ts:1493

Size a column to its widest visible cell (its title or any displayed value), floored to the column's minWidth and bounded by its maxWidth (or a generous default). Stores the result as an explicit width override. An unknown id is ignored.

Parameters

id

string

The column id.

Returns

void


bind()

bind<T>(reader, apply?, opts?): void

Defined in: ui/dist/view/view.d.ts:277

Bind a reactive value to a redraw. Creates an effect (owned by this view's scope) that reads reader() — subscribing to whatever signals it touches — runs the optional apply(value), then requests a frame: a repaint by default, or a reflow when { relayout: true }. It re-runs automatically whenever those signals change, and is disposed when the view unmounts.

Call it from onMount, not the constructor — the view's scope only exists once mounted, so a pre-mount bind throws rather than silently dropping the binding.

Type Parameters

T

T

Parameters

reader

() => T

Reads the reactive source; the signals it reads become dependencies.

apply?

(v) => void

Optional: apply the read value to the widget (e.g. store it in a field).

opts?

Pass { relayout: true } when the change affects layout, so it reflows instead of just repainting.

relayout?

boolean

Returns

void

Example

ts
import { View, signal, type DrawContext } from '@jsvision/ui';

const count = signal(0);

class StatusLine extends View {
  draw(ctx: DrawContext): void {
    ctx.text(0, 0, `${count()} pending`, ctx.color('statusBar'));
  }
}

const status = new StatusLine();
// In onMount, not the constructor: bind() needs the view's scope, which only exists once mounted.
status.onMount(() => {
  status.bind(() => count()); // repaint the status line whenever `count` changes
});

Inherited from

Group.bind


clearColumnWidth()

clearColumnWidth(id): void

Defined in: datagrid/src/grid.ts:1338

Remove a column's explicit width override, returning it to its auto/declared width. An unknown id is ignored (no throw). The reactive counterpart of setColumnWidth without the clamp/set.

Parameters

id

string

The column id.

Returns

void

Example

ts
import { signal } from '@jsvision/ui';
import { column, fromRows, EditableDataGrid } from '@jsvision/datagrid';

interface Row { id: number; amount: number }
const rows = signal<Row[]>([{ id: 1, amount: 42 }]);
const columns = [column({ id: 'amount', title: 'Amount', value: (r: Row) => r.amount })];
const grid = new EditableDataGrid<Row>({ columns, source: fromRows(rows, { rowKey: (r) => r.id }) });

grid.setColumnWidth('amount', 20);
grid.clearColumnWidth('amount'); // amount returns to its auto/declared width

clearFilter()

clearFilter(columnId?): void

Defined in: datagrid/src/grid.ts:1060

Clear one column's filter, or — with no argument — every filter.

Parameters

columnId?

string

The column whose filter to clear; omit to clear all filters.

Returns

void


clearSelection()

clearSelection(): void

Defined in: datagrid/src/grid.ts:1953

Clear the row selection and its range anchor.

Returns

void


clearSort()

clearSort(): void

Defined in: datagrid/src/grid.ts:1028

Clear all sort keys — the client path restores source order; a push-down source gets setSort([]).

Returns

void


columnOrder()

columnOrder(): string[]

Defined in: datagrid/src/grid.ts:1189

The visible column order (the full order minus hidden columns). Reactive — reading it inside an effect re-runs when the order, visibility, or a reorder changes.

Returns

string[]

The visible column ids, in order.


columns()

columns(): readonly GridColumnInfo[]

Defined in: datagrid/src/grid.ts:1216

The full column list — every column (hidden included), in full column order — as resolved GridColumnInfo metadata (id, title, visibility, resolved freeze side, resolved width). Reactive: reading it inside an effect re-runs on any order / visibility / freeze / width change. frozen reports the resolved partition, so an over-pinned column reads 'none' (matching frozen). Independently useful for an app-built column UI.

Returns

readonly GridColumnInfo[]

One info per column, in full column order.

Example

ts
import { signal } from '@jsvision/ui';
import { column, fromRows, EditableDataGrid } from '@jsvision/datagrid';

interface Row { id: number; name: string }
const rows = signal<Row[]>([{ id: 1, name: 'Ada' }]);
const columns = [column({ id: 'name', title: 'Name', value: (r: Row) => r.name })];
const grid = new EditableDataGrid<Row>({ columns, source: fromRows(rows, { rowKey: (r) => r.id }) });

for (const c of grid.columns()) {
  // { id, title, visible, frozen: 'left' | 'right' | 'none', width }
}

columnWidth()

columnWidth(id): number

Defined in: datagrid/src/grid.ts:1300

The resolved width of a column in cells: an explicit override if set, else the column's declared fixed width, else its measured auto width, else its title width. Reactive.

Parameters

id

string

The column id.

Returns

number

The resolved width in cells (0 for an unknown id).


defaultColumnLayout()

defaultColumnLayout(): readonly GridColumnInfo[]

Defined in: datagrid/src/grid.ts:1246

The construction-time column baseline — every column visible, in construction (declaration) order, with no freeze and no width overrides. The layout a personalization Reset restores to. Its width is the resolved declared/auto width for display only; a Reset restores no override (it never copies the number back).

Returns

readonly GridColumnInfo[]

One baseline GridColumnInfo per column, in construction order.

Example

ts
import { signal } from '@jsvision/ui';
import { column, fromRows, EditableDataGrid } from '@jsvision/datagrid';

interface Row { id: number; name: string }
const rows = signal<Row[]>([{ id: 1, name: 'Ada' }]);
const columns = [column({ id: 'name', title: 'Name', value: (r: Row) => r.name })];
const grid = new EditableDataGrid<Row>({ columns, source: fromRows(rows, { rowKey: (r) => r.id }) });

const base = grid.defaultColumnLayout(); // all visible, construction order, no freeze/overrides

deleteRows()

deleteRows(keys): void

Defined in: datagrid/src/grid.ts:2004

Remove rows by key through the data-source mutation seam, then prune those keys from the selection (a deleted row never stays selected). A no-op on the source when it is read-only (exposes no remove); the selection is still pruned either way. Keys not present are ignored.

Parameters

keys

readonly Key[]

The row keys to remove.

Returns

void

Example

ts
import { signal } from '@jsvision/ui';
import { column, fromRows, EditableDataGrid } from '@jsvision/datagrid';

interface Row { id: number; name: string }
const rows = signal<Row[]>([{ id: 10, name: 'Ada' }, { id: 11, name: 'Bo' }]);
const columns = [column({ id: 'name', title: 'Name', value: (r: Row) => r.name })];
const grid = new EditableDataGrid<Row>({ columns, source: fromRows(rows, { rowKey: (r) => r.id }) });

grid.deleteRows([10, 11]); // removed from the source and de-selected

derived()

protected derived<T>(fn): () => T

Defined in: ui/dist/view/view.d.ts:299

Create a stable derived accessor owned by this view's scope. The returned () => T keeps the same identity for the life of the view, so it is safe to build in the constructor and hand to child views before this view mounts. The backing computed is created lazily under the view's own scope, so it is always owned and disposed at unmount — unlike a bare computed() in the constructor, which would run before any scope exists, leak, and warn.

Reads behave sensibly across the lifecycle:

  • Before mount: evaluates fn() directly (correct current value, nothing persisted). Good for a pre-mount natural-size measure.
  • After mount: builds and memoizes a computed(fn) under the view's scope.
  • After an unmount→remount: the memo is keyed to the scope it was built under, so a remounted view (which gets a fresh scope) re-derives under the new scope instead of returning the previous mount's disposed, now-frozen computed — keeping a Show/For-remounted widget reactive.

Type Parameters

T

T

Parameters

fn

() => T

The derivation (pure; the signals it reads become the computed's dependencies).

Returns

A stable accessor; call it to read the derived value.

() => T

Inherited from

Group.derived


desiredCaret()

desiredCaret(): Point | null

Defined in: ui/dist/view/view.d.ts:211

Where this view wants the hardware text cursor, in view-local cells, or null for no cursor (the default — most views never show one). A focused text input overrides this to place the caret at the edit position; the event loop reads it after each frame, converts it to an absolute cell, and moves the terminal cursor there.

Returns

Point | null

The view-local caret point (0-based, relative to this view's origin), or null.

Inherited from

Group.desiredCaret


displayedRows()

displayedRows(): readonly T[]

Defined in: datagrid/src/grid.ts:1115

The filtered + sorted rows currently displayed (reactive). This is the loaded/in-memory set the footer aggregates fold over; reading it inside an effect re-runs when a row is edited, inserted, deleted, sorted, or filtered.

Windowed sources: for a windowed source (one exposing ensureRange) this returns a length-correct lazy view, not a materialized array. Only .length and integer indexing are supported — every whole-array operation (.map/.find/.filter/spread/for..of) throws. Read .length for the count and [i] for a row (undefined at an unloaded index); do not assume a dense T[]. An eager source returns an ordinary dense array.

Returns

readonly T[]

The displayed rows, in display order — a dense array (eager) or a lazy view (windowed).


draw()

draw(ctx): void

Defined in: ui/dist/view/group.d.ts:64

Fill the background role (if set) across the group rect; children are composed by the render root.

Parameters

ctx

DrawContext

Returns

void

Inherited from

Group.draw


duplicateRow()

duplicateRow(key): void

Defined in: datagrid/src/grid.ts:2039

Insert a structured clone of the row identified by key, adjacent to it, carrying a fresh key from the assignKey option. A no-op (with a dev warning) when assignKey is not configured — it never inserts a key-colliding row. Also a no-op when key is absent from the display, or when the row is not structured-cloneable (holds a function, a class instance, etc.) — the clone is attempted inside a guard, so a non-cloneable row warns instead of throwing and never leaves a partial insert.

Parameters

key

Key

The key of the row to duplicate.

Returns

void

Example

ts
import { signal } from '@jsvision/ui';
import { column, fromRows, EditableDataGrid } from '@jsvision/datagrid';

interface Row { id: number; name: string }
const rows = signal<Row[]>([{ id: 10, name: 'Ada' }]);
const columns = [column({ id: 'name', title: 'Name', value: (r: Row) => r.name })];

// duplicateRow only inserts when assignKey is configured to mint the fresh key:
let nextId = 1000;
const grid = new EditableDataGrid<Row>({
  columns,
  source: fromRows(rows, { rowKey: (r) => r.id }),
  assignKey: (clone) => ({ ...clone, id: nextId++ }),
});

grid.duplicateRow(10); // a clone with a fresh id is inserted right after row 10

exportView()

exportView(format): string

Defined in: datagrid/src/grid.ts:1153

Serialize the current view — the visible columns in display order, their formatted values, and the filtered + sorted rows — to CSV, HTML, JSON, or TSV. CSV/TSV are RFC-4180 (records CRLF-joined; a field with the delimiter, a ", or a newline is double-quoted, embedded quotes doubled) with spreadsheet formula-injection escaping (a cell that begins with = + - @ — the accepted tradeoff: a negative like -5 becomes '-5); HTML is a standalone document with a markup-escaped <table>; JSON is an array of objects holding the raw values keyed by column id. Hidden and synthetic (checkbox / row-number) columns are excluded; the grid never chooses a destination — it returns a string the caller writes to a file or clipboard.

Eager sources only. This serializes the resident displayed rows. On a windowed source (one exposing ensureRange) the displayed rows are a lazy window, not a full array, so this throws — a full-view export over a windowed source is a separate mechanism.

Parameters

format

ExportFormat

The target format ('csv' | 'html' | 'json' | 'tsv').

Returns

string

The serialized document as a string.

Throws

If the grid is over a windowed source.

Example

ts
import { signal } from '@jsvision/ui';
import { column, fromRows, EditableDataGrid } from '@jsvision/datagrid';

interface Row { id: number; name: string; total: number }
const rows = signal<Row[]>([{ id: 1, name: 'Ann', total: 10 }]);
const columns = [
  column({ id: 'name', title: 'Name', value: (r: Row) => r.name }),
  column({ id: 'total', title: 'Total', value: (r: Row) => r.total }),
];
const grid = new EditableDataGrid<Row>({ columns, source: fromRows(rows, { rowKey: (r) => r.id }) });

const csv = grid.exportView('csv');   // 'Name,Total\r\nAnn,10\r\n…' — RFC-4180, formula-escaped
const json = grid.exportView('json'); // [{ name: 'Ann', total: 10 }, …] — raw values, keyed by id

filteredCount()

filteredCount(): number

Defined in: datagrid/src/grid.ts:1087

The number of rows passing all active filters. Reactive. On the client path this is the filtered row count; on an eager push-down source source.length() already reflects the filtered set, so it equals totalCount() there (a documented v1 limitation until the windowing seam exposes a separate pre-filter total).

Returns

number

The count of rows currently shown — render "N of M" from this and totalCount.


filterModel()

filterModel(): FilterModel

Defined in: datagrid/src/grid.ts:1075

The current filter model. Reactive — reading it inside an effect re-runs when the filters change.

Returns

FilterModel

The active per-column filters (empty when nothing is filtered).


focusedKey()

focusedKey(): Key | undefined

Defined in: datagrid/src/grid.ts:1812

The rowKey of the focused record (reactive), or undefined when the grid is empty. Shares the same clamped cursor as focusedRow.

Returns

Key | undefined

The focused row's key, or undefined on an empty grid.


focusedRow()

focusedRow(): T | undefined

Defined in: datagrid/src/grid.ts:1799

The record under the row cursor (reactive), or undefined when the grid is empty. Re-anchored by rowKey after a sort/filter, so it follows the same record even as its display index moves — the natural binding target for a master-detail link. The cursor is clamped into range, so a transiently- stale cursor never returns undefined while rows exist.

Returns

T | undefined

The focused record, or undefined on an empty grid.


focusSignal()

focusSignal(): Signal<void>

Defined in: ui/dist/view/view.d.ts:168

Subscribe to this view's focus changes. Reading the returned signal inside a bind/effect re-runs that effect whenever this view gains or loses focus — including from another view (e.g. a Label repainting when the control it labels is focused). The signal notifies on every poke even without a value change. Lazy: the backing signal is created on first call.

Returns

Signal<void>

A signal that ticks whenever this view gains or loses focus.

Example

ts
import { View, Button, type DrawContext } from '@jsvision/ui';

// A caption that highlights while the control it labels holds focus.
class Caption extends View {
  constructor(
    private readonly text: string,
    private readonly target: View,
  ) {
    super();
    // Reading the target's focus signal inside bind() ties this view's repaint to the target's
    // focus flips — a view can observe focus it does not own.
    this.onMount(() => this.bind(() => this.target.focusSignal()()));
  }

  draw(ctx: DrawContext): void {
    ctx.text(0, 0, this.text, ctx.color(this.target.state.focused ? 'labelSelected' : 'label'));
  }
}

const ok = new Button('~O~K');
const caption = new Caption('Confirm:', ok);

Inherited from

Group.focusSignal


frozen()

frozen(): object

Defined in: datagrid/src/grid.ts:1366

The resolved frozen partition: which visible columns are pinned left and right (over-pinned columns are pushed back to the center, so this reflects what actually renders frozen). Reactive.

Returns

object

The left- and right-pinned column ids, in order.

left

left: string[]

right: string[]


insertRow()

insertRow(row, at?): void

Defined in: datagrid/src/grid.ts:1980

Insert a row through the data-source mutation seam. at is a source-array index (append when omitted). A no-op when the source is read-only (exposes no insert) — the grid never persists on its own. The row must already carry its rowKey (the caller owns key generation). With an active client sort the row re-sorts to its value-determined display position on the next derive; a push-down source owns its own ordering.

Parameters

row

T

The row to insert (already carrying its rowKey).

at?

number

The source index to splice at; appended when omitted.

Returns

void

Example

ts
import { signal } from '@jsvision/ui';
import { column, fromRows, EditableDataGrid } from '@jsvision/datagrid';

interface Row { id: number; name: string }
const rows = signal<Row[]>([{ id: 1, name: 'Ada' }]);
const columns = [column({ id: 'name', title: 'Name', value: (r: Row) => r.name })];
const grid = new EditableDataGrid<Row>({ columns, source: fromRows(rows, { rowKey: (r) => r.id }) });

grid.insertRow({ id: 10, name: 'New' });    // appended
grid.insertRow({ id: 11, name: 'Top' }, 0); // spliced at the front of the source

invalidate()

invalidate(): void

Defined in: ui/dist/view/view.d.ts:213

Request a repaint of this view. A no-op before the view is mounted (the first frame paints everything).

Returns

void

Inherited from

Group.invalidate


invalidateLayout()

invalidateLayout(): void

Defined in: ui/dist/view/view.d.ts:215

Request a reflow (re-run layout, then repaint). Use this when a change affects size/position, not just pixels.

Returns

void

Inherited from

Group.invalidateLayout


isBodyFocused()

isBodyFocused(): boolean

Defined in: datagrid/src/grid.ts:1817

Whether the grid body currently holds keyboard focus (used by the Tab-navigation helper).

Returns

boolean


isDirty()

isDirty(rowKey, columnId): boolean

Defined in: datagrid/src/grid.ts:934

Whether a specific cell has an unresolved (pending) commit. Reactive — reading it inside an effect re-runs when the cell's pending state changes.

Parameters

rowKey

string | number

The edited row's stable key.

columnId

string

The edited column id.

Returns

boolean

true while the cell's commit is in flight, false once it resolves.


isEditing()

isEditing(): boolean

Defined in: datagrid/src/grid.ts:1826

Whether an in-cell editor is currently open on this grid. Note that while editing the keyboard focus is on the editor overlay, not the body — so the Tab-navigation helper treats a grid as its active target when its body is focused OR it is editing (a Tab then commits and advances by cell).

Returns

boolean


isGridDirty()

isGridDirty(): boolean

Defined in: datagrid/src/grid.ts:955

Whether any cell anywhere in the grid has a pending commit. Reactive (see EditableDataGrid.isDirty).

Returns

boolean

true if at least one cell is pending.


isInvalid()

isInvalid(rowKey, columnId): boolean

Defined in: datagrid/src/grid.ts:968

Whether a specific cell is marked invalid — its last edit was blocked (a failed validate, an unparseable value, or a vetoed change) and never took. Reactive (see EditableDataGrid.isDirty). Clears on a successful re-commit or an abandoned edit.

Parameters

rowKey

string | number

The cell's row key.

columnId

string

The cell's column id.

Returns

boolean

true while the cell is marked invalid.


isRowDirty()

isRowDirty(rowKey): boolean

Defined in: datagrid/src/grid.ts:944

Whether any cell in a row has a pending commit. Reactive (see EditableDataGrid.isDirty).

Parameters

rowKey

string | number

The row's stable key.

Returns

boolean

true if at least one cell in the row is pending.


measure()?

optional measure(available): Size2D

Defined in: ui/dist/view/view.d.ts:85

Optional intrinsic-size hook for auto sizing — return the size this view wants for available.

Parameters

available

Size2D

Returns

Size2D

Inherited from

Group.measure


nextCell()

nextCell(): Promise<"moved" | "exit">

Defined in: datagrid/src/grid.ts:1856

Advance the cell cursor one step forward (left-to-right, top-to-bottom), wrapping at a row end. If an editor is open it is committed first; a vetoed commit keeps the editor open and returns 'moved' WITHOUT advancing — 'moved' means "the grid handled Tab; do not hand focus away", not "the cursor advanced". At the last cell of the last row it returns 'exit' (the caller moves to the next widget).

Returns

Promise<"moved" | "exit">

'moved' when the grid handled the step (advanced, or held an open editor), else 'exit'.

Example

ts
import { at, Group, createEventLoop, resolveCapabilities, signal } from '@jsvision/ui';
import { column, fromRows, EditableDataGrid } from '@jsvision/datagrid';

interface Row { id: number; name: string }
const rows = signal<Row[]>([{ id: 1, name: 'Ada' }]);
const columns = [column({ id: 'name', title: 'Name', value: (r: Row) => r.name })];
const grid = at(new EditableDataGrid<Row>({ columns, source: fromRows(rows, { rowKey: (r) => r.id }) }), 0, 0, 20, 6);

const root = new Group();
root.add(grid);
const loop = createEventLoop({ width: 20, height: 6 }, { caps: resolveCapabilities().profile });
loop.mount(root);

const r = await grid.nextCell();
if (r === 'exit') loop.focusNext(); // at the grid edge → leave to the next widget

onCleanup()

onCleanup(fn): void

Defined in: ui/dist/view/view.d.ts:315

Register a teardown callback that runs once when this view unmounts. Requires a mounted view — so call it from within onMount. Use it to release anything the view acquired (a timer, an external subscription).

Parameters

fn

() => void

The teardown callback.

Returns

void

Inherited from

Group.onCleanup


onEvent()

onEvent(_ev): void

Defined in: ui/dist/view/view.d.ts:195

Handle an input event — a no-op by default. The event loop wraps each event in a DispatchEvent envelope and routes it to the views; override this to react to keys/mouse and set ev.handled = true to consume the event so it does not propagate further.

Parameters

_ev

DispatchEvent

The dispatch envelope (the wrapped event plus the mutable handled flag).

Returns

void

Inherited from

Group.onEvent


onMount()

onMount(fn): void

Defined in: ui/dist/view/view.d.ts:307

Register a callback to run once when the view becomes live (after its first layout gives it bounds). This is where to call bind, since the view's reactive scope exists by then. Registering after the view is already live runs the callback immediately.

Parameters

fn

() => void

Post-mount setup.

Returns

void

Inherited from

Group.onMount


prevCell()

prevCell(): Promise<"moved" | "exit">

Defined in: datagrid/src/grid.ts:1885

Retreat the cell cursor one step (the mirror of nextCell), wrapping at column 0. An open edit is committed first; a vetoed commit keeps the editor open and returns 'moved' without moving. At (0, 0) it returns 'exit'.

Returns

Promise<"moved" | "exit">

'moved' when the grid handled the step, else 'exit' at the grid start.

Example

ts
import { at, Group, createEventLoop, resolveCapabilities, signal } from '@jsvision/ui';
import { column, fromRows, EditableDataGrid } from '@jsvision/datagrid';

interface Row { id: number; name: string }
const rows = signal<Row[]>([{ id: 1, name: 'Ada' }]);
const columns = [column({ id: 'name', title: 'Name', value: (r: Row) => r.name })];
const grid = at(new EditableDataGrid<Row>({ columns, source: fromRows(rows, { rowKey: (r) => r.id }) }), 0, 0, 20, 6);

const root = new Group();
root.add(grid);
const loop = createEventLoop({ width: 20, height: 6 }, { caps: resolveCapabilities().profile });
loop.mount(root);

const r = await grid.prevCell();
if (r === 'exit') loop.focusPrev();

remove()

remove(child): void

Defined in: ui/dist/view/group.d.ts:80

Remove a child: dispose its scope (recursively disposing its descendants and running their onCleanup), detach it, and schedule a reflow. Removing a non-child (or removing twice) is a safe no-op.

Parameters

child

View

The view to remove.

Returns

void

Inherited from

Group.remove


saveVariant()

saveVariant(name): GridVariant

Defined in: datagrid/src/grid.ts:1423

Capture the grid's full column layout — the column order, per-column width overrides and visibility, the frozen partition, the sort model, and the filter model — as a serializable GridVariant the caller persists. The grid stores nothing itself; pair it with applyVariant to restore.

Parameters

name

string

A caller-facing label for the variant.

Returns

GridVariant

The serializable layout snapshot (plain JSON — no functions).

Example

ts
import { signal } from '@jsvision/ui';
import { column, fromRows, EditableDataGrid } from '@jsvision/datagrid';

interface Row { id: number; name: string }
const rows = signal<Row[]>([{ id: 1, name: 'Ada' }]);
const columns = [column({ id: 'name', title: 'Name', value: (r: Row) => r.name })];
const grid = new EditableDataGrid<Row>({ columns, source: fromRows(rows, { rowKey: (r) => r.id }) });

const mine = grid.saveVariant('mine'); // { name, columns, freeze, sort, filter } — persist it

selectAllDisplayed()

selectAllDisplayed(): void

Defined in: datagrid/src/grid.ts:1948

Select every displayed (filtered/sorted) row — the header checkbox's select-all target.

Returns

void


selectByClick()?

optional selectByClick(): void

Defined in: ui/dist/view/view.d.ts:202

Optional "select + raise on click" hook. Left undefined on the base, so a plain view is not a select/raise target. A container that owns z-order (a Window) overrides it to select and raise itself. The hit-test invokes the first ancestor that defines this — before delivering the mouse-down — so a click always raises the window even if the interior also consumes the click.

Returns

void

Inherited from

Group.selectByClick


selectedKeys()

selectedKeys(): ReadonlySet<Key>

Defined in: datagrid/src/grid.ts:1787

The current row selection, keyed by rowKey. Reactive — reading it inside an effect re-runs when the selection changes. The set survives re-sort/re-filter (the keys are stable); a delete prunes it.

Returns

ReadonlySet<Key>

The selected row keys (empty when nothing is selected).

Example

ts
import { signal } from '@jsvision/ui';
import { column, fromRows, EditableDataGrid } from '@jsvision/datagrid';

interface Row { id: number; name: string }
const rows = signal<Row[]>([{ id: 1, name: 'Ada' }, { id: 3, name: 'Bo' }]);
const columns = [column({ id: 'name', title: 'Name', value: (r: Row) => r.name })];
const source = fromRows(rows, { rowKey: (r) => r.id });

const grid = new EditableDataGrid<Row>({ columns, source }); // default 'multi'
grid.selectRow(1); // select the row whose rowKey is 1
grid.toggleRow(3); // add row 3 → { 1, 3 }
[...grid.selectedKeys()]; // [1, 3]
grid.clearSelection(); // {}

selectRange()

selectRange(toKey): void

Defined in: datagrid/src/grid.ts:1943

Extend the selection to toKey as a contiguous display-order range from the current anchor (or, when no anchor is set, from the focused row). A no-op on an empty grid.

Parameters

toKey

Key

The far end of the range, in the current display order.

Returns

void


selectRow()

selectRow(key): void

Defined in: datagrid/src/grid.ts:1923

Select exactly key, replacing any prior selection, and make it the range anchor.

Parameters

key

Key

The row key to select.

Returns

void


setColumnOrder()

setColumnOrder(ids): void

Defined in: datagrid/src/grid.ts:1261

Reorder the visible columns. Accepts a permutation of the currently-visible ids; the new order is spliced back into the full order so hidden columns keep their anchor slots. A non- permutation (unknown id, wrong length, duplicate) is ignored.

Parameters

ids

string[]

The visible ids in their new order.

Returns

void


setColumnVisible()

setColumnVisible(id, visible): void

Defined in: datagrid/src/grid.ts:1352

Show or hide a column. A hidden column is omitted from the visible order/layout but stays addressable by id for sort/filter (its sort/filter state is retained and reappears when shown). An unknown id is ignored.

Parameters

id

string

The column id.

visible

boolean

false to hide, true to show.

Returns

void


setColumnWidth()

setColumnWidth(id, w): void

Defined in: datagrid/src/grid.ts:1311

Set a column's explicit width, clamped to the column's [minWidth, maxWidth]. An unknown id is ignored. The override makes the column apportion as a fixed width.

Parameters

id

string

The column id.

w

number

The requested width in cells (clamped).

Returns

void


setFilter()

setFilter(columnId, filter): void

Defined in: datagrid/src/grid.ts:1048

Set (or replace) a column's filter. The quick-filter row and the popups both call this. An unknown columnId is ignored — never added to the model and never forwarded to a push-down setFilter.

Parameters

columnId

string

The column to filter.

filter

ColumnFilter

The filter condition to apply.

Returns

void


setFrozen()

setFrozen(left, right): void

Defined in: datagrid/src/grid.ts:1399

Re-pin the frozen columns at runtime. Sets the left and right frozen panels to the given ids (an unknown or hidden id is ignored by the partition); the panels rebuild reactively. The over-pin guard still applies — pinning wider than the viewport peels the innermost frozen column back to the scrolling center (and, when every column would be frozen, the freeze is dropped with one dev warning so the grid stays scrollable). Freeze is otherwise construction-only; this is what lets applyVariant restore it.

Parameters

left

string[]

Column ids to pin to the left panel, in order.

right

string[]

Column ids to pin to the right panel, in order.

Returns

void

Example

ts
import { signal } from '@jsvision/ui';
import { column, fromRows, EditableDataGrid } from '@jsvision/datagrid';

interface Row { id: number; name: string }
const rows = signal<Row[]>([{ id: 1, name: 'Ada' }]);
const columns = [
  column({ id: 'id', title: 'Id', value: (r: Row) => r.id }),
  column({ id: 'name', title: 'Name', value: (r: Row) => r.name }),
  column({ id: 'actions', title: '', value: () => '…' }),
];
const grid = new EditableDataGrid<Row>({ columns, source: fromRows(rows, { rowKey: (r) => r.id }) });

grid.setFrozen(['id'], ['actions']); // id pinned left, actions pinned right
grid.setFrozen([], []);              // clear all freezing

setLayout()

setLayout(patch): void

Defined in: ui/dist/view/view.d.ts:246

Change some of this view's layout props and request a reflow — the only way to write layout. Props the patch does not name are kept, and the reflow happens for you.

The merge is shallow, deliberately: size and rect are replaced whole rather than merged field-by-field. That is what makes a variant swap correct — going from {kind:'fixed',cells:1} to {kind:'fr',weight:1} must not leave a stale cells behind. The cost is that per-side padding cannot be patched one side at a time; pass the whole padding value.

Two behaviours worth knowing:

  • An explicit undefined resets that prop to its layout default. setLayout({ size: undefined }) makes the view auto-sized again, and setLayout({ position: 'flow' }) puts an absolutely-placed view back in the flow (its now-unused rect is simply ignored).
  • Do not call it in a constructor of a class that subclasses may extend. A base constructor body runs before a subclass's override readonly layout = {…} field initializer, and that initializer installs a fresh object, so the call would be erased. Call it after construction, or from onMount.

Reflowing an unmounted view is a no-op, so calling it before mount is safe.

Parameters

patch

Partial<LayoutProps>

The layout props to change; anything omitted is preserved.

Returns

void

Example

ts
import { Group } from '@jsvision/ui';

const panel = new Group();
panel.setLayout({ direction: 'col', padding: 1 });
// Later — `direction` and `padding` survive; once `panel` is mounted this also reflows:
panel.setLayout({ size: { kind: 'fr', weight: 1 } });

Inherited from

Group.setLayout


sort()

sort(): SortKey[]

Defined in: datagrid/src/grid.ts:1037

The current sort model. Reactive — reading it inside an effect re-runs when the sort changes.

Returns

SortKey[]

The ordered SortKey[] (empty when unsorted); the first key is the primary.


sortBy()

sortBy(columnId, dir?): void

Defined in: datagrid/src/grid.ts:992

Sort by a single column (a plain header click / the primary API). With an explicit dir, sets exactly that key. Without dir: an unsorted or secondary column becomes the sole ascending key; re-issuing it on the sole sorted column cycles its direction (asc → desc → cleared). An unknown columnId is ignored (never forwarded to a source query).

Parameters

columnId

string

The column to sort by.

dir?

SortDir

Optional explicit direction; omit to toggle/cycle.

Returns

void


toggleRow()

toggleRow(key): void

Defined in: datagrid/src/grid.ts:1933

Toggle a row's membership under the selection mode (multi adds/removes; single replaces), and make it the range anchor.

Parameters

key

Key

The row key to toggle.

Returns

void


totalCount()

totalCount(): number

Defined in: datagrid/src/grid.ts:1098

The pre-filter row count. Reactive. On the client path this is the true total; on a push-down source it reflects whatever source.length() reports (see filteredCount).

Returns

number

The source's row count.