1.2.52 • Published 5 years ago

zebulon-table v1.2.52

Weekly downloads
4
License
MIT
Repository
-
Last release
5 years ago

Zebulon table

Zebulon table is a hight performance fully virtualized React editable table component.

Available demo at: http://polluxparis.github.io/zebulon-table/

Please, look at the demo using Chrome as it's not yet fully compatible with other browsers.

Help and suggestions would be welcome.

Main features

User experience

Server communication

Miscellaneous

Associated projects

zebulon-table has been developped originally to manage the description data of a business intelligence application based on a pivot grid component called zebulon-grid : https//github.com/polluxparis/zebulon-grid It shares some base functions and components, such as a virtualized scrollable area/grid, contextual menus... available in zebulon-controls https//github.com/polluxparis/zebulon-controls

Table of contents

Getting started

Install zebulon-table using npm.

npm install zebulon-table --save

And in your code:

import ZebulonTable from 'zebulon-table'

Simple Example

import React, { Component } from "react";
import ReactDOM from "react-dom";
import { ZebulonTable } from "zebulon-table";
import "zebulon-controls/lib/index.css";
import "zebulon-table/lib/index.css";

const buildData = () => {
  const n0 = 100,
    n1 = 100,
    n2 = 10;
  let data = [];
  for (let i0 = 0; i0 < n0; i0++) {
    for (let i1 = 0; i1 < n1; i1++) {
      for (let i2 = 0; i2 < n2; i2++) {
        data.push({
          totoId: i0,
          totoLabel: `toto${i0}`,
          titi: i1,
          tutu: `tutu${i2}`,
          qty: Math.round(50 * Math.random()) + 1,
          amt: Math.round(150 * Math.random()) + 3
        });
      }
    }
  }
  return data;
};
const buildMeta = () => {
  return {
    // serverPagination: true,
    table: {
      object: "dataset",
      editable: true,
      select: buildData,
      actions: [
        { type: "insert", caption: "New", enable: true },
        {
          type: "delete",
          caption: "Delete",
          enable: "is_selected"
        },
        {
          type: "duplicate",
          caption: "Duplicate",
          enable: "is_selected"
        }
      ]
    },
    row: {},
    properties: []
  };
};
class MyEditableTable extends Component {
  meta = buildMeta();
  render() {
    return (
      <div style={{ fontFamily: "sans-serif" }}>
        <ZebulonTable
          meta={this.meta}
          sizes={{
            height: 600,
            width: 1000,
            cellWidth: 80,
            cellHeight: 28,
            zoom: 0.9
          }}
        />
      </div>
    );
  }
}
ReactDOM.render(<MyEditableTable />, document.getElementById("root"));

Zebulon table props

PropertyType/requiredDescription
sizesobject requiredsizes of the grid (height, width), zoom, default cell sizes
metaobjectMeta description of the dataset and the way the dataset is managed by the table
functionsobject or arrayJS functions that can be referenced in the meta description
paramsobjectGlobal parameters used for the dataset manipulation (user data by example)
dataarrayDatasource as an array, a promise, an observable or a "pagination manager"
updatedRowsobjectUpdated rows with status (new, updated, deleted), row image before (row value when loaded), row image after (row updated value)
filtersobjectInitial or external filters
sortsobjectInitial or external sorts
statusobjectDataset loading status
keyEventeventKeyboard event
navigationKeyHandlerfunction(event, {nextCell, nextPageCell, endCell, selectCell})Custom function to overwrite key navigation in the grid
isActivebooleanIndicator that the component is active
errorHandlerobjectCustom error and user interractions management
contextualMenuobjectCustom contextual menu
closeRequiredcallbackcallback to execute after save requirement
onChangetable event callbackChange of cell value
onCellEntertable event callbackEnter a cell
onCellQuittable event callbackQuit a cell
onRowNewtable event callbackAdd a new row
onRowEntertable event callbackEnter a row
onRowQuittable event callbackQuit a row
onTableEntertable event callbackEnter the table (active)
onTableQuittable event callbackQuit the table
onTableClosetable event callbackClose the table
onFiltertable event callbackChange filters
onSorttable event callbackChange sorts
onGetDatatable event callbackGet data
onGetPagetable event callbackGet a new page
onSaveBeforetable event callbackChecks before save
onSavetable event callbackSave updated data
onSaveAftertable event callbackPost treatment after save

Data set

The data set (data property) can be:

  • an array of similar objects
[
    {
      totoId: 25,
      totoLabel: "toto25",
      titi: 3,
      tutu: "tutuA",
      qty: 100,
      amt: 12456
    },
    {
      totoId: 154,
      totoLabel: "toto154",
      titi: 12,
      tutu: "tutu",
      qty: 22,
      amt: 2539
    },
    ...
  ]

N.B. By default, if you pass an array as the data prop (from a store by example), changes made in the component will be applied directly on the data array. If you want to avoid mutations, you can change this behavior by setting meta.table.noDataMutation : true. In this case you may need an onSaveAfter function that will apply the changes,after saving, to the store.

  • a promise (from the server) that will be resolved as an array of objects.
  • an observable (from the server) that will push, by page, arrays of objects (the full dataset will be loaded in background).
  • a pagination manager as a function to retrieve the appropriate pages from the server. In this case, the full dataset is not loaded locally, but only the diplayed page.

Available functions and callbacks

In the manipulation of the dataset, you may need to call functions for data calculation, formating, validation... Those functions are passed (functions property) to the component as

  • an object
{
  "myDatasetObjectId": {
    "myFunctionType",: {
      "myFunctionId":()=>{},
      ...
      },
      ...
    },
    ...
  }
  • an array
[
  {
    id: "myFunctionId",
    visibility: "myDatasetObjectId",
    tp: "myFunctionType",
    caption: "myFunctionCaption",
    functionJS: ()=>{})
  },
  ...
]
Visibility

As you may need to call several instances of the component for different dataset objects, the visibility identified the object concerned by the function. Visibility can be defined as "global_".

Function types

Function types determine the role of the function and then, the parameters used to call it. (data->not filtered dataset, status-> updatedRowscurrentRow.index_, column-> meta.propertiescurrentColumn.index_). Available types are:

TypeParametersDescription
accessor(row, column, params, status, data)returns a calculated value (computed column) for a given row. Accessors can be used to find a value, a sort key, aggregation components...
format(value, row, column, params, status, data)returns the formated value (potentialy as JSX)
editable({column, row, params, status, data})returns if the column is editable as a boolean
default({column, row, params})returns the default value
validator({value, previousValue or previousRow, column or meta, row, params, status or updatedRows, data})returns as a boolean if the action ( changed value, cell quit, row quit, table quit, save...) can be continued. status or updatedRows errors entry can be updated
dml({dataObject, params, filters, sorts}) or ({dataObject, params, updatedRows, data})select function (returns dataset) or insert, update, delete functions (returns new updatedRows)

Meta description, functions and callbacks

A lot of keys in the meta description prop can refer to a value, a function or an accessor (function name) to a function. By example, an editable entry for a column X could take the values:

  • true or false
  • ({column,row})=>rowcolumn.id.isEditable
  • "isColumnEditable" name of a function (in the functions property with the global_ or object visibility and the type "editable") ({column,row})=>rowcolumn.id.isEditable

The component is designed to be fully defined with the properties sizes, meta and, if necessary, functions and params. Nevertheless callbacks are available (and will overwrite functions defined in the meta description) for validation and data manipulation functions:

  • onChange
  • onCellEnter
  • onCellQuit
  • onRowNew
  • onRowEnter
  • onRowQuit
  • onTableEnter
  • onTableQuit
  • onTableClose
  • onFilter
  • onSort
  • onGetData
  • onSaveBefore
  • onSaveAfter

Meta description

The meta property is an object describing how to manipulate the dataset, the way to display it, the controls and validation to apply when data are changed, the actions allowed...

Using accessors

An accessor is a descriptor(string) used to retrieve a function that returns data or execute different actions.

data accessors (on properties)

  • The default accessor function is ({row})=>rowproperty.id.
  • You can refer to an other property value :"row.", eg row.quantity. N.B. "row." is mandatory in this case to make the distinction with function accessors. Accessor function is ({row})=>rowreferenced property.id
  • You can refer to a key of an object stored in a property :row.., eg row.product.price. Accessor function is ({row})=>rowreferenced property.id.key
  • You can refer to a function accessor.

function accessors

Functions can be defined directly in the meta description or referenced by accessors:

f =typeof meta...x===function
  ? meta...x
  : functions[object][function type][meta...x]

meta object

It contains 3 sections: table, row and properties.

{
  serverPagination:false // default:false
  table:{...}
  row:{...}
  properties:{...}
}

table

N.B. if the table section is the only one defined, properties section will be initilalized using the first row of the dataset.

{
    object: "dataset",
    editable: true, // default : false
    select: "onSelect",
    primaryKey: "id", // unused yet
    onSave: "onSave",
    onSaveBefore: undefined,
    onSaveAfter: undefined,
    noDataMutation:false, // avoid the mutation of the dataset array prop. default false
    noFilter: false, // Indicator if the filter bar is diplayed. default : false
    noStatus: false, //  Indicator if the status bar is diplayed. default : false
    actions: []
}
DML functions
  • select
  • onSave
  • onSaveBefore
  • onSaveAfter
Actions

It's possible to define action buttons, displayed after the grid in the action property.

 actions:[{
          type: "select",
          id: "submit",
          caption: "Submit",
          enable: "is_selected",
          hidden: false,
          action: "myAction",
          onTableChange: "submit", // save requirement before action
          doubleClick: true,
          key:"f2"
        },
        ...]

types:

  • insert: insert a new row
  • duplicate: create a new row as selected one
  • delete: delete selected row
  • save: save changes
  • action: execute the action (defined in the action property)

row

row:{
  onEnter:undefined,
  onQuit:undefined
}
Validators
  • onEnter : Function triggered when a row is entered. Parameters : ({ row, status, data, params}).
  • onQuit : Function triggered before change of focused row. Parameters : ({ row, previousRow, status, data, params}).

properties

N.B. width and properties order can be defined on the table by drag and drop.

properties:[{
    id: "id", // mandatory
    caption: "Code", // default : id
    width: 100, // default : props.sizes.cellWidth
    hidden:false, // default : false
    dataType: "string", // boolean, number, date, string, object. default : first data row correponding property datatype
    editable: true, //value (true, false) or function or function accessor. default table.editable
    mandatory: true, // mandatory indicator, default false
    default:"toto", // default value, default undefined
    filterType:"between", // default: "starts", ">=" or "between" depending of the datatype
    select:["toto","tutu","titi"], // default undefined
    accessor:undefined,
    sortAccessor:undefined,
    format:undefined,
    onChange:undefined,
    onEnter:undefined,
    onQuit:undefined
    // specific data for window calculation (computed columns with aggregation)
    aggregation:undefined,
    comparisonAccesssor:undefined,
    groupByAccessord:undefined,
    windowStart:undefined,
    windowEnd:undefined
}]
Accessors
  • value accessor : Function (or function accessor) that returns a computed value. By default (row,column)=> rowcolumn.id.
  • sort accessor : Function (or function accessor) that returns the elements needed by the sort function. By default value accessor.
  • primaryKeyAccessor : (for object properties only) Function (or function accessor) that returns the value of the object primary key.
  • setForeignKeyAccessor : (for object properties only) Function (or function accessor) that set the value of the object primary key to the foreign key. When an object property loaded from a foreign key is updatable, you must change this foreign key in case of update.
Format

Formatting function that returns a formatted string or a JSX element. Parameters :(value, row, column, params, status, data).

Sort function

Custom sort function. Parameters :(sortAccesor(rowA),sortAccesor(rowB)).

Filter type

Filtering method used for the column.

  • starts : value.startsWith(filterValue)
  • startsNoCase : value.toUpperCase().startsWith(filterValue.toUpperCase())
  • contains
  • =
  • >=
  • <=
  • between : value >= filterValueFrom && value <= filterValueTo
  • values : value in existing dataset values. A checkable list is used to define filter.
Select

List of possible values. It can be an array of values, an object {id:caption,...}, or an object {id:{id:,caption:},...}

Validators
  • onChange : Function triggered on cell changes. Parameters :({value,previousValue, row, status,column,data,params}).
  • onEnter : Function triggered when a cell is entered. Parameters :({value,previousValue, row, status,column,data,params}).
  • onQuit : Function triggered before change of focused cell.Parameters :({value,previousValue, row, status,column,data,params}).

Object properties and foreign keys

A row entry can be an object (dataType="object"). properties of the object can be referenced in accessors as row.object property id.key (e.g row.currency.symbol). It can be retrieved, on the client side when the dataset is loaded, from a joined object (dataType="joined object", select = object accessor, accessor = foreign key). The foreign key is used to retrieve the object instance in the "select" object. If a property referencing the object is defined as editable, it will be displayed as a select input using the "select" object as the item list.
Usually accessors are evaluated only when the component renders, except for "joined object": the referenced object is stored initialy in the dataset and then, it's properties can be referenced by the other columns. N.B. The primary key in the "select" object must be used for object keys and referenced in the sub-object with key "pk_". Demo: products, currencies and countries are joined objects

currencies={
  1:{pk_:1,cd:"EUR", label:"Euro"},
  2:{pk_:2,cd:"USD", label:"US dollar"},
  ...
};
...
    { // foreign key
      id: "currency_id",
      width: 0,
      dataType: "number",
      hidden: true
    },
    { // object
      id: "currency",
      caption: "Currency",
      width: 0,
      dataType: "joined object",
      mandatory: true,
      hidden: true,
      accessor: "row.currency_id",
      select: "currencies"
    },
    {
      id: "currency_cd",
      caption: "Currency",
      width: 100,
      dataType: "string",
      accessor: "row.currency.cd",
      filterType: "values",
      editable: true, // if editable, the column will be displayed as a select input
      select: currencies
    }

A zebulon table class can be used to manage foreign objects. On cellQuit of the column referncing a foreign object, the component will be loaded. If no row match the filter (starts with the value entered in the column not case sensitive), the quit action will be canceled, if only 1 row match, the row will be set in the object referenced in the accessor of th column, and the action will be resumed, else the component will be displayed filtered as a modal dialog to select the appropriate row. Demo: MyThirdparties class is used to manage thirdparties

    {
      id: "thirdparty",
      caption: "Thirdparty",
      width: 0,
      dataType: "object",
      mandatory: true,
      hidden: true
    },
    {
      id: "thirdparty_cd",
      caption: "Thirdparty",
      width: 80,
      dataType: "string",
      accessor: "row.thirdparty.cd",
      filterType: "values",
      editable: true,
      foreignObject: MyThirdparties
    },

N.B. You may want to use a "joined object" for limited items lists (eg currencies), in this case, it may be loaded on the client as an object of objects { primary key 1:{pk:primary key 1,code:...}, primary key 2:{pk:primary key 2,code:...}}, ... } and referenced in the select accessor. Only the primary is needed in the original dataset and must be referenced in the object accessor. For more important foreign object (eg thirdparties), it is not loaded on the client but required from the server when needed. The referenced object should be loaded in the original dataset.

Updated rows

The updatedRows prop is an object with an entry for each updated rows in the table. The value of the key is the absolute index (or the primary key) of the row (index can be found in row.index_).

{
  [absolute index]:{
    updated_:true,
    new_:false,
    deleted_:false,
    row:[before update row],
    rowUpdated:[after update row],
    errors:{[column]:{[type]:error text}}
}

If you pass an updatedRows prop as an empty object, it will mutate at each update in the table. Validation functions should update the error property (updatedRows.errors) if needed. In consequence, the component calling the table component can know at any time all the changes since the loading (or refresh) of data. exported functions to manage errors log.

  • manageRowError: (updatedRows, index, object, type, error): log or remove an error in the row status object.
  • getRowErrors: (status, rowIndex): returns an array with logged errors of the specified row (absolute index).
  • getErrors: returns an array with all the errors logged for the dataset.

Filtering and sorting

Filtering and sorting can be done directly in the table. The way used to filter can be defined at the filterType entry of the meta.properties prop. You can sort ascending or descending on one or several columns by clicking on it's header. Double click on a header will reset the previous orders. Filters and sorts objects are parameters of any calls to the server. filters and sorts objects can be used as props for initial definition.

filters={
  {
  [columnId1]:{
    id: columnId1, 
    filterType: "between",
    dataType:"number",
    v:156,
    vTo:562},
  [columnId2]:{
    id: columnId2,
    filterType: "values", // in filter
    dataType:"number", 
    v:{[Id1]: "toto 3", [toto 4]id2]: "toto 4"}},
  ...
};
sorts={
  [columnId1]:{id: columnId1, direction: "asc", sortOrder: 0},
  [columnId2]:{id: columnId2, direction: "desc", sortOrder: 1},
  ...
};

Pagination manager

If you don't want to load the full dataset on the client, you can use a pagination manager (defined in the meta.table.select key) and let the server manage it. The pagination manager is a function called by the client on load and scroll actions with the first and last index of the diplayed rows. It must returns (as a promise) a page of rows including those two rows, the first index in the page, the number or rows in the dataset and the number of filtered rows. As only current page is known by the client, global functions as sorting and filtering must be managed by the server. With an editable grid, updates may have impacts on sorting and filtering. You may want to take into account those changes before commit. Filters and sorting informations plus updated rows are passed as arguments of the function({ startIndex, stopIndex, filters, sorts, params,updatedRows}). You can find an example in src/demo/datasource.

Actual restrictions with pagination manager

  • Filters with existing values is not implemented yet, values must be given by the server.
  • Computed columns with aggregation are not available.

Validation and saving process

Validations, error handling and saving

Each validation step will be executed in two phases:

  • A validation function:

Called with a "message" as parameter to detect errors and conflicts. Errors and conflicts must be written, mutating the object, in the errors, conflicts entries of the message. Returning false will cancel the action and the second phase won't be executed. For validation functions that may be executed asynchronously (onSaveBefore, onSave, onSaveAfter), a callback is added as a second parameter that must be executed, if the process is asynchronous, with true (if succeed or handling errors is required) or false as parameter.

  • An "errorHandler" function:

Manage the interactions with the user. If no errors or conflicts are found in the message, the error handler will return true and the action will be resumed. Else the function defined in the errorHandler prop is then called with the message. This prop function must build the element to display (as a string, an array of string or a JSX element) and to characterize the errors as blocking or subject to validation. For a blocking error, the error handler must return false and a modal dialog will be displayed with an Ok button. The initial action is cancelled.

Demo: In the demo, the error handler has been defined to consider any error on save as blocking errors.

For an alert, the error handler must return true. A modal dialog will be displayed with Yes and No buttons. The initial action is cancelled on No and resumed on Yes.

Demo: In the demo, the error handler has been defined to consider errors on row quit as an alert.

For conflicts, typically when same data has changed on the server and in the component, a "conflicts resolution" modal will be opened, (with an other instance of the component) to choose the versions that must be kept.

Demo: In the demo, you can test the "conflicts resolution" modal by subscribing to server changes:

  • update one or several rows (in the 10th first ordered by order#)
  • click on the "subscription to server events" button. That will update the 10th first rows.
  • click on the save button.
  • check the row versions to keep.

Cell level

  • onChange

The component checks the data type of the changed value, then call the function in the onChange prop (or defined in the meta description).

  • onCellQuit

If the cell has been updated and corresponds to a link with a foreign key on an other component (meta.property*.foreignObject), the value is searched in the "foreign object". If no row match the value, the action is cancelled, if only one row match, the value is updated and the action continues else the component is opened, filtered by the value, to select the relevant row. Then the onCellQuit prop function (or defined in the meta description) is called.

Demo: The Thirdparty column is defined as a linked to a foreign object: MyThirdparties.

Row level

  • onRowQuit

If the row has been updated, the mandatory columns and the unicity of the primary key are checked. Errors are stored in the "message". Then the onRowQuit prop function (or defined in the meta description) is called.

N.B. The unicity of the primary key is checked only on data loaded on the client. It should be done on the server side during the saving process.

Dataset level (saving process)

When the updated data must be saves, cell validations and row validations are executed first. All these functions can be executed asynchronously.

  • onSaveBefore: Execute the onSaveBefore prop function (or defined in the meta description).
  • onSave: Execute the onSave prop function (or defined in the meta description).
  • onSaveAfter: Execute the onSaveAfter prop function (or defined in the meta description).

Table level

Actions has refresh, filter ... may require to save data before.

  • onTableChange

If data have been updated, a confirmation modal with Yes, No and Cancel buttons is displayed. On Yes the whole saving process is executed and the action is resumed (if no intermediate cancellation occurs), on No, all updates are rolledback and action is resumed, On Cancel, the process stops.

  • closeRequired prop

You may need to save the update before an action called from outside of the component (exit, reload...). In this case, you can pass as a prop (closeRequired) the function to callback after the saving. It will fired the onTableChange event and return the callback in case of success.

Working with a Redux store

When using a store, you can create a container mapping the actions to the meta description in the mergeProps function as in the following example:

import { connect } from "react-redux";
import { select, save } from "../actions";
import { ZebulonTable } from "zebulon-table";

const mapStateToProps = (state, ownProps) => {
  const getMeta = (select, save) => ({
    table: {
      object: "dataset",
      select: select,
      onSave: save,
      // ...
      actions: [
        {
          type: "save",
          caption: "Save",
          enable: true
        }
        // ...
      ]
    },
    row: {},
    properties: [
      // ...
    ]
  });
  return {
    getMeta,
    sizes: { height: 300, width: 800 },
    ...ownProps
  };
};
const mapDispatchToProps = (dispatch, ownProps) => ({
  save: message => {
    dispatch(save(message));
  },
  select: message => {
    dispatch(select(message));
  }
});
const mergeProps = (stateProps, dispatchProps, ownProps) => {
  const { getMeta, ...restStateProps } = stateProps;
  const { select, save, ...restDispatchProps } = dispatchProps;
  return {
    meta: getMeta(select, save),
    ...restStateProps,
    ...restDispatchProps,
    ...ownProps
  };
};
export const MyDatasetContainer = connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps
)(ZebulonTable);

Computed columns

You can create any computed columns using an accessor, as the result of a function using the global parameters and the row data. Those columns are dinamically refreshed on every changes impacting the value ( on cell quit). You can also create computed columns calculated by a functions as an aggregation operation on a subset of the dataset: typically a rolling average. For that you'll have to define

  • a "window" function accessor with an order: previous 100 order by date.
  • an aggregation operation accessor : average
  • a group by accessor: product
{
  properties:{
    ...
    rollingAvg:{
      ...
      aggregation: "avg",
      groupByAccessor: "row.country.id",
      accessor: "amt_€",
      comparisonAccessor: "row.d",
      sortAccessor: "row.d",
      windowStart: "since30d",
      windowEnd: x => x
    }
  }
}

Those columns are computed on data refresh or on demand. Demo:

  • rolling average of amounts in € order by date since 30 days
  • sum of amounts in € by country

Audit

If your backend is able to produce the successive versions of a data row, either as a set of similar data rows, either as a set of objects only with changed values,

[
  {
    user_:"toto",
    time_:Fri Apr 06 2018 10:46:36,
    price: 123 // previous value (before saved) 
  }
]

you can add a function (or a function accessor) in the meta description;

{
  table:{...}
  row:{
    audit:getAudit,
    ...}
  properties:{...}
}

It will add an audit entry in the status cell contextual menu (right click on the left cell of the row) that will display the succesive states of the row.

Self description

It's easy to use the new instances of the component to describe the dataset you are working on.

In the demo, in the "Manage configuration" tab, you are able to :

  • update the original dataset,
  • add computed properties
  • modify formats, filters, sizes...
  • create new JS functions used as accessor for new properties...

Please follow the tutorial to reproduce the dataset as it's displayed in the first tab. ZebulonTableAndConfiguration is a component you can use to manage configurations. It's a 3 tabs window:

  • dataset,
  • properties,
  • functions,

It changes dinamically the restitution following your own description. You may add new tabs if needed: see zebulon Grid demo at http://polluxparis.github.io/zebulon-grid/ ("Configuration" checked) to define measures and dimensions on a pivot grid.

Key events and navigation key handler

To manage correctly key events, specialy with several instances of the component, you can pass from an upper component the event as a prop. Only active component,considering the isActive prop will handle the event. Key events are used to navigate in the the grid, select a range of cells, zoom, copy or paste. The actual behaviour is

  • ctrl c, ctrl v for copy paste,
  • ctrl -, ctrl + for zoom in, zoom out,
  • ctrl f for search in the table,

  • shift to extend the selection,

For a not editable table :

  • left and righ arrows to select the previous or next cell in the row,
  • up and down arrows to select the same cell in previous or next row,
  • page up and page down to select the same cell at previous or next page,
  • alt + page up or page down to select on the same row the on previous next, page,
  • home and end to select the cell on the first or last row,
  • alt + home or end to select the first or last cell on the row,

For an editable grid left and righ arrow must keep the default behavior in the editable cells. The alt key is used to force the navigation behavior.

You can overwrite the navigationKeyHandler functions setting your custom function in the navigationKeyHandler prop (see src/demo/navigation.handler.js).

Custom contextual menu

It's possible to add custom contextual menu on

  • row headers,
  • columns headers,
  • top left corner
  • cells
{
  "row-header-menu": [
    {
      code: "toto1",
      caption: "Toto1",
      type: "MenuItem",
      function: e => console.log("toto1")
    },
    ...
  ],
  "column-header-menu": [
    {
      code: "toto1",
      caption: "Toto1",
      type: "MenuItem",
      function: e => console.log("toto1")
    },
    ...
  ],
  "top-left-corner-menu": [
    {
      code: "toto1",
      caption: "Toto_1",
      type: "MenuItem",
      function: e => console.log("toto1")
    },
  ...
  ],
  "cell-menu": [
    {
      code: "toto1",
      caption: "Toto_1",
      type: "MenuItem",
      function: e => console.log("toto1")
    },
  ...
  ]
};

On menu click, function is called with {meta, data, params, row || column} as argument.

Managing privileges

The params prop object is used to propagate global data. It can contains user data in specific keys to manage privileges:

  • user_
  • privileges_
  • filter_ You can add restriction at the row level by adding a filter, at the column level overwriting the visibility and editability (editable, visible, hidden or an accessor for an editable function) or at the action level (enable, disable, hidden or or an accessor for an editable function).
params={
        user_: "Zébulon",
        privileges_: {
          dataset: { 
            table: "editable",
            actions: { New: "hidden", Duplicate: "hidden", Delete: "hidden" },
            properties: {
              thirdparty_cd: "hidden",
              qty: "visible",
              color: "visible",
              currency: "visible",
              d: "visible",
              product_lb: "visible",
              country_cd: "visible",
              id: "editable"
            }
          }
        },
        filter_: [
          {
            filterType: "values",
            id: "country_id",
            v: { 2: 2 }
          }
        ]
      };

Details and drilldown

Not yet documented

To do

  • Complete documentation.
  • Computed columns with aggregation functions improvement.
  • Styles and design improvement.
  • Replacement of input select and icons
  • Loading from observable improvement.
  • Pagination manager improvement.
  • Details improvement.
  • Parameterisation of "actions".
  • Grouped columns. *...
1.2.52

5 years ago

1.2.51

5 years ago

1.2.50

5 years ago

1.2.49

5 years ago

1.2.48

5 years ago

1.2.47

5 years ago

1.2.46

5 years ago

1.2.45

6 years ago

1.2.44

6 years ago

1.2.43

6 years ago

1.2.42

6 years ago

1.2.41

6 years ago

1.2.40

6 years ago

1.2.39

6 years ago

1.2.38

6 years ago

1.2.37

6 years ago

1.2.36

6 years ago

1.2.35

6 years ago

1.2.34

6 years ago

1.2.33

6 years ago

1.2.32

6 years ago

1.2.31

6 years ago

1.2.30

6 years ago

1.2.29

6 years ago

1.2.28

6 years ago

1.2.27

6 years ago

1.2.26

6 years ago

1.2.25

6 years ago

1.2.24

6 years ago

1.1.23

6 years ago

1.1.22

6 years ago

1.1.21

6 years ago

1.1.20

6 years ago

1.1.19

6 years ago

1.1.18

6 years ago

1.1.17

6 years ago

1.1.16

6 years ago

1.1.15

6 years ago

1.1.14

6 years ago

1.1.13

6 years ago

1.1.12

6 years ago

1.1.11

6 years ago

1.1.10

6 years ago

1.1.9

6 years ago

1.1.8

6 years ago

1.1.7

6 years ago

1.1.6

6 years ago

1.1.5

6 years ago

1.1.4

6 years ago

1.1.3

6 years ago

1.1.2

6 years ago

1.1.1

6 years ago

1.1.0

6 years ago

1.0.9

6 years ago

1.0.8

6 years ago

1.0.7

6 years ago

1.0.6

6 years ago

1.0.4

6 years ago

1.0.3

6 years ago

1.0.2

6 years ago

1.0.1

6 years ago

1.0.0

6 years ago