Compare commits

..

2 Commits

Author SHA1 Message Date
Faris Ansari
da0d731670 feat: groupBy
first cut
2018-07-07 20:57:05 +05:30
Faris Ansari
c49bc20f99 Apply sortOrder if it is set in initialization 2018-07-05 14:17:55 +05:30
58 changed files with 15090 additions and 8584 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@ -35,6 +35,7 @@
"block-scoped-var": 2,
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
"camelcase": [2, { "properties": "always" }],
"comma-dangle": [2, "never"],
"comma-spacing": [2, { "before": false, "after": true }],
"comma-style": [2, "last"],
"complexity": 0,

View File

@ -1,24 +0,0 @@
name: Test and Release
on:
push:
branches:
- master
jobs:
test-and-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Lint, build and test
uses: cypress-io/github-action@v2
with:
build: yarn lint-and-build
start: yarn cy:server
record: true
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Release
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: yarn semantic-release

View File

@ -1,16 +0,0 @@
name: Test Pull Request
on: pull_request
jobs:
test-pull-request:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Lint, build and test
uses: cypress-io/github-action@v2
with:
build: yarn lint-and-build
start: yarn cy:server
record: true
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

7
.gitignore vendored
View File

@ -37,9 +37,4 @@ npm-debug.log.*
# cypress
cypress/screenshots
cypress/videos
# dist
dist
.env
cypress/videos

12
.travis.yml Normal file
View File

@ -0,0 +1,12 @@
language: node_js
node_js:
- 8
cache:
directories:
- ~/.npm
- ~/.cache
- node_modules
install:
- npm install
script:
- npm test

View File

@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2018 Frappe Technologies Pvt. Ltd. <developers@frappe.io>
Copyright (c) 2018 Frappé Technologies Pvt. Ltd. <developers@frappe.io>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,37 +1,54 @@
<div align="center" markdown="1">
<div align="center">
<img src="https://github.com/frappe/design/blob/master/logos/data-table-logo.svg" height="128">
<h2>Frappe DataTable</h2>
<p align="center">
<p>
A modern datatable library for the web
</p>
<img width="80" alt="datatable-logo" src="https://github.com/user-attachments/assets/8235f4b9-993a-4329-97de-9431dcf63aae" >
<h1>Frappe DataTable</h1>
**A modern datatable library for the web**
[![Test and Release](https://github.com/frappe/datatable/workflows/Test%20and%20Release/badge.svg)](https://github.com/frappe/datatable/actions?query=workflow%3A%22Test+and+Release%22)
[![travis build](https://api.travis-ci.com/frappe/datatable.svg?branch=master)](https://travis-ci.org/frappe/datatable)
[![npm version](https://badge.fury.io/js/frappe-datatable.svg)](https://badge.fury.io/js/frappe-datatable)
![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/frappe-datatable.svg)
![datatable-demo-2](https://user-images.githubusercontent.com/9355208/40740030-5412aa40-6465-11e8-8542-b0247ab1daac.gif)
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT)
</div>
## Frappe Datatable
## Introduction
Frappe DataTable is a simple, modern and interactive datatable library for displaying tabular data. Originally built for [ERPNext](https://github.com/frappe/erpnext), it can be used to render large amount of rows without sacrificing performance and has the basic data grid features like inline editing and keyboard navigation. It does not require jQuery, unlike most data grids out there.
Frappe DataTable is a simple, modern and interactive datatable library for displaying tabular data. Originally built for [ERPNext](https://github.com/frappe/erpnext), it can be used to render large amount of rows without sacrificing performance and has the basic data grid features like inline editing and keyboard navigation. It does not require jQuery, like most data grids out there.
### Motivation
## Demo
I was trying to remove all legacy UI components from the [frappe](https://github.com/frappe/frappe) codebase. We were using [SlickGrid](https://github.com/mleibman/SlickGrid) for rendering tables. It was unmaintained and UI was dated. Other datatable solutions either didn't have the features we needed or were closed source. So we built our own.
![datatable-demo-2](https://user-images.githubusercontent.com/9355208/40740030-5412aa40-6465-11e8-8542-b0247ab1daac.gif)
## Features
### Key Features
### Cell Features
- **Cell**: Enable editing within individual cells and features like custom formatters, inline editing, and mouse selection. Users can easily copy cell content, navigate through cells using the keyboard, and take advantage of a custom cell editor for advanced functionality.
- **Column**: Columns are highly flexible, allowing users to reorder, resize, and sort them with ease. Additional features include hiding/removing columns and adding custom actions.
- **Row**: Rows support advanced interactions, including row selection, tree-structured organization, and inline filters for precise control. They handle large datasets efficiently with dynamic row heights.
* Custom Formatters
* Inline Editing
* Mouse Selection
* Copy Cells
* Keyboard Navigation
* Custom Cell Editor
### Column Features
## Usage
* Reorder Columns
* Sort by Column
* Remove / Hide Column
* Custom Actions
* Resize Column
* Flexible Layout
### Row Features
* Row Selection
* Tree Structured Rows
* Inline Filters
* Large Number of Rows
* Dynamic Row Height
## Install
```bash
yarn add frappe-datatable
@ -39,8 +56,9 @@ yarn add frappe-datatable
npm install frappe-datatable
```
> Note: [`sortablejs`](https://github.com/RubaXa/Sortable) is required to be installed as well.
> Note: [`sortablejs`](https://github.com/RubaXa/Sortable) and [`clusterize.js`](https://github.com/NeXTs/Clusterize.js) are required to be installed as well.
## Usage
```js
const datatable = new DataTable('#datatable', {
@ -52,24 +70,13 @@ const datatable = new DataTable('#datatable', {
});
```
## Development Setup
## Contribution
* `yarn start` - Start dev server
* Open `index.html` located in the root folder, and start development.
* Run `yarn lint` before committing changes
* This project uses [commitizen](https://github.com/commitizen/cz-cli) for conventional commit messages, use `yarn commit` command instead of `git commit`
## Links
Now you can open the `index.html` located in the root folder, and start development. Make sure to fix all eslint errors before making a Pull Request.
- [Making a new datatable for the web](https://medium.com/frapp%C3%A9-thoughts/things-i-learned-building-a-library-for-the-web-6846a588bf53)
<br>
<br>
<div align="center" style="padding-top: 0.75rem;">
<a href="https://frappe.io" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://frappe.io/files/Frappe-white.png">
<img src="https://frappe.io/files/Frappe-black.png" alt="Frappe Technologies" height="28"/>
</picture>
</a>
</div>
## License
[MIT](http://opensource.org/licenses/MIT)

View File

@ -1,4 +1,3 @@
{
"baseUrl": "http://localhost:8989",
"projectId": "2nsyux"
"baseUrl": "http://localhost:8989"
}

View File

@ -1,84 +0,0 @@
describe('Cell', function () {
before(function () {
cy.visit('/');
});
it('focuses cell on click', function () {
cy.clickCell(2, 0)
.should('have.class', 'dt-cell--focus');
});
it('not focuses cell which are not focusable', function () {
cy.clickCell(1, 0)
.should('not.have.class', 'dt-cell--focus');
});
it('edit cell on enter press', function () {
cy.getCell(4, 0).type('{enter}')
.should('have.class', 'dt-cell--editing')
.type('{enter}')
.should('not.have.class', 'dt-cell--editing');
});
it('edit cell on double click', function () {
cy.getCell(4, 0)
.as('target')
.dblclick({ force: true })
.should('have.class', 'dt-cell--editing');
cy.clickCell(3, 0);
cy.get('@target').should('not.have.class', 'dt-cell--editing');
});
it('edit cell', function () {
cy.getCell(4, 1).dblclick({ force: true });
cy.getCell(4, 1).find('input').click();
cy.focused().type('{selectall}{del}Test{enter}');
cy.getCell(4, 1).contains('Test');
});
it('if editing is false: editing should not activate', function () {
cy.getCell(3, 0).dblclick({ force: true })
.should('not.have.class', 'dt-cell--editing');
});
it('navigation using arrow keys', function () {
cy.clickCell(2, 0)
.type('{rightarrow}');
cy.get('.dt-cell--focus')
.should('have.class', 'dt-cell--3-0')
.click({ force: true })
.type('{downarrow}');
cy.get('.dt-cell--focus')
.should('have.class', 'dt-cell--3-1');
// TODO: test navigation over hidden rows
});
it('navigation using ctrl + arrow keys', function () {
cy.clickCell(2, 0)
.type('{ctrl}{rightarrow}');
cy.get('.dt-cell--focus')
.should('have.class', 'dt-cell--11-0');
});
it('cell selection using shift + arrow keys', function () {
cy.getCell(2, 1)
.type('{shift}{rightarrow}{rightarrow}{downarrow}');
// 6 cells and 2 headers
cy.get('.dt-cell--highlight').should('have.length', 6 + 2);
cy.clickCell(2, 0);
});
it('mouse selection', function () {
// TODO:
// cy.getCell(2, 1)
// .trigger('mousedown', { which: 1, pageX: 331, pageY: 207, force: true })
// .trigger('mousemove', { which: 1, pageX: 489, pageY: 312 })
// .trigger('mouseup');
});
});

View File

@ -1,70 +0,0 @@
describe('Column', function () {
before(function () {
cy.visit('/');
});
it('header dropdown toggles on click', function () {
cy.getColumnCell(2)
.find('.dt-dropdown__toggle')
.as('toggle')
.click();
cy.get('.dt-dropdown__list')
.as('dropdown-list')
.should('be.visible');
cy.getColumnCell(2).click();
cy.get('@dropdown-list').should('not.be.visible');
});
it('sort ascending button should work', function () {
cy.clickDropdown(2);
cy.clickDropdownItem(2, 'Sort Ascending');
cy.window().then(win => win.datatable.getColumn(2))
.its('sortOrder')
.should('eq', 'asc');
cy.window().then(win => win.datatable.datamanager)
.its('currentSort.colIndex')
.should('eq', 2);
cy.get('.dt-scrollable .dt-row:first div:nth-of-type(3)')
.contains('Airi Satou');
cy.clickDropdownItem(2, 'Reset sorting');
});
it('removes column using dropdown action', function () {
cy.get('.dt-cell--header').should('have.length', 12);
cy.clickDropdown(5);
cy.clickDropdownItem(5, 'Remove column');
cy.get('.dt-cell--header').should('have.length', 11);
});
it('resize column with mouse drag', function () {
cy.get('.dt-cell--header-4 .dt-cell__resize-handle').as('resize-handle');
cy
.get('@resize-handle')
.trigger('mousedown')
.trigger('mousemove', { pageX: 700, pageY: 20, which: 1 })
.trigger('mouseup');
cy.getColumnCell(4).invoke('css', 'width').then((width) => {
cy.getColumnCell(4)
.should('have.css', 'width', width);
cy.getCell(4, 1)
.should('have.css', 'width', width);
});
});
it('resize column using double click', function () {
cy.get('.dt-cell--header-4 .dt-cell__resize-handle').trigger('dblclick');
cy.getColumnCell(4).should('have.css', 'width')
.and('match', /9\dpx/);
cy.getCell(4, 1).should('have.css', 'width')
.and('match', /9\dpx/);
});
});

View File

@ -0,0 +1,165 @@
describe('DataTable', function () {
it('instance is created without any errors', () => {
cy.visit('/');
cy.window()
.its('DataTable')
.then(DataTable => {
// eslint-disable-next-line
new DataTable('#datatable2', {
columns: ['Name', 'Position'],
data: [
['Faris', 'Developer']
]
});
});
cy.get('#datatable2 .datatable')
.contains('Faris');
});
describe('Cell', function () {
before(function () {
cy.visit('/');
});
it('focuses cell on click', function () {
cy.clickCell(2, 0)
.should('have.class', 'dt-cell--focus');
});
it('not focuses cell which are not focusable', function () {
cy.clickCell(1, 0)
.should('not.have.class', 'dt-cell--focus');
});
it('edit cell on enter press', function () {
cy.getCell(4, 0).type('{enter}')
.should('have.class', 'dt-cell--editing')
.type('{enter}')
.should('not.have.class', 'dt-cell--editing');
});
it('edit cell on double click', function () {
cy.getCell(4, 0)
.as('target')
.dblclick({ force: true })
.should('have.class', 'dt-cell--editing');
cy.clickCell(3, 0);
cy.get('@target').should('not.have.class', 'dt-cell--editing');
});
it('edit cell', function () {
cy.getCell(4, 1).dblclick();
cy.getCell(4, 1).find('input').click();
cy.focused().type('{selectall}{del}Test{enter}');
cy.getCell(4, 1).contains('Test');
});
it('if editing is false: editing should not activate', function () {
cy.getCell(3, 0).dblclick({ force: true })
.should('not.have.class', 'dt-cell--editing');
});
it('navigation using arrow keys', function () {
cy.clickCell(2, 0)
.type('{rightarrow}');
cy.get('.dt-cell--focus')
.should('have.class', 'dt-cell--3-0')
.click({ force: true })
.type('{downarrow}');
cy.get('.dt-cell--focus')
.should('have.class', 'dt-cell--3-1');
// TODO: test navigation over hidden rows
});
it('navigation using ctrl + arrow keys', function () {
cy.clickCell(2, 0)
.type('{ctrl}{rightarrow}');
cy.get('.dt-cell--focus')
.should('have.class', 'dt-cell--7-0');
});
it('cell selection using shift + arrow keys', function () {
cy.getCell(2, 1)
.type('{shift}{rightarrow}{rightarrow}{downarrow}');
// 6 cells and 2 headers
cy.get('.dt-cell--highlight').should('have.length', 6 + 2);
cy.clickCell(2, 0);
});
it('mouse selection', function () {
// TODO
// cy.getCell(2, 1)
// .trigger('mousedown', { which: 1, pageX: 331, pageY: 207, force: true })
// .trigger('mousemove', { which: 1, pageX: 489, pageY: 312 })
// .trigger('mouseup');
});
});
describe('Column', function () {
before(function () {
cy.visit('/');
});
it('header dropdown toggles on click', function () {
cy.getColumnCell(2)
.find('.dt-dropdown__toggle')
.as('toggle')
.click();
cy.getColumnCell(2)
.find('.dt-dropdown__list')
.as('dropdown-list')
.should('be.visible');
cy.get('@toggle').click();
cy.get('@dropdown-list').should('not.be.visible');
});
it('sort ascending button should work', function () {
cy.clickDropdown(2);
cy.clickDropdownItem(2, 'Sort Ascending');
cy.window().then(win => win.datatable.getColumn(2))
.its('sortOrder')
.should('eq', 'asc');
cy.window().then(win => win.datatable.datamanager)
.its('currentSort.colIndex')
.should('eq', 2);
cy.get('.dt-body .dt-row:first')
.contains('Airi Satou');
cy.clickDropdownItem(2, 'Reset sorting');
});
it('removes column using dropdown action', function () {
cy.get('.dt-cell--header').should('have.length', 8);
cy.clickDropdown(5);
cy.clickDropdownItem(5, 'Remove column');
cy.get('.dt-cell--header').should('have.length', 7);
});
});
describe('Row', function () {
it('check / uncheck row', function () {
cy.get('.dt-body .dt-row:first')
.find('input[type="checkbox"]')
.click();
cy.get('[data-row-index="0"]').should('have.class', 'dt-row--highlight');
cy.get('.dt-toast').contains('1 row selected');
});
});
});

View File

@ -1,89 +0,0 @@
describe('Inline Filters', function () {
before(function () {
cy.visit('/');
});
beforeEach(function () {
cy.get('.dt-filter[data-col-index=4]').as('filterInput4');
cy.get('.dt-filter[data-col-index=5]').as('filterInput5');
cy.get('.dt-filter[data-col-index=6]').as('filterInput6');
cy.get('.dt-row[data-row-index=0]').should('be.visible');
});
it('simple text filter', function () {
cy.getCell(4, 0).click().type('{ctrl}f');
cy.get('@filterInput4').type('edin');
cy.get('.dt-row-0').should('be.visible');
cy.get('.dt-row-1').should('not.exist');
cy.get('@filterInput4').clear();
});
it('simple number filter', function () {
cy.get('@filterInput5').type('2360');
cy.get('.dt-row[data-row-index=8]').should('be.visible');
cy.get('.dt-row[data-row-index=15]').should('not.exist');
cy.get('.dt-row[data-row-index=22]').should('not.exist');
cy.get('@filterInput5').clear();
});
it('greater than', function () {
cy.get('@filterInput5').type('> 6000');
cy.get('.dt-row[data-row-index=0]').should('not.exist');
cy.get('.dt-row[data-row-index=3]').should('be.visible');
cy.get('@filterInput5').clear();
});
it('less than', function () {
cy.get('@filterInput5').type('< 2000');
cy.get('.dt-row[data-row-index=0]').should('not.exist');
cy.get('.dt-row[data-row-index=51]').should('be.visible');
cy.get('@filterInput5').clear();
});
it('range', function () {
cy.get('@filterInput5').type(' 2000: 5000');
cy.get('.dt-row[data-row-index=4]').should('not.exist');
cy.get('.dt-row[data-row-index=5]').should('be.visible');
cy.get('@filterInput5').clear();
});
it('equals', function () {
cy.get('@filterInput5').type('=9608');
cy.get('.dt-row-6').should('be.visible');
cy.get('@filterInput5').clear();
});
it('multiple filters', function () {
cy.get('@filterInput4').type('to');
cy.get('@filterInput5').type('54');
cy.get('.dt-row[data-row-index=4]').should('be.visible');
cy.get('.dt-row[data-row-index=1]').should('not.exist');
cy.get('@filterInput4').clear();
cy.get('@filterInput5').clear();
});
it('greater than for string type filters', function () {
cy.get('@filterInput6').type('> 01/07/2011');
cy.get('.dt-row[data-row-index=0]').should('not.exist');
cy.get('.dt-row[data-row-index=1]').should('be.visible');
cy.get('.dt-row[data-row-index=3]').should('be.visible');
cy.get('.dt-row[data-row-index=5]').should('be.visible');
cy.get('@filterInput6').clear();
});
it('filters with sorting', function () {
cy.visit('/');
cy.clickDropdown(7);
cy.clickDropdownItem(7, 'Sort Descending');
cy.get('.dt-filter[data-col-index=5]').as('filterInput5');
cy.getCell(5, 24).click().type('{ctrl}f');
cy.get('@filterInput5').type('>3000', {delay: 100});
cy.get('.dt-scrollable .dt-row:first')
.should('contain', 'Angelica')
.should('have.class', 'dt-row-24');
cy.get('@filterInput5').clear();
});
});

View File

@ -1,20 +0,0 @@
describe('DataTable init', function () {
it('instance is created without any errors', () => {
cy.visit('/');
cy.window()
.its('DataTable')
.then(DataTable => {
// eslint-disable-next-line
new DataTable('#datatable2', {
columns: ['Name', 'Position'],
data: [
['Faris', 'Developer']
]
});
});
cy.get('#datatable2 .datatable')
.contains('Faris');
});
});

View File

@ -1,15 +0,0 @@
describe('Row', function () {
before(function () {
cy.visit('/');
});
it('check / uncheck row', function () {
cy.get('.dt-scrollable .dt-row:first')
.find('input[type="checkbox"]')
.click();
cy.get('[data-row-index="0"]').should('have.class', 'dt-row--highlight');
cy.get('.dt-toast').contains('1 row selected');
});
});

View File

@ -12,6 +12,6 @@
// the project's config changing)
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
};
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}

View File

@ -43,15 +43,7 @@ Cypress.Commands.add('clickDropdown', (col) => {
});
Cypress.Commands.add('clickDropdownItem', (col, item) => {
return cy.get(`.dt-dropdown__list-item:contains("${item}")`)
return cy.getColumnCell(col)
.find(`.dt-dropdown__list-item:contains("${item}")`)
.click({ force: true });
});
Cypress.Commands.add('typeTab', (shiftKey, ctrlKey) => {
cy.focused().trigger('keydown', {
keyCode: 9,
which: 9,
shiftKey: shiftKey,
ctrlKey: ctrlKey
});
});

282
dist/frappe-datatable.cjs.css vendored Normal file
View File

@ -0,0 +1,282 @@
/* This file is processed by postcss */
/* variables */
.data-table {
/* styling */
position: relative;
overflow: auto;
}
/* resets */
.data-table *, .data-table *::after, .data-table *::before {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.data-table button, .data-table input {
overflow: visible;
font-family: inherit;
font-size: inherit;
line-height: inherit;
margin: 0;
padding: 0;
}
.data-table .input-style {
outline: none;
width: 100%;
border: none;
}
.data-table *, .data-table *:focus {
outline: none;
border-radius: 0px;
-webkit-box-shadow: none;
box-shadow: none;
}
.data-table table {
border-collapse: collapse;
}
.data-table table td {
padding: 0;
border: 1px solid #d1d8dd;
}
.data-table thead td {
border-bottom-width: 1px;
}
.data-table .freeze-container {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-ms-flex-line-pack: center;
align-content: center;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: #f5f7fa;
opacity: 0.5;
font-size: 2em;
}
.data-table .freeze-container span {
position: absolute;
top: 50%;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
}
.data-table .hide {
display: none;
}
.data-table .toast-message {
position: absolute;
bottom: 16px;
bottom: 1rem;
left: 50%;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
}
.data-table .toast-message span {
display: inline-block;
background-color: rgba(0, 0, 0, .8);
color: #dfe2e5;
border-radius: 3px;
padding: 8px 16px;
padding: 0.5rem 1rem;
}
.body-scrollable {
max-height: 500px;
overflow: auto;
border-bottom: 1px solid #d1d8dd;
}
.body-scrollable.row-highlight-all .data-table-row:not(.row-unhighlight) {
background-color: #f5f7fa;
}
.data-table-header {
position: absolute;
top: 0;
left: 0;
background-color: white;
font-weight: bold;
}
.data-table-header .content span:not(.column-resizer) {
cursor: pointer;
}
.data-table-header .column-resizer {
display: none;
position: absolute;
right: 0;
top: 0;
width: 4px;
width: 0.25rem;
height: 100%;
background-color: rgb(82, 146, 247);
cursor: col-resize;
}
.data-table-header .data-table-dropdown {
position: absolute;
right: 10px;
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
vertical-align: top;
text-align: left;
}
.data-table-header .data-table-dropdown.is-active .data-table-dropdown-list {
display: block;
}
.data-table-header .data-table-dropdown.is-active .data-table-dropdown-toggle {
display: block;
}
.data-table-header .data-table-dropdown-toggle {
display: none;
background-color: transparent;
border: none;
}
.data-table-header .data-table-dropdown-list {
display: none;
font-weight: normal;
position: absolute;
min-width: 128px;
min-width: 8rem;
top: 100%;
right: 0;
z-index: 1;
background-color: white;
border-radius: 3px;
-webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
padding-bottom: 8px;
padding-bottom: 0.5rem;
padding-top: 8px;
padding-top: 0.5rem;
}
.data-table-header .data-table-dropdown-list> div {
padding: 8px 16px;
padding: 0.5rem 1rem;
}
.data-table-header .data-table-dropdown-list> div:hover {
background-color: #f5f7fa;
}
.data-table-header .data-table-cell.remove-column {
background-color: #FD8B8B;
-webkit-transition: 300ms background-color ease-in-out;
transition: 300ms background-color ease-in-out;
}
.data-table-header .data-table-cell.sortable-chosen {
background-color: #f5f7fa;
}
.data-table-cell {
position: relative;
}
.data-table-cell .content {
padding: 8px;
padding: 0.5rem;
border: 2px solid transparent;
}
.data-table-cell .content.ellipsis {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.data-table-cell .edit-cell {
display: none;
padding: 8px;
padding: 0.5rem;
background: #fff;
z-index: 1;
height: 100%;
}
.data-table-cell.selected .content {
border: 2px solid rgb(82, 146, 247);
}
.data-table-cell.editing .content {
display: none;
}
.data-table-cell.editing .edit-cell {
border: 2px solid rgb(82, 146, 247);
display: block;
}
.data-table-cell.highlight {
background-color: #f5f7fa;
}
.data-table-cell:hover .column-resizer {
display: inline-block;
}
.data-table-cell:hover .data-table-dropdown-toggle {
display: block;
}
.data-table-cell .tree-node {
display: inline-block;
position: relative;
}
.data-table-cell .toggle {
display: inline-block;
position: absolute;
padding: 0 4px;
cursor: pointer;
}
.data-table-cell .toggle:before {
content: '▼';
}
.data-table-cell.tree-close .toggle:before {
content: '►';
}
.data-table-row.row-highlight {
background-color: #f5f7fa;
}
.noselect {
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
body.data-table-resize {
cursor: col-resize;
}

3764
dist/frappe-datatable.cjs.js vendored Normal file

File diff suppressed because it is too large Load Diff

257
dist/frappe-datatable.css vendored Normal file
View File

@ -0,0 +1,257 @@
.datatable *, .datatable *::after, .datatable *::before {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.datatable {
position: relative;
overflow: auto;
}
.dt-header {
border-collapse: collapse;
border-bottom: 1px solid #d1d8dd;
position: absolute;
top: 0;
left: 0;
background-color: #fff;
}
.dt-body {
border-collapse: collapse;
}
.dt-scrollable {
max-height: 40vw;
overflow: auto;
border-bottom: 1px solid #d1d8dd;
}
.dt-scrollable--highlight-all {
background-color: #fffce7;
}
.dt-scrollable__no-data {
text-align: center;
padding: 16px;
padding: 1rem;
border-left: 1px solid #d1d8dd;
border-right: 1px solid #d1d8dd;
}
.dt-row--highlight {
background-color: #fffce7;
}
.dt-row--unhighlight {
background-color: #fff;
}
.dt-row--hide {
display: none;
}
.dt-cell {
border: 1px solid #d1d8dd;
position: relative;
outline: none;
padding: 0;
}
.dt-cell__content {
padding: 8px;
padding: 0.5rem;
border: 2px solid transparent;
height: 100%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.dt-cell__edit {
display: none;
padding: 8px;
padding: 0.5rem;
background-color: #fff;
border: 2px solid rgb(255, 160, 10);
z-index: 1;
height: 100%;
}
.dt-cell__resize-handle {
opacity: 0;
position: absolute;
right: -3px;
top: 0;
width: 5px;
height: 100%;
cursor: col-resize;
z-index: 1;
}
.dt-cell--editing .dt-cell__content {
display: none;
}
.dt-cell--editing .dt-cell__edit {
display: block;
}
.dt-cell--focus .dt-cell__content {
border-color: rgb(82, 146, 247);
}
.dt-cell--highlight {
background-color: #f5f7fa;
}
.dt-cell--dragging {
background-color: #f5f7fa;
}
.dt-cell--header .dt-cell__content {
padding-right: 16px;
padding-right: 1rem;
font-weight: bold;
}
.dt-cell--header:hover .dt-dropdown__toggle {
opacity: 1;
}
.dt-cell--tree-close .dt-tree-node__toggle:before {
content: '►';
}
.dt-dropdown {
position: absolute;
right: 10px;
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
vertical-align: top;
text-align: left;
font-weight: normal;
cursor: pointer;
}
.dt-dropdown__toggle {
opacity: 0;
}
.dt-dropdown__list {
display: none;
position: absolute;
min-width: 128px;
min-width: 8rem;
top: 100%;
right: 0;
z-index: 1;
background-color: #fff;
border-radius: 3px;
padding: 8px 0;
padding: 0.5rem 0;
-webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
}
.dt-dropdown__list-item {
padding: 8px 16px;
padding: 0.5rem 1rem;
}
.dt-dropdown__list-item:hover {
background-color: #f5f7fa;
}
.dt-dropdown--active .dt-dropdown__list {
display: block;
}
.dt-tree-node {
display: inline-block;
position: relative;
}
.dt-tree-node__toggle {
display: inline-block;
position: absolute;
font-size: 10px;
padding: 1px;
cursor: pointer;
}
.dt-tree-node__toggle:before {
content: '▼';
}
.dt-toast {
position: absolute;
bottom: 16px;
bottom: 1rem;
left: 50%;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
}
.dt-toast__message {
display: inline-block;
background-color: rgba(0, 0, 0, .8);
color: #dfe2e5;
border-radius: 3px;
padding: 8px 16px;
padding: 0.5rem 1rem;
}
.dt-input {
outline: none;
width: 100%;
border: none;
overflow: visible;
font-family: inherit;
font-size: inherit;
line-height: inherit;
margin: 0;
padding: 0;
}
.dt-freeze {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-ms-flex-line-pack: center;
align-content: center;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: #f5f7fa;
opacity: 0.5;
font-size: 2em;
}
.dt-freeze__message {
position: absolute;
top: 50%;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
}
.dt-paste-target {
position: fixed;
left: -999em;
}
body.dt-resize {
cursor: col-resize;
}

3765
dist/frappe-datatable.js vendored Normal file

File diff suppressed because it is too large Load Diff

1
dist/frappe-datatable.min.css vendored Normal file
View File

@ -0,0 +1 @@
.datatable *,.datatable :after,.datatable :before{-webkit-box-sizing:border-box;box-sizing:border-box}.datatable{position:relative;overflow:auto}.dt-header{border-bottom:1px solid #d1d8dd;position:absolute;top:0;left:0;background-color:#fff}.dt-body,.dt-header{border-collapse:collapse}.dt-scrollable{max-height:40vw;overflow:auto;border-bottom:1px solid #d1d8dd}.dt-scrollable--highlight-all{background-color:#fffce7}.dt-scrollable__no-data{text-align:center;padding:16px;padding:1rem;border-left:1px solid #d1d8dd;border-right:1px solid #d1d8dd}.dt-row--highlight{background-color:#fffce7}.dt-row--unhighlight{background-color:#fff}.dt-row--hide{display:none}.dt-cell{border:1px solid #d1d8dd;position:relative;outline:none;padding:0}.dt-cell__content{border:2px solid transparent;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.dt-cell__content,.dt-cell__edit{padding:8px;padding:.5rem;height:100%}.dt-cell__edit{display:none;background-color:#fff;border:2px solid #ffa00a;z-index:1}.dt-cell__resize-handle{opacity:0;position:absolute;right:-3px;top:0;width:5px;height:100%;cursor:col-resize;z-index:1}.dt-cell--editing .dt-cell__content{display:none}.dt-cell--editing .dt-cell__edit{display:block}.dt-cell--focus .dt-cell__content{border-color:#5292f7}.dt-cell--dragging,.dt-cell--highlight{background-color:#f5f7fa}.dt-cell--header .dt-cell__content{padding-right:16px;padding-right:1rem;font-weight:700}.dt-cell--header:hover .dt-dropdown__toggle{opacity:1}.dt-cell--tree-close .dt-tree-node__toggle:before{content:"►"}.dt-dropdown{position:absolute;right:10px;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;vertical-align:top;text-align:left;font-weight:400;cursor:pointer}.dt-dropdown__toggle{opacity:0}.dt-dropdown__list{display:none;position:absolute;min-width:128px;min-width:8rem;top:100%;right:0;z-index:1;background-color:#fff;border-radius:3px;padding:8px 0;padding:.5rem 0;-webkit-box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1)}.dt-dropdown__list-item{padding:8px 16px;padding:.5rem 1rem}.dt-dropdown__list-item:hover{background-color:#f5f7fa}.dt-dropdown--active .dt-dropdown__list{display:block}.dt-tree-node{display:inline-block;position:relative}.dt-tree-node__toggle{display:inline-block;position:absolute;font-size:10px;padding:0 4px;cursor:pointer}.dt-tree-node__toggle:before{content:"▼"}.dt-toast{position:absolute;bottom:16px;bottom:1rem;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.dt-toast__message{display:inline-block;background-color:rgba(0,0,0,.8);color:#dfe2e5;border-radius:3px;padding:8px 16px;padding:.5rem 1rem}.dt-input{outline:none;width:100%;border:none;overflow:visible;font-family:inherit;font-size:inherit;line-height:inherit;margin:0;padding:0}.dt-freeze{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-ms-flex-line-pack:center;align-content:center;position:absolute;left:0;right:0;top:0;bottom:0;background-color:#f5f7fa;opacity:.5;font-size:2em}.dt-freeze__message{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.dt-paste-target{position:fixed;left:-999em}body.dt-resize{cursor:col-resize}

1
dist/frappe-datatable.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,257 @@
.datatable *, .datatable *::after, .datatable *::before {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.datatable {
position: relative;
overflow: auto;
}
.dt-header {
border-collapse: collapse;
border-bottom: 1px solid #d1d8dd;
position: absolute;
top: 0;
left: 0;
background-color: #fff;
}
.dt-body {
border-collapse: collapse;
}
.dt-scrollable {
max-height: 40vw;
overflow: auto;
border-bottom: 1px solid #d1d8dd;
}
.dt-scrollable--highlight-all {
background-color: #fffce7;
}
.dt-scrollable__no-data {
text-align: center;
padding: 16px;
padding: 1rem;
border-left: 1px solid #d1d8dd;
border-right: 1px solid #d1d8dd;
}
.dt-row--highlight {
background-color: #fffce7;
}
.dt-row--unhighlight {
background-color: #fff;
}
.dt-row--hide {
display: none;
}
.dt-cell {
border: 1px solid #d1d8dd;
position: relative;
outline: none;
padding: 0;
}
.dt-cell__content {
padding: 8px;
padding: 0.5rem;
border: 2px solid transparent;
height: 100%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.dt-cell__edit {
display: none;
padding: 8px;
padding: 0.5rem;
background-color: #fff;
border: 2px solid rgb(255, 160, 10);
z-index: 1;
height: 100%;
}
.dt-cell__resize-handle {
opacity: 0;
position: absolute;
right: -3px;
top: 0;
width: 5px;
height: 100%;
cursor: col-resize;
z-index: 1;
}
.dt-cell--editing .dt-cell__content {
display: none;
}
.dt-cell--editing .dt-cell__edit {
display: block;
}
.dt-cell--focus .dt-cell__content {
border-color: rgb(82, 146, 247);
}
.dt-cell--highlight {
background-color: #f5f7fa;
}
.dt-cell--dragging {
background-color: #f5f7fa;
}
.dt-cell--header .dt-cell__content {
padding-right: 16px;
padding-right: 1rem;
font-weight: bold;
}
.dt-cell--header:hover .dt-dropdown__toggle {
opacity: 1;
}
.dt-cell--tree-close .dt-tree-node__toggle:before {
content: '►';
}
.dt-dropdown {
position: absolute;
right: 10px;
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
vertical-align: top;
text-align: left;
font-weight: normal;
cursor: pointer;
}
.dt-dropdown__toggle {
opacity: 0;
}
.dt-dropdown__list {
display: none;
position: absolute;
min-width: 128px;
min-width: 8rem;
top: 100%;
right: 0;
z-index: 1;
background-color: #fff;
border-radius: 3px;
padding: 8px 0;
padding: 0.5rem 0;
-webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
}
.dt-dropdown__list-item {
padding: 8px 16px;
padding: 0.5rem 1rem;
}
.dt-dropdown__list-item:hover {
background-color: #f5f7fa;
}
.dt-dropdown--active .dt-dropdown__list {
display: block;
}
.dt-tree-node {
display: inline-block;
position: relative;
}
.dt-tree-node__toggle {
display: inline-block;
position: absolute;
font-size: 10px;
padding: 0 4px;
cursor: pointer;
}
.dt-tree-node__toggle:before {
content: '▼';
}
.dt-toast {
position: absolute;
bottom: 16px;
bottom: 1rem;
left: 50%;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
}
.dt-toast__message {
display: inline-block;
background-color: rgba(0, 0, 0, .8);
color: #dfe2e5;
border-radius: 3px;
padding: 8px 16px;
padding: 0.5rem 1rem;
}
.dt-input {
outline: none;
width: 100%;
border: none;
overflow: visible;
font-family: inherit;
font-size: inherit;
line-height: inherit;
margin: 0;
padding: 0;
}
.dt-freeze {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-ms-flex-line-pack: center;
align-content: center;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: #f5f7fa;
opacity: 0.5;
font-size: 2em;
}
.dt-freeze__message {
position: absolute;
top: 50%;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
}
.dt-paste-target {
position: fixed;
left: -999em;
}
body.dt-resize {
cursor: col-resize;
}

View File

@ -0,0 +1,99 @@
/*
Original highlight.js style (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
background: #F0F0F0;
}
/* Base color: saturation 0; */
.hljs,
.hljs-subst {
color: #444;
}
.hljs-comment {
color: #888888;
}
.hljs-keyword,
.hljs-attribute,
.hljs-selector-tag,
.hljs-meta-keyword,
.hljs-doctag,
.hljs-name {
font-weight: bold;
}
/* User color: hue: 0 */
.hljs-type,
.hljs-string,
.hljs-number,
.hljs-selector-id,
.hljs-selector-class,
.hljs-quote,
.hljs-template-tag,
.hljs-deletion {
color: #880000;
}
.hljs-title,
.hljs-section {
color: #880000;
font-weight: bold;
}
.hljs-regexp,
.hljs-symbol,
.hljs-variable,
.hljs-template-variable,
.hljs-link,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #BC6060;
}
/* Language color: hue: 90; */
.hljs-literal {
color: #78A960;
}
.hljs-built_in,
.hljs-bullet,
.hljs-code,
.hljs-addition {
color: #397300;
}
/* Meta color: hue: 200 */
.hljs-meta {
color: #1f7199;
}
.hljs-meta-string {
color: #4d99bf;
}
/* Misc effects */
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}

159
docs/assets/css/index.css Normal file
View File

@ -0,0 +1,159 @@
@import url('https://fonts.googleapis.com/css?family=Lato:400,700,900');
:root {
--border-color: #d1d8dd;
--border: 1px solid var(--border-color);
--text-color: #36414C;
--navbar-font-size: 1.25rem;
--light-bg: #f5f7fa;
--primary-color: #7575ff;
--primary-color-hover: #5b5be5;
}
*, *::after, *::before {
box-sizing: border-box;
padding: 0;
margin: 0;
}
html, body {
/* font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; */
font-family: Lato, sans-serif;
font-size: 12px;
color: var(--text-color);
}
body {
position: relative;
}
code {
font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
h1 {
font-size: 2.5rem;
margin: 2rem 0 4rem 0;
}
h3 {
font-size: 1.25rem;
}
p {
font-size: 1.25rem;
margin: 1rem 0;
}
.navbar {
position: sticky;
top: 0;
padding: 1.5rem;
background-color: white;
z-index: 1;
border-bottom: var(--border);
}
.navbar .container {
display: flex;
justify-content: space-between;
}
.navbar-links a {
color: var(--text-color);
text-decoration: none;
font-weight: bold;
font-size: var(--navbar-font-size);
}
.container {
max-width: 720px;
margin: 0 auto;
}
.logo {
max-width: 8rem;
}
.footer-logo {
max-width: 3rem;
max-height: 3rem;
padding: 0.5rem;
background-color: #fff;
transform: translateY(-50%);
}
.text-center {
text-align: center;
}
.padding-1 {
padding: 0.5rem;
}
.showcase {
display: flex;
flex-direction: column;
align-items: flex-start;
margin: 4rem 0;
}
.showcase.align-center {
align-items: center;
}
.features {
display: flex;
width: 100%;
font-size: 1.25rem;
margin-top: 1rem;
line-height: 2rem;
}
.features ul {
padding-left: 2rem;
flex: 1;
}
[class^="example-"] {
display: flex;
justify-content: center;
width: 100%;
}
.code {
text-align: left;
width: 100%;
margin: 1rem 0;
}
.hljs {
padding: 2rem;
border-left: 2px solid var(--border-color);
background-color: var(--light-bg);
}
footer {
border-top: 1px solid #d1d8dd;
}
.download-button {
color: #fff;
background-color: var(--primary-color);
border: 0px;
border-bottom: 3px solid rgba(0, 0, 0, 0.2);
font-size: 1.5rem;
padding: 1rem 2rem;
border-radius: 3px;
}
.download-button:hover {
cursor: pointer;
background-color: var(--primary-color-hover);
}
/* .data-table-header {
background: #f7fafc;
color: #8d99a6;
} */

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="340.42303" height="330.00024" viewBox="0 0 90.070259 87.312561" version="1.1" id="svg3970" inkscape:version="0.92.2 5c3e80d, 2017-08-06">
<defs id="defs3964"/>
<metadata id="metadata3967">
</metadata>
<g inkscape:label="Layer 1" id="layer1" transform="translate(-7.1255851,-173.21277)">
<rect transform="scale(-1,1)" ry="13.229167" rx="13.229167" y="173.21277" x="-94.438156" height="87.312546" width="87.312553" id="rect2004" style="opacity:1;fill:#edfdff;fill-opacity:1;stroke:none;stroke-width:1.74624979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;"/>
<path style="opacity:1;fill:#c3f1ff;fill-opacity:1;stroke:none;stroke-width:6.59999943;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 0 96.25 L 0 151.25 L 77.5 151.25 C 80.27 151.25 82.5 149.02 82.5 146.25 L 82.5 101.25 C 82.5 98.48 80.27 96.25 77.5 96.25 L 0 96.25 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect2006"/>
<path style="opacity:1;fill:#c3f1ff;fill-opacity:1;stroke:none;stroke-width:6.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 50 0 C 22.299998 0 1.4210855e-14 22.299998 0 50 L 0 54.5 L 78 54.5 C 80.77 54.5 83 52.27 83 49.5 L 83 4.5 C 83 2.503655 81.832377 0.80274423 80.148438 0 L 50 0 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect2008"/>
<rect style="opacity:1;fill:#71caff;fill-opacity:1;stroke:none;stroke-width:1.74624979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" id="rect2010" width="57.195782" height="14.552094" x="40.000061" y="198.74506" ry="1.3229167" rx="1.3229166"/>
<path style="opacity:1;fill:#c3f1ff;fill-opacity:1;stroke:none;stroke-width:6.59999943;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 0 193 L 0 248 L 77.5 248 C 80.27 248 82.5 245.77 82.5 243 L 82.5 198 C 82.5 195.23 80.27 193 77.5 193 L 0 193 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect2012"/>
<path style="fill:#87e34c;fill-opacity:1;stroke:none;stroke-width:1.00000012px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" clip-path="none" d="M 0 219.61328 L 0 284.29688 C 0 309.61626 27.643317 330 61.980469 330 L 268.01953 330 C 300.54576 330 327.04905 311.70603 329.75586 288.25586 L 330 248.5 C 256.7254 233.20714 83.19117 220.5514 0 219.61328 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="path2020"/>
<path style="opacity:1;fill:#c3f1ff;fill-opacity:1;stroke:none;stroke-width:6.59999943;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 129.25 193.5 C 126.48 193.5 124.25 195.73 124.25 198.5 L 124.25 243.5 C 124.25 246.27 126.48 248.5 129.25 248.5 L 330 248.5 L 330 193.5 L 129.25 193.5 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect2014"/>
<path style="opacity:1;fill:#c3f1ff;fill-opacity:1;stroke:none;stroke-width:6.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 127.10156 0 C 125.41762 0.80274423 124.25 2.503655 124.25 4.5 L 124.25 49.5 C 124.25 52.27 126.48 54.5 129.25 54.5 L 330 54.5 L 330 50 C 330 22.299998 307.7 0 280 0 L 127.10156 0 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect2016"/>
<path style="opacity:1;fill:#59b81c;fill-opacity:1;stroke:none;stroke-width:6.59999943;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 129.25 290.5 C 126.48 290.5 124.25 292.73 124.25 295.5 L 124.25 330 L 280 330 C 304.09518 330 324.09664 313.12363 328.89648 290.5 L 129.25 290.5 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect1135"/>
<path style="opacity:1;fill:#59b81c;fill-opacity:1;stroke:none;stroke-width:6.59999943;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 0.94921875 289.75 C 5.4614968 312.74994 25.639123 330 50 330 L 82.5 330 L 82.5 294.75 C 82.5 291.98 80.27 289.75 77.5 289.75 L 0.94921875 289.75 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect1139"/>
<path style="opacity:1;fill:#59b81c;fill-opacity:1;stroke:none;stroke-width:1.74624979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="m 7.1257773,231.31877 v 7.51065 H 27.630987 c 0.732896,0 1.322917,-0.59002 1.322917,-1.32292 v -5.36505 C 20.620295,231.67818 13.007809,231.3851 7.1257773,231.31877 Z" id="path1264"/>
<path style="fill:#59b81c;fill-opacity:1;stroke:none;stroke-width:0.26458335px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" clip-path="none" d="m 40.000259,232.82824 v 4.81056 c 0,0.73289 0.59002,1.32291 1.322916,1.32291 h 53.115108 c -11.982795,-2.50088 -34.095004,-4.73297 -54.438024,-6.13347 z" id="path1163"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="100%"
height="100%"
viewBox="0 0 260 260"
version="1.1"
xml:space="preserve"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"
id="svg16"
sodipodi:docname="frappe-bird-grey.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"><metadata
id="metadata22"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs20" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1029"
id="namedview18"
showgrid="false"
inkscape:snap-global="false"
inkscape:zoom="2.5673415"
inkscape:cx="125.36812"
inkscape:cy="132.66533"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="1"
inkscape:current-layer="svg16" /><path
inkscape:connector-curvature="0"
id="path846"
d="m 39.426993,41.26578 c -0.08308,-0.002 -0.162537,0.01476 -0.244177,0.01847 -0.195406,-0.01104 -0.394663,-0.0078 -0.595581,0.02219 -0.671494,0.09826 -1.359798,0.439289 -1.985268,1.097637 L 36.166705,42.984362 3.911539,86.00785 c -1.4521873,1.95837 -0.089029,5.089411 2.840468,4.758942 l 35.051779,-4.999463 7.486826,52.499391 c 0.143332,1.06941 0.708175,1.87792 1.45651,2.37853 0.06099,0.57446 0.300326,1.15825 0.786491,1.67605 l 54.221237,55.16042 c 0.35241,0.34946 0.76163,0.57974 1.18925,0.71967 l 43.2449,43.9932 c 2.19797,2.06517 5.73881,0.59111 5.73827,-2.44537 l 0.72154,-88.30898 100.76861,-99.226182 c 2.03273,-1.933848 0.52846,-5.096585 -1.98911,-5.01476 L 146.21842,46.315471 c -1.15721,-0.0078 -1.82288,0.589677 -2.08644,0.84183 L 70.489255,119.67376 95.234723,86.666413 c 1.320352,-1.808803 0.561745,-3.846193 -0.6872,-4.787579 L 41.349464,41.92492 C 40.706305,41.485816 40.049657,41.2839 39.427179,41.266357 Z"
style="fill:#29344a;fill-opacity:1;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

2
docs/assets/js/Sortable.min.js vendored Normal file

File diff suppressed because one or more lines are too long

18
docs/assets/js/clusterize.min.js vendored Normal file
View File

@ -0,0 +1,18 @@
/*
Clusterize.js - v0.18.0 - 2017-11-04
http://NeXTs.github.com/Clusterize.js/
Copyright (c) 2015 Denis Lukov; Licensed GPLv3 */
;(function(q,n){"undefined"!=typeof module?module.exports=n():"function"==typeof define&&"object"==typeof define.amd?define(n):this[q]=n()})("Clusterize",function(){function q(b,a,c){return a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)}function n(b,a,c){return a.removeEventListener?a.removeEventListener(b,c,!1):a.detachEvent("on"+b,c)}function r(b){return"[object Array]"===Object.prototype.toString.call(b)}function m(b,a){return window.getComputedStyle?window.getComputedStyle(a)[b]:
a.currentStyle[b]}var l=function(){for(var b=3,a=document.createElement("b"),c=a.all||[];a.innerHTML="\x3c!--[if gt IE "+ ++b+"]><i><![endif]--\x3e",c[0];);return 4<b?b:document.documentMode}(),x=navigator.platform.toLowerCase().indexOf("mac")+1,p=function(b){if(!(this instanceof p))return new p(b);var a=this,c={rows_in_block:50,blocks_in_cluster:4,tag:null,show_no_data_row:!0,no_data_class:"clusterize-no-data",no_data_text:"No data",keep_parity:!0,callbacks:{}};a.options={};for(var d="rows_in_block blocks_in_cluster show_no_data_row no_data_class no_data_text keep_parity tag callbacks".split(" "),
f=0,h;h=d[f];f++)a.options[h]="undefined"!=typeof b[h]&&null!=b[h]?b[h]:c[h];c=["scroll","content"];for(f=0;d=c[f];f++)if(a[d+"_elem"]=b[d+"Id"]?document.getElementById(b[d+"Id"]):b[d+"Elem"],!a[d+"_elem"])throw Error("Error! Could not find "+d+" element");a.content_elem.hasAttribute("tabindex")||a.content_elem.setAttribute("tabindex",0);var e=r(b.rows)?b.rows:a.fetchMarkup(),g={};b=a.scroll_elem.scrollTop;a.insertToDOM(e,g);a.scroll_elem.scrollTop=b;var k=!1,m=0,l=!1,t=function(){x&&(l||(a.content_elem.style.pointerEvents=
"none"),l=!0,clearTimeout(m),m=setTimeout(function(){a.content_elem.style.pointerEvents="auto";l=!1},50));k!=(k=a.getClusterNum())&&a.insertToDOM(e,g);a.options.callbacks.scrollingProgress&&a.options.callbacks.scrollingProgress(a.getScrollProgress())},u=0,v=function(){clearTimeout(u);u=setTimeout(a.refresh,100)};q("scroll",a.scroll_elem,t);q("resize",window,v);a.destroy=function(b){n("scroll",a.scroll_elem,t);n("resize",window,v);a.html((b?a.generateEmptyRow():e).join(""))};a.refresh=function(b){(a.getRowsHeight(e)||
b)&&a.update(e)};a.update=function(b){e=r(b)?b:[];b=a.scroll_elem.scrollTop;e.length*a.options.item_height<b&&(k=a.scroll_elem.scrollTop=0);a.insertToDOM(e,g);a.scroll_elem.scrollTop=b};a.clear=function(){a.update([])};a.getRowsAmount=function(){return e.length};a.getScrollProgress=function(){return this.options.scroll_top/(e.length*this.options.item_height)*100||0};var w=function(b,c){var d=r(c)?c:[];d.length&&(e="append"==b?e.concat(d):d.concat(e),a.insertToDOM(e,g))};a.append=function(a){w("append",
a)};a.prepend=function(a){w("prepend",a)}};p.prototype={constructor:p,fetchMarkup:function(){for(var b=[],a=this.getChildNodes(this.content_elem);a.length;)b.push(a.shift().outerHTML);return b},exploreEnvironment:function(b,a){var c=this.options;c.content_tag=this.content_elem.tagName.toLowerCase();b.length&&(l&&9>=l&&!c.tag&&(c.tag=b[0].match(/<([^>\s/]*)/)[1].toLowerCase()),1>=this.content_elem.children.length&&(a.data=this.html(b[0]+b[0]+b[0])),c.tag||(c.tag=this.content_elem.children[0].tagName.toLowerCase()),
this.getRowsHeight(b))},getRowsHeight:function(b){var a=this.options,c=a.item_height;a.cluster_height=0;if(b.length){b=this.content_elem.children;var d=b[Math.floor(b.length/2)];a.item_height=d.offsetHeight;"tr"==a.tag&&"collapse"!=m("borderCollapse",this.content_elem)&&(a.item_height+=parseInt(m("borderSpacing",this.content_elem),10)||0);"tr"!=a.tag&&(b=parseInt(m("marginTop",d),10)||0,d=parseInt(m("marginBottom",d),10)||0,a.item_height+=Math.max(b,d));a.block_height=a.item_height*a.rows_in_block;
a.rows_in_cluster=a.blocks_in_cluster*a.rows_in_block;a.cluster_height=a.blocks_in_cluster*a.block_height;return c!=a.item_height}},getClusterNum:function(){this.options.scroll_top=this.scroll_elem.scrollTop;return Math.floor(this.options.scroll_top/(this.options.cluster_height-this.options.block_height))||0},generateEmptyRow:function(){var b=this.options;if(!b.tag||!b.show_no_data_row)return[];var a=document.createElement(b.tag),c=document.createTextNode(b.no_data_text);a.className=b.no_data_class;
if("tr"==b.tag){var d=document.createElement("td");d.colSpan=100;d.appendChild(c)}a.appendChild(d||c);return[a.outerHTML]},generate:function(b,a){var c=this.options,d=b.length;if(d<c.rows_in_block)return{top_offset:0,bottom_offset:0,rows_above:0,rows:d?b:this.generateEmptyRow()};var f=Math.max((c.rows_in_cluster-c.rows_in_block)*a,0),h=f+c.rows_in_cluster,e=Math.max(f*c.item_height,0);c=Math.max((d-h)*c.item_height,0);d=[];var g=f;for(1>e&&g++;f<h;f++)b[f]&&d.push(b[f]);return{top_offset:e,bottom_offset:c,
rows_above:g,rows:d}},renderExtraTag:function(b,a){var c=document.createElement(this.options.tag);c.className=["clusterize-extra-row","clusterize-"+b].join(" ");a&&(c.style.height=a+"px");return c.outerHTML},insertToDOM:function(b,a){this.options.cluster_height||this.exploreEnvironment(b,a);var c=this.generate(b,this.getClusterNum()),d=c.rows.join(""),f=this.checkChanges("data",d,a),h=this.checkChanges("top",c.top_offset,a),e=this.checkChanges("bottom",c.bottom_offset,a),g=this.options.callbacks,
k=[];f||h?(c.top_offset&&(this.options.keep_parity&&k.push(this.renderExtraTag("keep-parity")),k.push(this.renderExtraTag("top-space",c.top_offset))),k.push(d),c.bottom_offset&&k.push(this.renderExtraTag("bottom-space",c.bottom_offset)),g.clusterWillChange&&g.clusterWillChange(),this.html(k.join("")),"ol"==this.options.content_tag&&this.content_elem.setAttribute("start",c.rows_above),this.content_elem.style["counter-increment"]="clusterize-counter "+(c.rows_above-1),g.clusterChanged&&g.clusterChanged()):
e&&(this.content_elem.lastChild.style.height=c.bottom_offset+"px")},html:function(b){var a=this.content_elem;if(l&&9>=l&&"tr"==this.options.tag){var c=document.createElement("div");for(c.innerHTML="<table><tbody>"+b+"</tbody></table>";b=a.lastChild;)a.removeChild(b);for(c=this.getChildNodes(c.firstChild.firstChild);c.length;)a.appendChild(c.shift())}else a.innerHTML=b},getChildNodes:function(b){b=b.children;for(var a=[],c=0,d=b.length;c<d;c++)a.push(b[c]);return a},checkChanges:function(b,a,c){var d=
a!=c[b];c[b]=a;return d}};return p});

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

718
docs/assets/js/index.js Normal file
View File

@ -0,0 +1,718 @@
/* global DataTable */
/* eslint-disable no-unused-vars */
const {
columns,
data
} = getSampleData();
// Hero
let datatable1 = new DataTable('.example-1', {
columns,
data,
checkboxColumn: true
});
// // Formatted Cells
// let datatable2 = new DataTable('.example-2', {
// columns: ['Name', 'Position', 'Office', 'Extn.', 'Start Date',
// { content: 'Salary', format: val => '$' + val, align: 'right' }],
// data
// });
// // Inline Filters
// let datatable3 = new DataTable('.example-3', {
// columns,
// data,
// enableInlineFilters: true
// });
// datatable3.showToastMessage('Click on a cell and press Ctrl/Cmd + F');
// // Keyboard
// let datatable4 = new DataTable('.example-4', {
// columns,
// data
// });
// datatable4.showToastMessage('Double click to edit');
// // Tree Structured Rows
// let datatable5 = new DataTable('.example-5', getTreeData());
// datatable5.showToastMessage('Expand/Collapse tree nodes');
function getSampleData(multiplier) {
let columns = ['Name', 'Position', 'Office', {name: 'Extn.', width: 120}, 'Start Date', 'Salary'];
let data = [
['Tiger Nixon', 'System Architect', 'Edinburgh', 5421, '2011/04/25', '320,800'],
['Garrett Winters', 'Accountant', 'Tokyo', 8422, '2011/07/25', '170,750'],
['Ashton Cox', 'Junior Technical Author', 'San Francisco', 1562, '2009/01/12', '86,000'],
['Cedric Kelly', 'Senior Javascript Developer', 'Edinburgh', 6224, '2012/03/29', '433,060'],
['Airi Satou', 'Accountant', 'Tokyo', 5407, '2008/11/28', '162,700'],
['Brielle Williamson', 'Integration Specialist', 'New York', 4804, '2012/12/02', '372,000'],
['Herrod Chandler', 'Sales Assistant', 'San Francisco', 9608, '2012/08/06', '137,500'],
['Rhona Davidson', 'Integration Specialist', 'Tokyo', 6200, '2010/10/14', '327,900'],
['Colleen Hurst', 'Javascript Developer', 'San Francisco', 2360, '2009/09/15', '205,500'],
['Sonya Frost', 'Software Engineer', 'Edinburgh', 1667, '2008/12/13', '103,600'],
['Jena Gaines', 'Office Manager', 'London', 3814, '2008/12/19', '90,560'],
['Quinn Flynn', 'Support Lead', 'Edinburgh', 9497, '2013/03/03', '342,000'],
['Charde Marshall', 'Regional Director', 'San Francisco', 6741, '2008/10/16', '470,600'],
['Haley Kennedy', 'Senior Marketing Designer', 'London', 3597, '2012/12/18', '313,500'],
['Tatyana Fitzpatrick', 'Regional Director', 'London', 1965, '2010/03/17', '385,750'],
['Michael Silva', 'Marketing Designer', 'London', 1581, '2012/11/27', '198,500'],
['Paul Byrd', 'Chief Financial Officer (CFO)', 'New York', 3059, '2010/06/09', '725,000'],
['Gloria Little', 'Systems Administrator', 'New York', 1721, '2009/04/10', '237,500'],
['Bradley Greer', 'Software Engineer', 'London', 2558, '2012/10/13', '132,000'],
['Dai Rios', 'Personnel Lead', 'Edinburgh', 2290, '2012/09/26', '217,500'],
['Jenette Caldwell', 'Development Lead', 'New York', 1937, '2011/09/03', '345,000'],
['Yuri Berry', 'Chief Marketing Officer (CMO)', 'New York', 6154, '2009/06/25', '675,000'],
['Caesar Vance', 'Pre-Sales Support', 'New York', 8330, '2011/12/12', '106,450'],
['Doris Wilder', 'Sales Assistant', 'Sidney', 3023, '2010/09/20', '85,600'],
['Angelica Ramos', 'Chief Executive Officer (CEO)', 'London', 5797, '2009/10/09', '1,200,000'],
['Gavin Joyce', 'Developer', 'Edinburgh', 8822, '2010/12/22', '92,575'],
['Jennifer Chang', 'Regional Director', 'Singapore', 9239, '2010/11/14', '357,650'],
['Brenden Wagner', 'Software Engineer', 'San Francisco', 1314, '2011/06/07', '206,850'],
['Fiona Green', 'Chief Operating Officer (COO)', 'San Francisco', 2947, '2010/03/11', '850,000'],
['Shou Itou', 'Regional Marketing', 'Tokyo', 8899, '2011/08/14', '163,000'],
['Michelle House', 'Integration Specialist', 'Sidney', 2769, '2011/06/02', '95,400'],
['Suki Burks', 'Developer', 'London', 6832, '2009/10/22', '114,500'],
['Prescott Bartlett', 'Technical Author', 'London', 3606, '2011/05/07', '145,000'],
['Gavin Cortez', 'Team Leader', 'San Francisco', 2860, '2008/10/26', '235,500'],
['Martena Mccray', 'Post-Sales support', 'Edinburgh', 8240, '2011/03/09', '324,050'],
['Unity Butler', 'Marketing Designer', 'San Francisco', 5384, '2009/12/09', '85,675'],
['Howard Hatfield', 'Office Manager', 'San Francisco', 7031, '2008/12/16', '164,500'],
['Hope Fuentes', 'Secretary', 'San Francisco', 6318, '2010/02/12', '109,850'],
['Vivian Harrell', 'Financial Controller', 'San Francisco', 9422, '2009/02/14', '452,500'],
['Timothy Mooney', 'Office Manager', 'London', 7580, '2008/12/11', '136,200'],
['Jackson Bradshaw', 'Director', 'New York', 1042, '2008/09/26', '645,750'],
['Olivia Liang', 'Support Engineer', 'Singapore', 2120, '2011/02/03', '234,500'],
['Bruno Nash', 'Software Engineer', 'London', 6222, '2011/05/03', '163,500'],
['Sakura Yamamoto', 'Support Engineer', 'Tokyo', 9383, '2009/08/19', '139,575'],
['Thor Walton', 'Developer', 'New York', 8327, '2013/08/11', '98,540'],
['Finn Camacho', 'Support Engineer', 'San Francisco', 2927, '2009/07/07', '87,500'],
['Serge Baldwin', 'Data Coordinator', 'Singapore', 8352, '2012/04/09', '138,575'],
['Zenaida Frank', 'Software Engineer', 'New York', 7439, '2010/01/04', '125,250'],
['Zorita Serrano', 'Software Engineer', 'San Francisco', 4389, '2012/06/01', '115,000'],
['Jennifer Acosta', 'Junior Javascript Developer', 'Edinburgh', 3431, '2013/02/01', '75,650'],
['Cara Stevens', 'Sales Assistant', 'New York', 3990, '2011/12/06', '145,600'],
['Hermione Butler', 'Regional Director', 'London', 1016, '2011/03/21', '356,250'],
['Lael Greer', 'Systems Administrator', 'London', 6733, '2009/02/27', '103,500'],
['Jonas Alexander', 'Developer', 'San Francisco', 8196, '2010/07/14', '86,500'],
['Shad Decker', 'Regional Director', 'Edinburgh', 6373, '2008/11/13', '183,000'],
['Michael Bruce', 'Javascript Developer', 'Singapore', 5384, '2011/06/27', '183,000'],
['Donna Snider', 'Customer Support', 'New York', 4226, '2011/01/25', '112,000']
];
if (multiplier) {
Array.from(new Array(multiplier - 1)).forEach(d => {
data = data.concat(data);
});
}
return {
columns,
data
};
}
function getTreeData() {
return {
columns: [{
'id': 'account',
'content': 'Account'
}, {
'id': 'opening_debit',
'content': 'Opening (Dr)'
}, {
'id': 'opening_credit',
'content': 'Opening (Cr)'
},
// {
// 'id': 'debit',
// 'content': 'Debit'
// },
// {
// 'id': 'credit',
// 'content': 'Credit'
// },
{
'id': 'closing_debit',
'content': 'Closing (Dr)'
}, {
'id': 'closing_credit',
'content': 'Closing (Cr)'
}, {
'id': 'currency',
'content': 'Currency',
'hidden': 1
}],
data: [{
'account_name': 'Application of Funds (Assets)',
'account': 'Application of Funds (Assets)',
'parent_account': null,
'indent': 0,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 12023729.54,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 12023729.54,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Current Assets',
'account': 'Current Assets',
'parent_account': 'Application of Funds (Assets)',
'indent': 1,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 13960649.54,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 13960649.54,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Accounts Receivable',
'account': 'Accounts Receivable',
'parent_account': 'Current Assets',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 742790.474,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 742790.474,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Debtors',
'account': 'Debtors',
'parent_account': 'Accounts Receivable',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 742790.474,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 742790.474,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Bank Accounts',
'account': 'Bank Accounts',
'parent_account': 'Current Assets',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 280676.822,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 280676.822,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Corporation Bank',
'account': 'Corporation Bank',
'parent_account': 'Bank Accounts',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 290676.822,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 290676.822,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'HDFC Bank',
'account': 'HDFC Bank',
'parent_account': 'Bank Accounts',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 10000.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 10000.0,
'has_value': true
}, {
'account_name': 'Cash In Hand',
'account': 'Cash In Hand',
'parent_account': 'Current Assets',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 229904.494,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 229904.494,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Cash',
'account': 'Cash',
'parent_account': 'Cash In Hand',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 229904.494,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 229904.494,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Stock Assets',
'account': 'Stock Assets',
'parent_account': 'Current Assets',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 12707277.75,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 12707277.75,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'All Warehouses',
'account': 'All Warehouses',
'parent_account': 'Stock Assets',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 12707277.75,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 12707277.75,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Finished Goods',
'account': 'Finished Goods',
'parent_account': 'All Warehouses',
'indent': 4,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 87320.3,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 87320.3,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Retail Stores',
'account': 'Retail Stores',
'parent_account': 'All Warehouses',
'indent': 4,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 4540590.0,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 4540590.0,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Bandra Store',
'account': 'Bandra Store',
'parent_account': 'Retail Stores',
'indent': 5,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 3246800.0,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 3246800.0,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Central Warehouse',
'account': 'Central Warehouse',
'parent_account': 'Retail Stores',
'indent': 5,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 1236790.0,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 1236790.0,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Lower Parel Store',
'account': 'Lower Parel Store',
'parent_account': 'Retail Stores',
'indent': 5,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 57000.0,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 57000.0,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Stores',
'account': 'Stores',
'parent_account': 'All Warehouses',
'indent': 4,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 8016525.27,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 8016525.27,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Work In Progress',
'account': 'Work In Progress',
'parent_account': 'All Warehouses',
'indent': 4,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 62842.18,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 62842.18,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Fixed Assets',
'account': 'Fixed Assets',
'parent_account': 'Application of Funds (Assets)',
'indent': 1,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 19920.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 19920.0,
'has_value': true
}, {
'account_name': 'Electronic Equipments',
'account': 'Electronic Equipments',
'parent_account': 'Fixed Assets',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 80.0,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 80.0,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Furnitures and Fixtures',
'account': 'Furnitures and Fixtures',
'parent_account': 'Fixed Assets',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 20000.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 20000.0,
'has_value': true
}, {
'account_name': 'Temporary Accounts',
'account': 'Temporary Accounts',
'parent_account': 'Application of Funds (Assets)',
'indent': 1,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 1917000.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 1917000.0,
'has_value': true
}, {
'account_name': 'Temporary Opening',
'account': 'Temporary Opening',
'parent_account': 'Temporary Accounts',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 1917000.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 1917000.0,
'has_value': true
}, {
'account_name': 'Source of Funds (Liabilities)',
'account': 'Source of Funds (Liabilities)',
'parent_account': null,
'indent': 0,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 2371628.002,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 2371628.002,
'has_value': true
}, {
'account_name': 'Current Liabilities',
'account': 'Current Liabilities',
'parent_account': 'Source of Funds (Liabilities)',
'indent': 1,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 2371628.002,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 2371628.002,
'has_value': true
}, {
'account_name': 'Accounts Payable',
'account': 'Accounts Payable',
'parent_account': 'Current Liabilities',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 368311.85,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 368311.85,
'has_value': true
}, {
'account_name': 'Creditors',
'account': 'Creditors',
'parent_account': 'Accounts Payable',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 194871.85,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 194871.85,
'has_value': true
}, {
'account_name': 'Salary Payable',
'account': 'Salary Payable',
'parent_account': 'Accounts Payable',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 173440.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 173440.0,
'has_value': true
}, {
'account_name': 'Duties and Taxes',
'account': 'Duties and Taxes',
'parent_account': 'Current Liabilities',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 150146.822,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 150146.822,
'has_value': true
}, {
'account_name': 'CGST',
'account': 'CGST',
'parent_account': 'Duties and Taxes',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 51479.591,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 51479.591,
'has_value': true
}, {
'account_name': 'IGST',
'account': 'IGST',
'parent_account': 'Duties and Taxes',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 1944.0,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 1944.0,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'SGST',
'account': 'SGST',
'parent_account': 'Duties and Taxes',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 97711.231,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 97711.231,
'has_value': true
}, {
'account_name': 'UGST',
'account': 'UGST',
'parent_account': 'Duties and Taxes',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 2900.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 2900.0,
'has_value': true
}, {
'account_name': 'Stock Liabilities',
'account': 'Stock Liabilities',
'parent_account': 'Current Liabilities',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 1853169.33,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 1853169.33,
'has_value': true
}, {
'account_name': 'Stock Received But Not Billed',
'account': 'Stock Received But Not Billed',
'parent_account': 'Stock Liabilities',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 1853169.33,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 1853169.33,
'has_value': true
}, {
'account_name': 'Equity',
'account': 'Equity',
'parent_account': null,
'indent': 0,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 10000.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 10000.0,
'has_value': true
}, {
'account_name': 'Capital Stock',
'account': 'Capital Stock',
'parent_account': 'Equity',
'indent': 1,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 10000.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 10000.0,
'has_value': true
}, {}, {
'account': 'Total',
'account_name': 'Total',
'warn_if_negative': true,
'opening_debit': 32260956.43,
'opening_credit': 22618854.891999997,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 32260956.43,
'closing_credit': 22618854.891999997,
'parent_account': null,
'indent': 0,
'has_value': true,
'currency': 'INR'
}]
};
}

172
docs/index.html Normal file
View File

@ -0,0 +1,172 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Frappe DataTable - A simple, modern datatable library for the web</title>
<link href="assets/css/frappe-datatable.css" rel="stylesheet">
<link href="assets/css/hljs-default.css" rel="stylesheet">
<link href="assets/css/index.css" rel="stylesheet">
</head>
<body>
<nav class="navbar">
<div class="container">
<h3>Frappe DataTable</h3>
<div class="navbar-links">
<a href="https://github.com/frappe/datatable">GitHub</a>
</div>
</div>
</nav>
<div class="container">
<section class="showcase align-center">
<img class="logo" src="assets/img/data-table-logo.svg" />
<h1 class="text-center">A simple, modern and interactive<br> datatable for the web</h1>
<div class="example-1"></div>
</section>
<section class="showcase installation">
<h3>Installation</h3>
<div class="code">
<pre><code class="shell"># Install using yarn
$ yarn add frappe-datatable
# or npm
$ npm install frappe-datatable</code></pre>
</section>
<section class="showcase installation">
<h3>Usage</h3>
<div class="code">
<pre><code>import DataTable from 'frappe-datatable';
// or add
// &lt;script src="frappe-datatable.js" &gt;&lt;/script&gt;
// in your html
let datatable = new DataTable({
columns: ['Name', 'Position', ...],
data: [
['Tiger Nixon', 'System Architect', ...],
['Garrett Winters', 'Accountant', ...],
...
]
});</code></pre>
</section>
<section class="showcase feature">
<h3>Cell Features</h3>
<div class="features">
<ul>
<li>Custom Formatters</li>
<li>Inline Editing</li>
<li>Mouse Selection</li>
</ul>
<ul>
<li>Copy Cells</li>
<li>Keyboard Navigation</li>
<li>Custom Cell Editor</li>
</ul>
</div>
</section>
<section class="showcase feature">
<h3>Column Features</h3>
<div class="features">
<ul>
<li>Reorder Columns</li>
<li>Sort by Column</li>
<li>Remove / Hide Column</li>
</ul>
<ul>
<li>Custom Actions</li>
<li>Resize Column</li>
<li>Flexible Layout</li>
</ul>
</div>
</section>
<section class="showcase feature">
<h3>Row Features</h3>
<div class="features">
<ul>
<li>Row Selection</li>
<li>Tree Structured Rows</li>
<li>Inline Filters</li>
</ul>
<ul>
<li>Large Number of Rows</li>
<li>Dynamic Row Height</li>
</ul>
</div>
</section>
<section class="showcase installation">
<h3>List of configurable options</h3>
<div class="code">
<pre><code>{
columns: [],
data: [],
dropdownButton: '▼',
headerDropdown: [
{
label: 'Custom Action',
action: console.log
}
],
events: {
onRemoveColumn(column) {
},
onSwitchColumn(column1, column2) {
},
onSortColumn(column) {
}
},
sortIndicator: {
asc: '↑',
desc: '↓',
none: ''
},
freezeMessage: '',
getEditor: () => {
},
addSerialNoColumn: true,
addCheckboxColumn: false,
enableClusterize: true,
enableLogs: false,
layout: 'fixed', // fixed, fluid
noDataMessage: 'No Data',
cellHeight: null,
enableInlineFilters: false
}</code></pre>
</div>
</section>
<section class="showcase align-center">
<p>
<button class="download-button">Download</button>
</p>
<p>
<a href="https://github.com/frappe/datatable">View on GitHub</a>
</p>
<p>
<span>MIT License</span>
</p>
</section>
</div>
<footer class="showcase align-center">
<img class="footer-logo" src="assets/img/frappe-bird-grey.svg" alt="Frappe Logo">
<section>Made with ❤️ by <a>Frappe</a></section>
</footer>
<script src="assets/js/clusterize.min.js"></script>
<script src="assets/js/Sortable.min.js"></script>
<script src="assets/js/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<script src="assets/js/frappe-datatable.js"></script>
<script src="assets/js/index.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -1,42 +1,29 @@
{
"name": "frappe-datatable",
"version": "0.0.0-development",
"version": "0.0.10",
"description": "A modern datatable library for the web",
"main": "dist/frappe-datatable.cjs.js",
"unpkg": "dist/frappe-datatable.min.js",
"jsdelivr": "dist/frappe-datatable.min.js",
"scripts": {
"start": "yarn run dev",
"build": "rollup -c && NODE_ENV=production rollup -c",
"build": "rollup -c",
"production": "rollup -c --production",
"build:docs": "rollup -c --docs",
"dev": "rollup -c -w",
"cy:server": "http-server -p 8989",
"cy:open": "cypress open",
"cy:run": "cypress run",
"test": "start-server-and-test cy:server http://localhost:8989 cy:run",
"test-local": "start-server-and-test cy:server http://localhost:8989 cy:open",
"travis-deploy-once": "travis-deploy-once",
"semantic-release": "semantic-release",
"lint": "eslint src",
"lint-and-build": "yarn lint && yarn build",
"commit": "npx git-cz"
"test": "start-server-and-test cy:server http://localhost:8989 cy:run"
},
"files": [
"dist",
"src"
],
"devDependencies": {
"autoprefixer": "^9.0.0",
"chai": "3.5.0",
"cypress": "^9.2.0",
"cz-conventional-changelog": "^2.1.0",
"cypress": "3.0.1",
"deepmerge": "^2.0.1",
"eslint": "^5.0.1",
"eslint-config-airbnb": "^16.1.0",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-plugin-import": "^2.11.0",
"http-server": "^0.11.1",
"mocha": "3.3.0",
"postcss-custom-properties": "^7.0.0",
"postcss-cssnext": "^3.1.0",
"postcss-nested": "^3.0.0",
"rollup": "^0.59.4",
"rollup-plugin-commonjs": "^8.3.0",
@ -45,9 +32,7 @@
"rollup-plugin-node-resolve": "^3.0.3",
"rollup-plugin-postcss": "^1.2.8",
"rollup-plugin-uglify-es": "^0.0.1",
"semantic-release": "^17.1.1",
"start-server-and-test": "^1.4.1",
"travis-deploy-once": "^5.0.1"
"start-server-and-test": "^1.4.1"
},
"repository": {
"type": "git",
@ -64,15 +49,10 @@
"bugs": {
"url": "https://github.com/frappe/datatable/issues"
},
"homepage": "https://frappe.io/datatable",
"homepage": "https://frappe.github.io/datatable",
"dependencies": {
"hyperlist": "^1.0.0-beta",
"clusterize.js": "^0.18.0",
"lodash": "^4.17.5",
"sortablejs": "^1.7.0"
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
}
}
}

View File

@ -4,12 +4,11 @@ import nodeResolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import postcss from 'rollup-plugin-postcss';
import nested from 'postcss-nested';
import customProperties from 'postcss-custom-properties';
import autoprefixer from 'autoprefixer';
import cssnext from 'postcss-cssnext';
import eslint from 'rollup-plugin-eslint';
import merge from 'deepmerge';
const production = process.env.NODE_ENV === 'production';
const production = process.argv[3] === '--production';
const baseJS = {
input: 'src/index.js',
@ -41,9 +40,8 @@ const baseCSS = {
extract: true,
minimize: production,
plugins: [
customProperties(),
nested(),
autoprefixer()
cssnext()
]
})
]
@ -87,8 +85,24 @@ const prodCSS = merge(devCSS, {
}
});
// docs
const docJS = merge(devIIFE, {
output: {
file: 'docs/assets/js/frappe-datatable.js'
}
});
const docCSS = merge(devCSS, {
output: {
file: 'docs/assets/css/frappe-datatable.css'
}
});
const developmentAssets = [devIIFE, devCjs, devCSS];
const documentationAssets = [docJS, docCSS];
const productionAssets = [prodIIFE, prodCSS];
const assets = production ? productionAssets : developmentAssets;
const docs = process.argv[3] === '--docs';
const assets = docs ? documentationAssets : production ? productionAssets : developmentAssets;
export default assets;

View File

@ -1,4 +1,6 @@
import HyperList from 'hyperlist';
import Clusterize from 'clusterize.js';
import $ from './dom';
import { nextTick } from './utils';
export default class BodyRenderer {
constructor(instance) {
@ -8,115 +10,60 @@ export default class BodyRenderer {
this.rowmanager = instance.rowmanager;
this.cellmanager = instance.cellmanager;
this.bodyScrollable = instance.bodyScrollable;
this.footer = this.instance.footer;
this.log = instance.log;
}
renderRows(rows) {
this.visibleRows = rows;
this.visibleRowIndices = rows.map(row => row.meta.rowIndex);
if (rows.length === 0) {
this.bodyScrollable.innerHTML = this.getNoDataHTML();
this.footer.innerHTML = '';
return;
}
// Create a temporary set for faster lookups.
// We can't change this.visibleRowIndices as it would be breaking for users.
let visibleRowIndicesSet = new Set(this.visibleRowIndices);
const rowViewOrder = this.datamanager.rowViewOrder.map(index => {
if (visibleRowIndicesSet.has(index)) {
return index;
}
return null;
}).filter(index => index !== null);
const computedStyle = getComputedStyle(this.bodyScrollable);
let config = {
width: computedStyle.width,
height: computedStyle.height,
itemHeight: this.options.cellHeight,
total: rows.length,
generate: (index) => {
const el = document.createElement('div');
const rowIndex = rowViewOrder[index];
const row = this.datamanager.getRow(rowIndex);
const rowHTML = this.rowmanager.getRowHTML(row, row.meta);
el.innerHTML = rowHTML;
return el.children[0];
},
afterRender: () => {
this.restoreState();
}
};
if (!this.hyperlist) {
this.hyperlist = new HyperList(this.bodyScrollable, config);
} else {
this.hyperlist.refresh(this.bodyScrollable, config);
}
this.renderFooter();
this.appendRemainingData = nextTick(this.appendRemainingData, this);
}
render() {
if (this.options.clusterize) {
this.renderBodyWithClusterize();
} else {
this.renderBodyHTML();
}
}
renderBodyHTML() {
const rows = this.datamanager.getRowsForView();
this.renderRows(rows);
// setDimensions requires atleast 1 row to exist in dom
this.bodyScrollable.innerHTML = this.getBodyHTML(rows);
this.instance.setDimensions();
this.restoreState();
}
renderFooter() {
if (!this.options.showTotalRow) return;
renderBodyWithClusterize() {
// first page
const rows = this.datamanager.getRowsForView(0, 20);
let initialData = this.getDataForClusterize(rows);
const totalRow = this.getTotalRow();
let html = this.rowmanager.getRowHTML(totalRow, { isTotalRow: 1, rowIndex: 'totalRow' });
if (initialData.length === 0) {
initialData = [this.getNoDataHTML()];
}
this.footer.innerHTML = html;
}
if (!this.clusterize) {
// empty body
this.bodyScrollable.innerHTML = this.getBodyHTML([]);
getTotalRow() {
const columns = this.datamanager.getColumns();
const totalRowTemplate = columns.map(col => {
let content = null;
if (['_rowIndex', '_checkbox'].includes(col.id)) {
content = '';
}
return {
content,
isTotalRow: 1,
colIndex: col.colIndex,
column: col
};
});
// first 20 rows will appended
// rest of them in nextTick
this.clusterize = new Clusterize({
rows: initialData,
scrollElem: this.bodyScrollable,
contentElem: $('tbody', this.bodyScrollable),
callbacks: {
clusterChanged: () => this.restoreState()
},
/* eslint-disable */
show_no_data_row: false,
/* eslint-enable */
});
const totalRow = totalRowTemplate.map((cell, i) => {
if (cell.content === '') return cell;
// setDimensions requires atleast 1 row to exist in dom
this.instance.setDimensions();
} else {
this.clusterize.update(initialData);
}
if (this.options.hooks.columnTotal) {
const columnValues = this.visibleRows.map(row => row[i].content);
const result = this.options.hooks.columnTotal.call(this.instance, columnValues, cell);
if (result != null) {
cell.content = result;
return cell;
}
}
cell.content = this.visibleRows.reduce((acc, prevRow) => {
const prevCell = prevRow[i];
if (typeof prevCell.content === 'number') {
if (acc == null) acc = 0;
return acc + prevCell.content;
}
return acc;
}, cell.content);
return cell;
});
return totalRow;
this.appendRemainingData();
}
restoreState() {
@ -125,6 +72,12 @@ export default class BodyRenderer {
this.cellmanager.focusCellOnClusterChanged();
}
appendRemainingData() {
const rows = this.datamanager.getRowsForView(20);
const data = this.getDataForClusterize(rows);
this.clusterize.append(data);
}
showToastMessage(message, hideAfter) {
this.instance.toastMessage.innerHTML = this.getToastMessageHTML(message);
@ -139,6 +92,20 @@ export default class BodyRenderer {
this.instance.toastMessage.innerHTML = '';
}
getDataForClusterize(rows) {
return rows.map(row => this.rowmanager.getRowHTML(row, row.meta));
}
getBodyHTML(rows) {
return `
<table class="dt-body">
<tbody>
${rows.map(row => this.rowmanager.getRowHTML(row, row.meta)).join('')}
</tbody>
</table>
`;
}
getNoDataHTML() {
return `<div class="dt-scrollable__no-data">${this.options.noDataMessage}</div>`;
}

View File

@ -2,11 +2,9 @@ import {
copyTextToClipboard,
makeDataAttributeString,
throttle,
linkProperties,
escapeHTML,
linkProperties
} from './utils';
import $ from './dom';
import icons from './icons';
export default class CellManager {
constructor(instance) {
@ -15,18 +13,14 @@ export default class CellManager {
'wrapper',
'options',
'style',
'header',
'bodyScrollable',
'columnmanager',
'rowmanager',
'datamanager',
'keyboard',
'footer'
'keyboard'
]);
this.bindEvents();
this.stickyRowWidth = 0;
this.stickyColWitdh = [];
}
bindEvents() {
@ -55,12 +49,34 @@ export default class CellManager {
this.activateEditing(this.$focusedCell);
} else if (this.$editingCell) {
// enter keypress on editing cell
this.submitEditing();
this.deactivateEditing();
}
});
}
bindKeyboardNav() {
const focusCell = (direction) => {
if (!this.$focusedCell || this.$editingCell) {
return false;
}
let $cell = this.$focusedCell;
if (direction === 'left' || direction === 'shift+tab') {
$cell = this.getLeftCell$($cell);
} else if (direction === 'right' || direction === 'tab') {
$cell = this.getRightCell$($cell);
} else if (direction === 'up') {
$cell = this.getAboveCell$($cell);
} else if (direction === 'down') {
$cell = this.getBelowCell$($cell);
}
this.focusCell($cell);
return true;
};
const focusLastCell = (direction) => {
if (!this.$focusedCell || this.$editingCell) {
return false;
@ -87,13 +103,13 @@ export default class CellManager {
};
['left', 'right', 'up', 'down', 'tab', 'shift+tab']
.map(direction => this.keyboard.on(direction, () => this.focusCellInDirection(direction)));
.map(direction => this.keyboard.on(direction, () => focusCell(direction)));
['left', 'right', 'up', 'down']
.map(direction => this.keyboard.on(`ctrl+${direction}`, () => focusLastCell(direction)));
this.keyboard.on('esc', () => {
this.deactivateEditing(false);
this.deactivateEditing();
this.columnmanager.toggleFilter(false);
});
@ -105,10 +121,6 @@ export default class CellManager {
this.activateFilter(colIndex);
return true;
});
$.on(this.header, 'focusin', '.dt-filter', () => {
this.unfocusCell(this.$focusedCell);
});
}
}
@ -137,10 +149,7 @@ export default class CellManager {
bindCopyCellContents() {
this.keyboard.on('ctrl+c', () => {
const noOfCellsCopied = this.copyCellContents(this.$focusedCell, this.$selectionCursor);
const message = this.instance.translate('{count} cells copied', {
count: noOfCellsCopied
});
const message = `${noOfCellsCopied} cell${noOfCellsCopied > 1 ? 's' : ''} copied`;
if (noOfCellsCopied) {
this.instance.showToastMessage(message, 2);
}
@ -175,14 +184,6 @@ export default class CellManager {
mouseDown = false;
});
if (this.options.showTotalRow) {
$.on(this.footer, 'click', '.dt-cell', (e) => {
this.focusCell($(e.delegatedTarget));
});
}
const selectArea = (e) => {
if (!mouseDown) return;
this.selectArea($(e.delegatedTarget));
@ -205,9 +206,7 @@ export default class CellManager {
}
focusCell($cell, {
skipClearSelection = 0,
skipDOMFocus = 0,
skipScrollToCell = 0
skipClearSelection = 0
} = {}) {
if (!$cell) return;
@ -227,9 +226,7 @@ export default class CellManager {
return;
}
if (!skipScrollToCell) {
this.scrollToCell($cell);
}
this.scrollToCell($cell);
this.deactivateEditing();
if (!skipClearSelection) {
@ -243,27 +240,12 @@ export default class CellManager {
this.$focusedCell = $cell;
$cell.classList.add('dt-cell--focus');
if (!skipDOMFocus) {
// so that keyboard nav works
$cell.focus();
}
// so that keyboard nav works
$cell.focus();
this.highlightRowColumnHeader($cell);
}
unfocusCell($cell) {
if (!$cell) return;
// remove cell border
$cell.classList.remove('dt-cell--focus');
this.$focusedCell = null;
// reset header background
if (this.lastHeaders) {
this.lastHeaders.forEach(header => header && header.classList.remove('dt-cell--highlight'));
}
}
highlightRowColumnHeader($cell) {
const {
colIndex,
@ -275,14 +257,14 @@ export default class CellManager {
const rowHeaderSelector = `.dt-cell--${srNoColIndex}-${rowIndex}`;
if (this.lastHeaders) {
this.lastHeaders.forEach(header => header && header.classList.remove('dt-cell--highlight'));
this.lastHeaders.forEach(header => header.classList.remove('dt-cell--highlight'));
}
const colHeader = $(colHeaderSelector, this.wrapper);
const rowHeader = $(rowHeaderSelector, this.wrapper);
this.lastHeaders = [colHeader, rowHeader];
this.lastHeaders.forEach(header => header && header.classList.add('dt-cell--highlight'));
this.lastHeaders.forEach(header => header.classList.add('dt-cell--highlight'));
}
selectAreaOnClusterChanged() {
@ -312,15 +294,11 @@ export default class CellManager {
const $cell = this.getCell$(colIndex, rowIndex);
if (!$cell) return;
// this function is called after hyperlist renders the rows after scroll,
// this function is called after selectAreaOnClusterChanged,
// focusCell calls clearSelection which resets the area selection
// so a flag to skip it
// we also skip DOM focus and scroll to cell
// because it fights with the user scroll
this.focusCell($cell, {
skipClearSelection: 1,
skipDOMFocus: 1,
skipScrollToCell: 1
skipClearSelection: 1
});
}
@ -456,10 +434,7 @@ export default class CellManager {
}
}
deactivateEditing(submitValue = true) {
if (submitValue) {
this.submitEditing();
}
deactivateEditing() {
// keep focus on the cell so that keyboard navigation works
if (this.$focusedCell) this.$focusedCell.focus();
@ -510,9 +485,7 @@ export default class CellManager {
}
submitEditing() {
let promise = Promise.resolve();
if (!this.$editingCell) return promise;
if (!this.$editingCell) return;
const $cell = this.$editingCell;
const {
rowIndex,
@ -531,15 +504,12 @@ export default class CellManager {
valuePromise = Promise.resolve(valuePromise);
}
promise = valuePromise.then((value) => {
valuePromise.then((value) => {
const done = editor.setValue(value, rowIndex, col);
const oldValue = this.getCell(colIndex, rowIndex).content;
if (oldValue === value) return false;
const done = editor.setValue(value, rowIndex, col);
// update cell immediately
this.updateCell(colIndex, rowIndex, value, true);
this.updateCell(colIndex, rowIndex, value);
$cell.focus();
if (done && done.then) {
@ -549,13 +519,11 @@ export default class CellManager {
this.updateCell(colIndex, rowIndex, oldValue);
});
}
return done;
});
}
}
this.currentCellEditor = null;
return promise;
}
copyCellContents($cell1, $cell2) {
@ -563,18 +531,10 @@ export default class CellManager {
// copy only focusedCell
const {
colIndex,
rowIndex,
isTotalRow
rowIndex
} = $.data($cell1);
let copiedContent = '';
if (isTotalRow) {
let choosenFooterCell = this.$focusedCell;
copiedContent = choosenFooterCell.children[0].title;
} else {
const cell = this.getCell(colIndex, rowIndex);
copiedContent = cell.content;
}
copyTextToClipboard(copiedContent);
const cell = this.getCell(colIndex, rowIndex);
copyTextToClipboard(cell.content);
return 1;
}
const cells = this.getCellsInRange($cell1, $cell2);
@ -625,7 +585,7 @@ export default class CellManager {
let rowIndex = i + focusedCell.rowIndex;
row.forEach((cell, j) => {
let colIndex = j + focusedCell.colIndex;
this.updateCell(colIndex, rowIndex, cell, true);
this.updateCell(colIndex, rowIndex, cell);
});
});
}
@ -636,20 +596,20 @@ export default class CellManager {
if (!this.columnmanager.isFilterShown) {
// put focus back on cell
this.$focusedCell && this.$focusedCell.focus();
this.$focusedCell.focus();
}
}
updateCell(colIndex, rowIndex, value, refreshHtml = false) {
updateCell(colIndex, rowIndex, value) {
const cell = this.datamanager.updateCell(colIndex, rowIndex, {
content: value
});
this.refreshCell(cell, refreshHtml);
this.refreshCell(cell);
}
refreshCell(cell, refreshHtml = false) {
refreshCell(cell) {
const $cell = $(this.selector(cell.colIndex, cell.rowIndex), this.bodyScrollable);
$cell.innerHTML = this.getCellContent(cell, refreshHtml);
$cell.innerHTML = this.getCellContent(cell);
}
toggleTreeButton(rowIndex, flag) {
@ -665,49 +625,6 @@ export default class CellManager {
return colIndex < this.columnmanager.getFirstColumnIndex();
}
focusCellInDirection(direction) {
if (!this.$focusedCell || (this.$editingCell && ['left', 'right', 'up', 'down'].includes(direction))) {
return false;
} else if (this.$editingCell && ['tab', 'shift+tab'].includes(direction)) {
this.deactivateEditing();
}
let $cell = this.$focusedCell;
if (direction === 'left' || direction === 'shift+tab') {
$cell = this.getLeftCell$($cell);
} else if (direction === 'right' || direction === 'tab') {
$cell = this.getRightCell$($cell);
} else if (direction === 'up') {
$cell = this.getAboveCell$($cell);
} else if (direction === 'down') {
$cell = this.getBelowCell$($cell);
}
if (!$cell) {
return false;
}
const {
colIndex
} = $.data($cell);
const column = this.columnmanager.getColumn(colIndex);
if (!column.focusable) {
let $prevFocusedCell = this.$focusedCell;
this.unfocusCell($prevFocusedCell);
this.$focusedCell = $cell;
let ret = this.focusCellInDirection(direction);
if (!ret) {
this.focusCell($prevFocusedCell);
}
return ret;
}
this.focusCell($cell);
return true;
}
getCell$(colIndex, rowIndex) {
return $(this.selector(colIndex, rowIndex), this.bodyScrollable);
}
@ -773,7 +690,7 @@ export default class CellManager {
}
scrollToCell($cell) {
if ($.inViewport($cell, this.bodyScrollable) || $.inViewport($cell, this.footer)) return false;
if ($.inViewport($cell, this.bodyScrollable)) return false;
const {
rowIndex
@ -791,54 +708,16 @@ export default class CellManager {
rowIndex,
colIndex,
isHeader,
isFilter,
isTotalRow
isFilter
} = cell;
const dataAttr = makeDataAttributeString({
rowIndex,
colIndex,
isHeader,
isFilter,
isTotalRow
isFilter
});
let styles = '';
const row = this.datamanager.getRow(rowIndex);
const isBodyCell = !(isHeader || isFilter || isTotalRow);
const serialNoColIndex = !this.options.checkboxColumn && this.options.serialNoColumn ? 0 : 1;
let sticky = false;
let checkboxserialNoclass = '';
if (colIndex === 0 && this.options.checkboxColumn) {
if (cell.isHeader && !(cell.id in this.stickyColWitdh)) this.stickyRowWidth = 34;
checkboxserialNoclass = 'dt-cell-checkbox';
sticky = true;
} else if (colIndex === serialNoColIndex && this.options.serialNoColumn) {
if (cell.isHeader && !(cell.id in this.stickyColWitdh)) {
this.stickyColWitdh[cell.id] = this.stickyRowWidth;
this.stickyRowWidth += (cell.width || 37);
checkboxserialNoclass = 'dt-cell-serial-no';
}
styles = `left:${this.stickyColWitdh[isBodyCell ? cell.column.id : cell.id]}px;`;
sticky = true;
} else if (cell.sticky) {
if (cell.isHeader && !(cell.id in this.stickyColWitdh)) {
this.stickyColWitdh[cell.id] = this.stickyRowWidth;
this.stickyRowWidth += ((cell.width || 100) + 1);
}
styles = `left:${this.stickyColWitdh[cell.id]}px;`;
sticky = true;
} else if ((isBodyCell || isTotalRow) && cell.column.sticky) {
styles = `left:${this.stickyColWitdh[cell.column.id]}px;`;
sticky = true;
}
const isBodyCell = !(isHeader || isFilter);
const className = [
'dt-cell',
@ -847,20 +726,17 @@ export default class CellManager {
isBodyCell ? 'dt-cell--row-' + rowIndex : '',
isHeader ? 'dt-cell--header' : '',
isHeader ? `dt-cell--header-${colIndex}` : '',
isFilter ? 'dt-cell--filter' : '',
isBodyCell && (row && row.meta.isTreeNodeClose) ? 'dt-cell--tree-close' : '',
sticky ? 'dt-sticky-col' : '',
checkboxserialNoclass,
isFilter ? 'dt-cell--filter' : ''
].join(' ');
return `
<div class="${className}" ${dataAttr} tabindex="0" style="${styles}">
<td class="${className}" ${dataAttr} tabindex="0">
${this.getCellContent(cell)}
</div>
</td>
`;
}
getCellContent(cell, refreshHtml = false) {
getCellContent(cell) {
const {
isHeader,
isFilter,
@ -883,41 +759,31 @@ export default class CellManager {
const hasDropdown = isHeader && cell.dropdown !== false;
const dropdown = hasDropdown ? this.columnmanager.getDropdownHTML() : '';
let customFormatter = CellManager.getCustomCellFormatter(cell);
const customFormatter = cell.format || (cell.column && cell.column.format) || null;
let contentHTML;
if (isHeader || isFilter || !customFormatter) {
contentHTML = cell.content;
} else {
if (!cell.html || refreshHtml) {
const row = this.datamanager.getRow(cell.rowIndex);
const data = this.datamanager.getData(cell.rowIndex);
contentHTML = customFormatter(cell.content, row, cell.column, data);
} else {
contentHTML = cell.html;
}
const row = this.datamanager.getRow(cell.rowIndex);
const data = this.datamanager.getData(cell.rowIndex);
contentHTML = customFormatter(cell.content, row, cell.column, data);
}
cell.html = contentHTML;
if (this.options.treeView && !(isHeader || isFilter) && cell.indent !== undefined) {
const nextRow = this.datamanager.getRow(cell.rowIndex + 1);
const addToggle = nextRow && nextRow.meta.indent > cell.indent;
const leftPadding = 20;
const unit = 'px';
const leftPadding = 1;
// Add toggle and indent in the first column
const firstColumnIndex = this.datamanager.getColumnIndexById('_rowIndex') + 1;
if (firstColumnIndex === cell.colIndex) {
const padding = ((cell.indent || 0)) * leftPadding;
const padding = ((cell.indent || 0) + 1) * leftPadding;
const toggleHTML = addToggle ?
`<span class="dt-tree-node__toggle" style="left: ${padding - leftPadding}${unit}">
<span class="icon-open">${icons.chevronDown}</span>
<span class="icon-close">${icons.chevronRight}</span>
</span>` : '';
contentHTML = `<span class="dt-tree-node" style="padding-left: ${padding}${unit}">
${toggleHTML}
<span>${contentHTML}</span>
</span>`;
`<span class="dt-tree-node__toggle" style="left: ${padding - leftPadding}rem"></span>` : '';
contentHTML = `<span class="dt-tree-node" style="padding-left: ${padding}rem">
${toggleHTML}${contentHTML}</span>`;
}
}
@ -926,7 +792,7 @@ export default class CellManager {
isHeader ? `dt-cell__content--header-${colIndex}` : `dt-cell__content--col-${colIndex}`
].join(' ');
let cellContentHTML = `
return `
<div class="${className}">
${contentHTML}
${sortIndicator}
@ -935,16 +801,6 @@ export default class CellManager {
</div>
${editCellHTML}
`;
let div = document.createElement('div');
div.innerHTML = contentHTML;
let textContent = div.textContent;
textContent = textContent.replace(/\s+/g, ' ').trim();
cellContentHTML = cellContentHTML.replace('>', ` title="${escapeHTML(textContent)}">`);
return cellContentHTML;
}
getEditCellHTML(colIndex) {
@ -954,8 +810,4 @@ export default class CellManager {
selector(colIndex, rowIndex) {
return `.dt-cell--${colIndex}-${rowIndex}`;
}
static getCustomCellFormatter(cell) {
return cell.format || (cell.column && cell.column.format) || null;
}
}

View File

@ -14,19 +14,17 @@ export default class ColumnManager {
'fireEvent',
'header',
'datamanager',
'cellmanager',
'style',
'wrapper',
'rowmanager',
'bodyScrollable',
'bodyRenderer'
'bodyScrollable'
]);
this.bindEvents();
}
renderHeader() {
this.header.innerHTML = '<div></div>';
this.header.innerHTML = '<thead></thead>';
this.refreshHeader();
}
@ -34,9 +32,9 @@ export default class ColumnManager {
const columns = this.datamanager.getColumns();
// refresh html
$('div', this.header).innerHTML = this.getHeaderHTML(columns);
$('thead', this.header).innerHTML = this.getHeaderHTML(columns);
this.$filterRow = $('.dt-row-filter', this.header);
this.$filterRow = $('.dt-row[data-is-filter]', this.header);
if (this.$filterRow) {
$.style(this.$filterRow, { display: 'none' });
}
@ -60,77 +58,50 @@ export default class ColumnManager {
bindEvents() {
this.bindDropdown();
this.bindResizeColumn();
this.bindPerfectColumnWidth();
this.bindFilter();
}
bindDropdown() {
let $activeDropdown;
let activeClass = 'dt-dropdown--active';
let toggleClass = '.dt-dropdown__toggle';
let dropdownClass = '.dt-dropdown__list';
// attach the dropdown list to container
this.instance.dropdownContainer.innerHTML = this.getDropdownListHTML();
this.$dropdownList = this.instance.dropdownContainer.firstElementChild;
$.on(this.header, 'click', toggleClass, (e, $button) => {
const $dropdown = $.closest('.dt-dropdown', $button);
$.on(this.header, 'click', toggleClass, e => {
this.openDropdown(e);
if (!$dropdown.classList.contains(activeClass)) {
deactivateDropdown();
$dropdown.classList.add(activeClass);
$activeDropdown = $dropdown;
} else {
deactivateDropdown();
}
});
const deactivateDropdownOnBodyClick = (e) => {
const selector = [
toggleClass, toggleClass + ' *',
dropdownClass, dropdownClass + ' *'
].join(',');
if (e.target.matches(selector)) return;
$.on(document.body, 'click', (e) => {
if (e.target.matches(toggleClass)) return;
deactivateDropdown();
};
$.on(document.body, 'click', deactivateDropdownOnBodyClick);
document.addEventListener('scroll', deactivateDropdown, true);
this.instance.on('onDestroy', () => {
$.off(document.body, 'click', deactivateDropdownOnBodyClick);
$.off(document, 'scroll', deactivateDropdown);
});
$.on(this.$dropdownList, 'click', '.dt-dropdown__list-item', (e, $item) => {
if (!this._dropdownActiveColIndex) return;
const dropdownItems = this.options.headerDropdown;
const { index } = $.data($item);
const colIndex = this._dropdownActiveColIndex;
const dropdownItems = this.options.headerDropdown;
$.on(this.header, 'click', '.dt-dropdown__list-item', (e, $item) => {
const $col = $.closest('.dt-cell', $item);
const {
index
} = $.data($item);
const {
colIndex
} = $.data($col);
let callback = dropdownItems[index].action;
callback && callback.call(this.instance, this.getColumn(colIndex));
this.hideDropdown();
});
const _this = this;
function deactivateDropdown(e) {
_this.hideDropdown();
$activeDropdown && $activeDropdown.classList.remove(activeClass);
$activeDropdown = null;
}
this.hideDropdown();
}
openDropdown(e) {
if (!this._dropdownWidth) {
$.style(this.$dropdownList, { display: '' });
this._dropdownWidth = $.style(this.$dropdownList, 'width');
}
$.style(this.$dropdownList, {
display: '',
left: (e.clientX - this._dropdownWidth + 4) + 'px',
top: (e.clientY + 4) + 'px'
});
const $cell = $.closest('.dt-cell', e.target);
const { colIndex } = $.data($cell);
this._dropdownActiveColIndex = colIndex;
}
hideDropdown() {
$.style(this.$dropdownList, {
display: 'none'
});
this._dropdownActiveColIndex = null;
}
bindResizeColumn() {
@ -155,7 +126,7 @@ export default class ColumnManager {
startX = e.pageX;
});
const onMouseup = (e) => {
$.on(document.body, 'mouseup', (e) => {
document.body.classList.remove('dt-resize');
if (!$resizingCell) return;
isDragging = false;
@ -166,74 +137,27 @@ export default class ColumnManager {
this.setColumnWidth(colIndex);
this.style.setBodyStyle();
$resizingCell = null;
};
$.on(document.body, 'mouseup', onMouseup);
this.instance.on('onDestroy', () => {
$.off(document.body, 'mouseup', onMouseup);
});
const onMouseMove = (e) => {
$.on(document.body, 'mousemove', (e) => {
if (!isDragging) return;
let delta = e.pageX - startX;
if (this.options.direction === 'rtl') {
delta = -1 * delta;
}
const finalWidth = startWidth + delta;
const finalWidth = startWidth + (e.pageX - startX);
const {
colIndex
} = $.data($resizingCell);
let columnMinWidth = this.options.minimumColumnWidth;
if (columnMinWidth > finalWidth) {
// don't resize past 30 pixels
if (this.getColumnMinWidth(colIndex) > finalWidth) {
// don't resize past minWidth
return;
}
this.datamanager.updateColumn(colIndex, {
width: finalWidth
});
this.setColumnHeaderWidth(colIndex);
};
$.on(document.body, 'mousemove', onMouseMove);
this.instance.on('onDestroy', () => {
$.off(document.body, 'mousemove', onMouseMove);
});
}
bindPerfectColumnWidth() {
$.on(this.header, 'dblclick', '.dt-cell .dt-cell__resize-handle', (e, $handle) => {
const $cell = $handle.parentNode.parentNode;
const { colIndex } = $.data($cell);
let longestCell = this.bodyRenderer.visibleRows
.map(d => d[colIndex])
.reduce((acc, curr) => acc.content.length > curr.content.length ? acc : curr);
let $longestCellHTML = this.cellmanager.getCellHTML(longestCell);
let $div = document.createElement('div');
$div.innerHTML = $longestCellHTML;
let cellText = $div.querySelector('.dt-cell__content').textContent;
let {
borderLeftWidth,
borderRightWidth,
paddingLeft,
paddingRight
} = $.getStyle(this.bodyScrollable.querySelector('.dt-cell__content'));
let padding = [borderLeftWidth, borderRightWidth, paddingLeft, paddingRight]
.map(parseFloat)
.reduce((sum, val) => sum + val);
let width = $.measureTextWidth(cellText) + padding;
this.datamanager.updateColumn(colIndex, { width });
this.setColumnHeaderWidth(colIndex);
this.setColumnWidth(colIndex);
});
}
bindMoveColumn() {
if (this.options.disableReorderColumn) return;
const $parent = $('.dt-row', this.header);
this.sortable = Sortable.create($parent, {
@ -304,8 +228,6 @@ export default class ColumnManager {
}
toggleFilter(flag) {
if (!this.options.inlineFilters) return;
let showFilter;
if (flag === undefined) {
showFilter = !this.isFilterShown;
@ -326,38 +248,31 @@ export default class ColumnManager {
focusFilter(colIndex) {
if (!this.isFilterShown) return;
const $filterInput = $(`.dt-cell--col-${colIndex} .dt-filter`, this.$filterRow);
const $filterInput = $(`[data-col-index="${colIndex}"] .dt-filter`, this.$filterRow);
$filterInput.focus();
}
bindFilter() {
if (!this.options.inlineFilters) return;
const handler = e => {
this.applyFilter(this.getAppliedFilters());
const $filterCell = $.closest('.dt-cell', e.target);
const {
colIndex
} = $.data($filterCell);
const keyword = e.target.value;
this.datamanager.filterRows(keyword, colIndex)
.then(({
rowsToHide,
rowsToShow
}) => {
this.rowmanager.hideRows(rowsToHide);
this.rowmanager.showRows(rowsToShow);
});
};
$.on(this.header, 'keydown', '.dt-filter', debounce(handler, 300));
}
applyFilter(filters) {
this.datamanager.filterRows(filters)
.then(({
rowsToShow
}) => {
this.rowmanager.showRows(rowsToShow);
});
}
getAppliedFilters() {
const filters = {};
$.each('.dt-filter', this.header).map((input) => {
const value = input.value;
if (value) {
filters[input.dataset.colIndex] = value;
}
});
return filters;
}
applyDefaultSortOrder() {
// sort rows if any 1 column has a default sortOrder set
const columnsToSort = this.getColumns().filter(col => col.sortOrder !== 'none');
@ -432,24 +347,17 @@ export default class ColumnManager {
}
getDropdownHTML() {
const { dropdownButton } = this.options;
const { dropdownButton, headerDropdown: dropdownItems } = this.options;
return `
<div class="dt-dropdown">
<div class="dt-dropdown__toggle">${dropdownButton}</div>
<div class="dt-dropdown__list">
${dropdownItems.map((d, i) => `
<div class="dt-dropdown__list-item" data-index="${i}">${d.label}</div>
`).join('')}
</div>
</div>
`;
}
getDropdownListHTML() {
const { headerDropdown: dropdownItems } = this.options;
return `
<div class="dt-dropdown__list">
${dropdownItems.map((d, i) => `
<div class="dt-dropdown__list-item" data-index="${i}">${d.label}</div>
`).join('')}
</div>
`;
}
}

View File

@ -1,11 +0,0 @@
.datatable {
--dt-border-color: #424242;
--dt-light-bg: #2e3538;
--dt-text-color: #dfe2e5;
--dt-text-light: #dfe2e5;
--dt-cell-bg: #1c1f20;
--dt-focus-border-width: 1px;
--dt-selection-highlight-color: var(--dt-light-bg);
--dt-toast-message-border: 1px solid var(--dt-border-color);
--dt-header-cell-bg: #262c2e;
}

View File

@ -11,7 +11,7 @@ export default class DataManager {
this.sortRows = nextTick(this.sortRows, this);
this.switchColumn = nextTick(this.switchColumn, this);
this.removeColumn = nextTick(this.removeColumn, this);
this.options.filterRows = nextTick(this.options.filterRows, this);
this.filterRows = nextTick(this.filterRows, this);
}
init(data, columns) {
@ -29,11 +29,11 @@ export default class DataManager {
this.rows = [];
this.prepareColumns();
this.validateData(this.data);
this.rows = this.prepareRows(this.data);
this.prepareRows();
this.prepareTreeRows();
this.prepareRowView();
this.prepareNumericColumns();
this.prepareGroupBy();
}
// computed property
@ -142,8 +142,77 @@ export default class DataManager {
});
}
prepareRows(data) {
return data.map((d, i) => {
prepareGroupBy() {
if (!this.options.groupBy) return;
const groupKey = this.options.groupBy;
const column = this.columns.find(column => column.id === groupKey);
if (!column) {
throw new Error('Invalid column id provided in groupBy option');
}
const groups =
this.rows
// get values
.map(row => row[column.colIndex].content)
// remove duplicates
.filter((value, i, self) => {
return self.indexOf(value) === i;
})
.sort();
const rowsByGroup = {};
for (const row of this.rows) {
const groupKey = row[column.colIndex].content;
rowsByGroup[groupKey] = rowsByGroup[groupKey] || [];
rowsByGroup[groupKey].push(row);
}
let rows = [];
const makeGroupRow = (groupValue) => {
const row = this.getColumns().map(c => ({ editable: false }));
const firstColumnIndex = this.getStandardColumnCount();
row[firstColumnIndex] = groupValue;
const meta = {
indent: 0
};
return this.prepareRow(row, meta);
};
for (let groupKey of groups) {
rowsByGroup[groupKey].forEach(row => {
row.meta.indent = 1;
});
rows = [
...rows,
makeGroupRow(groupKey),
...rowsByGroup[groupKey]
];
}
this.rows = rows.map((row, i) => {
row.meta.rowIndex = i;
row.forEach(cell => {
cell.rowIndex = i;
cell.indent = row.meta.indent;
});
row[1].content = i + 1;
return row;
});
}
prepareRows() {
this.validateData(this.data);
this.rows = this.data.map((d, i) => {
const index = this._getNextRowCount();
let row = [];
@ -192,7 +261,6 @@ export default class DataManager {
row.meta.isLeaf = !nextRow ||
notSet(nextRow.meta.indent) ||
nextRow.meta.indent <= row.meta.indent;
row.meta.isTreeNodeClose = false;
}
});
}
@ -242,9 +310,8 @@ export default class DataManager {
appendRows(rows) {
this.validateData(rows);
this.rows = this.rows.concat(this.prepareRows(rows));
this.prepareTreeRows();
this.prepareRowView();
this.rows.push(...this.prepareRows(rows));
}
sortRows(colIndex, sortOrder = 'none') {
@ -280,11 +347,8 @@ export default class DataManager {
this.rowViewOrder.sort((a, b) => {
const aIndex = a;
const bIndex = b;
let aContent = this.getCell(colIndex, a).content;
let bContent = this.getCell(colIndex, b).content;
aContent = aContent == null ? '' : aContent;
bContent = bContent == null ? '' : bContent;
const aContent = this.getCell(colIndex, a).content;
const bContent = this.getCell(colIndex, b).content;
if (sortOrder === 'none') {
return aIndex - bIndex;
@ -426,37 +490,32 @@ export default class DataManager {
return column;
}
filterRows(filters) {
return this.options.filterRows(this.rows, filters, this)
.then(result => {
if (!result) {
result = this.getAllRowIndices();
}
filterRows(keyword, colIndex) {
let rowsToHide = [];
let rowsToShow = [];
const cells = this.rows.map(row => row[colIndex]);
if (!result.then) {
result = Promise.resolve(result);
}
cells.forEach(cell => {
const hay = String(cell.content || '').toLowerCase();
const needle = (keyword || '').toLowerCase();
return result.then(rowsToShow => {
this._filteredRows = rowsToShow;
if (!needle || hay.includes(needle)) {
rowsToShow.push(cell.rowIndex);
} else {
rowsToHide.push(cell.rowIndex);
}
});
const rowsToHide = this.getAllRowIndices()
.filter(index => !rowsToShow.includes(index));
this._filteredRows = rowsToShow;
return {
rowsToHide,
rowsToShow
};
});
});
return {
rowsToHide,
rowsToShow
};
}
getFilteredRowIndices() {
return this._filteredRows || this.getAllRowIndices();
}
getAllRowIndices() {
return this.rows.map(row => row.meta.rowIndex);
return this._filteredRows || this.rows.map(row => row.meta.rowIndex);
}
getRowCount() {

View File

@ -6,18 +6,7 @@ import RowManager from './rowmanager';
import BodyRenderer from './body-renderer';
import Style from './style';
import Keyboard from './keyboard';
import TranslationManager from './translationmanager';
import getDefaultOptions from './defaults';
let defaultComponents = {
DataManager,
CellManager,
ColumnManager,
RowManager,
BodyRenderer,
Style,
Keyboard
};
import DEFAULT_OPTIONS from './defaults';
class DataTable {
constructor(wrapper, options) {
@ -32,11 +21,16 @@ class DataTable {
throw new Error('Invalid argument given for `wrapper`');
}
this.initializeTranslations(options);
this.setDefaultOptions();
this.buildOptions(options);
this.prepare();
this.initializeComponents();
this.style = new Style(this);
this.keyboard = new Keyboard(this.wrapper);
this.datamanager = new DataManager(this.options);
this.rowmanager = new RowManager(this);
this.columnmanager = new ColumnManager(this);
this.cellmanager = new CellManager(this);
this.bodyRenderer = new BodyRenderer(this);
if (this.options.data) {
this.refresh();
@ -44,36 +38,23 @@ class DataTable {
}
}
initializeTranslations(options) {
this.language = options.language || 'en';
this.translationManager = new TranslationManager(this.language);
if (options.translations) {
this.translationManager.addTranslations(options.translations);
}
}
setDefaultOptions() {
this.DEFAULT_OPTIONS = getDefaultOptions(this);
}
buildOptions(options) {
this.options = this.options || {};
this.options = Object.assign(
{}, this.DEFAULT_OPTIONS,
{}, DEFAULT_OPTIONS,
this.options || {}, options
);
options.headerDropdown = options.headerDropdown || [];
this.options.headerDropdown = [
...this.DEFAULT_OPTIONS.headerDropdown,
...DEFAULT_OPTIONS.headerDropdown,
...options.headerDropdown
];
// custom user events
this.events = Object.assign(
{}, this.DEFAULT_OPTIONS.events,
{}, DEFAULT_OPTIONS.events,
this.options.events || {},
options.events || {}
);
@ -85,34 +66,12 @@ class DataTable {
this.unfreeze();
}
initializeComponents() {
let components = Object.assign({}, defaultComponents, this.options.overrideComponents);
let {
Style,
Keyboard,
DataManager,
RowManager,
ColumnManager,
CellManager,
BodyRenderer
} = components;
this.style = new Style(this);
this.keyboard = new Keyboard(this.wrapper);
this.datamanager = new DataManager(this.options);
this.rowmanager = new RowManager(this);
this.columnmanager = new ColumnManager(this);
this.cellmanager = new CellManager(this);
this.bodyRenderer = new BodyRenderer(this);
}
prepareDom() {
this.wrapper.innerHTML = `
<div class="datatable" dir="${this.options.direction}">
<div class="datatable-content">
<div class="dt-header"></div>
<div class="dt-scrollable"></div>
<div class="dt-footer"></div>
<div class="datatable">
<table class="dt-header">
</table>
<div class="dt-scrollable">
</div>
<div class="dt-freeze">
<span class="dt-freeze__message">
@ -120,19 +79,16 @@ class DataTable {
</span>
</div>
<div class="dt-toast"></div>
<div class="dt-dropdown-container"></div>
<textarea class="dt-paste-target"></textarea>
</div>
`;
this.datatableWrapper = $('.datatable', this.wrapper);
this.header = $('.dt-header', this.wrapper);
this.footer = $('.dt-footer', this.wrapper);
this.bodyScrollable = $('.dt-scrollable', this.wrapper);
this.freezeContainer = $('.dt-freeze', this.wrapper);
this.toastMessage = $('.dt-toast', this.wrapper);
this.pasteTarget = $('.dt-paste-target', this.wrapper);
this.dropdownContainer = $('.dt-dropdown-container', this.wrapper);
}
refresh(data, columns) {
@ -144,7 +100,6 @@ class DataTable {
destroy() {
this.wrapper.innerHTML = '';
this.style.destroy();
this.fireEvent('onDestroy');
}
appendRows(rows) {
@ -238,22 +193,7 @@ class DataTable {
}
fireEvent(eventName, ...args) {
// fire internalEventHandlers if any
// and then user events
const handlers = [
...(this._internalEventHandlers[eventName] || []),
this.events[eventName]
].filter(Boolean);
for (let handler of handlers) {
handler.apply(this, args);
}
}
on(event, handler) {
this._internalEventHandlers = this._internalEventHandlers || {};
this._internalEventHandlers[event] = this._internalEventHandlers[event] || [];
this._internalEventHandlers[event].push(handler);
this.events[eventName].apply(this, args);
}
log() {
@ -261,10 +201,6 @@ class DataTable {
console.log.apply(console, arguments);
}
}
translate(str, args) {
return this.translationManager.translate(str, args);
}
}
DataTable.instances = 0;

View File

@ -1,73 +1,57 @@
import filterRows from './filterRows';
import icons from './icons';
export default function getDefaultOptions(instance) {
return {
columns: [],
data: [],
dropdownButton: icons.chevronDown,
headerDropdown: [
{
label: instance.translate('Sort Ascending'),
action: function (column) {
this.sortColumn(column.colIndex, 'asc');
}
},
{
label: instance.translate('Sort Descending'),
action: function (column) {
this.sortColumn(column.colIndex, 'desc');
}
},
{
label: instance.translate('Reset sorting'),
action: function (column) {
this.sortColumn(column.colIndex, 'none');
}
},
{
label: instance.translate('Remove column'),
action: function (column) {
this.removeColumn(column.colIndex);
}
export default {
columns: [],
data: [],
dropdownButton: '▼',
headerDropdown: [
{
label: 'Sort Ascending',
action: function (column) {
this.sortColumn(column.colIndex, 'asc');
}
],
events: {
onRemoveColumn(column) {},
onSwitchColumn(column1, column2) {},
onSortColumn(column) {},
onCheckRow(row) {},
onDestroy() {}
},
hooks: {
columnTotal: null
{
label: 'Sort Descending',
action: function (column) {
this.sortColumn(column.colIndex, 'desc');
}
},
sortIndicator: {
asc: '↑',
desc: '↓',
none: ''
{
label: 'Reset sorting',
action: function (column) {
this.sortColumn(column.colIndex, 'none');
}
},
overrideComponents: {
// ColumnManager: CustomColumnManager
},
filterRows: filterRows,
freezeMessage: '',
getEditor: null,
serialNoColumn: true,
checkboxColumn: false,
clusterize: true,
logs: false,
layout: 'fixed', // fixed, fluid, ratio
noDataMessage: instance.translate('No Data'),
cellHeight: 40,
minimumColumnWidth: 30,
inlineFilters: false,
treeView: false,
checkedRowStatus: true,
dynamicRowHeight: false,
pasteFromClipboard: false,
showTotalRow: false,
direction: 'ltr',
disableReorderColumn: false
};
{
label: 'Remove column',
action: function (column) {
this.removeColumn(column.colIndex);
}
}
],
events: {
onRemoveColumn(column) {},
onSwitchColumn(column1, column2) {},
onSortColumn(column) {},
onCheckRow(row) {}
},
sortIndicator: {
asc: '↑',
desc: '↓',
none: ''
},
groupBy: '', // column id
freezeMessage: '',
getEditor: null,
serialNoColumn: true,
checkboxColumn: false,
clusterize: true,
logs: false,
layout: 'fixed', // fixed, fluid, ratio
noDataMessage: 'No Data',
cellHeight: null,
inlineFilters: false,
treeView: false,
checkedRowStatus: true,
dynamicRowHeight: false,
pasteFromClipboard: false
};

View File

@ -138,10 +138,6 @@ $.removeStyle = (elements, styleProps) => {
};
$.getStyle = (element, prop) => {
if (!prop) {
return getComputedStyle(element);
}
let val = getComputedStyle(element)[prop];
if (['width', 'height'].includes(prop)) {
@ -184,16 +180,7 @@ $.scrollTop = function scrollTop(element, pixels) {
});
};
$.scrollbarSize = function scrollbarSize() {
if (!$.scrollBarSizeValue) {
$.scrollBarSizeValue = getScrollBarSize();
}
return $.scrollBarSizeValue;
};
function getScrollBarSize() {
// assume scrollbar width and height would be the same
$.scrollbarWidth = function scrollbarWidth() {
// Create the measurement node
const scrollDiv = document.createElement('div');
$.style(scrollDiv, {
@ -212,24 +199,4 @@ function getScrollBarSize() {
document.body.removeChild(scrollDiv);
return scrollbarWidth;
}
$.hasVerticalOverflow = function (element) {
return element.scrollHeight > element.offsetHeight + 10;
};
$.hasHorizontalOverflow = function (element) {
return element.scrollWidth > element.offsetWidth + 10;
};
$.measureTextWidth = function (text) {
const div = document.createElement('div');
div.style.position = 'absolute';
div.style.visibility = 'hidden';
div.style.height = 'auto';
div.style.width = 'auto';
div.style.whiteSpace = 'nowrap';
div.innerText = text;
document.body.appendChild(div);
return div.clientWidth + 1;
};

View File

@ -1,209 +0,0 @@
import { isNumber, stripHTML } from './utils';
import CellManager from './cellmanager';
export default function filterRows(rows, filters, data) {
let filteredRowIndices = [];
if (Object.keys(filters).length === 0) {
return rows.map(row => row.meta.rowIndex);
}
for (let colIndex in filters) {
const keyword = filters[colIndex];
const filteredRows = filteredRowIndices.length ?
filteredRowIndices.map(i => rows[i]) :
rows;
const cells = filteredRows.map(row => row[colIndex]);
let filter = guessFilter(keyword);
let filterMethod = getFilterMethod(rows, data, filter);
if (filterMethod) {
filteredRowIndices = filterMethod(filter.text, cells);
} else {
filteredRowIndices = cells.map(cell => cell.rowIndex);
}
}
return filteredRowIndices;
};
function getFilterMethod(rows, allData, filter) {
const getFormattedValue = cell => {
let formatter = CellManager.getCustomCellFormatter(cell);
let rowData = rows[cell.rowIndex];
if (allData && allData.data && allData.data.length) {
rowData = allData.data[cell.rowIndex];
}
if (formatter && cell.content) {
cell.html = formatter(cell.content, rows[cell.rowIndex], cell.column, rowData, filter);
return stripHTML(cell.html);
}
return cell.content || '';
};
const stringCompareValue = cell =>
String(stripHTML(cell.html || '') || getFormattedValue(cell)).toLowerCase();
const numberCompareValue = cell => parseFloat(cell.content);
const getCompareValues = (cell, keyword) => {
if (cell.column.compareValue) {
const compareValues = cell.column.compareValue(cell, keyword);
if (compareValues && Array.isArray(compareValues)) return compareValues;
}
// check if it can be converted to number
const float = numberCompareValue(cell);
if (!isNaN(float)) {
return [float, keyword];
}
return [stringCompareValue(cell), keyword];
};
let filterMethodMap = {
contains(keyword, cells) {
return cells
.filter(cell => {
const needle = (keyword || '').toLowerCase();
return !needle ||
(cell.content || '').toLowerCase().includes(needle) ||
stringCompareValue(cell).includes(needle);
})
.map(cell => cell.rowIndex);
},
greaterThan(keyword, cells) {
return cells
.filter(cell => {
const [compareValue, keywordValue] = getCompareValues(cell, keyword);
return compareValue > keywordValue;
})
.map(cell => cell.rowIndex);
},
lessThan(keyword, cells) {
return cells
.filter(cell => {
const [compareValue, keywordValue] = getCompareValues(cell, keyword);
return compareValue < keywordValue;
})
.map(cell => cell.rowIndex);
},
equals(keyword, cells) {
return cells
.filter(cell => {
const value = parseFloat(cell.content);
return value === keyword;
})
.map(cell => cell.rowIndex);
},
notEquals(keyword, cells) {
return cells
.filter(cell => {
const value = parseFloat(cell.content);
return value !== keyword;
})
.map(cell => cell.rowIndex);
},
range(rangeValues, cells) {
return cells
.filter(cell => {
const values1 = getCompareValues(cell, rangeValues[0]);
const values2 = getCompareValues(cell, rangeValues[1]);
const value = values1[0];
return value >= values1[1] && value <= values2[1];
})
.map(cell => cell.rowIndex);
},
containsNumber(keyword, cells) {
return cells
.filter(cell => {
let number = parseFloat(keyword, 10);
let string = keyword;
let hayNumber = numberCompareValue(cell);
let hayString = stringCompareValue(cell);
return number === hayNumber || hayString.includes(string);
})
.map(cell => cell.rowIndex);
}
};
return filterMethodMap[filter.type];
}
function guessFilter(keyword = '') {
if (keyword.length === 0) return {};
let compareString = keyword;
if (['>', '<', '='].includes(compareString[0])) {
compareString = keyword.slice(1);
} else if (compareString.startsWith('!=')) {
compareString = keyword.slice(2);
}
if (keyword.startsWith('>')) {
if (compareString) {
return {
type: 'greaterThan',
text: compareString.trim()
};
}
}
if (keyword.startsWith('<')) {
if (compareString) {
return {
type: 'lessThan',
text: compareString.trim()
};
}
}
if (keyword.startsWith('=')) {
if (isNumber(compareString)) {
return {
type: 'equals',
text: Number(keyword.slice(1).trim())
};
}
}
if (isNumber(compareString)) {
return {
type: 'containsNumber',
text: compareString
};
}
if (keyword.startsWith('!=')) {
if (isNumber(compareString)) {
return {
type: 'notEquals',
text: Number(keyword.slice(2).trim())
};
}
}
if (keyword.split(':').length === 2 && keyword.split(':').every(v => isNumber(v.trim()))) {
compareString = keyword.split(':');
return {
type: 'range',
text: compareString.map(v => v.trim())
};
}
return {
type: 'contains',
text: compareString.toLowerCase()
};
}

View File

@ -1,10 +0,0 @@
/* eslint-disable max-len */
// Icons from https://feathericons.com/
let icons = {
chevronDown: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg>',
chevronRight: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right"><polyline points="9 18 15 12 9 6"></polyline></svg>'
};
export default icons;

View File

@ -3,9 +3,7 @@ import {
makeDataAttributeString,
nextTick,
ensureArray,
linkProperties,
uniq,
numberSortAsc
linkProperties
} from './utils';
export default class RowManager {
@ -16,8 +14,7 @@ export default class RowManager {
'fireEvent',
'wrapper',
'bodyScrollable',
'bodyRenderer',
'style'
'bodyRenderer'
]);
this.bindEvents();
@ -42,7 +39,7 @@ export default class RowManager {
// map of checked rows
this.checkMap = [];
$.on(this.wrapper, 'click', '.dt-cell--col-0 [type="checkbox"]', (e, $checkbox) => {
$.on(this.wrapper, 'click', '.dt-cell[data-col-index="0"] [type="checkbox"]', (e, $checkbox) => {
const $cell = $checkbox.closest('.dt-cell');
const {
rowIndex,
@ -67,7 +64,7 @@ export default class RowManager {
const _row = this.datamanager.updateRow(row, rowIndex);
_row.forEach(cell => {
this.cellmanager.refreshCell(cell, true);
this.cellmanager.refreshCell(cell);
});
}
@ -94,7 +91,8 @@ export default class RowManager {
checkRow(rowIndex, toggle) {
const value = toggle ? 1 : 0;
const selector = rowIndex => `.dt-cell--0-${rowIndex} [type="checkbox"]`;
const selector = rowIndex =>
`.dt-cell[data-row-index="${rowIndex}"][data-col-index="0"] [type="checkbox"]`;
// update internal map
this.checkMap[rowIndex] = value;
// set checkbox value explicitly
@ -113,25 +111,18 @@ export default class RowManager {
// update internal map
if (toggle) {
if (this.datamanager._filteredRows) {
this.datamanager._filteredRows.forEach(f => {
this.checkRow(f, toggle);
});
} else {
this.checkMap = Array.from(Array(this.getTotalRows())).map(c => value);
}
this.checkMap = Array.from(Array(this.getTotalRows())).map(c => value);
} else {
this.checkMap = [];
}
// set checkbox value
$.each('.dt-cell--col-0 [type="checkbox"]', this.bodyScrollable)
$.each('.dt-cell[data-col-index="0"] [type="checkbox"]', this.bodyScrollable)
.map(input => {
input.checked = toggle;
});
// highlight all
this.highlightAll(toggle);
this.showCheckStatus();
this.fireEvent('onCheckRow');
}
showCheckStatus() {
@ -139,10 +130,7 @@ export default class RowManager {
const checkedRows = this.getCheckedRows();
const count = checkedRows.length;
if (count > 0) {
let message = this.instance.translate('{count} rows selected', {
count: count
});
this.bodyRenderer.showToastMessage(message);
this.bodyRenderer.showToastMessage(`${count} row${count > 1 ? 's' : ''} selected`);
} else {
this.bodyRenderer.clearToastMessage();
}
@ -185,104 +173,42 @@ export default class RowManager {
}
}
hideRows(rowIndices) {
rowIndices = ensureArray(rowIndices);
rowIndices.map(rowIndex => {
const $tr = this.getRow$(rowIndex);
$tr.classList.add('dt-row--hide');
});
}
showRows(rowIndices) {
rowIndices = ensureArray(rowIndices);
const rows = rowIndices.map(rowIndex => this.datamanager.getRow(rowIndex));
this.bodyRenderer.renderRows(rows);
}
showAllRows() {
const rowIndices = this.datamanager.getAllRowIndices();
this.showRows(rowIndices);
}
getChildrenToShowForNode(rowIndex) {
const row = this.datamanager.getRow(rowIndex);
row.meta.isTreeNodeClose = false;
return this.datamanager.getImmediateChildren(rowIndex);
rowIndices.map(rowIndex => {
const $tr = this.getRow$(rowIndex);
$tr.classList.remove('dt-row--hide');
});
}
openSingleNode(rowIndex) {
const childrenToShow = this.getChildrenToShowForNode(rowIndex);
const visibleRowIndices = this.bodyRenderer.visibleRowIndices;
const rowsToShow = uniq([...childrenToShow, ...visibleRowIndices]).sort(numberSortAsc);
const rowsToShow = this.datamanager.getImmediateChildren(rowIndex);
this.showRows(rowsToShow);
}
getChildrenToHideForNode(rowIndex) {
const row = this.datamanager.getRow(rowIndex);
row.meta.isTreeNodeClose = true;
const rowsToHide = this.datamanager.getChildren(rowIndex);
rowsToHide.forEach(rowIndex => {
const row = this.datamanager.getRow(rowIndex);
if (!row.meta.isLeaf) {
row.meta.isTreeNodeClose = true;
}
});
return rowsToHide;
this.cellmanager.toggleTreeButton(rowIndex, true);
}
closeSingleNode(rowIndex) {
const rowsToHide = this.getChildrenToHideForNode(rowIndex);
const visibleRows = this.bodyRenderer.visibleRowIndices;
const rowsToShow = visibleRows
.filter(rowIndex => !rowsToHide.includes(rowIndex))
.sort(numberSortAsc);
this.showRows(rowsToShow);
}
expandAllNodes() {
let rows = this.datamanager.getRows();
let rootNodes = rows.filter(row => !row.meta.isLeaf);
const childrenToShow = rootNodes.map(row => this.getChildrenToShowForNode(row.meta.rowIndex)).flat();
const visibleRowIndices = this.bodyRenderer.visibleRowIndices;
const rowsToShow = uniq([...childrenToShow, ...visibleRowIndices]).sort(numberSortAsc);
this.showRows(rowsToShow);
}
collapseAllNodes() {
let rows = this.datamanager.getRows();
let rootNodes = rows.filter(row => row.meta.indent === 0);
const rowsToHide = rootNodes.map(row => this.getChildrenToHideForNode(row.meta.rowIndex)).flat();
const visibleRows = this.bodyRenderer.visibleRowIndices;
const rowsToShow = visibleRows
.filter(rowIndex => !rowsToHide.includes(rowIndex))
.sort(numberSortAsc);
this.showRows(rowsToShow);
}
setTreeDepth(depth) {
let rows = this.datamanager.getRows();
const rowsToOpen = rows.filter(row => row.meta.indent < depth);
const rowsToClose = rows.filter(row => row.meta.indent >= depth);
const rowsToHide = rowsToClose.filter(row => row.meta.indent > depth);
rowsToClose.forEach(row => {
if (!row.meta.isLeaf) {
row.meta.isTreeNodeClose = true;
const children = this.datamanager.getImmediateChildren(rowIndex);
children.forEach(childIndex => {
const row = this.datamanager.getRow(childIndex);
if (row.meta.isLeaf) {
// close
this.hideRows(childIndex);
this.cellmanager.toggleTreeButton(childIndex, false);
} else {
this.closeSingleNode(childIndex);
this.hideRows(childIndex);
}
});
rowsToOpen.forEach(row => {
if (!row.meta.isLeaf) {
row.meta.isTreeNodeClose = false;
}
});
const rowsToShow = rows
.filter(row => !rowsToHide.includes(row))
.map(row => row.meta.rowIndex)
.sort(numberSortAsc);
this.showRows(rowsToShow);
this.cellmanager.toggleTreeButton(rowIndex, false);
}
getRow$(rowIndex) {
@ -329,41 +255,31 @@ export default class RowManager {
getRowHTML(row, props) {
const dataAttr = makeDataAttributeString(props);
let rowIdentifier = props.rowIndex;
if (props.isFilter) {
row = row.map(cell => (Object.assign({}, cell, {
content: this.getFilterInput({
colIndex: cell.colIndex,
name: cell.name
colIndex: cell.colIndex
}),
isFilter: 1,
isHeader: undefined,
editable: false
})));
rowIdentifier = 'filter';
}
if (props.isHeader) {
rowIdentifier = 'header';
}
return `
<div class="dt-row dt-row-${rowIdentifier}" ${dataAttr}>
<tr class="dt-row" ${dataAttr}>
${row.map(cell => this.cellmanager.getCellHTML(cell)).join('')}
</div>
</tr>
`;
}
getFilterInput(props) {
let title = `title="Filter based on ${props.name || 'Index'}"`;
const dataAttr = makeDataAttributeString(props);
return `<input class="dt-filter dt-input" type="text" ${dataAttr} tabindex="1"
${props.colIndex === 0 ? 'disabled' : title} />`;
return `<input class="dt-filter dt-input" type="text" ${dataAttr} />`;
}
selector(rowIndex) {
return `.dt-row-${rowIndex}`;
return `.dt-row[data-row-index="${rowIndex}"]`;
}
}

View File

@ -1,308 +1,259 @@
:root {
--dt-border-color: #d1d8dd;
--dt-primary-color: rgb(82, 146, 247);
--dt-light-bg: #f5f7fa;
--dt-light-red: #FD8B8B;
--dt-light-yellow: #fffce7;
--dt-orange: rgb(255, 160, 10);
--dt-text-color: #000000;
--dt-text-light: #dfe2e5;
--dt-spacer-1: 0.25rem;
--dt-spacer-2: 0.5rem;
--dt-spacer-3: 1rem;
--dt-border-radius: 3px;
--dt-cell-bg: #fff;
--dt-focus-border-width: 2px;
--dt-selection-highlight-color: var(--dt-light-yellow);
--dt-toast-message-border: none;
--dt-header-cell-bg: var(--dt-cell-bg);
--border-color: #d1d8dd;
--primary-color: rgb(82, 146, 247);
--light-bg: #f5f7fa;
--light-red: #FD8B8B;
--light-yellow: #fffce7;
--orange: rgb(255, 160, 10);
--text-color: #000000;
--text-light: #dfe2e5;
--spacer-1: 0.25rem;
--spacer-2: 0.5rem;
--spacer-3: 1rem;
--border-radius: 3px;
--cell-bg: #fff;
}
.datatable {
*, *::after, *::before {
box-sizing: border-box;
}
box-sizing: border-box;
}
}
.datatable {
position: relative;
overflow: hidden;
.dt-cell--col-0{
border-left: 1px solid var(--dt-border-color);
}
position: relative;
overflow: auto;
}
.dt-header{
border-bottom: 2px solid var(--dt-border-color);
.dt-header {
border-collapse: collapse;
border-bottom: 1px solid var(--border-color);
position: absolute;
top: 0;
left: 0;
background-color: var(--cell-bg);
}
.datatable-content{
.dt-header{
display: flex;
}
.dt-body {
border-collapse: collapse;
}
.dt-scrollable {
height: 40vw;
max-height: 40vw;
overflow: auto;
border-bottom: 1px solid var(--border-color);
&--highlight-all {
background-color: var(--dt-selection-highlight-color);
}
&--highlight-all {
background-color: var(--light-yellow);
}
&__no-data {
text-align: center;
padding: var(--dt-spacer-3);
border-left: 1px solid var(--dt-border-color);
border-right: 1px solid var(--dt-border-color);
}
&__no-data {
text-align: center;
padding: var(--spacer-3);
border-left: 1px solid var(--border-color);
border-right: 1px solid var(--border-color);
}
}
.dt-row {
display: flex;
&--highlight {
background-color: var(--light-yellow);
}
&--highlight .dt-cell {
background-color: var(--dt-selection-highlight-color);
}
&--unhighlight {
background-color: var(--cell-bg);
}
&--unhighlight .dt-cell {
background-color: var(--dt-cell-bg);
}
&--hide {
display: none;
}
&:last-child:not(.dt-row-filter) {
border-bottom: 1px solid var(--dt-border-color);
}
&--hide {
display: none;
}
}
.dt-cell {
border: 1px solid var(--dt-border-color);
border-bottom: none;
border-left: none;
position: relative;
outline: none;
padding: 0;
background-color: var(--dt-cell-bg);
color: var(--dt-text-color);
/*
Fix for firefox and Edge
https://stackoverflow.com/a/16337203
firefox paints td background over border
*/
background-clip: padding-box;
user-select: none;
border: 1px solid var(--border-color);
position: relative;
outline: none;
padding: 0;
&__content {
padding: var(--dt-spacer-2);
border: var(--dt-focus-border-width) solid transparent;
height: 100%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
&__content {
padding: var(--spacer-2);
border: 2px solid transparent;
height: 100%;
user-select: none;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
&__edit {
display: none;
padding: var(--dt-spacer-2);
background-color: var(--dt-cell-bg);
border: var(--dt-focus-border-width) solid var(--dt-orange);
z-index: 1;
height: 100%;
}
&__edit {
display: none;
padding: var(--spacer-2);
background-color: var(--cell-bg);
border: 2px solid var(--orange);
z-index: 1;
height: 100%;
}
&__resize-handle {
opacity: 0;
position: absolute;
right: -3px;
top: 0;
width: 5px;
height: 100%;
cursor: col-resize;
z-index: 1;
}
&__resize-handle {
opacity: 0;
position: absolute;
right: -3px;
top: 0;
width: 5px;
height: 100%;
cursor: col-resize;
z-index: 1;
}
&--editing &__content {
display: none;
}
&--editing &__content {
display: none;
}
&--editing &__edit {
display: block;
}
&--editing &__edit {
display: block;
}
&--focus &__content {
border-color: var(--dt-primary-color);
}
&--focus &__content {
border-color: var(--primary-color);
}
&--highlight {
background-color: var(--dt-light-bg);
}
&--highlight {
background-color: var(--light-bg);
}
&--dragging {
background-color: var(--dt-light-bg);
}
&--dragging {
background-color: var(--light-bg);
}
&--header {
background-color: var(--dt-header-cell-bg);
}
&--header &__content {
padding-right: var(--spacer-3);
font-weight: bold;
}
&--header:last-child {
border-right: 1px solid var(--dt-border-color);
}
&--header:hover .dt-dropdown__toggle {
opacity: 1;
}
&--header &__content {
padding-right: var(--dt-spacer-3);
font-weight: bold;
}
&--header:hover .dt-dropdown__toggle {
opacity: 1;
}
&--tree-close {
.icon-open {
display: none;
}
.icon-close {
display: flex;
}
}
&:last-child {
border-right: 1px solid var(--dt-border-color);
}
}
.datatable[dir=rtl] .dt-cell__resize-handle {
right: unset;
left: -3px;
}
.icon-open, .icon-close {
width: 16px;
height: 16px;
}
.icon-open {
display: flex;
}
.icon-close {
display: none;
&--tree-close {
.dt-tree-node__toggle:before {
content: '►';
}
}
}
.dt-dropdown {
position: absolute;
right: 10px;
display: inline-flex;
vertical-align: top;
text-align: left;
font-weight: normal;
cursor: pointer;
position: absolute;
right: 10px;
display: inline-flex;
vertical-align: top;
text-align: left;
font-weight: normal;
cursor: pointer;
&__toggle {
opacity: 0;
background-color: var(--dt-header-cell-bg);
}
&__toggle {
opacity: 0;
}
&__list {
position: fixed;
min-width: 8rem;
z-index: 1;
cursor: pointer;
background-color: var(--dt-cell-bg);
border-radius: var(--dt-border-radius);
padding: var(--dt-spacer-2) 0;
box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);
}
&__list {
display: none;
&__list-item {
padding: var(--dt-spacer-2) var(--dt-spacer-3);
position: absolute;
min-width: 8rem;
top: 100%;
right: 0;
z-index: 1;
background-color: var(--cell-bg);
border-radius: var(--border-radius);
padding: var(--spacer-2) 0;
box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);
}
&:hover {
background-color: var(--dt-light-bg);
}
}
&__list-item {
padding: var(--spacer-2) var(--spacer-3);
&--active &__list {
display: block;
}
&:hover {
background-color: var(--light-bg);
}
}
&--active &__list {
display: block;
}
}
.dt-tree-node {
display: flex;
align-items: center;
position: relative;
display: inline-block;
position: relative;
&__toggle {
display: inline-block;
cursor: pointer;
margin-right: 0.2rem;
}
&__toggle {
display: inline-block;
position: absolute;
font-size: 10px;
padding: 1px;
cursor: pointer;
}
&__toggle:before {
content: '▼';
}
}
.dt-toast {
position: absolute;
bottom: var(--dt-spacer-3);
left: 50%;
transform: translateX(-50%);
position: absolute;
bottom: var(--spacer-3);
left: 50%;
transform: translateX(-50%);
&__message {
display: inline-block;
background-color: rgba(0, 0, 0, 0.8);
color: var(--dt-text-light);
border-radius: var(--dt-border-radius);
padding: var(--dt-spacer-2) var(--dt-spacer-3);
border: var(--dt-toast-message-border);
}
&__message {
display: inline-block;
background-color: rgba(0, 0, 0, 0.8);
color: var(--text-light);
border-radius: var(--border-radius);
padding: var(--spacer-2) var(--spacer-3);
}
}
.dt-input {
outline: none;
width: 100%;
border: none;
overflow: visible;
font-family: inherit;
font-size: inherit;
line-height: inherit;
background-color: inherit;
color: inherit;
margin: 0;
padding: 0;
outline: none;
width: 100%;
border: none;
overflow: visible;
font-family: inherit;
font-size: inherit;
line-height: inherit;
margin: 0;
padding: 0;
}
.dt-freeze {
display: flex;
justify-content: center;
align-content: center;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: var(--dt-light-bg);
opacity: 0.5;
font-size: 2em;
display: flex;
justify-content: center;
align-content: center;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: var(--light-bg);
opacity: 0.5;
font-size: 2em;
&__message {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
&__message {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
}
.dt-paste-target {
position: fixed;
left: -999em;
position: fixed;
left: -999em;
}
body.dt-resize {
cursor: col-resize;
cursor: col-resize;
}
.dt-sticky-col {
position: sticky;
z-index: 1;
left: 0;
}

View File

@ -11,8 +11,8 @@ export default class Style {
linkProperties(this, this.instance, [
'options', 'datamanager', 'columnmanager',
'header', 'footer', 'bodyScrollable', 'datatableWrapper',
'getColumn', 'bodyRenderer'
'header', 'bodyScrollable', 'datatableWrapper',
'getColumn'
]);
this.scopeClass = 'dt-instance-' + instance.constructor.instances;
@ -23,7 +23,6 @@ export default class Style {
this.styleEl = styleEl;
this.bindResizeWindow();
this.bindScrollHeader();
}
get stylesheet() {
@ -31,77 +30,18 @@ export default class Style {
}
bindResizeWindow() {
this.onWindowResize = this.onWindowResize.bind(this);
this.onWindowResize = throttle(this.onWindowResize, 300);
if (this.options.layout === 'fluid') {
$.on(window, 'resize', this.onWindowResize);
$.on(window, 'resize', throttle(() => {
this.distributeRemainingWidth();
this.refreshColumnWidth();
this.compensateScrollbarWidth();
this.setBodyStyle();
}, 300));
}
}
bindScrollHeader() {
this._settingHeaderPosition = false;
$.on(this.bodyScrollable, 'scroll', (e) => {
if (this._settingHeaderPosition) return;
this._settingHeaderPosition = true;
requestAnimationFrame(() => {
const scrollLeft = e.target.scrollLeft;
// Move non-sticky header and footer cells normally
const nonStickyHeaderCells = this.header.querySelectorAll('.dt-cell:not(.dt-sticky-col)');
const nonStickyFooterCells = this.footer.querySelectorAll('.dt-cell:not(.dt-sticky-col)');
nonStickyHeaderCells.forEach(cell => {
$.style(cell, { transform: `translateX(${-scrollLeft}px)` });
});
nonStickyFooterCells.forEach(cell => {
$.style(cell, { transform: `translateX(${-scrollLeft}px)` });
});
const stickyHeaderCells = this.header.querySelectorAll(
'.dt-cell.dt-sticky-col:not(.dt-cell-serial-no):not(.dt-cell-checkbox)'
);
stickyHeaderCells.forEach((headerCell) => {
const colIndex = headerCell.getAttribute('data-col-index');
const bodyCell = this.bodyScrollable.querySelector(`.dt-cell[data-col-index="${colIndex}"]`);
const colLeft = parseFloat(headerCell.style.left) || 0; // get left position of the column
// Find corresponding footer cell
const footerCell = this.footer.querySelector(`.dt-cell[data-col-index="${colIndex}"]`);
if (~~(bodyCell.offsetLeft - scrollLeft) > colLeft) {
headerCell.style.transform = `translateX(${-scrollLeft - 1}px)`;
if (footerCell) {
footerCell.style.transform = `translateX(${scrollLeft ? -scrollLeft - 1 : 0}px)`;
}
} else {
headerCell.style.transform = `translateX(${colLeft - headerCell.offsetLeft}px)`;
if (footerCell) footerCell.style.transform = `translateX(${colLeft - footerCell.offsetLeft}px)`;
}
});
this._settingHeaderPosition = false;
});
});
}
onWindowResize() {
this.distributeRemainingWidth();
this.refreshColumnWidth();
this.setBodyStyle();
}
destroy() {
this.styleEl.remove();
$.off(window, 'resize', this.onWindowResize);
}
setStyle(selector, styleObject) {
@ -114,14 +54,14 @@ export default class Style {
return;
}
selector = selector.trim();
if (!selector) return;
this._styleRulesMap = this._styleRulesMap || {};
const prefixedSelector = this._getPrefixedSelector(selector);
if (this._styleRulesMap[prefixedSelector]) {
this.removeStyle(selector);
// find and remove
const index = Array.from(this.stylesheet.cssRules)
.findIndex(rule => rule.selectorText === prefixedSelector);
this.stylesheet.deleteRule(index);
// merge with old styleobject
styleObject = Object.assign({}, this._styleRulesMap[prefixedSelector], styleObject);
@ -134,28 +74,6 @@ export default class Style {
this.stylesheet.insertRule(ruleString);
}
removeStyle(selector) {
if (selector.includes(',')) {
selector.split(',')
.map(s => s.trim())
.forEach(selector => {
this.removeStyle(selector);
});
return;
}
selector = selector.trim();
if (!selector) return;
// find and remove
const prefixedSelector = this._getPrefixedSelector(selector);
const index = Array.from(this.stylesheet.cssRules)
.findIndex(rule => rule.selectorText === prefixedSelector);
if (index === -1) return;
this.stylesheet.deleteRule(index);
}
_getPrefixedSelector(selector) {
return `.${this.scopeClass} ${selector}`;
}
@ -173,23 +91,35 @@ export default class Style {
}
setDimensions() {
this.setCellHeight();
this.setHeaderStyle();
this.setupMinWidth();
this.setupNaturalColumnWidth();
this.setupColumnWidth();
this.distributeRemainingWidth();
this.setColumnStyle();
this.compensateScrollbarWidth();
this.setDefaultCellHeight();
this.setBodyStyle();
}
setCellHeight() {
this.setStyle('.dt-cell', {
height: this.options.cellHeight + 'px'
setHeaderStyle() {
if (this.options.layout === 'fluid') {
// setting width as 0 will ensure that the
// header doesn't take the available space
$.style(this.header, {
width: 0
});
}
$.style(this.header, {
margin: 0
});
}
setupMinWidth() {
$.each('.dt-cell--header', this.header).map(col => {
$.each('.dt-cell[data-is-header]', this.header).map(col => {
const { colIndex } = $.data(col);
const column = this.getColumn(colIndex);
@ -204,19 +134,8 @@ export default class Style {
setupNaturalColumnWidth() {
if (!$('.dt-row')) return;
$.each('.dt-row-header .dt-cell', this.header).map($headerCell => {
const { colIndex } = $.data($headerCell);
const column = this.datamanager.getColumn(colIndex);
let width = $.style($('.dt-cell__content', $headerCell), 'width');
if (typeof width === 'number' && width >= this.options.minimumColumnWidth) {
column.naturalWidth = width;
} else {
column.naturalWidth = this.options.minimumColumnWidth;
}
});
// set initial width as naturally calculated by table's first row
$.each('.dt-row-0 .dt-cell', this.bodyScrollable).map($cell => {
$.each('.dt-row[data-row-index="0"] .dt-cell', this.bodyScrollable).map($cell => {
const {
colIndex
} = $.data($cell);
@ -224,11 +143,12 @@ export default class Style {
let naturalWidth = $.style($('.dt-cell__content', $cell), 'width');
if (typeof naturalWidth === 'number' && naturalWidth >= column.naturalWidth) {
column.naturalWidth = naturalWidth;
} else {
column.naturalWidth = column.naturalWidth;
if (column.id === '_rowIndex') {
naturalWidth = this.getRowIndexColumnWidth(naturalWidth);
column.width = naturalWidth;
}
column.naturalWidth = naturalWidth;
});
}
@ -272,32 +192,29 @@ export default class Style {
if (!column.width) {
column.width = column.naturalWidth;
}
if (column.id === '_rowIndex') {
column.width = this.getRowIndexColumnWidth();
}
if (column.width < this.options.minimumColumnWidth) {
column.width = this.options.minimumColumnWidth;
if (column.width < column.minWidth) {
column.width = column.minWidth;
}
});
}
}
compensateScrollbarWidth() {
requestAnimationFrame(() => {
const scrollbarWidth = $.scrollbarWidth();
const lastCol = this.datamanager.getColumn(-1);
const width = lastCol.width - scrollbarWidth;
this.columnmanager.setColumnWidth(lastCol.colIndex, width);
});
}
distributeRemainingWidth() {
if (this.options.layout !== 'fluid') return;
const wrapperWidth = $.style(this.instance.datatableWrapper, 'width');
let firstRow = $('.dt-row', this.bodyScrollable);
let firstRowWidth = wrapperWidth;
if (!firstRow) {
let headerRow = $('.dt-row', this.instance.header);
let cellWidths = Array.from(headerRow.children)
.map(cell => cell.offsetWidth);
firstRowWidth = cellWidths.reduce((sum, a) => sum + a, 0);
} else {
firstRowWidth = $.style(firstRow, 'width');
}
const headerWidth = $.style(this.header, 'width');
const resizableColumns = this.datamanager.getColumns().filter(col => col.resizable);
const deltaWidth = (wrapperWidth - firstRowWidth) / resizableColumns.length;
const deltaWidth = (wrapperWidth - headerWidth) / resizableColumns.length;
resizableColumns.map(col => {
const width = $.style(this.getColumnHeaderElement(col.colIndex), 'width');
@ -309,6 +226,25 @@ export default class Style {
});
}
setDefaultCellHeight() {
if (this.options.dynamicRowHeight) return;
if (this.__cellHeightSet) return;
const $firstCell = $('.dt-cell[data-is-header]', this.instance.header);
if (!$firstCell) return;
const height = this.options.cellHeight || $.style($firstCell, 'height');
if (height) {
this.setCellHeight(height);
this.__cellHeightSet = true;
}
}
setCellHeight(height) {
this.setStyle('.dt-cell__content, .dt-cell__edit', {
height: height + 'px'
});
}
setColumnStyle() {
// align columns
this.datamanager.getColumns()
@ -328,6 +264,7 @@ export default class Style {
this.columnmanager.setColumnHeaderWidth(column.colIndex);
this.columnmanager.setColumnWidth(column.colIndex);
});
this.setBodyStyle();
}
refreshColumnWidth() {
@ -339,67 +276,48 @@ export default class Style {
}
setBodyStyle() {
const bodyWidth = $.style(this.datatableWrapper, 'width');
const firstRow = $('.dt-row', this.bodyScrollable);
if (!firstRow) return;
const rowWidth = $.style(firstRow, 'width');
requestAnimationFrame(() => {
const width = $.style(this.header, 'width');
let width = bodyWidth > rowWidth ? rowWidth + 10 : bodyWidth;
$.style(this.bodyScrollable, {
width: width + 'px'
});
$.style(this.bodyScrollable, {
width: width + 'px'
});
// remove the body height, so that it resets to it's original
$.removeStyle(this.bodyScrollable, 'height');
const $body = $('.dt-body', this.bodyScrollable);
// when there are less rows than the container
// adapt the container height
let bodyHeight = $.getStyle(this.bodyScrollable, 'height');
const scrollHeight = (this.bodyRenderer.hyperlist || {})._scrollHeight || Infinity;
const hasHorizontalOverflow = $.hasHorizontalOverflow(this.bodyScrollable);
let height;
if (scrollHeight < bodyHeight) {
height = scrollHeight;
// account for scrollbar size when
// there is horizontal overflow
if (hasHorizontalOverflow) {
height += $.scrollbarSize();
if ($body) {
$.style($body, {
height: '0px'
});
}
$.style(this.bodyScrollable, {
height: height + 'px'
marginTop: $.style(this.header, 'height') + 'px'
});
}
const verticalOverflow = this.bodyScrollable.scrollHeight - this.bodyScrollable.offsetHeight;
if (verticalOverflow < $.scrollbarSize()) {
// if verticalOverflow is less than scrollbar size
// then most likely scrollbar is causing the scroll
// which is not needed
$.style(this.bodyScrollable, {
overflowY: 'hidden'
$.style($('table', this.bodyScrollable), {
margin: 0,
width: '100%'
});
}
if (this.options.layout === 'fluid') {
$.style(this.bodyScrollable, {
overflowX: 'hidden'
});
}
});
}
getColumnHeaderElement(colIndex) {
colIndex = +colIndex;
if (colIndex < 0) return null;
return $(`.dt-cell--col-${colIndex}`, this.header);
return $(`.dt-cell[data-col-index="${colIndex}"]`, this.header);
}
getRowIndexColumnWidth() {
getRowIndexColumnWidth(baseWidth) {
this._rowIndexColumnWidthMap = this._rowIndexColumnWidthMap || {};
const rowCount = this.datamanager.getRowCount();
const padding = 22;
return $.measureTextWidth(rowCount + '') + padding;
const digits = (rowCount + '').length;
if (!this._rowIndexColumnWidthMap[digits]) {
// add 8px for each unit
this._rowIndexColumnWidthMap[digits] = baseWidth + ((digits - 1) * 8);
}
return this._rowIndexColumnWidthMap[digits];
}
}

View File

@ -1,30 +0,0 @@
import { format } from './utils';
import getTranslations from './translations';
export default class TranslationManager {
constructor(language) {
this.language = language;
this.translations = getTranslations();
}
addTranslations(translations) {
this.translations = Object.assign(this.translations, translations);
}
translate(sourceText, args) {
let translation = (this.translations[this.language] &&
this.translations[this.language][sourceText]) || sourceText;
if (typeof translation === 'object') {
translation = args && args.count ?
this.getPluralizedTranslation(translation, args.count) :
sourceText;
}
return format(translation, args || {});
}
getPluralizedTranslation(translations, count) {
return translations[count] || translations['default'];
}
};

View File

@ -1,15 +0,0 @@
{
"Sort Ascending": "Aufsteigend sortieren",
"Sort Descending": "Absteigend sortieren",
"Reset sorting": "Sortierung zurücksetzen",
"Remove column": "Spalte entfernen",
"No Data": "Keine Daten",
"{count} cells copied": {
"1": "{count} Zelle kopiert",
"default": "{count} Zellen kopiert"
},
"{count} rows selected": {
"1": "{count} Zeile ausgewählt",
"default": "{count} Zeilen ausgewählt"
}
}

View File

@ -1,15 +0,0 @@
{
"Sort Ascending": "Sort Ascending",
"Sort Descending": "Sort Descending",
"Reset sorting": "Reset sorting",
"Remove column": "Remove column",
"No Data": "No Data",
"{count} cells copied": {
"1": "{count} cell copied",
"default": "{count} cells copied"
},
"{count} rows selected": {
"1": "{count} row selected",
"default": "{count} rows selected"
}
}

View File

@ -1,15 +0,0 @@
{
"Sort Ascending": "Trier par ordre croissant",
"Sort Descending": "Trier par ordre décroissant",
"Reset sorting": "Réinitialiser le tri",
"Remove column": "Supprimer colonne",
"No Data": "Pas de données",
"{count} cells copied": {
"1": "{count} cellule copiée",
"default": "{count} cellules copiées"
},
"{count} rows selected": {
"1": "{count} ligne sélectionnée",
"default": "{count} lignes sélectionnées"
}
}

View File

@ -1,13 +0,0 @@
import en from './en.json';
import de from './de.json';
import fr from './fr.json';
import it from './it.json';
export default function getTranslations() {
return {
en,
de,
fr,
it,
};
};

View File

@ -1,15 +0,0 @@
{
"Sort Ascending": "Ordinamento ascendente",
"Sort Descending": "Ordinamento decrescente",
"Reset sorting": "Azzeramento ordinamento",
"Remove column": "Rimuovi colonna",
"No Data": "Nessun dato",
"{count} cells copied": {
"1": "Copiato {count} cella",
"default": "{count} celle copiate"
},
"{count} rows selected": {
"1": "{count} linea selezionata",
"default": "{count} linee selezionate"
}
}

View File

@ -1,6 +1,5 @@
import _throttle from 'lodash/throttle';
import _debounce from 'lodash/debounce';
import _uniq from 'lodash/uniq';
export function camelCaseToDash(str) {
return str.replace(/([A-Z])/g, (g) => `-${g[0].toLowerCase()}`);
@ -126,42 +125,3 @@ export function ensureArray(val) {
}
return val;
}
export function uniq(arr) {
return _uniq(arr);
}
export function numberSortAsc(a, b) {
return a - b;
};
export function stripHTML(html) {
return html.replace(/<[^>]*>/g, '');
};
export function format(str, args) {
if (!str) return str;
Object.keys(args).forEach(arg => {
let regex = new RegExp(`{(${arg})}`, 'g');
str = str.replace(regex, args[arg]);
});
return str;
};
export function escapeHTML(txt) {
if (!txt) return '';
let escapeHtmlMapping = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
'/': '&#x2F;',
'`': '&#x60;',
'=': '&#x3D;',
};
return String(txt).replace(/[&<>"'`=/]/g, (char) => escapeHtmlMapping[char] || char);
};

7187
yarn.lock

File diff suppressed because it is too large Load Diff