Compare commits
210 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
487908974f | ||
|
|
bf65f4bc9f | ||
|
|
82582a0b20 | ||
|
|
007a7bf92a | ||
|
|
a5cc12f7ec | ||
|
|
4758cf2cd4 | ||
|
|
584d964a72 | ||
|
|
6d381d8f6b | ||
|
|
f1c2cd916c | ||
|
|
f05049c67a | ||
|
|
ba9814228d | ||
|
|
4b124f0b1d | ||
|
|
9193fd9b06 | ||
|
|
ecc660ed68 | ||
|
|
bed2708bd5 | ||
|
|
dbde62ce40 | ||
|
|
fb465579a0 | ||
|
|
cca14e09eb | ||
|
|
dc09e1d1b2 | ||
|
|
007ec69609 | ||
|
|
e209ec2ed0 | ||
|
|
89926b680c | ||
|
|
bbb4eb1f7e | ||
|
|
b0e6261285 | ||
|
|
89d3149695 | ||
|
|
497d420ef6 | ||
|
|
f46e7142b7 | ||
|
|
04a0202fc3 | ||
|
|
dfbd98a502 | ||
|
|
ae07ce3712 | ||
|
|
b3ff486934 | ||
|
|
98835d8d59 | ||
|
|
1979641623 | ||
|
|
b8a1af27d2 | ||
|
|
f0044603a1 | ||
|
|
49cc051392 | ||
|
|
d29615bca9 | ||
|
|
48d5b97e55 | ||
|
|
8f074a80c2 | ||
|
|
2c36123663 | ||
|
|
50b44bfb09 | ||
|
|
6d29880fb7 | ||
|
|
739182562a | ||
|
|
b21e547794 | ||
|
|
7ee8ca9304 | ||
|
|
17cddc05f1 | ||
|
|
ddb2dd3986 | ||
|
|
71272f8f9e | ||
|
|
6c79a19b29 | ||
|
|
f990c1ff87 | ||
|
|
4f07f5fe4e | ||
|
|
0b1ce23a33 | ||
|
|
aa3f3be67f | ||
|
|
eda1540f3b | ||
|
|
430d7cfe48 | ||
|
|
b4428639cb | ||
|
|
d6a6c16fdf | ||
|
|
42a236d9c8 | ||
|
|
d2c4e21cca | ||
|
|
fea45f4a31 | ||
|
|
59e7f6635a | ||
|
|
a278075247 | ||
|
|
5db24187e6 | ||
|
|
95a239963c | ||
|
|
4549a5176b | ||
|
|
6fcce176c3 | ||
|
|
00d924c5c0 | ||
|
|
6d6602f202 | ||
|
|
88a361f053 | ||
|
|
8ceadaccba | ||
|
|
cb2729424b | ||
|
|
0042e3172b | ||
|
|
16983cfbf0 | ||
|
|
279a876f7d | ||
|
|
a0901768d7 | ||
|
|
c263ab410e | ||
|
|
18964c0346 | ||
|
|
3475f6ccaa | ||
|
|
31264d1f4d | ||
|
|
1a9c11edf0 | ||
|
|
1be233d054 | ||
|
|
d92fc5e969 | ||
|
|
ad0e0b592a | ||
|
|
a47e1f07eb | ||
|
|
8b9cd641b1 | ||
|
|
3712aaada3 | ||
|
|
63bfb5658d | ||
|
|
b565f32886 | ||
|
|
df5e4af0ee | ||
|
|
cccb4aa523 | ||
|
|
40c8e51e86 | ||
|
|
b1962dffcf | ||
|
|
1a0eba65a0 | ||
|
|
bbca6d8f60 | ||
|
|
e25ef86a63 | ||
|
|
b075fb5d71 | ||
|
|
e014960e97 | ||
|
|
67f9717827 | ||
|
|
17295a1209 | ||
|
|
8d96f953cd | ||
|
|
ffab69de34 | ||
|
|
944010d113 | ||
|
|
007579f55e | ||
|
|
637d37deb8 | ||
|
|
9a5a0b66e8 | ||
|
|
3b088a9d78 | ||
|
|
f191c8bd53 | ||
|
|
7b325a5ba0 | ||
|
|
3446488b08 | ||
|
|
1845adc46e | ||
|
|
0f44a51da3 | ||
|
|
836b13bd88 | ||
|
|
3e044a5ffa | ||
|
|
3fc8ac9e72 | ||
|
|
d494590b70 | ||
|
|
acfe0e49cd | ||
|
|
26a37b539f | ||
|
|
302c1f83d5 | ||
|
|
d6aa52fb16 | ||
|
|
92dcc94b62 | ||
|
|
19fb921ed2 | ||
|
|
bdbdc90578 | ||
|
|
d14a16c9a0 | ||
|
|
fb4370020f | ||
|
|
1791b30077 | ||
|
|
b6d4a0eea7 | ||
|
|
188b61bf1b | ||
|
|
c559faa67b | ||
|
|
1e20502d09 | ||
|
|
96c7b723da | ||
|
|
164979f7d6 | ||
|
|
f5df997c00 | ||
|
|
f791c91df6 | ||
|
|
ec08d75261 | ||
|
|
33f7ab9c25 | ||
|
|
89f13b530a | ||
|
|
6fe075467d | ||
|
|
cfbb891737 | ||
|
|
cc27d66138 | ||
|
|
1f27d194ca | ||
|
|
7eaabcbfe0 | ||
|
|
7f779a3dd8 | ||
|
|
932afb6cbe | ||
|
|
ffe0d2a3f5 | ||
|
|
84b7fa3d83 | ||
|
|
01d74ef6ad | ||
|
|
cbe9f4858b | ||
|
|
51b112687e | ||
|
|
f790a6729a | ||
|
|
d01e045157 | ||
|
|
2ce132c0bc | ||
|
|
4f57bb1b64 | ||
|
|
87a44a94f0 | ||
|
|
a04a5a1d7c | ||
|
|
7531e13d17 | ||
|
|
307de6ed55 | ||
|
|
cdb276abfd | ||
|
|
ae6415c950 | ||
|
|
bbc54853c8 | ||
|
|
ffac2cbe0b | ||
|
|
18fa7d2eca | ||
|
|
7417b0fdcd | ||
|
|
1f387c9079 | ||
|
|
312335d419 | ||
|
|
0856f6cdf7 | ||
|
|
226aea24df | ||
|
|
8db22e9db2 | ||
|
|
cc6128b2b6 | ||
|
|
bddb3c27ce | ||
|
|
f9714673b4 | ||
|
|
78666f8ab2 | ||
|
|
1b5292a7f0 | ||
|
|
2cf2191f0f | ||
|
|
9e4d0bb9c8 | ||
|
|
5f25514639 | ||
|
|
9ed446194d | ||
|
|
48e0034eb1 | ||
|
|
47aa83dce8 | ||
|
|
d225308151 | ||
|
|
cab9fff5e6 | ||
|
|
3a050cdfb6 | ||
|
|
cd4645e5b3 | ||
|
|
f040f5c584 | ||
|
|
978682e1df | ||
|
|
2af2cebf14 | ||
|
|
2130a46c21 | ||
|
|
96a9235a12 | ||
|
|
c95fa505dd | ||
|
|
bc3bd3accb | ||
|
|
291cc101f5 | ||
|
|
fe79d6b2ec | ||
|
|
11ae5d6be4 | ||
|
|
2559ae3119 | ||
|
|
2987fcd302 | ||
|
|
27cfc76a7e | ||
|
|
e02af2077c | ||
|
|
2d1bb23cda | ||
|
|
c387c649a5 | ||
|
|
4a8cdd0362 | ||
|
|
db82d7fff4 | ||
|
|
b61f7fdcab | ||
|
|
faeef3856c | ||
|
|
e6a8cb8d7f | ||
|
|
f3a8b465ef | ||
|
|
f9775ecdf2 | ||
|
|
737430671e | ||
|
|
00b8000d49 | ||
|
|
c61e53da8c | ||
|
|
a64c51bfdf | ||
|
|
4b2c98fe32 |
@ -7,7 +7,8 @@
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true
|
||||
"node": true,
|
||||
"mocha": true
|
||||
},
|
||||
|
||||
"globals": {
|
||||
@ -21,7 +22,9 @@
|
||||
"it": true,
|
||||
"expect": true,
|
||||
"sinon": true,
|
||||
"Clusterize": true
|
||||
"Clusterize": true,
|
||||
"cy": true,
|
||||
"Cypress": true
|
||||
},
|
||||
|
||||
"plugins": [
|
||||
@ -32,7 +35,6 @@
|
||||
"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,
|
||||
|
||||
24
.github/workflows/test-and-release.yml
vendored
Normal file
24
.github/workflows/test-and-release.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
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
|
||||
16
.github/workflows/test-pull-request.yml
vendored
Normal file
16
.github/workflows/test-pull-request.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
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 }}
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@ -34,3 +34,12 @@ node_modules
|
||||
npm-debug.log.*
|
||||
|
||||
.DS_Store
|
||||
|
||||
# cypress
|
||||
cypress/screenshots
|
||||
cypress/videos
|
||||
|
||||
# dist
|
||||
dist
|
||||
|
||||
.env
|
||||
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2018 Frappé Technologies Pvt. Ltd. <developers@frappe.io>
|
||||
Copyright (c) 2018 Frappe 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
|
||||
|
||||
86
README.md
86
README.md
@ -1,39 +1,75 @@
|
||||
<div align="center">
|
||||
<img src="https://github.com/frappe/design/blob/master/logos/data-table-logo.svg" height="128">
|
||||
<h2>Frappe DataTables</h2>
|
||||
<p align="center">
|
||||
<p>
|
||||
A modern datatable library for the web
|
||||
</p>
|
||||
<div align="center" markdown="1">
|
||||
|
||||
<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**
|
||||
|
||||
[](https://github.com/frappe/datatable/actions?query=workflow%3A%22Test+and+Release%22)
|
||||
[](https://badge.fury.io/js/frappe-datatable)
|
||||

|
||||
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
## Features
|
||||
## Frappe Datatable
|
||||
|
||||
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.
|
||||
|
||||
### Motivation
|
||||
|
||||
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.
|
||||
|
||||
|
||||
### Key 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.
|
||||
|
||||
* Resizable columns
|
||||
* Sorting
|
||||
* Custom formatted data
|
||||
* In place editing
|
||||
* Efficient rendering of large data
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
yarn add frappe-datatable
|
||||
# or
|
||||
npm install frappe-datatable
|
||||
```
|
||||
|
||||
> Note: [`sortablejs`](https://github.com/RubaXa/Sortable) is required to be installed as well.
|
||||
|
||||
|
||||
```js
|
||||
var grid = new DataTable(document.querySelector('#data-table'), {
|
||||
data: {
|
||||
columns: [ 'Sr No.', 'First Name', 'Last Name' ],
|
||||
rows: [
|
||||
[ '1', 'Don', 'Joe' ],
|
||||
[ '2', 'Mary', 'Jane' ]
|
||||
const datatable = new DataTable('#datatable', {
|
||||
columns: [ 'First Name', 'Last Name', 'Position' ],
|
||||
data: [
|
||||
[ 'Don', 'Joe', 'Designer' ],
|
||||
[ 'Mary', 'Jane', 'Software Developer' ]
|
||||
]
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Contribute
|
||||
## Development Setup
|
||||
|
||||
* `yarn start` - Start rollup watch
|
||||
* `yarn build` - Build js/css bundles
|
||||
* `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`
|
||||
|
||||
## License
|
||||
## Links
|
||||
|
||||
MIT
|
||||
- [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>
|
||||
|
||||
4
cypress.json
Normal file
4
cypress.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:8989",
|
||||
"projectId": "2nsyux"
|
||||
}
|
||||
5
cypress/fixtures/example.json
Normal file
5
cypress/fixtures/example.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
84
cypress/integration/cell.js
Normal file
84
cypress/integration/cell.js
Normal file
@ -0,0 +1,84 @@
|
||||
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');
|
||||
});
|
||||
});
|
||||
70
cypress/integration/column.js
Normal file
70
cypress/integration/column.js
Normal file
@ -0,0 +1,70 @@
|
||||
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/);
|
||||
});
|
||||
});
|
||||
89
cypress/integration/inline_filters.js
Normal file
89
cypress/integration/inline_filters.js
Normal file
@ -0,0 +1,89 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
20
cypress/integration/new_instance.js
Normal file
20
cypress/integration/new_instance.js
Normal file
@ -0,0 +1,20 @@
|
||||
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');
|
||||
});
|
||||
});
|
||||
15
cypress/integration/row.js
Normal file
15
cypress/integration/row.js
Normal file
@ -0,0 +1,15 @@
|
||||
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');
|
||||
});
|
||||
});
|
||||
17
cypress/plugins/index.js
Normal file
17
cypress/plugins/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// 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
|
||||
};
|
||||
57
cypress/support/commands.js
Normal file
57
cypress/support/commands.js
Normal file
@ -0,0 +1,57 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
|
||||
Cypress.Commands.add('getCell', (col, row) => {
|
||||
return cy.get(`.dt-cell--${col}-${row}`);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('clickCell', (col, row) => {
|
||||
return cy.getCell(col, row).click({ force: true });
|
||||
});
|
||||
|
||||
Cypress.Commands.add('getColumnCell', (col) => {
|
||||
return cy.get(`.dt-cell--header-${col}`);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('clickDropdown', (col) => {
|
||||
return cy.getColumnCell(col)
|
||||
.find('.dt-dropdown__toggle')
|
||||
.click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('clickDropdownItem', (col, item) => {
|
||||
return cy.get(`.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
|
||||
});
|
||||
});
|
||||
20
cypress/support/index.js
Normal file
20
cypress/support/index.js
Normal file
@ -0,0 +1,20 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands';
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
12
cypress/tsconfig.json
Normal file
12
cypress/tsconfig.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"baseUrl": "../node_modules",
|
||||
"types": [
|
||||
"cypress"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.*"
|
||||
]
|
||||
}
|
||||
282
dist/frappe-datatable.cjs.css
vendored
282
dist/frappe-datatable.cjs.css
vendored
@ -1,282 +0,0 @@
|
||||
/* 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;
|
||||
}
|
||||
3622
dist/frappe-datatable.cjs.js
vendored
3622
dist/frappe-datatable.cjs.js
vendored
File diff suppressed because it is too large
Load Diff
252
dist/frappe-datatable.css
vendored
252
dist/frappe-datatable.css
vendored
@ -1,252 +0,0 @@
|
||||
.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%);
|
||||
}
|
||||
|
||||
body.dt-resize {
|
||||
cursor: col-resize;
|
||||
}
|
||||
3623
dist/frappe-datatable.js
vendored
3623
dist/frappe-datatable.js
vendored
File diff suppressed because it is too large
Load Diff
1
dist/frappe-datatable.min.css
vendored
1
dist/frappe-datatable.min.css
vendored
@ -1 +0,0 @@
|
||||
.data-table{position:relative;overflow:auto}.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:0;-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:.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:.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}.body-scrollable .no-data td{text-align:center;padding:8px;padding:.5rem}.data-table-header{position:absolute;top:0;left:0;background-color:#fff;font-weight:700}.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:.25rem;height:100%;background-color:#5292f7;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,.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:400;position:absolute;min-width:128px;min-width:8rem;top:100%;right:0;z-index:1;background-color:#fff;border-radius:3px;-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);padding-bottom:8px;padding-bottom:.5rem;padding-top:8px;padding-top:.5rem}.data-table-header .data-table-dropdown-list>div{padding:8px 16px;padding:.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:background-color .3s ease-in-out;transition:background-color .3s 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:.5rem;border:2px solid transparent;height:100%}.data-table-cell .content.ellipsis{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.data-table-cell .edit-cell{display:none;padding:8px;padding:.5rem;background-color:#fff;z-index:1;height:100%}.data-table-cell.selected .content{border:2px solid #5292f7}.data-table-cell.editing .content{display:none}.data-table-cell.editing .edit-cell{border:2px solid #ffa00a;border-style:dashed;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}
|
||||
1
dist/frappe-datatable.min.js
vendored
1
dist/frappe-datatable.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,289 +0,0 @@
|
||||
/* 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;
|
||||
}
|
||||
|
||||
.body-scrollable .no-data td {
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.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;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.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-color: #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(255, 160, 10);
|
||||
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;
|
||||
}
|
||||
@ -1,99 +0,0 @@
|
||||
/*
|
||||
|
||||
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;
|
||||
}
|
||||
@ -1,159 +0,0 @@
|
||||
@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;
|
||||
} */
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
<?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>
|
||||
|
Before Width: | Height: | Size: 5.7 KiB |
@ -1,46 +0,0 @@
|
||||
<?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>
|
||||
|
Before Width: | Height: | Size: 2.6 KiB |
2
docs/assets/js/Sortable.min.js
vendored
2
docs/assets/js/Sortable.min.js
vendored
File diff suppressed because one or more lines are too long
18
docs/assets/js/clusterize.min.js
vendored
18
docs/assets/js/clusterize.min.js
vendored
@ -1,18 +0,0 @@
|
||||
/*
|
||||
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
@ -1,718 +0,0 @@
|
||||
/* 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
172
docs/index.html
@ -1,172 +0,0 @@
|
||||
<!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
|
||||
// <script src="frappe-datatable.js" ></script>
|
||||
// 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>
|
||||
272
index.html
272
index.html
File diff suppressed because one or more lines are too long
45
package.json
45
package.json
@ -1,31 +1,53 @@
|
||||
{
|
||||
"name": "frappe-datatable",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.0-development",
|
||||
"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",
|
||||
"production": "rollup -c --production",
|
||||
"build:docs": "rollup -c --docs",
|
||||
"build": "rollup -c && NODE_ENV=production rollup -c",
|
||||
"dev": "rollup -c -w",
|
||||
"test": "mocha --compilers js:babel-core/register --colors ./test/*.spec.js"
|
||||
"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"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"src"
|
||||
],
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^9.0.0",
|
||||
"chai": "3.5.0",
|
||||
"cypress": "^9.2.0",
|
||||
"cz-conventional-changelog": "^2.1.0",
|
||||
"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-cssnext": "^3.1.0",
|
||||
"postcss-custom-properties": "^7.0.0",
|
||||
"postcss-nested": "^3.0.0",
|
||||
"rollup": "^0.59.4",
|
||||
"rollup-plugin-commonjs": "^8.3.0",
|
||||
"rollup-plugin-eslint": "^4.0.0",
|
||||
"rollup-plugin-json": "^2.3.0",
|
||||
"rollup-plugin-node-resolve": "^3.0.3",
|
||||
"rollup-plugin-postcss": "^1.2.8",
|
||||
"rollup-plugin-uglify-es": "^0.0.1"
|
||||
"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"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -42,10 +64,15 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/frappe/datatable/issues"
|
||||
},
|
||||
"homepage": "https://frappe.github.io/datatable",
|
||||
"homepage": "https://frappe.io/datatable",
|
||||
"dependencies": {
|
||||
"clusterize.js": "^0.18.0",
|
||||
"hyperlist": "^1.0.0-beta",
|
||||
"lodash": "^4.17.5",
|
||||
"sortablejs": "^1.7.0"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "cz-conventional-changelog"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,11 +4,12 @@ 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 cssnext from 'postcss-cssnext';
|
||||
import customProperties from 'postcss-custom-properties';
|
||||
import autoprefixer from 'autoprefixer';
|
||||
import eslint from 'rollup-plugin-eslint';
|
||||
import merge from 'deepmerge';
|
||||
|
||||
const production = process.argv[3] === '--production';
|
||||
const production = process.env.NODE_ENV === 'production';
|
||||
|
||||
const baseJS = {
|
||||
input: 'src/index.js',
|
||||
@ -40,8 +41,9 @@ const baseCSS = {
|
||||
extract: true,
|
||||
minimize: production,
|
||||
plugins: [
|
||||
customProperties(),
|
||||
nested(),
|
||||
cssnext()
|
||||
autoprefixer()
|
||||
]
|
||||
})
|
||||
]
|
||||
@ -85,24 +87,8 @@ 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 docs = process.argv[3] === '--docs';
|
||||
const assets = docs ? documentationAssets : production ? productionAssets : developmentAssets;
|
||||
const assets = production ? productionAssets : developmentAssets;
|
||||
|
||||
export default assets;
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
import Clusterize from 'clusterize.js';
|
||||
import $ from './dom';
|
||||
import { nextTick } from './utils';
|
||||
import HyperList from 'hyperlist';
|
||||
|
||||
export default class BodyRenderer {
|
||||
constructor(instance) {
|
||||
@ -10,60 +8,115 @@ 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;
|
||||
this.appendRemainingData = nextTick(this.appendRemainingData, this);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.options.clusterize) {
|
||||
this.renderBodyWithClusterize();
|
||||
} else {
|
||||
this.renderBodyHTML();
|
||||
}
|
||||
}
|
||||
|
||||
renderBodyHTML() {
|
||||
const rows = this.datamanager.getRowsForView();
|
||||
|
||||
this.bodyScrollable.innerHTML = this.getBodyHTML(rows);
|
||||
this.instance.setDimensions();
|
||||
this.restoreState();
|
||||
}
|
||||
|
||||
renderBodyWithClusterize() {
|
||||
// first page
|
||||
const rows = this.datamanager.getRowsForView(0, 20);
|
||||
let initialData = this.getDataForClusterize(rows);
|
||||
|
||||
if (initialData.length === 0) {
|
||||
initialData = [this.getNoDataHTML()];
|
||||
}
|
||||
|
||||
if (!this.clusterize) {
|
||||
// empty body
|
||||
this.bodyScrollable.innerHTML = this.getBodyHTML([]);
|
||||
|
||||
// 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 */
|
||||
});
|
||||
|
||||
this.renderRows(rows);
|
||||
// setDimensions requires atleast 1 row to exist in dom
|
||||
this.instance.setDimensions();
|
||||
} else {
|
||||
this.clusterize.update(initialData);
|
||||
}
|
||||
|
||||
this.appendRemainingData();
|
||||
renderFooter() {
|
||||
if (!this.options.showTotalRow) return;
|
||||
|
||||
const totalRow = this.getTotalRow();
|
||||
let html = this.rowmanager.getRowHTML(totalRow, { isTotalRow: 1, rowIndex: 'totalRow' });
|
||||
|
||||
this.footer.innerHTML = html;
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
});
|
||||
|
||||
const totalRow = totalRowTemplate.map((cell, i) => {
|
||||
if (cell.content === '') return cell;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
restoreState() {
|
||||
@ -72,12 +125,6 @@ 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);
|
||||
|
||||
@ -92,20 +139,6 @@ 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>`;
|
||||
}
|
||||
|
||||
@ -2,9 +2,11 @@ import {
|
||||
copyTextToClipboard,
|
||||
makeDataAttributeString,
|
||||
throttle,
|
||||
linkProperties
|
||||
linkProperties,
|
||||
escapeHTML,
|
||||
} from './utils';
|
||||
import $ from './dom';
|
||||
import icons from './icons';
|
||||
|
||||
export default class CellManager {
|
||||
constructor(instance) {
|
||||
@ -13,14 +15,18 @@ export default class CellManager {
|
||||
'wrapper',
|
||||
'options',
|
||||
'style',
|
||||
'header',
|
||||
'bodyScrollable',
|
||||
'columnmanager',
|
||||
'rowmanager',
|
||||
'datamanager',
|
||||
'keyboard'
|
||||
'keyboard',
|
||||
'footer'
|
||||
]);
|
||||
|
||||
this.bindEvents();
|
||||
this.stickyRowWidth = 0;
|
||||
this.stickyColWitdh = [];
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
@ -49,34 +55,12 @@ 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;
|
||||
@ -103,13 +87,14 @@ export default class CellManager {
|
||||
};
|
||||
|
||||
['left', 'right', 'up', 'down', 'tab', 'shift+tab']
|
||||
.map(direction => this.keyboard.on(direction, () => focusCell(direction)));
|
||||
.map(direction => this.keyboard.on(direction, () => this.focusCellInDirection(direction)));
|
||||
|
||||
['left', 'right', 'up', 'down']
|
||||
.map(direction => this.keyboard.on(`ctrl+${direction}`, () => focusLastCell(direction)));
|
||||
|
||||
this.keyboard.on('esc', () => {
|
||||
this.deactivateEditing();
|
||||
this.deactivateEditing(false);
|
||||
this.columnmanager.toggleFilter(false);
|
||||
});
|
||||
|
||||
if (this.options.inlineFilters) {
|
||||
@ -120,6 +105,10 @@ export default class CellManager {
|
||||
this.activateFilter(colIndex);
|
||||
return true;
|
||||
});
|
||||
|
||||
$.on(this.header, 'focusin', '.dt-filter', () => {
|
||||
this.unfocusCell(this.$focusedCell);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,7 +137,10 @@ export default class CellManager {
|
||||
bindCopyCellContents() {
|
||||
this.keyboard.on('ctrl+c', () => {
|
||||
const noOfCellsCopied = this.copyCellContents(this.$focusedCell, this.$selectionCursor);
|
||||
const message = `${noOfCellsCopied} cell${noOfCellsCopied > 1 ? 's' : ''} copied`;
|
||||
const message = this.instance.translate('{count} cells copied', {
|
||||
count: noOfCellsCopied
|
||||
});
|
||||
|
||||
if (noOfCellsCopied) {
|
||||
this.instance.showToastMessage(message, 2);
|
||||
}
|
||||
@ -183,6 +175,14 @@ 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,7 +205,9 @@ export default class CellManager {
|
||||
}
|
||||
|
||||
focusCell($cell, {
|
||||
skipClearSelection = 0
|
||||
skipClearSelection = 0,
|
||||
skipDOMFocus = 0,
|
||||
skipScrollToCell = 0
|
||||
} = {}) {
|
||||
if (!$cell) return;
|
||||
|
||||
@ -225,7 +227,9 @@ export default class CellManager {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!skipScrollToCell) {
|
||||
this.scrollToCell($cell);
|
||||
}
|
||||
|
||||
this.deactivateEditing();
|
||||
if (!skipClearSelection) {
|
||||
@ -239,12 +243,27 @@ export default class CellManager {
|
||||
this.$focusedCell = $cell;
|
||||
$cell.classList.add('dt-cell--focus');
|
||||
|
||||
if (!skipDOMFocus) {
|
||||
// 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,
|
||||
@ -256,14 +275,14 @@ export default class CellManager {
|
||||
const rowHeaderSelector = `.dt-cell--${srNoColIndex}-${rowIndex}`;
|
||||
|
||||
if (this.lastHeaders) {
|
||||
this.lastHeaders.forEach(header => header.classList.remove('dt-cell--highlight'));
|
||||
this.lastHeaders.forEach(header => 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.classList.add('dt-cell--highlight'));
|
||||
this.lastHeaders.forEach(header => header && header.classList.add('dt-cell--highlight'));
|
||||
}
|
||||
|
||||
selectAreaOnClusterChanged() {
|
||||
@ -293,11 +312,15 @@ export default class CellManager {
|
||||
const $cell = this.getCell$(colIndex, rowIndex);
|
||||
|
||||
if (!$cell) return;
|
||||
// this function is called after selectAreaOnClusterChanged,
|
||||
// this function is called after hyperlist renders the rows after scroll,
|
||||
// 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
|
||||
skipClearSelection: 1,
|
||||
skipDOMFocus: 1,
|
||||
skipScrollToCell: 1
|
||||
});
|
||||
}
|
||||
|
||||
@ -433,7 +456,10 @@ export default class CellManager {
|
||||
}
|
||||
}
|
||||
|
||||
deactivateEditing() {
|
||||
deactivateEditing(submitValue = true) {
|
||||
if (submitValue) {
|
||||
this.submitEditing();
|
||||
}
|
||||
// keep focus on the cell so that keyboard navigation works
|
||||
if (this.$focusedCell) this.$focusedCell.focus();
|
||||
|
||||
@ -484,7 +510,9 @@ export default class CellManager {
|
||||
}
|
||||
|
||||
submitEditing() {
|
||||
if (!this.$editingCell) return;
|
||||
let promise = Promise.resolve();
|
||||
if (!this.$editingCell) return promise;
|
||||
|
||||
const $cell = this.$editingCell;
|
||||
const {
|
||||
rowIndex,
|
||||
@ -496,12 +524,22 @@ export default class CellManager {
|
||||
const editor = this.currentCellEditor;
|
||||
|
||||
if (editor) {
|
||||
const value = editor.getValue();
|
||||
const done = editor.setValue(value, rowIndex, col);
|
||||
let valuePromise = editor.getValue();
|
||||
|
||||
// convert to stubbed Promise
|
||||
if (!valuePromise.then) {
|
||||
valuePromise = Promise.resolve(valuePromise);
|
||||
}
|
||||
|
||||
promise = valuePromise.then((value) => {
|
||||
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);
|
||||
this.updateCell(colIndex, rowIndex, value, true);
|
||||
$cell.focus();
|
||||
|
||||
if (done && done.then) {
|
||||
@ -511,10 +549,13 @@ export default class CellManager {
|
||||
this.updateCell(colIndex, rowIndex, oldValue);
|
||||
});
|
||||
}
|
||||
return done;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.currentCellEditor = null;
|
||||
return promise;
|
||||
}
|
||||
|
||||
copyCellContents($cell1, $cell2) {
|
||||
@ -522,10 +563,18 @@ export default class CellManager {
|
||||
// copy only focusedCell
|
||||
const {
|
||||
colIndex,
|
||||
rowIndex
|
||||
rowIndex,
|
||||
isTotalRow
|
||||
} = $.data($cell1);
|
||||
let copiedContent = '';
|
||||
if (isTotalRow) {
|
||||
let choosenFooterCell = this.$focusedCell;
|
||||
copiedContent = choosenFooterCell.children[0].title;
|
||||
} else {
|
||||
const cell = this.getCell(colIndex, rowIndex);
|
||||
copyTextToClipboard(cell.content);
|
||||
copiedContent = cell.content;
|
||||
}
|
||||
copyTextToClipboard(copiedContent);
|
||||
return 1;
|
||||
}
|
||||
const cells = this.getCellsInRange($cell1, $cell2);
|
||||
@ -576,7 +625,7 @@ export default class CellManager {
|
||||
let rowIndex = i + focusedCell.rowIndex;
|
||||
row.forEach((cell, j) => {
|
||||
let colIndex = j + focusedCell.colIndex;
|
||||
this.updateCell(colIndex, rowIndex, cell);
|
||||
this.updateCell(colIndex, rowIndex, cell, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -587,20 +636,20 @@ export default class CellManager {
|
||||
|
||||
if (!this.columnmanager.isFilterShown) {
|
||||
// put focus back on cell
|
||||
this.$focusedCell.focus();
|
||||
this.$focusedCell && this.$focusedCell.focus();
|
||||
}
|
||||
}
|
||||
|
||||
updateCell(colIndex, rowIndex, value) {
|
||||
updateCell(colIndex, rowIndex, value, refreshHtml = false) {
|
||||
const cell = this.datamanager.updateCell(colIndex, rowIndex, {
|
||||
content: value
|
||||
});
|
||||
this.refreshCell(cell);
|
||||
this.refreshCell(cell, refreshHtml);
|
||||
}
|
||||
|
||||
refreshCell(cell) {
|
||||
refreshCell(cell, refreshHtml = false) {
|
||||
const $cell = $(this.selector(cell.colIndex, cell.rowIndex), this.bodyScrollable);
|
||||
$cell.innerHTML = this.getCellContent(cell);
|
||||
$cell.innerHTML = this.getCellContent(cell, refreshHtml);
|
||||
}
|
||||
|
||||
toggleTreeButton(rowIndex, flag) {
|
||||
@ -616,6 +665,49 @@ 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);
|
||||
}
|
||||
@ -681,7 +773,7 @@ export default class CellManager {
|
||||
}
|
||||
|
||||
scrollToCell($cell) {
|
||||
if ($.inViewport($cell, this.bodyScrollable)) return false;
|
||||
if ($.inViewport($cell, this.bodyScrollable) || $.inViewport($cell, this.footer)) return false;
|
||||
|
||||
const {
|
||||
rowIndex
|
||||
@ -699,16 +791,54 @@ export default class CellManager {
|
||||
rowIndex,
|
||||
colIndex,
|
||||
isHeader,
|
||||
isFilter
|
||||
isFilter,
|
||||
isTotalRow
|
||||
} = cell;
|
||||
const dataAttr = makeDataAttributeString({
|
||||
rowIndex,
|
||||
colIndex,
|
||||
isHeader,
|
||||
isFilter
|
||||
isFilter,
|
||||
isTotalRow
|
||||
});
|
||||
|
||||
const isBodyCell = !(isHeader || 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 className = [
|
||||
'dt-cell',
|
||||
@ -717,17 +847,20 @@ export default class CellManager {
|
||||
isBodyCell ? 'dt-cell--row-' + rowIndex : '',
|
||||
isHeader ? 'dt-cell--header' : '',
|
||||
isHeader ? `dt-cell--header-${colIndex}` : '',
|
||||
isFilter ? 'dt-cell--filter' : ''
|
||||
isFilter ? 'dt-cell--filter' : '',
|
||||
isBodyCell && (row && row.meta.isTreeNodeClose) ? 'dt-cell--tree-close' : '',
|
||||
sticky ? 'dt-sticky-col' : '',
|
||||
checkboxserialNoclass,
|
||||
].join(' ');
|
||||
|
||||
return `
|
||||
<td class="${className}" ${dataAttr} tabindex="0">
|
||||
<div class="${className}" ${dataAttr} tabindex="0" style="${styles}">
|
||||
${this.getCellContent(cell)}
|
||||
</td>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
getCellContent(cell) {
|
||||
getCellContent(cell, refreshHtml = false) {
|
||||
const {
|
||||
isHeader,
|
||||
isFilter,
|
||||
@ -738,7 +871,11 @@ export default class CellManager {
|
||||
const editCellHTML = editable ? this.getEditCellHTML(colIndex) : '';
|
||||
|
||||
const sortable = isHeader && cell.sortable !== false;
|
||||
const sortIndicator = sortable ? '<span class="sort-indicator"></span>' : '';
|
||||
const sortIndicator = sortable ?
|
||||
`<span class="sort-indicator">
|
||||
${this.options.sortIndicator[cell.sortOrder]}
|
||||
</span>` :
|
||||
'';
|
||||
|
||||
const resizable = isHeader && cell.resizable !== false;
|
||||
const resizeColumn = resizable ? '<span class="dt-cell__resize-handle"></span>' : '';
|
||||
@ -746,29 +883,41 @@ export default class CellManager {
|
||||
const hasDropdown = isHeader && cell.dropdown !== false;
|
||||
const dropdown = hasDropdown ? this.columnmanager.getDropdownHTML() : '';
|
||||
|
||||
const customFormatter = cell.format || (cell.column && cell.column.format) || null;
|
||||
|
||||
let customFormatter = CellManager.getCustomCellFormatter(cell);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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';
|
||||
|
||||
// Add toggle and indent in the first column
|
||||
const firstColumnIndex = this.datamanager.getColumnIndexById('_rowIndex') + 1;
|
||||
if (firstColumnIndex === cell.colIndex) {
|
||||
const padding = ((cell.indent || 0) + 1) * 1.5;
|
||||
const padding = ((cell.indent || 0)) * leftPadding;
|
||||
const toggleHTML = addToggle ?
|
||||
`<span class="dt-tree-node__toggle" style="left: ${padding - 1.5}rem"></span>` : '';
|
||||
contentHTML = `<span class="dt-tree-node" style="padding-left: ${padding}rem">
|
||||
${toggleHTML}${contentHTML}</span>`;
|
||||
`<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>`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -777,7 +926,7 @@ export default class CellManager {
|
||||
isHeader ? `dt-cell__content--header-${colIndex}` : `dt-cell__content--col-${colIndex}`
|
||||
].join(' ');
|
||||
|
||||
return `
|
||||
let cellContentHTML = `
|
||||
<div class="${className}">
|
||||
${contentHTML}
|
||||
${sortIndicator}
|
||||
@ -786,6 +935,16 @@ 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) {
|
||||
@ -795,4 +954,8 @@ export default class CellManager {
|
||||
selector(colIndex, rowIndex) {
|
||||
return `.dt-cell--${colIndex}-${rowIndex}`;
|
||||
}
|
||||
|
||||
static getCustomCellFormatter(cell) {
|
||||
return cell.format || (cell.column && cell.column.format) || null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,57 +14,35 @@ export default class ColumnManager {
|
||||
'fireEvent',
|
||||
'header',
|
||||
'datamanager',
|
||||
'cellmanager',
|
||||
'style',
|
||||
'wrapper',
|
||||
'rowmanager',
|
||||
'bodyScrollable'
|
||||
'bodyScrollable',
|
||||
'bodyRenderer'
|
||||
]);
|
||||
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
renderHeader() {
|
||||
this.header.innerHTML = '<thead></thead>';
|
||||
this.header.innerHTML = '<div></div>';
|
||||
this.refreshHeader();
|
||||
}
|
||||
|
||||
refreshHeader() {
|
||||
const columns = this.datamanager.getColumns();
|
||||
const $cols = $.each('.dt-cell--header', this.header);
|
||||
|
||||
const refreshHTML =
|
||||
// first init
|
||||
!$('.dt-cell', this.header) ||
|
||||
// deleted column
|
||||
columns.length < $cols.length;
|
||||
|
||||
if (refreshHTML) {
|
||||
// refresh html
|
||||
$('thead', this.header).innerHTML = this.getHeaderHTML(columns);
|
||||
$('div', this.header).innerHTML = this.getHeaderHTML(columns);
|
||||
|
||||
this.$filterRow = $('.dt-row[data-is-filter]', this.header);
|
||||
this.$filterRow = $('.dt-row-filter', this.header);
|
||||
if (this.$filterRow) {
|
||||
$.style(this.$filterRow, { display: 'none' });
|
||||
}
|
||||
} else {
|
||||
// update data-attributes
|
||||
$cols.map(($col, i) => {
|
||||
const column = columns[i];
|
||||
// column sorted or order changed
|
||||
// update colIndex of each header cell
|
||||
$.data($col, {
|
||||
colIndex: column.colIndex
|
||||
});
|
||||
|
||||
// refresh sort indicator
|
||||
const sortIndicator = $('.sort-indicator', $col);
|
||||
if (sortIndicator) {
|
||||
sortIndicator.innerHTML = this.options.sortIndicator[column.sortOrder];
|
||||
}
|
||||
});
|
||||
}
|
||||
// reset columnMap
|
||||
this.$columnMap = [];
|
||||
this.bindMoveColumn();
|
||||
}
|
||||
|
||||
getHeaderHTML(columns) {
|
||||
@ -82,51 +60,77 @@ export default class ColumnManager {
|
||||
bindEvents() {
|
||||
this.bindDropdown();
|
||||
this.bindResizeColumn();
|
||||
this.bindMoveColumn();
|
||||
this.bindPerfectColumnWidth();
|
||||
this.bindFilter();
|
||||
}
|
||||
|
||||
bindDropdown() {
|
||||
let $activeDropdown;
|
||||
let activeClass = 'dt-dropdown--active';
|
||||
let toggleClass = '.dt-dropdown__toggle';
|
||||
let dropdownClass = '.dt-dropdown__list';
|
||||
|
||||
$.on(this.header, 'click', toggleClass, (e, $button) => {
|
||||
const $dropdown = $.closest('.dt-dropdown', $button);
|
||||
// attach the dropdown list to container
|
||||
this.instance.dropdownContainer.innerHTML = this.getDropdownListHTML();
|
||||
this.$dropdownList = this.instance.dropdownContainer.firstElementChild;
|
||||
|
||||
if (!$dropdown.classList.contains(activeClass)) {
|
||||
deactivateDropdown();
|
||||
$dropdown.classList.add(activeClass);
|
||||
$activeDropdown = $dropdown;
|
||||
} else {
|
||||
deactivateDropdown();
|
||||
}
|
||||
$.on(this.header, 'click', toggleClass, e => {
|
||||
this.openDropdown(e);
|
||||
});
|
||||
|
||||
$.on(document.body, 'click', (e) => {
|
||||
if (e.target.matches(toggleClass)) return;
|
||||
const deactivateDropdownOnBodyClick = (e) => {
|
||||
const selector = [
|
||||
toggleClass, toggleClass + ' *',
|
||||
dropdownClass, dropdownClass + ' *'
|
||||
].join(',');
|
||||
if (e.target.matches(selector)) 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;
|
||||
|
||||
$.on(this.header, 'click', '.dt-dropdown__list-item', (e, $item) => {
|
||||
const $col = $.closest('.dt-cell', $item);
|
||||
const {
|
||||
index
|
||||
} = $.data($item);
|
||||
const {
|
||||
colIndex
|
||||
} = $.data($col);
|
||||
const { index } = $.data($item);
|
||||
const colIndex = this._dropdownActiveColIndex;
|
||||
let callback = dropdownItems[index].action;
|
||||
|
||||
callback && callback.call(this.instance, this.getColumn(colIndex));
|
||||
this.hideDropdown();
|
||||
});
|
||||
|
||||
const _this = this;
|
||||
function deactivateDropdown(e) {
|
||||
$activeDropdown && $activeDropdown.classList.remove(activeClass);
|
||||
$activeDropdown = null;
|
||||
_this.hideDropdown();
|
||||
}
|
||||
|
||||
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() {
|
||||
@ -151,7 +155,7 @@ export default class ColumnManager {
|
||||
startX = e.pageX;
|
||||
});
|
||||
|
||||
$.on(document.body, 'mouseup', (e) => {
|
||||
const onMouseup = (e) => {
|
||||
document.body.classList.remove('dt-resize');
|
||||
if (!$resizingCell) return;
|
||||
isDragging = false;
|
||||
@ -162,36 +166,73 @@ 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);
|
||||
});
|
||||
|
||||
$.on(document.body, 'mousemove', (e) => {
|
||||
const onMouseMove = (e) => {
|
||||
if (!isDragging) return;
|
||||
const finalWidth = startWidth + (e.pageX - startX);
|
||||
let delta = e.pageX - startX;
|
||||
if (this.options.direction === 'rtl') {
|
||||
delta = -1 * delta;
|
||||
}
|
||||
const finalWidth = startWidth + delta;
|
||||
const {
|
||||
colIndex
|
||||
} = $.data($resizingCell);
|
||||
|
||||
if (this.getColumnMinWidth(colIndex) > finalWidth) {
|
||||
// don't resize past minWidth
|
||||
let columnMinWidth = this.options.minimumColumnWidth;
|
||||
if (columnMinWidth > finalWidth) {
|
||||
// don't resize past 30 pixels
|
||||
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() {
|
||||
let initialized;
|
||||
|
||||
const initialize = () => {
|
||||
if (initialized) {
|
||||
$.off(document.body, 'mousemove', initialize);
|
||||
return;
|
||||
}
|
||||
const ready = $('.dt-cell', this.header);
|
||||
if (!ready) return;
|
||||
if (this.options.disableReorderColumn) return;
|
||||
|
||||
const $parent = $('.dt-row', this.header);
|
||||
|
||||
@ -214,9 +255,6 @@ export default class ColumnManager {
|
||||
chosenClass: 'dt-cell--dragging',
|
||||
animation: 150
|
||||
});
|
||||
};
|
||||
|
||||
$.on(document.body, 'mousemove', initialize);
|
||||
}
|
||||
|
||||
sortColumn(colIndex, nextSortOrder) {
|
||||
@ -266,6 +304,8 @@ export default class ColumnManager {
|
||||
}
|
||||
|
||||
toggleFilter(flag) {
|
||||
if (!this.options.inlineFilters) return;
|
||||
|
||||
let showFilter;
|
||||
if (flag === undefined) {
|
||||
showFilter = !this.isFilterShown;
|
||||
@ -286,31 +326,48 @@ export default class ColumnManager {
|
||||
focusFilter(colIndex) {
|
||||
if (!this.isFilterShown) return;
|
||||
|
||||
const $filterInput = $(`[data-col-index="${colIndex}"] .dt-filter`, this.$filterRow);
|
||||
const $filterInput = $(`.dt-cell--col-${colIndex} .dt-filter`, this.$filterRow);
|
||||
$filterInput.focus();
|
||||
}
|
||||
|
||||
bindFilter() {
|
||||
if (!this.options.inlineFilters) return;
|
||||
const handler = e => {
|
||||
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);
|
||||
});
|
||||
this.applyFilter(this.getAppliedFilters());
|
||||
};
|
||||
$.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');
|
||||
|
||||
if (columnsToSort.length === 1) {
|
||||
const column = columnsToSort[0];
|
||||
this.sortColumn(column.colIndex, column.sortOrder);
|
||||
}
|
||||
}
|
||||
|
||||
sortRows(colIndex, sortOrder) {
|
||||
return this.datamanager.sortRows(colIndex, sortOrder);
|
||||
}
|
||||
@ -325,11 +382,9 @@ export default class ColumnManager {
|
||||
|
||||
setColumnWidth(colIndex, width) {
|
||||
colIndex = +colIndex;
|
||||
this._columnWidthMap = this._columnWidthMap || [];
|
||||
|
||||
let columnWidth = width || this.getColumn(colIndex).width;
|
||||
|
||||
let index = this._columnWidthMap[colIndex];
|
||||
const selector = [
|
||||
`.dt-cell__content--col-${colIndex}`,
|
||||
`.dt-cell__edit--col-${colIndex}`
|
||||
@ -339,11 +394,7 @@ export default class ColumnManager {
|
||||
width: columnWidth + 'px'
|
||||
};
|
||||
|
||||
index = this.style.setStyle(selector, styles, index);
|
||||
|
||||
if (index !== undefined) {
|
||||
this._columnWidthMap[colIndex] = index;
|
||||
}
|
||||
this.style.setStyle(selector, styles);
|
||||
}
|
||||
|
||||
setColumnHeaderWidth(colIndex) {
|
||||
@ -381,17 +432,24 @@ export default class ColumnManager {
|
||||
}
|
||||
|
||||
getDropdownHTML() {
|
||||
const { dropdownButton, headerDropdown: dropdownItems } = this.options;
|
||||
const { dropdownButton } = this.options;
|
||||
|
||||
return `
|
||||
<div class="dt-dropdown">
|
||||
<div class="dt-dropdown__toggle">${dropdownButton}</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>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
11
src/dark.css
Normal file
11
src/dark.css
Normal file
@ -0,0 +1,11 @@
|
||||
.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;
|
||||
}
|
||||
@ -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.filterRows = nextTick(this.filterRows, this);
|
||||
this.options.filterRows = nextTick(this.options.filterRows, this);
|
||||
}
|
||||
|
||||
init(data, columns) {
|
||||
@ -29,10 +29,10 @@ export default class DataManager {
|
||||
this.rows = [];
|
||||
|
||||
this.prepareColumns();
|
||||
this.prepareRows();
|
||||
this.validateData(this.data);
|
||||
this.rows = this.prepareRows(this.data);
|
||||
this.prepareTreeRows();
|
||||
this.prepareRowView();
|
||||
|
||||
this.prepareNumericColumns();
|
||||
}
|
||||
|
||||
@ -134,7 +134,7 @@ export default class DataManager {
|
||||
this.columns = this.columns.map((column, i) => {
|
||||
|
||||
const cellValue = row0[i].content;
|
||||
if (!column.align && cellValue && isNumeric(cellValue)) {
|
||||
if (!column.align && isNumeric(cellValue)) {
|
||||
column.align = 'right';
|
||||
}
|
||||
|
||||
@ -142,10 +142,8 @@ export default class DataManager {
|
||||
});
|
||||
}
|
||||
|
||||
prepareRows() {
|
||||
this.validateData(this.data);
|
||||
|
||||
this.rows = this.data.map((d, i) => {
|
||||
prepareRows(data) {
|
||||
return data.map((d, i) => {
|
||||
const index = this._getNextRowCount();
|
||||
|
||||
let row = [];
|
||||
@ -194,6 +192,7 @@ export default class DataManager {
|
||||
row.meta.isLeaf = !nextRow ||
|
||||
notSet(nextRow.meta.indent) ||
|
||||
nextRow.meta.indent <= row.meta.indent;
|
||||
row.meta.isTreeNodeClose = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -243,8 +242,9 @@ export default class DataManager {
|
||||
|
||||
appendRows(rows) {
|
||||
this.validateData(rows);
|
||||
|
||||
this.rows.push(...this.prepareRows(rows));
|
||||
this.rows = this.rows.concat(this.prepareRows(rows));
|
||||
this.prepareTreeRows();
|
||||
this.prepareRowView();
|
||||
}
|
||||
|
||||
sortRows(colIndex, sortOrder = 'none') {
|
||||
@ -280,8 +280,11 @@ export default class DataManager {
|
||||
this.rowViewOrder.sort((a, b) => {
|
||||
const aIndex = a;
|
||||
const bIndex = b;
|
||||
const aContent = this.getCell(colIndex, a).content;
|
||||
const bContent = this.getCell(colIndex, b).content;
|
||||
|
||||
let aContent = this.getCell(colIndex, a).content;
|
||||
let bContent = this.getCell(colIndex, b).content;
|
||||
aContent = aContent == null ? '' : aContent;
|
||||
bContent = bContent == null ? '' : bContent;
|
||||
|
||||
if (sortOrder === 'none') {
|
||||
return aIndex - bIndex;
|
||||
@ -423,32 +426,37 @@ export default class DataManager {
|
||||
return column;
|
||||
}
|
||||
|
||||
filterRows(keyword, colIndex) {
|
||||
let rowsToHide = [];
|
||||
let rowsToShow = [];
|
||||
const cells = this.rows.map(row => row[colIndex]);
|
||||
|
||||
cells.forEach(cell => {
|
||||
const hay = String(cell.content || '').toLowerCase();
|
||||
const needle = (keyword || '').toLowerCase();
|
||||
|
||||
if (!needle || hay.includes(needle)) {
|
||||
rowsToShow.push(cell.rowIndex);
|
||||
} else {
|
||||
rowsToHide.push(cell.rowIndex);
|
||||
filterRows(filters) {
|
||||
return this.options.filterRows(this.rows, filters, this)
|
||||
.then(result => {
|
||||
if (!result) {
|
||||
result = this.getAllRowIndices();
|
||||
}
|
||||
});
|
||||
|
||||
if (!result.then) {
|
||||
result = Promise.resolve(result);
|
||||
}
|
||||
|
||||
return result.then(rowsToShow => {
|
||||
this._filteredRows = rowsToShow;
|
||||
|
||||
const rowsToHide = this.getAllRowIndices()
|
||||
.filter(index => !rowsToShow.includes(index));
|
||||
|
||||
return {
|
||||
rowsToHide,
|
||||
rowsToShow
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getFilteredRowIndices() {
|
||||
return this._filteredRows || this.rows.map(row => row.meta.rowIndex);
|
||||
return this._filteredRows || this.getAllRowIndices();
|
||||
}
|
||||
|
||||
getAllRowIndices() {
|
||||
return this.rows.map(row => row.meta.rowIndex);
|
||||
}
|
||||
|
||||
getRowCount() {
|
||||
|
||||
104
src/datatable.js
104
src/datatable.js
@ -6,7 +6,18 @@ import RowManager from './rowmanager';
|
||||
import BodyRenderer from './body-renderer';
|
||||
import Style from './style';
|
||||
import Keyboard from './keyboard';
|
||||
import DEFAULT_OPTIONS from './defaults';
|
||||
import TranslationManager from './translationmanager';
|
||||
import getDefaultOptions from './defaults';
|
||||
|
||||
let defaultComponents = {
|
||||
DataManager,
|
||||
CellManager,
|
||||
ColumnManager,
|
||||
RowManager,
|
||||
BodyRenderer,
|
||||
Style,
|
||||
Keyboard
|
||||
};
|
||||
|
||||
class DataTable {
|
||||
constructor(wrapper, options) {
|
||||
@ -21,36 +32,48 @@ class DataTable {
|
||||
throw new Error('Invalid argument given for `wrapper`');
|
||||
}
|
||||
|
||||
this.initializeTranslations(options);
|
||||
this.setDefaultOptions();
|
||||
this.buildOptions(options);
|
||||
this.prepare();
|
||||
|
||||
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);
|
||||
this.initializeComponents();
|
||||
|
||||
if (this.options.data) {
|
||||
this.refresh();
|
||||
this.columnmanager.applyDefaultSortOrder();
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
{}, DEFAULT_OPTIONS,
|
||||
{}, this.DEFAULT_OPTIONS,
|
||||
this.options || {}, options
|
||||
);
|
||||
|
||||
this.options.headerDropdown
|
||||
.push(...(options.headerDropdown || []));
|
||||
options.headerDropdown = options.headerDropdown || [];
|
||||
this.options.headerDropdown = [
|
||||
...this.DEFAULT_OPTIONS.headerDropdown,
|
||||
...options.headerDropdown
|
||||
];
|
||||
|
||||
// custom user events
|
||||
this.events = Object.assign(
|
||||
{}, DEFAULT_OPTIONS.events,
|
||||
{}, this.DEFAULT_OPTIONS.events,
|
||||
this.options.events || {},
|
||||
options.events || {}
|
||||
);
|
||||
@ -62,12 +85,34 @@ 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">
|
||||
<table class="dt-header">
|
||||
</table>
|
||||
<div class="dt-scrollable">
|
||||
<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>
|
||||
<div class="dt-freeze">
|
||||
<span class="dt-freeze__message">
|
||||
@ -75,16 +120,19 @@ 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) {
|
||||
@ -96,6 +144,7 @@ class DataTable {
|
||||
destroy() {
|
||||
this.wrapper.innerHTML = '';
|
||||
this.style.destroy();
|
||||
this.fireEvent('onDestroy');
|
||||
}
|
||||
|
||||
appendRows(rows) {
|
||||
@ -189,7 +238,22 @@ class DataTable {
|
||||
}
|
||||
|
||||
fireEvent(eventName, ...args) {
|
||||
this.events[eventName].apply(this, 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);
|
||||
}
|
||||
|
||||
log() {
|
||||
@ -197,6 +261,10 @@ class DataTable {
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
translate(str, args) {
|
||||
return this.translationManager.translate(str, args);
|
||||
}
|
||||
}
|
||||
|
||||
DataTable.instances = 0;
|
||||
|
||||
@ -1,28 +1,32 @@
|
||||
export default {
|
||||
import filterRows from './filterRows';
|
||||
import icons from './icons';
|
||||
|
||||
export default function getDefaultOptions(instance) {
|
||||
return {
|
||||
columns: [],
|
||||
data: [],
|
||||
dropdownButton: '▼',
|
||||
dropdownButton: icons.chevronDown,
|
||||
headerDropdown: [
|
||||
{
|
||||
label: 'Sort Ascending',
|
||||
label: instance.translate('Sort Ascending'),
|
||||
action: function (column) {
|
||||
this.sortColumn(column.colIndex, 'asc');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Sort Descending',
|
||||
label: instance.translate('Sort Descending'),
|
||||
action: function (column) {
|
||||
this.sortColumn(column.colIndex, 'desc');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Reset sorting',
|
||||
label: instance.translate('Reset sorting'),
|
||||
action: function (column) {
|
||||
this.sortColumn(column.colIndex, 'none');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Remove column',
|
||||
label: instance.translate('Remove column'),
|
||||
action: function (column) {
|
||||
this.removeColumn(column.colIndex);
|
||||
}
|
||||
@ -32,13 +36,21 @@ export default {
|
||||
onRemoveColumn(column) {},
|
||||
onSwitchColumn(column1, column2) {},
|
||||
onSortColumn(column) {},
|
||||
onCheckRow(row) {}
|
||||
onCheckRow(row) {},
|
||||
onDestroy() {}
|
||||
},
|
||||
hooks: {
|
||||
columnTotal: null
|
||||
},
|
||||
sortIndicator: {
|
||||
asc: '↑',
|
||||
desc: '↓',
|
||||
none: ''
|
||||
},
|
||||
overrideComponents: {
|
||||
// ColumnManager: CustomColumnManager
|
||||
},
|
||||
filterRows: filterRows,
|
||||
freezeMessage: '',
|
||||
getEditor: null,
|
||||
serialNoColumn: true,
|
||||
@ -46,11 +58,16 @@ export default {
|
||||
clusterize: true,
|
||||
logs: false,
|
||||
layout: 'fixed', // fixed, fluid, ratio
|
||||
noDataMessage: 'No Data',
|
||||
cellHeight: null,
|
||||
noDataMessage: instance.translate('No Data'),
|
||||
cellHeight: 40,
|
||||
minimumColumnWidth: 30,
|
||||
inlineFilters: false,
|
||||
treeView: false,
|
||||
checkedRowStatus: true,
|
||||
dynamicRowHeight: false,
|
||||
pasteFromClipboard: false
|
||||
pasteFromClipboard: false,
|
||||
showTotalRow: false,
|
||||
direction: 'ltr',
|
||||
disableReorderColumn: false
|
||||
};
|
||||
};
|
||||
|
||||
35
src/dom.js
35
src/dom.js
@ -138,6 +138,10 @@ $.removeStyle = (elements, styleProps) => {
|
||||
};
|
||||
|
||||
$.getStyle = (element, prop) => {
|
||||
if (!prop) {
|
||||
return getComputedStyle(element);
|
||||
}
|
||||
|
||||
let val = getComputedStyle(element)[prop];
|
||||
|
||||
if (['width', 'height'].includes(prop)) {
|
||||
@ -180,7 +184,16 @@ $.scrollTop = function scrollTop(element, pixels) {
|
||||
});
|
||||
};
|
||||
|
||||
$.scrollbarWidth = function scrollbarWidth() {
|
||||
$.scrollbarSize = function scrollbarSize() {
|
||||
if (!$.scrollBarSizeValue) {
|
||||
$.scrollBarSizeValue = getScrollBarSize();
|
||||
}
|
||||
return $.scrollBarSizeValue;
|
||||
};
|
||||
|
||||
function getScrollBarSize() {
|
||||
// assume scrollbar width and height would be the same
|
||||
|
||||
// Create the measurement node
|
||||
const scrollDiv = document.createElement('div');
|
||||
$.style(scrollDiv, {
|
||||
@ -199,4 +212,24 @@ $.scrollbarWidth = function scrollbarWidth() {
|
||||
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;
|
||||
};
|
||||
|
||||
209
src/filterRows.js
Normal file
209
src/filterRows.js
Normal file
@ -0,0 +1,209 @@
|
||||
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()
|
||||
};
|
||||
}
|
||||
10
src/icons.js
Normal file
10
src/icons.js
Normal file
@ -0,0 +1,10 @@
|
||||
/* 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;
|
||||
@ -3,7 +3,9 @@ import {
|
||||
makeDataAttributeString,
|
||||
nextTick,
|
||||
ensureArray,
|
||||
linkProperties
|
||||
linkProperties,
|
||||
uniq,
|
||||
numberSortAsc
|
||||
} from './utils';
|
||||
|
||||
export default class RowManager {
|
||||
@ -14,7 +16,8 @@ export default class RowManager {
|
||||
'fireEvent',
|
||||
'wrapper',
|
||||
'bodyScrollable',
|
||||
'bodyRenderer'
|
||||
'bodyRenderer',
|
||||
'style'
|
||||
]);
|
||||
|
||||
this.bindEvents();
|
||||
@ -39,7 +42,7 @@ export default class RowManager {
|
||||
// map of checked rows
|
||||
this.checkMap = [];
|
||||
|
||||
$.on(this.wrapper, 'click', '.dt-cell[data-col-index="0"] [type="checkbox"]', (e, $checkbox) => {
|
||||
$.on(this.wrapper, 'click', '.dt-cell--col-0 [type="checkbox"]', (e, $checkbox) => {
|
||||
const $cell = $checkbox.closest('.dt-cell');
|
||||
const {
|
||||
rowIndex,
|
||||
@ -64,7 +67,7 @@ export default class RowManager {
|
||||
const _row = this.datamanager.updateRow(row, rowIndex);
|
||||
|
||||
_row.forEach(cell => {
|
||||
this.cellmanager.refreshCell(cell);
|
||||
this.cellmanager.refreshCell(cell, true);
|
||||
});
|
||||
}
|
||||
|
||||
@ -91,8 +94,7 @@ export default class RowManager {
|
||||
|
||||
checkRow(rowIndex, toggle) {
|
||||
const value = toggle ? 1 : 0;
|
||||
const selector = rowIndex =>
|
||||
`.dt-cell[data-row-index="${rowIndex}"][data-col-index="0"] [type="checkbox"]`;
|
||||
const selector = rowIndex => `.dt-cell--0-${rowIndex} [type="checkbox"]`;
|
||||
// update internal map
|
||||
this.checkMap[rowIndex] = value;
|
||||
// set checkbox value explicitly
|
||||
@ -111,18 +113,25 @@ 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);
|
||||
}
|
||||
} else {
|
||||
this.checkMap = [];
|
||||
}
|
||||
// set checkbox value
|
||||
$.each('.dt-cell[data-col-index="0"] [type="checkbox"]', this.bodyScrollable)
|
||||
$.each('.dt-cell--col-0 [type="checkbox"]', this.bodyScrollable)
|
||||
.map(input => {
|
||||
input.checked = toggle;
|
||||
});
|
||||
// highlight all
|
||||
this.highlightAll(toggle);
|
||||
this.showCheckStatus();
|
||||
this.fireEvent('onCheckRow');
|
||||
}
|
||||
|
||||
showCheckStatus() {
|
||||
@ -130,7 +139,10 @@ export default class RowManager {
|
||||
const checkedRows = this.getCheckedRows();
|
||||
const count = checkedRows.length;
|
||||
if (count > 0) {
|
||||
this.bodyRenderer.showToastMessage(`${count} row${count > 1 ? 's' : ''} selected`);
|
||||
let message = this.instance.translate('{count} rows selected', {
|
||||
count: count
|
||||
});
|
||||
this.bodyRenderer.showToastMessage(message);
|
||||
} else {
|
||||
this.bodyRenderer.clearToastMessage();
|
||||
}
|
||||
@ -173,42 +185,104 @@ 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);
|
||||
rowIndices.map(rowIndex => {
|
||||
const $tr = this.getRow$(rowIndex);
|
||||
$tr.classList.remove('dt-row--hide');
|
||||
});
|
||||
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);
|
||||
}
|
||||
|
||||
openSingleNode(rowIndex) {
|
||||
const rowsToShow = this.datamanager.getImmediateChildren(rowIndex);
|
||||
const childrenToShow = this.getChildrenToShowForNode(rowIndex);
|
||||
const visibleRowIndices = this.bodyRenderer.visibleRowIndices;
|
||||
const rowsToShow = uniq([...childrenToShow, ...visibleRowIndices]).sort(numberSortAsc);
|
||||
|
||||
this.showRows(rowsToShow);
|
||||
this.cellmanager.toggleTreeButton(rowIndex, true);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
closeSingleNode(rowIndex) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
});
|
||||
this.cellmanager.toggleTreeButton(rowIndex, false);
|
||||
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);
|
||||
}
|
||||
|
||||
getRow$(rowIndex) {
|
||||
@ -255,31 +329,41 @@ 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
|
||||
colIndex: cell.colIndex,
|
||||
name: cell.name
|
||||
}),
|
||||
isFilter: 1,
|
||||
isHeader: undefined,
|
||||
editable: false
|
||||
})));
|
||||
|
||||
rowIdentifier = 'filter';
|
||||
}
|
||||
|
||||
if (props.isHeader) {
|
||||
rowIdentifier = 'header';
|
||||
}
|
||||
|
||||
return `
|
||||
<tr class="dt-row" ${dataAttr}>
|
||||
<div class="dt-row dt-row-${rowIdentifier}" ${dataAttr}>
|
||||
${row.map(cell => this.cellmanager.getCellHTML(cell)).join('')}
|
||||
</tr>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
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} />`;
|
||||
return `<input class="dt-filter dt-input" type="text" ${dataAttr} tabindex="1"
|
||||
${props.colIndex === 0 ? 'disabled' : title} />`;
|
||||
}
|
||||
|
||||
selector(rowIndex) {
|
||||
return `.dt-row[data-row-index="${rowIndex}"]`;
|
||||
return `.dt-row-${rowIndex}`;
|
||||
}
|
||||
}
|
||||
|
||||
195
src/style.css
195
src/style.css
@ -1,20 +1,21 @@
|
||||
:root {
|
||||
--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;
|
||||
--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);
|
||||
}
|
||||
|
||||
.datatable {
|
||||
@ -25,65 +26,78 @@
|
||||
|
||||
.datatable {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
overflow: hidden;
|
||||
.dt-cell--col-0{
|
||||
border-left: 1px 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);
|
||||
border-bottom: 2px solid var(--dt-border-color);
|
||||
}
|
||||
|
||||
.dt-body {
|
||||
border-collapse: collapse;
|
||||
.datatable-content{
|
||||
.dt-header{
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.dt-scrollable {
|
||||
max-height: 40vw;
|
||||
overflow: auto;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
height: 40vw;
|
||||
|
||||
&--highlight-all {
|
||||
background-color: var(--light-yellow);
|
||||
background-color: var(--dt-selection-highlight-color);
|
||||
}
|
||||
|
||||
&__no-data {
|
||||
text-align: center;
|
||||
padding: var(--spacer-3);
|
||||
border-left: 1px solid var(--border-color);
|
||||
border-right: 1px solid var(--border-color);
|
||||
padding: var(--dt-spacer-3);
|
||||
border-left: 1px solid var(--dt-border-color);
|
||||
border-right: 1px solid var(--dt-border-color);
|
||||
}
|
||||
}
|
||||
|
||||
.dt-row {
|
||||
&--highlight {
|
||||
background-color: var(--light-yellow);
|
||||
display: flex;
|
||||
|
||||
&--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);
|
||||
}
|
||||
}
|
||||
|
||||
.dt-cell {
|
||||
border: 1px solid var(--border-color);
|
||||
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;
|
||||
|
||||
&__content {
|
||||
padding: var(--spacer-2);
|
||||
border: 2px solid transparent;
|
||||
padding: var(--dt-spacer-2);
|
||||
border: var(--dt-focus-border-width) solid transparent;
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
@ -91,9 +105,9 @@
|
||||
|
||||
&__edit {
|
||||
display: none;
|
||||
padding: var(--spacer-2);
|
||||
background-color: var(--cell-bg);
|
||||
border: 2px solid var(--orange);
|
||||
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%;
|
||||
}
|
||||
@ -118,19 +132,27 @@
|
||||
}
|
||||
|
||||
&--focus &__content {
|
||||
border-color: var(--primary-color);
|
||||
border-color: var(--dt-primary-color);
|
||||
}
|
||||
|
||||
&--highlight {
|
||||
background-color: var(--light-bg);
|
||||
background-color: var(--dt-light-bg);
|
||||
}
|
||||
|
||||
&--dragging {
|
||||
background-color: var(--light-bg);
|
||||
background-color: var(--dt-light-bg);
|
||||
}
|
||||
|
||||
&--header {
|
||||
background-color: var(--dt-header-cell-bg);
|
||||
}
|
||||
|
||||
&--header:last-child {
|
||||
border-right: 1px solid var(--dt-border-color);
|
||||
}
|
||||
|
||||
&--header &__content {
|
||||
padding-right: var(--spacer-3);
|
||||
padding-right: var(--dt-spacer-3);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@ -139,10 +161,36 @@
|
||||
}
|
||||
|
||||
&--tree-close {
|
||||
.dt-tree-node__toggle:before {
|
||||
content: '►';
|
||||
.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;
|
||||
}
|
||||
|
||||
.dt-dropdown {
|
||||
@ -156,27 +204,25 @@
|
||||
|
||||
&__toggle {
|
||||
opacity: 0;
|
||||
background-color: var(--dt-header-cell-bg);
|
||||
}
|
||||
|
||||
&__list {
|
||||
display: none;
|
||||
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
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;
|
||||
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-item {
|
||||
padding: var(--spacer-2) var(--spacer-3);
|
||||
padding: var(--dt-spacer-2) var(--dt-spacer-3);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--light-bg);
|
||||
background-color: var(--dt-light-bg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,34 +232,30 @@
|
||||
}
|
||||
|
||||
.dt-tree-node {
|
||||
display: inline-block;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
&__toggle {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
font-size: 10px;
|
||||
padding: 0 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__toggle:before {
|
||||
content: '▼';
|
||||
margin-right: 0.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.dt-toast {
|
||||
position: absolute;
|
||||
bottom: var(--spacer-3);
|
||||
bottom: var(--dt-spacer-3);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
&__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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,6 +267,8 @@
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
@ -238,7 +282,7 @@
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: var(--light-bg);
|
||||
background-color: var(--dt-light-bg);
|
||||
opacity: 0.5;
|
||||
font-size: 2em;
|
||||
|
||||
@ -257,3 +301,8 @@
|
||||
body.dt-resize {
|
||||
cursor: col-resize;
|
||||
}
|
||||
.dt-sticky-col {
|
||||
position: sticky;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
}
|
||||
323
src/style.js
323
src/style.js
@ -11,8 +11,8 @@ export default class Style {
|
||||
|
||||
linkProperties(this, this.instance, [
|
||||
'options', 'datamanager', 'columnmanager',
|
||||
'header', 'bodyScrollable', 'datatableWrapper',
|
||||
'getColumn'
|
||||
'header', 'footer', 'bodyScrollable', 'datatableWrapper',
|
||||
'getColumn', 'bodyRenderer'
|
||||
]);
|
||||
|
||||
this.scopeClass = 'dt-instance-' + instance.constructor.instances;
|
||||
@ -23,6 +23,7 @@ export default class Style {
|
||||
this.styleEl = styleEl;
|
||||
|
||||
this.bindResizeWindow();
|
||||
this.bindScrollHeader();
|
||||
}
|
||||
|
||||
get stylesheet() {
|
||||
@ -30,78 +31,165 @@ 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', throttle(() => {
|
||||
$.on(window, 'resize', this.onWindowResize);
|
||||
}
|
||||
}
|
||||
|
||||
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.compensateScrollbarWidth();
|
||||
this.setBodyStyle();
|
||||
}, 300));
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.styleEl.remove();
|
||||
$.off(window, 'resize', this.onWindowResize);
|
||||
}
|
||||
|
||||
setStyle(selector, styleMap, index = -1) {
|
||||
const styles = Object.keys(styleMap)
|
||||
.map(prop => {
|
||||
if (!prop.includes('-')) {
|
||||
prop = camelCaseToDash(prop);
|
||||
setStyle(selector, styleObject) {
|
||||
if (selector.includes(',')) {
|
||||
selector.split(',')
|
||||
.map(s => s.trim())
|
||||
.forEach(selector => {
|
||||
this.setStyle(selector, styleObject);
|
||||
});
|
||||
return;
|
||||
}
|
||||
return `${prop}:${styleMap[prop]};`;
|
||||
|
||||
selector = selector.trim();
|
||||
if (!selector) return;
|
||||
|
||||
this._styleRulesMap = this._styleRulesMap || {};
|
||||
const prefixedSelector = this._getPrefixedSelector(selector);
|
||||
|
||||
if (this._styleRulesMap[prefixedSelector]) {
|
||||
this.removeStyle(selector);
|
||||
|
||||
// merge with old styleobject
|
||||
styleObject = Object.assign({}, this._styleRulesMap[prefixedSelector], styleObject);
|
||||
}
|
||||
|
||||
const styleString = this._getRuleString(styleObject);
|
||||
const ruleString = `${prefixedSelector} { ${styleString} }`;
|
||||
|
||||
this._styleRulesMap[prefixedSelector] = styleObject;
|
||||
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}`;
|
||||
}
|
||||
|
||||
_getRuleString(styleObject) {
|
||||
return Object.keys(styleObject)
|
||||
.map(prop => {
|
||||
let dashed = prop;
|
||||
if (!prop.includes('-')) {
|
||||
dashed = camelCaseToDash(prop);
|
||||
}
|
||||
return `${dashed}:${styleObject[prop]};`;
|
||||
})
|
||||
.join('');
|
||||
let prefixedSelector = selector
|
||||
.split(',')
|
||||
.map(r => `.${this.scopeClass} ${r}`)
|
||||
.join(',');
|
||||
|
||||
let ruleString = `${prefixedSelector} { ${styles} }`;
|
||||
|
||||
if (!this.stylesheet) return;
|
||||
|
||||
let _index = this.stylesheet.cssRules.length;
|
||||
if (index !== -1) {
|
||||
this.stylesheet.deleteRule(index);
|
||||
_index = index;
|
||||
}
|
||||
|
||||
this.stylesheet.insertRule(ruleString, _index);
|
||||
return _index; // eslint-disable-line
|
||||
}
|
||||
|
||||
setDimensions() {
|
||||
this.setHeaderStyle();
|
||||
|
||||
this.setCellHeight();
|
||||
this.setupMinWidth();
|
||||
this.setupNaturalColumnWidth();
|
||||
this.setupColumnWidth();
|
||||
this.distributeRemainingWidth();
|
||||
this.setColumnStyle();
|
||||
this.compensateScrollbarWidth();
|
||||
|
||||
this.setDefaultCellHeight();
|
||||
this.setBodyStyle();
|
||||
}
|
||||
|
||||
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
|
||||
setCellHeight() {
|
||||
this.setStyle('.dt-cell', {
|
||||
height: this.options.cellHeight + 'px'
|
||||
});
|
||||
}
|
||||
|
||||
setupMinWidth() {
|
||||
$.each('.dt-cell[data-is-header]', this.header).map(col => {
|
||||
$.each('.dt-cell--header', this.header).map(col => {
|
||||
const { colIndex } = $.data(col);
|
||||
const column = this.getColumn(colIndex);
|
||||
|
||||
@ -116,8 +204,19 @@ 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[data-row-index="0"] .dt-cell', this.bodyScrollable).map($cell => {
|
||||
$.each('.dt-row-0 .dt-cell', this.bodyScrollable).map($cell => {
|
||||
const {
|
||||
colIndex
|
||||
} = $.data($cell);
|
||||
@ -125,12 +224,11 @@ export default class Style {
|
||||
|
||||
let naturalWidth = $.style($('.dt-cell__content', $cell), 'width');
|
||||
|
||||
if (column.id === '_rowIndex') {
|
||||
naturalWidth = this.getRowIndexColumnWidth(naturalWidth);
|
||||
column.width = naturalWidth;
|
||||
}
|
||||
|
||||
if (typeof naturalWidth === 'number' && naturalWidth >= column.naturalWidth) {
|
||||
column.naturalWidth = naturalWidth;
|
||||
} else {
|
||||
column.naturalWidth = column.naturalWidth;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -174,29 +272,32 @@ export default class Style {
|
||||
if (!column.width) {
|
||||
column.width = column.naturalWidth;
|
||||
}
|
||||
if (column.width < column.minWidth) {
|
||||
column.width = column.minWidth;
|
||||
if (column.id === '_rowIndex') {
|
||||
column.width = this.getRowIndexColumnWidth();
|
||||
}
|
||||
if (column.width < this.options.minimumColumnWidth) {
|
||||
column.width = this.options.minimumColumnWidth;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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');
|
||||
const headerWidth = $.style(this.header, '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 resizableColumns = this.datamanager.getColumns().filter(col => col.resizable);
|
||||
const deltaWidth = (wrapperWidth - headerWidth) / resizableColumns.length;
|
||||
const deltaWidth = (wrapperWidth - firstRowWidth) / resizableColumns.length;
|
||||
|
||||
resizableColumns.map(col => {
|
||||
const width = $.style(this.getColumnHeaderElement(col.colIndex), 'width');
|
||||
@ -208,40 +309,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()
|
||||
.map(column => {
|
||||
// alignment
|
||||
if (['left', 'center', 'right'].includes(column.align)) {
|
||||
if (!column.align) {
|
||||
column.align = 'left';
|
||||
}
|
||||
if (!['left', 'center', 'right'].includes(column.align)) {
|
||||
column.align = 'left';
|
||||
}
|
||||
this.setStyle(`.dt-cell--col-${column.colIndex}`, {
|
||||
'text-align': column.align
|
||||
});
|
||||
}
|
||||
|
||||
// width
|
||||
this.columnmanager.setColumnHeaderWidth(column.colIndex);
|
||||
this.columnmanager.setColumnWidth(column.colIndex);
|
||||
});
|
||||
this.setBodyStyle();
|
||||
}
|
||||
|
||||
refreshColumnWidth() {
|
||||
@ -253,48 +339,67 @@ export default class Style {
|
||||
}
|
||||
|
||||
setBodyStyle() {
|
||||
requestAnimationFrame(() => {
|
||||
const width = $.style(this.header, 'width');
|
||||
const bodyWidth = $.style(this.datatableWrapper, 'width');
|
||||
const firstRow = $('.dt-row', this.bodyScrollable);
|
||||
if (!firstRow) return;
|
||||
const rowWidth = $.style(firstRow, 'width');
|
||||
|
||||
let width = bodyWidth > rowWidth ? rowWidth + 10 : bodyWidth;
|
||||
$.style(this.bodyScrollable, {
|
||||
width: width + 'px'
|
||||
});
|
||||
|
||||
const $body = $('.dt-body', this.bodyScrollable);
|
||||
// remove the body height, so that it resets to it's original
|
||||
$.removeStyle(this.bodyScrollable, 'height');
|
||||
|
||||
if ($body) {
|
||||
$.style($body, {
|
||||
height: '0px'
|
||||
});
|
||||
// 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();
|
||||
}
|
||||
|
||||
$.style(this.bodyScrollable, {
|
||||
marginTop: $.style(this.header, 'height') + 'px'
|
||||
height: height + 'px'
|
||||
});
|
||||
}
|
||||
|
||||
$.style($('table', this.bodyScrollable), {
|
||||
margin: 0,
|
||||
width: '100%'
|
||||
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'
|
||||
});
|
||||
}
|
||||
|
||||
if (this.options.layout === 'fluid') {
|
||||
$.style(this.bodyScrollable, {
|
||||
overflowX: 'hidden'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getColumnHeaderElement(colIndex) {
|
||||
colIndex = +colIndex;
|
||||
if (colIndex < 0) return null;
|
||||
return $(`.dt-cell[data-col-index="${colIndex}"]`, this.header);
|
||||
return $(`.dt-cell--col-${colIndex}`, this.header);
|
||||
}
|
||||
|
||||
getRowIndexColumnWidth(baseWidth) {
|
||||
this._rowIndexColumnWidthMap = this._rowIndexColumnWidthMap || {};
|
||||
getRowIndexColumnWidth() {
|
||||
const rowCount = this.datamanager.getRowCount();
|
||||
const digits = (rowCount + '').length;
|
||||
|
||||
if (!this._rowIndexColumnWidthMap[digits]) {
|
||||
// add 8px for each unit
|
||||
this._rowIndexColumnWidthMap[digits] = baseWidth + ((digits - 1) * 8);
|
||||
}
|
||||
|
||||
return this._rowIndexColumnWidthMap[digits];
|
||||
const padding = 22;
|
||||
return $.measureTextWidth(rowCount + '') + padding;
|
||||
}
|
||||
}
|
||||
|
||||
30
src/translationmanager.js
Normal file
30
src/translationmanager.js
Normal file
@ -0,0 +1,30 @@
|
||||
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'];
|
||||
}
|
||||
};
|
||||
15
src/translations/de.json
Normal file
15
src/translations/de.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
15
src/translations/en.json
Normal file
15
src/translations/en.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
15
src/translations/fr.json
Normal file
15
src/translations/fr.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
13
src/translations/index.js
Normal file
13
src/translations/index.js
Normal file
@ -0,0 +1,13 @@
|
||||
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,
|
||||
};
|
||||
};
|
||||
15
src/translations/it.json
Normal file
15
src/translations/it.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
40
src/utils.js
40
src/utils.js
@ -1,5 +1,6 @@
|
||||
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()}`);
|
||||
@ -125,3 +126,42 @@ 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 = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
'/': '/',
|
||||
'`': '`',
|
||||
'=': '=',
|
||||
};
|
||||
|
||||
return String(txt).replace(/[&<>"'`=/]/g, (char) => escapeHtmlMapping[char] || char);
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user