Data grid
DataGrid()
Section titled “DataGrid()”function
declare function DataGrid<R>(props: DataGridProps<R>): JSX.Element;A table of a query document’s rows, sortable by header, filtered from a filter row, and paged.
<DataGrid doc={ foos } columns={ ["name", "num", "owner", "created"] }/>Each column is derived from its field path and the schema, see resolveColumn(). Given a working set, the grid shows its drafts and marks rows and cells by what happened to them, see useGridRows(). The grid owns a component of the document’s condition, see useFilters(), so a search form writing a component of its own filters the same rows. When an update() fails, the grid’s own or anyone else’s, it says so above the rows, which stay the ones from before.
Composed from resolveColumn(), useFilters(), useGridRows(), SortHeader, FilterInput and Pager and nothing else. Where it doesn’t fit, copying it into the application and changing it is a reasonable answer.
DataGridProps
Section titled “DataGridProps”type
type DataGridProps<R> = { /** * Query document snapshot to show. */ doc: GridDocument<R>; /** * The columns: field paths of the rows, or objects stating what differs from the defaults derived from one. */ columns: readonly NoInfer<GridColumn<R>>[]; /** * Component id of the grid's filters in the document's condition. Default "grid"; a view sharing the condition * with another owner gives each owner its own. */ id?: string; /** * Working set the rows are edited in. The grid then shows the drafts, the rows created in the working set on the * first page, and what has happened to each row and cell, see useGridRows(). */ workingSet?: WorkingSet | null; /** * Whether other people's writes show as they happen, see useGridRows(). Without a working set, the grid offers * to reload once a row it shows changed. */ watch?: boolean; /** * Classes of a row's element. */ rowClassName?: (row: R) => string | undefined; /** * Tooltip of a column's header cell, e.g. a description of the sort field its arrow stands for. What that says * and how it's put is the application's; `matchSort()` and `matchSortPart()` find the sort field. A column with a * `title` of its own keeps that. */ headerTitle?: (column: ResolvedColumn) => string | undefined; /** * Key of the row to highlight, e.g. the one open in a detail pane. Rows are keyed as `rowKey()` keys them. */ highlighted?: string | null; /** * Page sizes the pager offers, see usePagination(). */ pageSizes?: readonly number[]; /** * Added to the grid's own classes, e.g. `qlive-grid-striped`. */ className?: string;};GridDocument
Section titled “GridDocument”interface
interface GridDocument<R> extends PageableDocument, FilterableDocumentWhat DataGrid needs of a query document: its type, config, rows, row count and update(), and its error if it has
one. A snapshot from useInjection() or useQueryDocument() is one as long as its query selects type, config,
rows and rowCount.
GridDocument.rows
Section titled “GridDocument.rows”rows: readonly R[];GridDocument.error
Section titled “GridDocument.error”error?: Error | null;Why the last update() failed, see QueryDocument#error. The grid shows it above the rows, which are still the ones from before.
GridColumn
Section titled “GridColumn”type
type GridColumn<R = any> = FieldPath<R> | DataGridColumn<R>;A column as a grid takes it: a field path, which means {field: path}, or the object form.
DataGridColumn
Section titled “DataGridColumn”interface
interface DataGridColumn<R = any>A column where the defaults derived from its field path won’t do. Everything but the field is optional and overrides what is derived.
DataGridColumn.field
Section titled “DataGridColumn.field”field?: FieldPath<R>;The field path. Without it the column is an action or computed column and needs render.
DataGridColumn.heading
Section titled “DataGridColumn.heading”heading?: ReactNode;The heading, in place of the i18n() of Type.field.
DataGridColumn.title
Section titled “DataGridColumn.title”title?: string | ((column: ResolvedColumn<R>) => string | undefined);Tooltip of the header cell, fixed or from the resolved column, e.g. to add what it sorts by. The grid’s
headerTitle titles only the columns without one.
DataGridColumn.render
Section titled “DataGridColumn.render”render?(row: R): unknown;The cell content. An element is used as it is, any other value is formatted like a value of the column’s field.
DataGridColumn.sort
Section titled “DataGridColumn.sort”sort?: FieldExpression | false;What the column sorts by, a field path or a FilterDSL expression without a direction, in place of the field;
false for a column that doesn’t sort.
DataGridColumn.filter
Section titled “DataGridColumn.filter”filter?: ColumnFilter<any> | false;The column’s filter in place of the one derived from the field’s type; false for a column without one. A
computed column has none unless it names one, and filters by what it sorts by: between on a sum, where the
column sorts by the sum.
DataGridColumn.className
Section titled “DataGridColumn.className”className?: string | ((row: R) => string | undefined);Added to the classes of the column’s cells, fixed or per row.
DataGridColumn.nowrap
Section titled “DataGridColumn.nowrap”nowrap?: boolean;Keeps the cell content on one line.
DataGridColumn.minWidth
Section titled “DataGridColumn.minWidth”minWidth?: string;CSS width the column doesn’t get narrower than
DataGridColumn.maxWidth
Section titled “DataGridColumn.maxWidth”maxWidth?: string;CSS width the column doesn’t get wider than
FieldPath
Section titled “FieldPath”type
type FieldPath<T, D extends number = 4> = [D] extends [never] ? never : T extends object ? { [K in keyof T & string]-?: PathOf<K, NonNullable<T[K]>, D>; }[keyof T & string] : never;The field paths of a row type: its fields, and the fields of the to-one relations it holds, as "owner.login",
down to a depth of 4. Lists drop out, since a column shows one value per row.
Taken from the result type of the grid’s query, a path is only valid if the query selects it, so a typo and a field the query doesn’t select are both compile errors.
resolveColumn()
Section titled “resolveColumn()”function
declare function resolveColumn<R = any>(type: string, column: NoInfer<GridColumn<R>>): ResolvedColumn<R>;Completes a grid column: the heading, the display, the sort key and the filter, derived from the field path and the schema where the column doesn’t state them.
- A scalar path shows the value formatted for its type (see
formatValue()), sorts by the field, and filters by type:containsIgnoreCasefor String, a yes/no select for Boolean,eqfor Int, Short, Byte and Float, a date range for Date and Timestamp. Other types have no default filter. - A to-one relation path (
"owner") stands for the related row, named by the target type’snameFieldsmeta: it shows the name fields, most significant first, and sorts and filters by the first one. The query has to select them (owner { id login }). A filter that picks the related row, likepick(), filters the foreign key instead.
The heading is the i18n() of the owning type and the field, "Foo.name", "AppUser.login", so a type’s fields
are labeled once for every grid showing them.
A path the schema doesn’t have, a list, a non-scalar that isn’t a to-one relation, or a relation whose type has no name fields where the column needs them is an error naming the column.
Parameters
type |
row type of the grid’s document |
column |
the column as given |
ResolvedColumn
Section titled “ResolvedColumn”interface
interface ResolvedColumn<R = any>A column with everything derived that can be: what DataGrid renders, and what a hand-written table built on the same hooks gets.
ResolvedColumn.field
Section titled “ResolvedColumn.field”field: string | null;the field path, null for a computed column
ResolvedColumn.heading
Section titled “ResolvedColumn.heading”heading: ReactNode;ResolvedColumn.title
Section titled “ResolvedColumn.title”title?: string;tooltip of the header cell, if the column has one
ResolvedColumn.render
Section titled “ResolvedColumn.render”render(row: R): ReactNode;the cell content of a row
ResolvedColumn.sort
Section titled “ResolvedColumn.sort”sort: FieldExpression | null;what the column sorts by, null if it doesn’t sort
ResolvedColumn.filter
Section titled “ResolvedColumn.filter”filter: FilterColumn | null;The column’s filter and the field it filters, null if it has none. For a relation column that is its first
name field, "owner.login", not the relation, or its foreign key, "ownerId", for a filter that picks the
related row (ColumnFilter.key). For a computed column it is the sort key. The filter is labeled with the
heading where that is text, so its inputs are called what the header says.
ResolvedColumn.className
Section titled “ResolvedColumn.className”className(row: R): string | undefined;classes of the column’s cells for a row
ResolvedColumn.statusField
Section titled “ResolvedColumn.statusField”statusField: string | null;The field of the row whose working set status the cells show: the field itself for a field of the row, the
foreign key for a relation of it. null for a field of a related row and for a computed column.
ResolvedColumn.nowrap
Section titled “ResolvedColumn.nowrap”nowrap: boolean;ResolvedColumn.minWidth
Section titled “ResolvedColumn.minWidth”minWidth?: string;ResolvedColumn.maxWidth
Section titled “ResolvedColumn.maxWidth”maxWidth?: string;rowKey()
Section titled “rowKey()”function
declare function rowKey(type: string, row: any): string;The key of a row: its primary key, from the uniqueKeys meta of the type, or its id for a type without one.
The values of a key of several fields are joined with commas.
Parameters
type |
row type |
row |
the row |
useGridRows()
Section titled “useGridRows()”function
declare function useGridRows<R>(doc: PageableDocument & { rows: readonly R[];}, options?: GridRowsOptions): GridRows<R>;The rows a list shows, and what has happened to each of them and their fields.
const {rows, status, fieldClass} = useGridRows(foos, {workingSet: ws, watch: true})Without a working set the rows are the document’s own. With one they are its drafts, so a row being edited elsewhere, in a detail pane or a dialog, shows the edits before they are saved; the rows created in the working set are listed first on the first page. Which of them is decided the way a query decides which rows it returns: when the condition changes, the created rows it matches then show, evaluated in the browser as conditionPredicate() has it, and so does every row created after. A row created under a filter shows although it is still empty, and neither it nor a row of the query leaves the list because an edit made it stop matching. The document is registered with the working set here, so its rows can be edited without doing that first.
The calling component re-renders with every change to the working set. DataGrid is this plus markup; a table of its own gets the same through this hook.
Rules of hooks apply.
Parameters
doc |
query document snapshot |
options |
working set and watching, see GridRowsOptions |
GridRows
Section titled “GridRows”interface
interface GridRows<R>The rows of a list and what has happened to them.
GridRows.rows
Section titled “GridRows.rows”rows: readonly R[];The rows to show. Without a working set, the document’s rows. With one, the drafts of them, preceded on the first page by the rows created in it that the document’s condition matched when it was set, and the rows created since.
GridRows.status
Section titled “GridRows.status”status(row: R): GridRowStatus;what has happened to a row
GridRows.fieldStatus
Section titled “GridRows.fieldStatus”fieldStatus(row: R, field: string): MergeFieldStatus;What has happened to a field of a row. A new row’s fields are unchanged: the row says it all.
GridRows.fieldClass
Section titled “GridRows.fieldClass”fieldClass(row: R, field: string): string;What has happened to a field of a row, as the class a cell or input carries (see MergeField.className),
empty for an unchanged field: fieldClassName(fieldStatus(row, field)).
GridRows.stale
Section titled “GridRows.stale”stale: boolean;true once somebody else changed a row shown, when watching the document rather than a working set. The
document’s update({}) reads the rows again.
GridRowsOptions
Section titled “GridRowsOptions”interface
interface GridRowsOptionsOptions for useGridRows,
GridRowsOptions.workingSet
Section titled “GridRowsOptions.workingSet”workingSet?: WorkingSet | null;The working set the rows are edited in. The rows are then its drafts, so unsaved edits show in the list, and the rows created in it show on the first page, those the document’s condition matches.
GridRowsOptions.watch
Section titled “GridRowsOptions.watch”watch?: boolean;Whether other people’s writes to the rows show as they happen: through the working set if there is one, see
useWorkingSet(), through useDocumentWatch() on the document otherwise. Default false.
GridRowStatus
Section titled “GridRowStatus”type
type GridRowStatus = "gone" | "deleted" | "conflict" | "new" | "changed" | "remoteChanged" | "unchanged";What has happened to a row, the most pressing first:
gone– somebody else deleted itdeleted– marked for deletion, deleted by the next mergeconflict– a field of it both writes changed, and nobody decided which value winsnew– created in the working set and not saved yetchanged– changed in the working setremoteChanged– somebody else changed it, and the working set’s user didn’tunchanged– none of that