Editing
By default, editing is not enabled.
To enable editing globally, you can use the
columnDefaultEditable boolean prop on the InfiniteTable component. This will enable the editing on all columns.Or you can be more specific and choose to make individual columns editable via the
column.defaultEditable prop. This overrides the global columnDefaultEditable.All columns (except id) are editable.
View Mode
Fork Forkimport { InfiniteTable, DataSource, InfiniteTablePropColumns, } from '@infinite-table/infinite-react'; import * as React from 'react'; type Developer = { id: number; firstName: string; lastName: string; country: string; city: string; currency: string; preferredLanguage: string; stack: string; canDesign: 'yes' | 'no'; hobby: string; salary: number; age: number; }; const dataSource = () => { return fetch('https://infinite-table.com/.netlify/functions/json-server' + '/developers100') .then((r) => r.json()) .then((data: Developer[]) => data); }; const columns: InfiniteTablePropColumns<Developer> = { id: { field: 'id', defaultWidth: 80, defaultEditable: false }, stack: { field: 'stack', contentFocusable: true, header: 'Stack', }, firstName: { field: 'firstName', header: 'Name', }, age: { field: 'age', header: 'Age', }, hobby: { field: 'hobby', header: 'Hobby', }, preferredLanguage: { header: 'Language', field: 'preferredLanguage', }, }; export default function ColumnValueGetterExample() { return ( <> <DataSource<Developer> primaryKey="id" data={dataSource}> <InfiniteTable<Developer> columns={columns} columnDefaultEditable /> </DataSource> </>
Column Editors
Read about how you can configure various editors for your columns.
Editing Flow Chart
A picture is worth a thousand words - see a chart for the editing flow.
If it is a function, it will be called when an edit is triggered on the column. The function will be called with a single object that contains the following properties:
value- the current value of the cell (the value currently displayed, so aftercolumns.valueFormatterandcolumns.renderValuehave been applied)rawValue- the current value of the cell, but before any formatting and custom rendering has been applied. This is either the field value from the current data object, or the result of the columnvalueGetterfunction.data- the data object (of typeDATA_TYPE) for the current rowrowInfo- the row info object that underlies the rowcolumn- the current column on which editing is invokedapi- a reference to the InfiniteTable APIdataSourceApi- - a reference to the DataSource API
The function can return a boolean value or a Promise that resolves to a boolean value - this means you can asynchronously decide whether the cell is editable or not.
Making
column.defaultEditable a function gives you the ability to granularly control which cells are editable or not (even within the same column, based on the cell value or other values you have access to).In addition to the flags mentioned above, you can use the
editable prop on the InfiniteTable component. This overrides all other properties and when it is defined, is the only source of truth for whether something is editable or not.The
editable prop allows you to centralize editing logic in one place.It has the same signature as the
column.defaultEditable function.Start Editing#
Editing can be started either by user interaction or programmatically via the API.
The user can start editing by double-clicking on a cell or by pressing the
Enter key while the cell is active (see Keyboard Navigation for Cells).To start editing programmatically, use the
startEdit({ columnId, rowIndex }) method.View Mode
Fork Forkimport { InfiniteTable, DataSource, InfiniteTablePropColumns, InfiniteTableApi, } from '@infinite-table/infinite-react'; import { useCallback, useRef, useState } from 'react'; type Developer = { id: number; firstName: string; currency: string; stack: string; hobby: string; salary: string; }; const dataSource: Developer[] = [ { id: 1, firstName: 'John', currency: 'USD', stack: 'frontend', hobby: 'gaming', salary: 'USD 1000', }, { id: 2, firstName: 'Jane', currency: 'EUR', stack: 'backend', hobby: 'reading', salary: 'EUR 2000', }, { id: 3, firstName: 'Jack', currency: 'GBP', stack: 'frontend', hobby: 'gaming', salary: 'GBP 3000', }, { id: 4, firstName: 'Jill', currency: 'USD', stack: 'backend', hobby: 'reading', salary: 'USD 4000', }, ]; const columns: InfiniteTablePropColumns<Developer> = { id: { field: 'id', defaultWidth: 80, defaultEditable: false }, salary: { defaultWidth: 320, field: 'salary', header: 'Salary - edit accepts numbers only', style: { color: 'tomato' }, getValueToEdit: ({ value }) => { return parseInt(value.substr(4), 10); }, getValueToPersist: ({ value, data }) => { return `${data!.currency} ${parseInt(value, 10)}`; }, shouldAcceptEdit: ({ value }) => { return parseInt(value, 10) == value; }, }, firstName: { field: 'firstName', header: 'Name', }, currency: { field: 'currency', header: 'Currency', }, }; export default function InlineEditingExample() { const [activeRowIndex, setActiveRowIndex] = useState<number>(2); const apiRef = useRef<InfiniteTableApi<Developer> | null>(null); const onReady = useCallback( ({ api }: { api: InfiniteTableApi<Developer> }) => { apiRef.current = api; }, [], ); return ( <> <button style={{ border: '1px solid magenta', margin: 2, padding: 10, background: 'var(--infinite-background)', color: 'var(--infinite-cell-color)', }} onClick={() => { apiRef.current!.startEdit({ rowIndex: activeRowIndex, columnId: 'salary', }); }} > Edit the salary column for active row </button> <DataSource<Developer> primaryKey="id" data={dataSource}> <InfiniteTable<Developer> onReady={onReady} columns={columns} columnDefaultEditable activeRowIndex={activeRowIndex} onActiveRowIndexChange={setActiveRowIndex} /> </DataSource> </>
Either way, be it user interaction or API call, those actions will trigger checks to see if the cell is editable - taking into account the
columnDefaultEditable, column.defaultEditable or editable props, as described in the paragraphs above. Only if the result is true will the cell editor be displayed.Customize Edit Value When Editing Starts#
When editing starts, the column editor is displayed with the value that was in the cell. This (initial) edit value can be customized via the
column.getValueToEdit prop. This allows you to start editing with a different value than the one that is displayed in the cell - and even with a value fetched asynchronously.const columns = {
salary: {
field: 'salary',
// this can return a Promise
getValueToEdit: ({ value, data, rowInfo, column }) => {
// suppose the value is a string like '$1000'
// but we want to start editing with the number 1000
return value.replace('$', '');
},
},
}; COPY
Try editing the salary column - it has a custom getter for the edit value, which removes the curency string.
View Mode
Fork Forkimport { InfiniteTable, DataSource, InfiniteTablePropColumns, } from '@infinite-table/infinite-react'; type Developer = { id: number; firstName: string; currency: string; stack: string; hobby: string; salary: string; }; const dataSource: Developer[] = [ { id: 1, firstName: 'John', currency: 'USD', stack: 'frontend', hobby: 'gaming', salary: 'USD 1000', }, { id: 2, firstName: 'Jane', currency: 'EUR', stack: 'backend', hobby: 'reading', salary: 'EUR 2000', }, { id: 3, firstName: 'Jack', currency: 'GBP', stack: 'frontend', hobby: 'gaming', salary: 'GBP 3000', }, { id: 4, firstName: 'Jill', currency: 'USD', stack: 'backend', hobby: 'reading', salary: 'USD 4000', }, ]; const columns: InfiniteTablePropColumns<Developer> = { id: { field: 'id', defaultWidth: 80, defaultEditable: false }, salary: { defaultWidth: 320, field: 'salary', header: 'Salary - edit accepts numbers only', style: { color: 'tomato' }, getValueToEdit: ({ value }) => { return parseInt(value.substr(4), 10); }, getValueToPersist: ({ value, data }) => { return `${data!.currency} ${parseInt(value, 10)}`; }, shouldAcceptEdit: ({ value }) => { return parseInt(value, 10) == value; }, }, firstName: { field: 'firstName', header: 'Name', }, currency: { field: 'currency', header: 'Currency', }, }; export default function InlineEditingExample() { return ( <> <DataSource<Developer> primaryKey="id" data={dataSource}> <InfiniteTable<Developer> columns={columns} columnDefaultEditable /> </DataSource> </>
Finishing an Edit#
An edit is generally finished by user interaction - either the user confirms the edit by pressing the
Enter key or cancels it by pressing the Escape key.As soon as the edit is confirmed by the user, the
InfiniteTable needs to decide whether the edit should be accepted or not.In order to decide (either synchronously or asynchronously) whether an edit should be accepted or not, you can use the global
shouldAcceptEdit prop or the column-level column.shouldAcceptEdit alternative.When neither the global
shouldAcceptEdit nor the column-level column.shouldAcceptEdit are defined, all edits are accepted by default.Once an edit is accepted, the
onEditAccepted callback prop is called, if defined.When an edit is rejected, the
onEditRejected callback prop is called instead.The accept/reject status of an edit is decided by using the
shouldAcceptEdit props described above. However an edit can also be cancelled by the user pressing the Escape key in the cell editor - to be notified of this, use the onEditCancelled callback prop.In this example, the
salary column is configured with a shouldAcceptEdit function property that rejects non-numeric values.View Mode
Fork Forkimport { InfiniteTable, DataSource, InfiniteTablePropColumns, } from '@infinite-table/infinite-react'; type Developer = { id: number; firstName: string; currency: string; stack: string; hobby: string; salary: string; }; const dataSource: Developer[] = [ { id: 1, firstName: 'John', currency: 'USD', stack: 'frontend', hobby: 'gaming', salary: 'USD 1000', }, { id: 2, firstName: 'Jane', currency: 'EUR', stack: 'backend', hobby: 'reading', salary: 'EUR 2000', }, { id: 3, firstName: 'Jack', currency: 'GBP', stack: 'frontend', hobby: 'gaming', salary: 'GBP 3000', }, { id: 4, firstName: 'Jill', currency: 'USD', stack: 'backend', hobby: 'reading', salary: 'USD 4000', }, ]; const columns: InfiniteTablePropColumns<Developer> = { id: { field: 'id', defaultWidth: 80, defaultEditable: false }, salary: { defaultWidth: 320, field: 'salary', header: 'Salary - edit accepts numbers only', style: { color: 'tomato' }, getValueToEdit: ({ value }) => { return parseInt(value.substr(4), 10); }, getValueToPersist: ({ value, data }) => { return `${data!.currency} ${parseInt(value, 10)}`; }, shouldAcceptEdit: ({ value }) => { return parseInt(value, 10) == value; }, }, firstName: { field: 'firstName', header: 'Name', }, currency: { field: 'currency', header: 'Currency', }, }; export default function InlineEditingExample() { return ( <> <DataSource<Developer> primaryKey="id" data={dataSource}> <InfiniteTable<Developer> columns={columns} columnDefaultEditable /> </DataSource> </>
Persisting an Edit#
By default, accepted edits are persisted to the
DataSource via the DataSourceAPI.updateData method.To change how you persist values (which might include persisting to remote locations), use the
persistEdit function prop on the InfiniteTable component.The
persistEdit function prop can return a Promise for async persistence. To signal that the persisting failed, reject the promise or resolve it with an Error object.After persisting the edit, if all went well, the
onEditPersistSuccess callback prop is called. If the persisting failed (was rejected), the onEditPersistError callback prop is called instead.