Elyra
Elyra The coding agent eTerm The terminal that knows where each command ends Huskarl AI colleagues on your own Mac Starf An activity monitor for Apple silicon that never invents a number etrans An SSH and SFTP client for macOS Elyra VM Virtual machines for macOS, Linux and Windows on your Mac Litr A small, native web browser for macOS Notr A notebook for macOS e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Refr Local-first PDF workspace for macOS Elyra Workspace A desktop workspace for coding agents Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Elyra
Client core API (TypeScript)

Client core API (TypeScript)

@elyra/datagrid-client-core is the headless, framework-agnostic engine. The Svelte and Vue clients wrap it; you can also use it directly with any framework.

import { createGrid } from '@elyra/datagrid-client-core';
const grid = createGrid(options);
grid.subscribe((state) => { /* re-render */ });

GridOptions

interface GridOptions {
  columns: ColumnDef[];
  fetch: (request: GridRequest) => Promise<GridResponse>;
  ask?: (query: string) => Promise<FilterGroup | null>; // natural-language filter (shows the "ask" box)
  key?: string;                 // key column, always fetched (default 'id')
  rowId?: (row) => RowId;       // default: row[key]

  // features — false | true | { ...options }
  header?: Feature<HeaderOptions>;       // default on
  sorting?: Feature<SortingOptions>;     // default on; { multi }
  paging?: Feature<PagingOptions>;       // default on (paged, 20)
  filtering?: Feature<FilteringOptions>; // default on; { facets, row, builder }
  search?: Feature<SearchOptions>;       // default off; { mode, fields, vectorField }
  selection?: Feature<SelectionOptions>; // default off; { mode, checkbox }
  grouping?: Feature<GroupingOptions>;   // default off; { aggregates, detailRows, detailLimit }
  columnOps?: Feature<ColumnOpsOptions>; // default off; { reorder, resize, freeze, visibility }
  masterDetail?: MasterDetailOptions;    // { load, initiallyExpanded }
  editing?: EditingOptions;              // { mode, trigger, batch, mutate } (mutate required)
  columnVirtual?: boolean | { defaultWidth?: number; overscan?: number };
  tree?: { parentField: string; rootValue?: unknown; hasChildrenField?: string };
  headerGroups?: { header: string; columns: string[] }[];

  aggregates?: AggregateSpec[];  // footer aggregates
  meta?: MetaFlag[];             // extra opt-ins
  autoFetch?: boolean;           // default true
  debounceMs?: number;           // free-text refetch debounce (default 250)
  messages?: Partial<Messages>;  // i18n overrides
  exportUrl?: string;            // POST target for export
  persistKey?: string;           // localStorage persistence
  initialData?: GridResponse;    // server-seeded first page (skips initial fetch)
  initial?: { sort?; filter?; group?; viewport? };
}

Feature rule

Core display features (header, sorting, paging, filtering) default on. Interaction features (selection, grouping, search, columnOps, masterDetail, editing, columnVirtual) default off. false always forces off; true uses defaults; an object gives full control.

Feature option shapes

interface FilteringOptions { facets?: boolean; row?: boolean; builder?: boolean } // default true, except builder
interface GroupingOptions {
  aggregates?: AggregateSpec[];
  detailRows?: boolean;   // expand an innermost group to reveal its rows (lazy)
  detailLimit?: number;   // max detail rows per expanded group (default 200)
}
interface EditingOptions {
  mode?: 'inline' | 'modal' | 'slideover' | 'incell';
  trigger?: 'cell' | 'row' | 'button';
  batch?: boolean;        // incell: accumulate dirty edits, persist via saveBatch()
  mutate: (m: GridMutation) => Promise<GridMutationResult>;   // required
}
// ColumnDef.frozen: boolean | 'left' | 'right'   (true = left)

PagingOptions

{ mode?: 'paged' | 'virtual'; pageSize?: number;   // default 20
  pageSizeOptions?: number[];                        // [20,50,100,200]
  allowAll?: boolean; maxAll?: number;               // "All" (capped 1000)
  rowHeight?: number; overscan?: number }            // virtual scroll

The Grid object

interface Grid {
  readonly features: ResolvedFeatures;
  getState(): GridState;
  subscribe(listener: (state: GridState) => void): () => void;
  refresh(): Promise<void>;

  // sorting
  toggleSort(field, additive?); setSort(sort);
  // filtering
  setFilter(filter | null); setSearch(query);
  setColumnFilterValue(field, value); setColumnFilterOp(field, op);
  clearColumnFilter(field); columnFilterOf(field);
  loadFacet(field, top?); isFacetLoading(field);
  askFilter(query): Promise<void>;   // natural language -> filter (needs options.ask)
  // grouping
  groupBy(field); ungroup(field); setGroups(fields);
  toggleGroup(path); isGroupCollapsed(path);
  // selection
  toggleSelect(id); isSelected(id); selectAllOnPage(); clearSelection(); selectedIds();
  // paging
  setViewport(skip, take); setPageSize(size | 'all'); goToPage(page); onScroll(scrollTop, viewportHeight); pageCount();
  // grouping detail-rows (grouping.detailRows)
  toggleGroupDetail(path); isGroupDetailExpanded(path); groupDetailOf(path); isGroupDetailLoading(path);
  // master-detail
  toggleExpand(id); isExpanded(id); detailOf(id);
  // editing (inline/modal/slideover/incell)
  beginEdit(id, field?); beginCreate(); setDraft(field, value);
  cancelEdit(); commitEdit(); removeRow(id); isEditing(id, field?);
  // batch / in-cell editing (editing.mode 'incell')
  commitCell(); isDirty(id, field?); dirtyValueOf(id, field); cellValue(row, field);
  dirtyRowCount(); saveBatch(); cancelBatch();
  // columns
  setColumnVisible(field, visible); moveColumn(field, toIndex);
  moveColumnLeft(field); moveColumnRight(field); resizeColumn(field, width); freezeColumn(field, frozen);
  // clipboard + pinning + tree + column virtualization
  pasteAt(text); togglePinRow(id); isPinned(id);
  toggleTreeNode(id); isTreeExpanded(id); onScrollX(scrollLeft, viewportWidth);
  // keyboard / cell selection
  setActiveCell(r, c, extend?); isActiveCell(r, c); inSelectionRange(r, c); copySelection();
  moveActive(key, extend?, opts?: { ctrl?: boolean; pageRows?: number });
  //   key: Arrow*/Home/End/PageUp/PageDown; ctrl+Home/End => first/last cell of grid
  // export
  exportData();

  destroy();   // call on unmount: cancels pending debounce/persist timers,
               // drops the store subscription, invalidates in-flight fetches
}

The Vue (onScopeDispose) and Svelte (onDestroy) adapters call destroy() for you. If you use createGrid directly, call it when your component unmounts so a debounced fetch can't fire against a torn-down component.

GridState

interface GridState {
  columns: ResolvedColumn[];
  sort: SortSpec[];
  filter: FilterGroup | null;
  columnFilters: Record<string, ColumnFilter>;
  search: { query: string } | null;
  group: string[];
  collapsedGroups: string[];
  selection: { mode; ids: Set<RowId>; allPagesSelected };
  viewport: { skip; take };
  expanded: Set<RowId>;
  editing: EditingState | null;
  activeCell: CellRef | null; anchor: CellRef | null;
  pinnedRows: RowId[];
  treeExpanded: RowId[];
  colWindow: ColWindow | null;
  dirty: Record<string, Record<string, unknown>>;   // unsaved batch/in-cell edits (rowId => field=>value)
  expandedGroups: string[];                          // innermost-group paths showing detail rows
  groupDetail: Record<string, Row[]>;                // lazily loaded detail rows per group path
  groupDetailLoading: string[];
  data: { rows; total; groups; aggregates; facets; facetsLoading; loading; error };
}

Helpers

import { pageItems, computeWindow, computeColumnWindow,
         operatorsForType, defaultOpForType, headerBandRow,
         explainFilter, explainView, askDropMessage, askVocabulary,
         resolveMessages, defaultMessages,
         // advanced filter-builder tree helpers (immutable, path-addressed)
         newGroup, newCondition, addCondition, addGroup, setLogic,
         updateCondition, removeNode, isGroup, isEmptyFilter, countConditions }
  from '@elyra/datagrid-client-core';
  • pageItems(current, totalPages) — numbered pager items with ellipses.
  • computeWindow(input) — virtual row window from scroll position.
  • computeColumnWindow(widths, scrollLeft, viewportWidth, overscan) — column window.
  • operatorsForType(type) / defaultOpForType(type) — filter operators. Never offers one the server refuses (contains on a number is a PostgreSQL type error).
  • explainFilter(group, { columns, phrases }) / explainView(group, search, …) — render a filter tree as a sentence. A formatter, not a model call: faithful by construction, free, and deterministic. Mirrors PHP's FilterExplainer.
  • askDropMessage(drop, columns, messages) — one refused condition as a sentence.
  • askVocabulary(columns) — what the grid can be filtered on, for a no-match hint.
  • headerBandRow(cols, groups) — multi-column-header banner cells.
  • resolveMessages(partial) — merge i18n overrides onto English defaults.

Filter-builder helpers

Immutable helpers for building nested AND/OR filter trees (used by the advanced filter builder; edits are addressed by a path of child indices from the root group):

newGroup(logic?)                     // { logic, filters: [] }
newCondition(field, op?, value?)     // a leaf condition
addCondition(root, groupPath, cond)  // -> new tree
addGroup(root, groupPath, logic?)    // -> new tree
setLogic(root, groupPath, logic)     // AND <-> OR
updateCondition(root, path, patch)   // patch a leaf
removeNode(root, path)               // drop a node
isGroup(node) · isEmptyFilter(node) · countConditions(node)

Build a tree and hand it to grid.setFilter(tree).