2.1.2 • Published 2 years ago

gridy-grid v2.1.2

Weekly downloads
1
License
ISC
Repository
bitbucket
Last release
2 years ago

Gridy Grid

Web Components based data grid (table).

Features

  • W3C browser standards and native modern technologies compilant
  • Modular and flexible to use and extend for any complexity level
  • Can be bootstrapped completely from markup as far as from js code (dynamic render)
  • Skinnable and templatable UI, can mimic to framework of your choice
  • Modern code style with no transpilations required, but you are still free to do that
  • Options to have wide browser support (including legacy browsers like IE11)

If you are looking for web components based widgets collection check out skinny-widgets project.

Install

As for the moment you must install one of theme packages by your choice, e.g. gridy-grid-default, gridy-grid-antd or gridy-grid-jquery in addition to this package and specify the chosen theme as sk-config or gridy-grid element's attribute.

Prebuilt bundle can be found here: gridy-grid-bundle

npm i gridy-grid gridy-grid-jquery sk-theme-jquery --save

Bundles that are prebuilt js and html code for static plug can be found in gridy-grid-bundle package

Simple Usage

Let's see the example for the data loaded from json within javascript execution context.

<gridy-table base-path="/node_modules/gridy-grid/src" theme="jquery" id="gridyTable"></gridy-table>

<script type="module">
    import { GridyTable } from '/node_modules/gridy-grid/src/table/gridy-table.js';
    import { DataSourceLocal } from '/node_modules/gridy-grid/src/datasource/data-source-local.js';
    let dataSource = new DataSourceLocal();
    dataSource.fields = [
        { title: 'Title', path: '$.title' },
        { title: 'Price', path: '$.price' }
    ];
    let data = [];
    for (let i = 0; i < 10; i++) {
        data.push({ title: 'row' + i, price: 100 * i })
    }
    // local datasource data load should be called explicitly
    gridyTable.dataSource = dataSource;
    customElements.define('gridy-table', GridyTable);
    dataSource.loadData(data);
</script>

GridyGrid is the container element that simplifies components configuration

GridyGrid is the container element that simplifies components configuration and instantiation. Just wrap your components with it and bind DataSource instance or use gridy-data-source element.

gridy-grid will instantiate and bind dataSource when found gridy-data-source subelement. Don't forget to start demo server to check outh the code sample below.

<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/antd/3.19.8/antd.css" />

<gridy-grid theme="antd" id="gridyGrid" base-path="/node_modules/gridy-grid/src" sort-field="$.title">
    <gridy-data-source fields='[{ "title": "Title", "path": "$.title" },{ "title": "Price", "path": "$.price" }]'
                       datasource-type="ajax" url='http://127.0.0.1:8080/unpaged' datapath="$.data"></gridy-data-source>
    <gridy-spinner></gridy-spinner>
    <gridy-table-info id="gridyTableInfo"></gridy-table-info>
    <gridy-filter id="gridyFilter"></gridy-filter>
    <gridy-table id="gridyTable"></gridy-table>
    <gridy-pager id="gridyPager"></gridy-pager>
    <gridy-table-info id="gridyTableInfo"></gridy-table-info>
</gridy-grid>

gridy-data-source

    <gridy-data-source fields='[{ "title": "Title", "displayTitle": "Some title", "path": "$.title" },{ "title": "Price", "path": "$.price"},{ "title": "Created", "path": "$.created"}]'
                       req-type="POST" req-param-map='{"perPage": "per_page", "sortField": {"name": "sort_field", "type": "body"}}' datasource-type="ajax" url='http://127.0.0.1:8080/paged' back-paged data-path="$.data"></gridy-data-source>

Attributes

datasource-type - attribute can have values either 'ajax' or 'local' (used when no value is defined) or any class with name DataSource${type} assigned to window

fields - row fields configuration json

data-path - json path for row data to be extracted

http-client-type - ajax only, used to switch between XmlHttpClient and FetchHttpClient or any custom ${Type}HttpClient assigned to window

http-client-cn - ajax only custom http client class name, completely overrides classname

http-client-options - ajax only custom http client class constructor args json

back-paged - ajax only defines if all data if fetched with one request or backend pagination and filtration is used.

Response may have the following payload (Note: data-path is set to "$.data"):

{"data":[{"field1": "FooBar11", "field2": 22}, ...], "page":2, "perPage":10, "totalResults": 16 }

and understand the following request parameters (that can be remapped with req-param-map attribute configuration) returning different pages:

?page=2&perPage=10

url - ajax only request url

req-type - request type, possible values: "GET", "POST", default: (node set -> "GET")

req-param-map - ajax only request parameter map json for backend pagination, in case your backend needs special parameter names, the following parameters can be used by dataSource: page, perPage, sortField, sortDirection. table-sort-field-id gridy-table element attribute can be used to specify sortField value from fields configuration property like name, title, path etc

req-headers - ajax only request headers json

resp-param-map - ajax only response paramter map json, value can be provided as property name or json path, fields supported: totalResults, sortField, sortDirection

<gridy-data-source fields='[{ "title": "Name", "path": "$.name", "name": "name" },{ "title": "Url", "path": "$.url", "name": "url"}]'
                   datasource-type="ajax" url='http://127.0.0.1:8080/view_paged' back-paged data-path="$.data" resp-param-map='{ "totalResults": "total" }'
></gridy-data-source>
Fields properties

title - more like field id

displayTitle - attribute and its equal json property displayTitle can be used to alter title in table headings, e.g. with localized labels

path json path relative to row

noRender any truable value prevents field from rendering in table

Render from markup

You can alter grid layout with own markup

<gridy-grid id="gridyGrid" base-path="/node_modules/gridy-grid/src" sort-field="$.title">
    <style>
        .gridy-heading {
            display: flex; 
            width: 100%;    
            justify-content: space-between;
        }
    </style>
    <div class="gridy-heading">
        <gridy-table-info id="gridyTableInfo"></gridy-table-info>
        <gridy-per-page id="gridyPerPage"></gridy-per-page>
    </div>
    <gridy-filter id="gridyFilter"></gridy-filter>
    <gridy-table id="gridyTable"></gridy-table>
    <gridy-pager id="gridyPager"></gridy-pager>
</gridy-grid>

For local data source we may need less args:

<gridy-data-source fields='[{ "title": "Title", "path": "$.title" },{ "title": "Price", "path": "$.price" }]'
                   dataref="gridData"></gridy-data-source>

It's also possible to configure data-source with markup instead of attribute:

<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/antd/3.19.8/antd.css">

<gridy-grid theme="antd" id="gridyGrid" base-path="/node_modules/gridy-grid/src">
    <gridy-data-source dataref="gridData">
        <gridy-data-field title="Title" path="$.title"></gridy-data-field>
        <gridy-data-field title="Price" path="$.price"></gridy-data-field>
    </gridy-data-source>
    <gridy-spinner></gridy-spinner>
    <gridy-table id="gridyTable"></gridy-table>
    <gridy-pager id="gridyPager"></gridy-pager>
</gridy-grid>

You can load data programmatically by markup defined datasource:

<gridy-grid theme="jquery" id="gridyGrid" base-path="/node_modules/gridy-grid/src" sort-field="$.title">
    <gridy-data-source fields='[{ "title": "Title", "path": "$.title" },{ "title": "Price", "path": "$.price" }]'
                       datapath="$.data"></gridy-data-source>
    <gridy-spinner></gridy-spinner>
    <gridy-table-info id="gridyTableInfo"></gridy-table-info>
    <gridy-filter id="gridyFilter"></gridy-filter>
    <gridy-table id="gridyTable"></gridy-table>
    <gridy-pager id="gridyPager"></gridy-pager>
    <gridy-table-info id="gridyTableInfo"></gridy-table-info>
</gridy-grid>
<script type="module">
    import { SkConfig } from './node_modules/sk-core/src/sk-config.js';

    customElements.define('sk-config', SkConfig);
    import { GridyGrid } from '/node_modules/gridy-grid/src/gridy-grid.js';

    window.data = [];
    for (let i = 0; i < 25; i++) {
        window.data.push({ title: 'row' + i, price: 100 * i })
    }
    let grid = document.querySelector('#gridyGrid');
    customElements.define('gridy-grid', GridyGrid);

    grid.whenBootstraped(() => {
        grid.dataSource.loadData(data);
    });
</script>

And you are still free to instantiate and configure dataSource programmatically:

<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/antd/3.19.8/antd.css">
<gridy-grid theme="antd" id="gridyGrid">
    <gridy-filter id="gridyFilter"></gridy-filter>
    <gridy-table id="gridyTable"></gridy-table>
    <gridy-pager id="gridyPager"></gridy-pager>
</gridy-grid>

<style>
    th.sort.sort-desc::after {
        content: '▼';
    }
    th.sort.sort-asc::after {
        content: '▲';
    }
</style>
<script type="module">
    import { GridyGrid } from '/node_modules/gridy-grid/src/gridy-grid.js';

    import { DataSourceAjax } from '/node_modules/gridy-grid/src/datasource/data-source-ajax.js';

    let dataSource = new DataSourceAjax();
    dataSource.fields = [
        { title: 'Title', path: '$.title' },
        { title: 'Price', path: '$.price', fmt: (field, value, row) => `<b>$ ${value}</b>`, html: true  }
    ];
    dataSource.url = 'http://127.0.0.1:8080/paged';

    let grid = document.querySelector('#gridyGrid');
    grid.dataSource = dataSource;

    customElements.define('gridy-grid', GridyGrid);

</script>

Separate declarative elements configuration

Without gridy-grid container you'll have to provider every element with configurations and link with the following attributes:

ds-ref - to specify DataSource element id/global name

pg-ref - to link table to pager

<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/antd/3.19.8/antd.css">

<gridy-data-source dataref="gridData" id="dataSource">
    <gridy-data-field title="Title" path="$.title"></gridy-data-field>
    <gridy-data-field title="Price" path="$.price"></gridy-data-field>
</gridy-data-source>
<gridy-table theme="antd" id="gridyGrid" base-path="/node_modules/gridy-grid/src" id="gridyTable"
             ds-ref="dataSource" pg-ref="gridyPager" sort-field="$.title"></gridy-table>
<gridy-pager theme="antd" base-path="/node_modules/gridy-grid/src" id="gridyPager" ds-ref="dataSource"></gridy-pager>
<script type="module">
    import { GridyTable } from '/node_modules/gridy-grid/src/table/gridy-table.js';
    let data = [];
    for (let i = 0; i < 25; i++) {
        data.push({ title: 'row' + i, price: 100 * i })
    }
    window.gridData = data;
    customElements.define('gridy-table', GridyTable);
</script>

Configuration with external element host

Gridy elements can load common configuration attributes from special elements in DOM so you don't need to repeat them for each element, currently gridy-config and sk-config (from skinny-widgets) are supported. You can alternate config element lookup selector with config-sl attribute.

<sk-config
        theme="antd"
        base-path="/node_modules/gridy-grid/src"
        lang="ru_RU"
        id="myConfig"
></sk-config>

Adding filters

to use date filter you will have to install datepicker packages. Also filter package have to be installed or you will have to plug preloaded templates

npm i sk-datepicker sk-datepicker-antd gridy-filter-date
<sk-config
        lang="ru" id="skConfig" theme="antd"></sk-config>
<gridy-grid theme="antd" id="gridyGrid">
    <gridy-data-source dataref="gridData">
        <gridy-data-field title="Title" path="$.title"></gridy-data-field>
        <gridy-data-field title="Price" path="$.price"></gridy-data-field>
        <gridy-data-field title="Created" path="$.created"></gridy-data-field>
    </gridy-data-source>
    <gridy-spinner></gridy-spinner>
    <gridy-filter dri="date" filter-mode="ge" field-title="Created" id="gridyFilter"></gridy-filter>
    <gridy-table id="gridyTable">
    </gridy-table>
    <gridy-pager id="gridyPager"></gridy-pager>
    <gridy-table-info id="gridyTableInfo"></gridy-table-info>
</gridy-grid>
<script type="module">
    import { GridyGrid } from '/node_modules/gridy-grid/src/gridy-grid.js';
    import { SkDatePicker } from './node_modules/sk-datepicker/src/sk-datepicker.js';

    let data = [];
    for (let i = 0; i < 25; i++) {
        let today = new Date();
        today.setDate(today.getDate() + i);
        data.push({title: 'row' + i, price: 100 * i, created: today.toLocaleDateString("en-US")})
    }
    let grid = document.querySelector('#gridyGrid');
    grid.addEventListener('bootstrap', () => {
        grid.dataSource.loadData(data);
        grid.table.whenRendered(() => {
            console.log('table rendered');
        });
    });
    customElements.define('gridy-grid', GridyGrid);
    customElements.define('sk-datepicker', SkDatePicker);
</script>

Pagination

By default data is displayed without pagination.

gridy-grid element will enable pagination when gridy-pager element is found in internals or it has per-page configuration attribute specified by forwarding it to supported subelements.

Build grid from markup

You can generate some usual table markup with data by yourself and init Gridy Grid for it.

<gridy-table id="gridyTable" pgref="gridyPager" base-path="/node_modules/gridy-grid/src" theme="antd">
    <table>
        <tbody class="ant-table-tbody">
            <tr><td>row hand 1</td><td><b>$ 0</b></td></tr>
            <tr><td>row hand 2</td><td><b>$ 100</b></td></tr>
            <tr><td>row hand 3</td><td><b>$ 100</b></td></tr>
            <tr><td>row hand 4</td><td><b>$ 100</b></td></tr>
            <tr><td>row hand 5</td><td><b>$ 100</b></td></tr>
        </tbody>
    </table>
</gridy-table>
<gridy-pager id="gridyPager" dsref="gridyTable.dataSource" base-path="/node_modules/gridy-grid/src" theme="antd"></gridy-pager>

<script type="module">
    import { GridyTable } from '/node_modules/gridy-grid/src/table/gridy-table.js';
    customElements.define('gridy-table', GridyTable);
</script>

or with datasource:

<gridy-grid id="gridyGrid" base-path="/node_modules/gridy-grid/src" sort-field="$.title">

    <gridy-data-source fields='[{ "title": "Title", "path": "$.[0]" },{ "title": "Price", "path": "$.[1]"}]'></gridy-data-source>


    <gridy-table selectable="multi" selection-emit="ctrl+click" theme="antd" id="gridyTable">
        <table>
        <tbody class="ant-table-tbody">
            <tr><td>row hand 1</td><td>0</td></tr>
            <tr><td>row hand 2</td><td>100</td></tr>
            <tr><td>row hand 3</td><td>100</td></tr>
            <tr><td>row hand 4</td><td>100</td></tr>
            <tr><td>row hand 5</td><td>100</td></tr>
        </tbody>
    </table>
    
    </gridy-table>

    <gridy-pager id="gridyPager2"></gridy-pager>
</gridy-grid>

thead contents also can be parsed, but only for ds field props that are not defined

Allow rows selection

Attribute selectable will enable selection behaviour when added to gridy-grid or gridy-table element. When value is set as "multi" there will be possible to select multiple rows, when any other or no value - only one row.

selection-emit attribute choses how selection will be emited, e.g. on long click/tap or click, possible values: "tap" - select by long click/tap, "click", "ctrl+click", "shift+click", "alt+click" (default: no -> click)

time-to-select - time to select for tap mode in miliseconds, (default: 700x)

GridyTable properties selectedRow, selectedRows and prevSelectedRow store links to selected rows.

Access row by dataItem

you can get link to rendered (visually present on table) table row by dataItem (data row mapped by field title) provided with rowByDataItem(dataIitem) gridy-table method

let gridyGrid = document.getElementById('gridyGrid');
let dataItem = {'title': 'foobar9', 'price': 600, 'created': '13.04.2022'};
let rowEl = gridyGrid.table.rowByDataItem(dataItem);
console.log(`row for ${JSON.stringify(dataItem)}`, rowEl);

Define row click callback

You can either bind it with general event api using gridy-grid element or gridy-table and rely to path attribue values or use special property onrowclick or onhrowclick (for heading) for convenience:

    gridyTable.onrowclick = function(event, row) {
        console.log('rowclick', event, row, this);
    };

onrowrclick is for right button mouse click handling

Pane opened on click

You can easily implement details pane with your own template.

    <gridy-table id="gridyTable" row-pane>

    </gridy-table>
    <template id="tableRowPaneTpl">
        <div>Price:</div><div>{{ price }}</div>
    </template>

Row data mapped to field is exposed info template rendering context.

attributes

row-pane-tpl-path - template loading url, can be specified to anything if template predefined dom

row-state - preserve row pane state between paging and sorting operations (default: not set -> false)

Define attributes for cell td element

You can specify any own td's attribute as value or lambda with attr options subset, e.g. you want to implement cell hover hint

    dataSource.fields = [
        { title: 'Title', path: '$.title', attr: { title: (field, value) => `${field.title}` }},
        { title: 'Price', path: '$.price' }
    ];

rowattr can be also specified in the same way for tr element, beware multiple same attrs will overwrite each other

Custom formatting functions

tplPath - path for cell template to render, html option is also recommended to be enabled with this parameter, row data (dataItem) and executed value are flatmapped to rendering context

calc - calculation map with fields to functions, that evaluted on page is rendered, the result is passed to fmt function by key. Here is the example you can add hashcode calculation by executing method from dataSource. The first (0) argument of evaluted function is dataItem and context is binded to dataSource. If you use sort in LocalDataSource and calc is provided as hashmap then first calculated key is taken to compare.

calculation cache is controlled by table-calc-cache attr for gridy-table or config element, define it to enable, rows must have unique contents (e.g. has id field) to have caching work properly

<gridy-data-source fields='[{ "title": "Hashcode", "name": "hashcode", "fmt": "{{ hash }}", "calc": { "hash": "return this.hashDataItem(arguments[0])" }}]'></gridy-data-source>

or

<gridy-data-source fields='[{ "title": "Hashcode", "name": "hashcode", "fmt": "return arguments[0].calc;", "calc": { "return this.hashDataItem(arguments[0])" }]'></gridy-data-source>

fmt - field attribute allows to have string templating with value and row field values are available in rendering context or evaluted callback (when you use javascript to configure fields). The args of this callback are: field, value, dataItem, rendered. To evalute html from fmt you should also add html: true attribute.

if fmt string starts with words 'return', it will be evaluted as function with the following map as argument { value: value, ...dataItem, ...calcs, rendered: rendered } with context binded to table component

<gridy-data-source fields='[{ "title": "Hashcode", "name": "hashcode", "fmt": "return arguments[0].hash;", "calc": { "hash": "return this.hashDataItem(arguments[0])" }}]'></gridy-data-source>

formatting result cache is controlled by table-fmt-cache attr for gridy-table or config element, define it to enable, rows must have unique contents (e.g. has id field) to have caching work properly

in case tplPath and fmt both specified they can do accumulative rendering, template rendering result is passed as rendered var to fmt method or 4th arg to function.

    <gridy-grid id="itemsGrid">
        <gridy-data-source fields='[{ "title": "", "path": "$.*", "fmt": "<sk-checkbox class=\"item-chk no-rowclick\" value=\"{{ value }}\"></sk-checkbox>", "html": "true"}, { "title": "Содержание", "path": "$.contents" },{ "title": "Исполнитель", "path": "$.executor" },{ "title": "Ответственный", "path": "$.responsible" },{ "title": "Время выполнения", "path": "$.unitsToComplete" }]'
                           datasource-type="DataSourceLocal" datapath="$.data"></gridy-data-source>
        <gridy-table id="itemsTable"></gridy-table>
        <gridy-pager id="itemsPager"></gridy-pager>
    </gridy-grid>

Configurations with stored-cfg

hide column

<gridy-table id="gridyTable" stored-cfg='{"columns": {"Name": { "display": "none"}}}'></gridy-table>

set custom styles (in camel-case)

<gridy-table id="gridyTable" stored-cfg='{"columns": {"Name": {"style": {"color": "red"}}}}'></gridy-table>

set column attribute by it's name

<gridy-table id="gridyTable" stored-cfg='{"columns": {"Name": { "attr": {"attr": {"style": "color:red"}}}}'></gridy-table>

reorder columns

<gridy-table id="gridyTable" stored-cfg='{"columnsOrder": ["Color", "Value", "Name"]}'></gridy-table>

you can preset any instance property when specify in stored-props attr json

<gridy-grid id="gridyGrid" base-path="/node_modules/gridy-grid/src" sort-field="$.name" stored-cfg='{"curPageNum": "2"}' stored-props='["curPageNum"]'></gridy-grid>

Table header context menu

To add context menu with settings add gridy-header-menu element to gridy-grid container

<gridy-grid id="gridyGrid">
    <gridy-header-menu></gridy-header-menu>
    ...
</gridy-grid>

Toolbars

To add context menu with settings add gridy-header-menu element to gridy-grid container

    <gridy-toolbar id="gridyToolbar">
        <gridy-toolbar-item>add</gridy-toolbar-item>
        <gridy-toolbar-item>edit</gridy-toolbar-item>
        <gridy-toolbar-item>delete</gridy-toolbar-item>
    </gridy-toolbar>

events

toolbaritemclick - triggered when toolbar item is clicked, detail: item - dom obj of item is clicked, origEvent - mouse click event

Subscribe to lifecycle events

Gridy grid components are emitting events that can help you to hook the process, e.g. you want to do some stuff after table header is rendered. All you have to do is to add event listener to particular event.

    gridyTable.addEventListener('headrender', function(event) {
        console.log('headrender', event, this);
    });

List of events by elements:

gridy-table: headerRowRendered (deprecated) headrowrender headersRendered (deprecated) headrender bodyRowRendered (deprecated) bodyrowrender bodyRendered (deprecated) bodyrender rowclick (bubbles: true, composed: true) itemselect (bubbles: true, composed: true) itemdeselect (bubbles: true, composed: true)

gridy-view: itemselect (bubbles: true, composed: true) itemdeselect (bubbles: true, composed: true)

gridy-table-info: tableInfoRendered

gridy-pager: pageChanged (deprecated) pagechange pagerButtonRendered pagerRendered

gridy-filter: filterChanged (deprecated) filterchange gridy-per-page: perPageChanged (deprecated) perpagechange

Override and Cache Templates

You can place contents inside template tag with matching ids to make renderer overriding templates with by your versions. The highest priority is for lookup inside current html element, then document is searched and after that template path attribute is loaded.

So you can copy markup from theme inside tag, modify and component will use it. Here is the map of template attributes to loaded element ids below.

gridy-table: head-tpl-path -> tableHeadTpl body-tpl-path -> tableBodyTpl row-pane-tpl-path -> tableRowPaneTpl

gridy-table-info: tpl-path -> tableInfoTpl noent-tpl-path -> noEntriesInfoTpl (optional)

gridy-view: tpl-path -> GridyViewTpl : view.tpl.html

gridy-pager: body-tpl-path -> pagerBodyTpl item-active-tpl-path -> pagerActiveItemTpl item-tpl-path -> pagerItemTpl

gridy-filter: tpl-path -> pagerItemTpl

gridy-spinner: tpl-path -> spinnerTpl

gridy-per-page: tpl-path -> perPageTpl

Configuration Attributes

all:

theme - theme name (default: 'default')
base-path - base path to files
config-sl - config tag selector
lang - lang code
tpl-vars - json with data for templates prerendering

gridy-grid:

impl-path - path to impl class
sort-field - sort field jsonpath
sort-direction - sort fied initial direction (default: 'asc')
config-tn - confiruation element tag name (default: 'gridy-config')
table-tn - table element tag name (default: 'gridy-table')
ds-tn - datasource element tag name (default: 'gridy-data-source')
df-tn - datafield element tag name (default: 'gridy-data-field')
filter-tn - filter element tag name (default: 'gridy-filter')
pager-tn - pager element tag name (default: 'gridy-pager')
info-tn - info element tag name (default: 'gridy-table-info')
spinner-tn - spinner element tag name (default: 'gridy-spinner')
per-page-tn - per-page element tag name (default: 'gridy-per-page')
per-page - number of elements per page (default: not set)
cur-page-num - current page number (default: not set)
selectable - user can select rows by click, options: 'multi', (default: not set -> no)

gridy-table:

impl-path - path to impl class
sort-direction
headers | show-headers - show field titles in heading (default: not set -> true)
show-footers - render table footer (default: not set -> false)
head-in-foot - render column names in footer just as in header
sort-field - sort field jsonpath
tpl-path - path to base template
head-tpl-path - path to head template
body-tpl-path - path to body template
foot-tpl-path - path to body template
row-pane-tpl-path - path to row pane template
ds-ref - datasource instance reference
pg-ref - pager instance reference
per-page - number of elements per page (default: not set)
cur-page-num - current page number (default: not set)
selectable - user can select rows by click, options: 'multi', (default: not set -> no)
table-tn - table tag name (default: 'table')
head-tn - table header tag name (default: 'thead')
foot-tn - table footer tag name (default: 'tfoot')
hcell-tn - table footer tag name (default: 'th')
row-tn - table footer tag name (default: 'tr')
cell-tn - table footer tag name (default: 'td')

gridy-view: impl-path - path to impl class tpl-path - path to base template ds-ref - datasource instance reference pg-ref - pager instance reference dri - driver code selectable - user can select rows by click, options: 'multi', (default: not set -> no)

gridy-table-info:

impl-path - path to impl class
tpl-path - path to base template
noent-tpl-path - no entries message template path
lang-path - language relative path
lang-full-path - language full path
per-page - number of elements per page (default: not set)
    

gridy-pager:

impl-path - path to impl class
body-tpl-path - body template path
item-tpl-path - item template path
item-active-tpl-path - active item template path
per-page - number of results per page
cur-page-num - current page number
total-results - total results number
total-pages - total pages number
ds-ref - datasource instance reference

gridy-filter:

impl-path - path to impl class
field-title - filter by particular field (selected by Title)
field-label - overrides label for field
filter-mode - filter mode, "gt" - keep greater values, "ge" - keep greater or equal values, 
	"lt" keep smaler values, "le" - keep smaler or equal values,
	"cntn" - keep by substring containity, "eq" - keep equals, "ne" - keep not equal values
    default: unset -> keep by substring containity

gridy-spinner:

impl-path - path to impl class

gridy-per-page:

impl-path - path to impl class
per-page - number or rows for one page (default: 10)
per-page-options - number or rows for one page (default: 10, 25, 50, 100)
per-page-default - override default per page number (default: not set -> 10)

Loading templates from bundles

http/2+ allows to do request multiplexing that means a lot of smaller requests to the same host are better than big blocking requests to multiple hosts. But if you think you still need to load all in one bundle, you can use aggregated template bundles loaded with js or included to page by server.

<script>
    const loadTemplates = async () => {
        await fetch('node_modules/gridy-grid/dist/gridy-antd.tpls.native.html')
            .then(response => response.text())
            .then(text => document.body.insertAdjacentHTML('beforeend', text));
    };
    loadTemplates();
</script>

Tweak renderer behaviour

You can control template loading/caching behaviour either individually or with gridy-grid element attributes:

rd-cache - enables/disables templates caching and inline overriding (default: enabled)

rd-cache-global - if cached template wasn't found allows to lookup in document (default: enabled)

You can even enable rendering with Handlebars templating library for your overriden templates. To have this you must provide Handlebars static import.

<script src="./node_modules/handlebars/dist/handlebars.js"></script>

or any other way to set global Handlebars instance to window or inject renderer property (not attribute).

Also add option rd-var-render enabled for renderer instance passed to gridy-grid or gridy-table element as attribute. It can be 'handlebars' to load handlebars or loader window function name to call. If attribute is not defined, simplified built-in renderer that support only variables will be used.

There is an option to use handlebars templates format instead of Native Templates to support legacy browsers like Internet Explorer. It can be enabled with rd-tpl-fmt="handlebars" attribute to gridy-grid element. You will also need a kinda transpiler with modules, classes etc. support enabled to be able to see rendering results. In addition handlebars tpl format requires jquery library present in runtime.

Ajax Data Source

Data is loaded from REST service, ant.design framework theming enabled. Paging and filtering components added;

<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/antd/3.19.8/antd.css">

<gridy-filter id="gridyFilter"></gridy-filter>
<gridy-table id="gridyTable"></gridy-table>
<gridy-pager id="gridyPager"></gridy-pager>

<style>
    th.sort.sort-desc::after {
        content: '▼';
    }
    th.sort.sort-asc::after {
        content: '▲';
    }
</style>
<script type="module">
    import { GridyTable } from '/node_modules/gridy-grid/src/table/gridy-table.js';
    import { GridyPager } from '/node_modules/gridy-grid/src/pager/gridy-pager.js';
    import { GridyFilter } from '/node_modules/gridy-grid/src/filter/gridy-filter.js';
    import { DataSourceLocal } from '/node_modules/gridy-grid/src/datasource/data-source-local.js';
    import { DataSourceAjax } from '/node_modules/gridy-grid/src/datasource/data-source-ajax.js';

    //let dataSource = new DataSourceLocal();
    let dataSource = new DataSourceAjax();
    dataSource.fields = [
        { title: 'Title', path: '$.title' },
        { title: 'Price', path: '$.price' }
    ];
    dataSource.url = 'http://127.0.0.1:8080/paged';

    let filter = document.getElementById('gridyFilter');
    filter.tplPath = '/node_modules/gridy-grid/src/theme/antd/filter.tpl.html';
    filter.dataSource = dataSource;

    let pager = document.getElementById('gridyPager');
    pager.dataSource = dataSource;
    pager.theme = 'antd';
    pager.bodyTplPath = '/node_modules/gridy-grid/src/theme/antd/pager-body.tpl.html';
    pager.itemTplPath = '/node_modules/gridy-grid/src/theme/antd/pager-item.tpl.html';
    pager.itemActiveTplPath = '/node_modules/gridy-grid/src/theme/antd/pager-item-active.tpl.html';

    let table = document.getElementById('gridyTable');
    table.dataSource = dataSource;
    table.pager = pager;
    table.filter = filter;

    table.headers = true;
    table.theme = 'antd';
    table.headTplPath = '/node_modules/gridy-grid/src/theme/antd/table-head.tpl.html';
    table.bodyTplPath = '/node_modules/gridy-grid/src/theme/antd/table-body.tpl.html';

    customElements.define('gridy-filter', GridyFilter);
    customElements.define('gridy-table', GridyTable);
    customElements.define('gridy-pager', GridyPager);

</script>

Charts

Gridy has chart component that uses datasources to initialize chart libraries.

e.g. for Chart.js install library

npm i chart.js gridy-chart-chartjs

and plug it to your page

<script src="/node_modules/chart.js/dist/chart.js"></script>

then add element to your container or setup it programmatically

    <gridy-grid id="gridyGrid" base-path="/node_modules/gridy-grid/src" sort-field="$.title">
        <gridy-data-source fields='[{ "title": "Price1", "path": "$.price1", "borderColor": "red", "backgroundColor": "green", "borderWidth": 1 },{ "title": "Price2", "path": "$.price2", "borderColor": "green", "backgroundColor": "yellow", "borderWidth": 1 }]'
                       datasource-type="DataSourceLocal" datapath="$.data"></gridy-data-source>
    
        <gridy-chart dri="chart-js" type="line" width="400" height="300" options='{"scales":{"y":{ "beginAtZero": true }}'></gridy-chart>
    
    </gridy-grid>
    <script>
        let data = [];
        for (let i = 0; i < 10; i++) {
            data.push({ price1: 200 * i, price2: 100 * i })
        }
        let grid = document.querySelector('#gridyGrid');
        grid.addEventListener('bootstrap', () => {
            //grid2.charts[0].dri = function() { return dri }; // you can override driver at runtime
            grid.charts[0].addEventListener('skrender', (event) => {
                grid.dataSource.loadData(data);
            });
        });
    </script>
    

When each field is data provider for own chart set, chart dataset parameter can be passed as field props or attribues

    <gridy-data-source
                       datasource-type="DataSourceLocal" datapath="$.data">
        <gridy-data-field title="Price1" path="$.price1" border-color="red" background-color="green" border-width=1></gridy-data-field>
        <gridy-data-field title="Price2" path="$.price2" border-color="blue" background-color="white" border-width=1></gridy-data-field>
    </gridy-data-source>

To use without gryd-grid container you must specify at leas ds-ref attribue pointing to gridy-data-source element id or setup dataSource programmatically.

    <gridy-data-source id="gridyDs" fields='[{ "title": "x", "path": "$.x" },{ "title": "y", "path": "$.y"},{ "title": "Group", "path": "$.group"},{ "title": "Color", "path": "$.color"}]'
                       datasource-type="DataSourceLocal" datapath="$.data"></gridy-data-source>
	<gridy-chart id="gridyChart" ds-ref="gridyDs" type="line" tooltip dri="d3" field-color="Color" field-x="x" field-y="y" width="400" height="400"></cbr-chart>
    <script type="module">
        let data2 = [];
        data2.push({ x: 100, y: 100, group: 'a', color: '#98abc5' });
        data2.push({ x: 200, y: 150, group: 'a', color: '#98abc5' });
        data2.push({ x: 300, y: 270, group: 'a', color: '#98abc5' });
        data2.push({ x: 400, y: 350, group: 'b', color: '#98abc5' });
        data2.push({ x: 500, y: 460, group: 'b', color: '#98abc5' });
        data2.push({ x: 600, y: 770, group: 'b', color: '#98abc5' });
        data2.push({ x: 700, y: 840, group: 'c', color: '#98abc5' });
        data2.push({ x: 800, y: 990, group: 'c', color: '#98abc5' });
        let chart = document.querySelector('#gridyChart');
        let ds = document.querySelector('#gridyDs');
        
        customElements.define('gridy-data-source', GridyDataSource);
        customElements.define('gridy-chart', GridyChart);
        chart.whenRendered(() => {
            chart.dataSource.loadData(data2);
        });
    </script>

attributes

ds-ref - to specify DataSource element id/global name

pg-ref - to link table to pager

Configuration

attributes

share-chart-dri - share chart driver instance from cache than instancinate, ok when all types of chart on page have the same type

Supported Drivers:

gridy-chart-chartjs

gridy-chart-apexcharts

gridy-chart-d3

gridy-chart-echarts

Views

Gridy has gridy-view that allows extensible and templatable components to display from datasource.

E.g. you can install gridy-view-icongrid package and use it transparently.

<gridy-grid id="gridyGrid2" base-path="/node_modules/gridy-grid/src" sort-field="$.title">
    <gridy-data-source fields='[{ "title": "Name", "path": "$.name" },{ "title": "Value", "path": "$.value"},{ "title": "Group", "path": "$.group"}]'
                       datasource-type="DataSourceLocal" datapath="$.data"></gridy-data-source>
	<gridy-view

        dri="icongrid" id="gridyChart2" field-name="Name" field-value="Value" width="400" height="400"></gridy-view>

    <gridy-pager id="gridyPager2"></gridy-pager>
</gridy-grid>

attributes

dri - driver code

field-{name}={title} - field mappings from data sources, valus will be mapped to rendering context by {name}

tpl-path - path for template to load

item-tpl-path - path for item template to load

item-tpl-str - template item with string value

tpl-vars - variables for prerendering templates

impl-path - path to theme based impl override

ds-ref - to specify DataSource element id/global name

pg-ref - to link table to pager

selectable - enables ability to select items, selection can be taken from props selectedItems, selectedItem, prevSelectedItem, possible values: multi or any/empty means single, default (empty -> single selection)

selection-emit - choses how selection will be emited, e.g. on long click/tap or click, possible values: "tap" - select by long click/tap, "click", "ctrl+click", "shift+click", "alt+click" (default: no -> click)

time-to-select - time to select for tap mode in miliseconds, (default: 700x)

share-dri - share chart driver instance from cache than instancinate, ok when all types of chart on page have the same type

Supported Drivers:

gridy-view-icongrid

Internationalization

gridy-grid element supports lang attribued that allows developer to provide elements translated by locale resources. Resources for translation are shipped with themes.

    <gridy-grid theme="antd" id="gridyGrid" lang="ru_RU">
        <gridy-filter id="gridyFilter"></gridy-filter>
        <gridy-table id="gridyTable"></gridy-table>
        <gridy-pager id="gridyPager"></gridy-pager>
    </gridy-grid>

Plugins

You can use plugins as derived from SkElement, with possibility to load from element attributes configuration as follows

	<gridy-chart id="gridyChart2" title="Foobar" legend='{"data": ["sales", "markt"], "orient": "vertical", "right": 10, "top": "center"}'
         options='{"xAxis": { "data": "names" }, "yAxis": {}}' 
         type="multi:bar:sales,line:markt" dri="echarts" echarts-renderer="svg"
         attr-plugins="true" plg-foo-cn="MyPlugin" plg-foo-path="/my-plugin.js"
         value-field field-name="Name" field-value="Value" field-group="Group" inner-radius="50" width="400" height="400"></gridy-chart>

Use with compatibility bundle (old browser support)

To have browsers up to IE11 supported you definitely need to load WebComponents polyfills.

Rendering engine must be changed to handlebars.

Also jquery needs to be loaded before bundle as it is used to generate table markup.

Initializing code must be wrapped into WebComponentsReady hook event.

In the example below polyfills from skinny-widgets library are used. You should install it or plug your own.

npm i skinny-widgets --save
<script src="/node_modules/skinny-widgets/compat/dialog-polyfill.js"></script>
<script src="/node_modules/skinny-widgets/compat/fetch.umd.js"></script>
<script src="/node_modules/skinny-widgets/compat/polyfill.min.js"></script>
<script src="/node_modules/skinny-widgets/compat/webcomponents-lite.js"></script>
<script src="/node_modules/skinny-widgets/compat/custom-elements-es5-adapter.js"></script>
<script src="/node_modules/skinny-widgets/compat/event-target.js"></script>

<link rel="stylesheet" type="text/css" href="/node_modules/gridy-grid/antd.min.css">

<sk-config 
        theme="antd" 
        base-path="/node_modules/gridy-grid/src" 
        lang="ru" 
        id="gridConfig"
></sk-config>

<gridy-table config-sl="#gridConfig" id="grid" pgref="pager" rd-tpl-fmt="handlebars"></gridy-table>
<gridy-pager config-sl="#gridConfig" id="pager" dsref="dataSource"></gridy-pager>

<script src="/node_modules/skinny-widgets/dist/skinny-widgets-bundle.js"></script>
<script src="/node_modules/jquery/dist/jquery.js"></script>
<script src="/node_modules/gridy-grid/dist/gridy-grid-bundle.js"></script>
<script>
    window.addEventListener('WebComponentsReady', function(e) {
        var dataSource = new DataSourceLocal();
        dataSource.fields = [
            { title: 'Title', path: '$.title' },
            { title: 'Price', path: '$.price' }
        ];
        var data = [];
        for (let i = 0; i < 25; i++) {
            data.push({ title: 'row' + i, price: 100 * i })
        }
        grid.dataSource = dataSource;
        
        customElements.define('sk-config', SkConfig);
        customElements.define('gridy-pager', GridyPager);
        customElements.define('gridy-table', GridyTableAntd4);
        dataSource.loadData(data);
    });
</script>

Development

You can run sample data backend and index.html serving from this project's root.

npm run server

Use with SystemJS module loader

Gridy Grid currently has only one external runtime dependency that is actually source bundled to avoid problems in untranspilled environments. So in case you want to use that library (complets) from it's own npm package or somewhere else e.g. shared bundle you may use SystemJS loader map to resolve Gridy's internal dependencies.

First add and setup module loader for your project.

npm i systemjs systemjs-transform-babel --save

And do configuration that will transpile and resolve the code.

<script src="/node_modules/systemjs/dist/system.js"></script>
<script src="/node_modules/systemjs/dist/extras/transform.js"></script>
<script src="/node_modules/systemjs-transform-babel/dist/babel-transform.js"></script>
<script type="systemjs-importmap">
{
  "imports": {
    "gridy-grid": "./node_modules/gridy-grid/src/gridy-grid.js",
    "data-source-local": "./node_modules/gridy-grid/src/datasource/data-source-local.js",
    "/node_modules/gridy-grid/complets/": "/node_modules/complets/"
  }
}
</script>
<script>
    Promise.all([System.import('gridy-grid'), System.import('data-source-local')]).then((m) => {
        let dataSource = new m[1].DataSourceLocal();
        //let dataSource = new DataSourceAjax();
        dataSource.fields = [
            { title: 'Title', path: '$.title' },
            { title: 'Price', path: '$.price' }
        ];
        let grid = document.querySelector('#gridyGrid');
        grid.dataSource = dataSource;

        customElements.define('gridy-grid', m[0].GridyGrid);

        // generate sample data for DataSourceLocal
        let data = [];
        for (let i = 0; i < 25; i++) {
            data.push({ title: 'row' + i, price: 100 * i })
        }
        dataSource.loadData(data);
    });
</script>

Update complets library version

git submodule update --remote --merge

Running tests

open test/tests.html from browser with statically served project root

or install webcomponents test runner

sudo npm install --global web-component-tester --unsafe-perm

and run with

wct --npm
2.1.2

2 years ago

2.0.28

2 years ago

2.0.29

2 years ago

2.1.1

2 years ago

2.1.0

2 years ago

2.0.26

2 years ago

2.0.27

2 years ago

2.0.25

2 years ago

2.0.24

2 years ago

2.0.22

2 years ago

2.0.23

2 years ago

2.0.21

2 years ago

2.0.15

2 years ago

2.0.16

2 years ago

2.0.13

2 years ago

2.0.14

2 years ago

2.0.19

2 years ago

2.0.17

2 years ago

2.0.18

2 years ago

2.0.20

2 years ago

2.0.12

2 years ago

2.0.11

3 years ago

2.0.10

3 years ago

2.0.9

3 years ago

2.0.8

3 years ago

2.0.7

3 years ago

2.0.5

3 years ago

2.0.6

3 years ago

2.0.3

3 years ago

2.0.4

3 years ago

2.0.2

3 years ago

2.0.1

3 years ago

2.0.0

3 years ago

1.4.4

4 years ago

1.4.3

4 years ago

1.4.2

4 years ago

1.4.1

4 years ago

1.4.0

4 years ago

1.3.34

4 years ago

1.3.32

4 years ago

1.3.33

4 years ago

1.3.31

4 years ago

1.3.30

4 years ago

1.3.29

4 years ago

1.3.28

4 years ago

1.3.27

4 years ago

1.3.26

4 years ago

1.3.25

4 years ago

1.3.24

4 years ago

1.3.23

4 years ago

1.3.22

4 years ago

1.3.21

4 years ago

1.3.20

4 years ago

1.3.19

4 years ago

1.3.18

4 years ago

1.3.17

4 years ago

1.3.16

4 years ago

1.3.15

4 years ago

1.3.14

4 years ago

1.3.13

4 years ago

1.3.12

5 years ago

1.3.11

5 years ago

1.3.10

5 years ago

1.3.9

5 years ago

1.3.8

5 years ago

1.3.7

5 years ago

1.3.6

5 years ago

1.3.5

5 years ago

1.3.4

5 years ago

1.3.3

5 years ago

1.3.2

5 years ago

1.3.0

5 years ago

1.2.10

5 years ago

1.2.9

5 years ago

1.2.8

5 years ago

1.2.7

5 years ago

1.2.6

5 years ago

1.2.5

5 years ago

1.2.4

5 years ago

1.2.3

5 years ago

1.2.2

5 years ago

1.2.1

5 years ago

1.2.0

5 years ago

1.1.3

5 years ago

1.1.2

5 years ago

1.1.1

5 years ago

1.1.0

5 years ago

1.0.1

5 years ago

1.0.0

5 years ago