Compare commits
2 Commits
master
...
feat-group
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da0d731670 | ||
|
|
c49bc20f99 |
@ -35,6 +35,7 @@
|
||||
"block-scoped-var": 2,
|
||||
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
|
||||
"camelcase": [2, { "properties": "always" }],
|
||||
"comma-dangle": [2, "never"],
|
||||
"comma-spacing": [2, { "before": false, "after": true }],
|
||||
"comma-style": [2, "last"],
|
||||
"complexity": 0,
|
||||
|
||||
24
.github/workflows/test-and-release.yml
vendored
24
.github/workflows/test-and-release.yml
vendored
@ -1,24 +0,0 @@
|
||||
name: Test and Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
test-and-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Lint, build and test
|
||||
uses: cypress-io/github-action@v2
|
||||
with:
|
||||
build: yarn lint-and-build
|
||||
start: yarn cy:server
|
||||
record: true
|
||||
env:
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Release
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: yarn semantic-release
|
||||
16
.github/workflows/test-pull-request.yml
vendored
16
.github/workflows/test-pull-request.yml
vendored
@ -1,16 +0,0 @@
|
||||
name: Test Pull Request
|
||||
on: pull_request
|
||||
jobs:
|
||||
test-pull-request:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Lint, build and test
|
||||
uses: cypress-io/github-action@v2
|
||||
with:
|
||||
build: yarn lint-and-build
|
||||
start: yarn cy:server
|
||||
record: true
|
||||
env:
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@ -37,9 +37,4 @@ npm-debug.log.*
|
||||
|
||||
# cypress
|
||||
cypress/screenshots
|
||||
cypress/videos
|
||||
|
||||
# dist
|
||||
dist
|
||||
|
||||
.env
|
||||
cypress/videos
|
||||
12
.travis.yml
Normal file
12
.travis.yml
Normal file
@ -0,0 +1,12 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 8
|
||||
cache:
|
||||
directories:
|
||||
- ~/.npm
|
||||
- ~/.cache
|
||||
- node_modules
|
||||
install:
|
||||
- npm install
|
||||
script:
|
||||
- npm test
|
||||
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2018 Frappe Technologies Pvt. Ltd. <developers@frappe.io>
|
||||
Copyright (c) 2018 Frappé Technologies Pvt. Ltd. <developers@frappe.io>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
83
README.md
83
README.md
@ -1,37 +1,54 @@
|
||||
<div align="center" markdown="1">
|
||||
<div align="center">
|
||||
<img src="https://github.com/frappe/design/blob/master/logos/data-table-logo.svg" height="128">
|
||||
<h2>Frappe DataTable</h2>
|
||||
<p align="center">
|
||||
<p>
|
||||
A modern datatable library for the web
|
||||
</p>
|
||||
|
||||
<img width="80" alt="datatable-logo" src="https://github.com/user-attachments/assets/8235f4b9-993a-4329-97de-9431dcf63aae" >
|
||||
|
||||
<h1>Frappe DataTable</h1>
|
||||
|
||||
**A modern datatable library for the web**
|
||||
|
||||
[](https://github.com/frappe/datatable/actions?query=workflow%3A%22Test+and+Release%22)
|
||||
[](https://travis-ci.org/frappe/datatable)
|
||||
[](https://badge.fury.io/js/frappe-datatable)
|
||||

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

|
||||
[](http://opensource.org/licenses/MIT)
|
||||
|
||||
</div>
|
||||
|
||||
## Frappe Datatable
|
||||
## Introduction
|
||||
|
||||
Frappe DataTable is a simple, modern and interactive datatable library for displaying tabular data. Originally built for [ERPNext](https://github.com/frappe/erpnext), it can be used to render large amount of rows without sacrificing performance and has the basic data grid features like inline editing and keyboard navigation. It does not require jQuery, unlike most data grids out there.
|
||||
Frappe DataTable is a simple, modern and interactive datatable library for displaying tabular data. Originally built for [ERPNext](https://github.com/frappe/erpnext), it can be used to render large amount of rows without sacrificing performance and has the basic data grid features like inline editing and keyboard navigation. It does not require jQuery, like most data grids out there.
|
||||
|
||||
### Motivation
|
||||
## Demo
|
||||
|
||||
I was trying to remove all legacy UI components from the [frappe](https://github.com/frappe/frappe) codebase. We were using [SlickGrid](https://github.com/mleibman/SlickGrid) for rendering tables. It was unmaintained and UI was dated. Other datatable solutions either didn't have the features we needed or were closed source. So we built our own.
|
||||

|
||||
|
||||
## Features
|
||||
|
||||
### Key Features
|
||||
### Cell Features
|
||||
|
||||
- **Cell**: Enable editing within individual cells and features like custom formatters, inline editing, and mouse selection. Users can easily copy cell content, navigate through cells using the keyboard, and take advantage of a custom cell editor for advanced functionality.
|
||||
- **Column**: Columns are highly flexible, allowing users to reorder, resize, and sort them with ease. Additional features include hiding/removing columns and adding custom actions.
|
||||
- **Row**: Rows support advanced interactions, including row selection, tree-structured organization, and inline filters for precise control. They handle large datasets efficiently with dynamic row heights.
|
||||
* Custom Formatters
|
||||
* Inline Editing
|
||||
* Mouse Selection
|
||||
* Copy Cells
|
||||
* Keyboard Navigation
|
||||
* Custom Cell Editor
|
||||
|
||||
### Column Features
|
||||
|
||||
## Usage
|
||||
* Reorder Columns
|
||||
* Sort by Column
|
||||
* Remove / Hide Column
|
||||
* Custom Actions
|
||||
* Resize Column
|
||||
* Flexible Layout
|
||||
|
||||
### Row Features
|
||||
|
||||
* Row Selection
|
||||
* Tree Structured Rows
|
||||
* Inline Filters
|
||||
* Large Number of Rows
|
||||
* Dynamic Row Height
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
yarn add frappe-datatable
|
||||
@ -39,8 +56,9 @@ yarn add frappe-datatable
|
||||
npm install frappe-datatable
|
||||
```
|
||||
|
||||
> Note: [`sortablejs`](https://github.com/RubaXa/Sortable) is required to be installed as well.
|
||||
> Note: [`sortablejs`](https://github.com/RubaXa/Sortable) and [`clusterize.js`](https://github.com/NeXTs/Clusterize.js) are required to be installed as well.
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
const datatable = new DataTable('#datatable', {
|
||||
@ -52,24 +70,13 @@ const datatable = new DataTable('#datatable', {
|
||||
});
|
||||
```
|
||||
|
||||
## Development Setup
|
||||
## Contribution
|
||||
|
||||
* `yarn start` - Start dev server
|
||||
* Open `index.html` located in the root folder, and start development.
|
||||
* Run `yarn lint` before committing changes
|
||||
* This project uses [commitizen](https://github.com/commitizen/cz-cli) for conventional commit messages, use `yarn commit` command instead of `git commit`
|
||||
|
||||
## Links
|
||||
Now you can open the `index.html` located in the root folder, and start development. Make sure to fix all eslint errors before making a Pull Request.
|
||||
|
||||
- [Making a new datatable for the web](https://medium.com/frapp%C3%A9-thoughts/things-i-learned-building-a-library-for-the-web-6846a588bf53)
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<div align="center" style="padding-top: 0.75rem;">
|
||||
<a href="https://frappe.io" target="_blank">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://frappe.io/files/Frappe-white.png">
|
||||
<img src="https://frappe.io/files/Frappe-black.png" alt="Frappe Technologies" height="28"/>
|
||||
</picture>
|
||||
</a>
|
||||
</div>
|
||||
## License
|
||||
|
||||
[MIT](http://opensource.org/licenses/MIT)
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:8989",
|
||||
"projectId": "2nsyux"
|
||||
"baseUrl": "http://localhost:8989"
|
||||
}
|
||||
|
||||
@ -1,84 +0,0 @@
|
||||
describe('Cell', function () {
|
||||
before(function () {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
it('focuses cell on click', function () {
|
||||
cy.clickCell(2, 0)
|
||||
.should('have.class', 'dt-cell--focus');
|
||||
});
|
||||
|
||||
it('not focuses cell which are not focusable', function () {
|
||||
cy.clickCell(1, 0)
|
||||
.should('not.have.class', 'dt-cell--focus');
|
||||
});
|
||||
|
||||
it('edit cell on enter press', function () {
|
||||
cy.getCell(4, 0).type('{enter}')
|
||||
.should('have.class', 'dt-cell--editing')
|
||||
.type('{enter}')
|
||||
.should('not.have.class', 'dt-cell--editing');
|
||||
});
|
||||
|
||||
it('edit cell on double click', function () {
|
||||
cy.getCell(4, 0)
|
||||
.as('target')
|
||||
.dblclick({ force: true })
|
||||
.should('have.class', 'dt-cell--editing');
|
||||
|
||||
cy.clickCell(3, 0);
|
||||
|
||||
cy.get('@target').should('not.have.class', 'dt-cell--editing');
|
||||
});
|
||||
|
||||
it('edit cell', function () {
|
||||
cy.getCell(4, 1).dblclick({ force: true });
|
||||
cy.getCell(4, 1).find('input').click();
|
||||
cy.focused().type('{selectall}{del}Test{enter}');
|
||||
cy.getCell(4, 1).contains('Test');
|
||||
});
|
||||
|
||||
it('if editing is false: editing should not activate', function () {
|
||||
cy.getCell(3, 0).dblclick({ force: true })
|
||||
.should('not.have.class', 'dt-cell--editing');
|
||||
});
|
||||
|
||||
it('navigation using arrow keys', function () {
|
||||
cy.clickCell(2, 0)
|
||||
.type('{rightarrow}');
|
||||
|
||||
cy.get('.dt-cell--focus')
|
||||
.should('have.class', 'dt-cell--3-0')
|
||||
.click({ force: true })
|
||||
.type('{downarrow}');
|
||||
|
||||
cy.get('.dt-cell--focus')
|
||||
.should('have.class', 'dt-cell--3-1');
|
||||
// TODO: test navigation over hidden rows
|
||||
});
|
||||
|
||||
it('navigation using ctrl + arrow keys', function () {
|
||||
cy.clickCell(2, 0)
|
||||
.type('{ctrl}{rightarrow}');
|
||||
cy.get('.dt-cell--focus')
|
||||
.should('have.class', 'dt-cell--11-0');
|
||||
});
|
||||
|
||||
it('cell selection using shift + arrow keys', function () {
|
||||
cy.getCell(2, 1)
|
||||
.type('{shift}{rightarrow}{rightarrow}{downarrow}');
|
||||
|
||||
// 6 cells and 2 headers
|
||||
cy.get('.dt-cell--highlight').should('have.length', 6 + 2);
|
||||
|
||||
cy.clickCell(2, 0);
|
||||
});
|
||||
|
||||
it('mouse selection', function () {
|
||||
// TODO:
|
||||
// cy.getCell(2, 1)
|
||||
// .trigger('mousedown', { which: 1, pageX: 331, pageY: 207, force: true })
|
||||
// .trigger('mousemove', { which: 1, pageX: 489, pageY: 312 })
|
||||
// .trigger('mouseup');
|
||||
});
|
||||
});
|
||||
@ -1,70 +0,0 @@
|
||||
describe('Column', function () {
|
||||
before(function () {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
it('header dropdown toggles on click', function () {
|
||||
cy.getColumnCell(2)
|
||||
.find('.dt-dropdown__toggle')
|
||||
.as('toggle')
|
||||
.click();
|
||||
cy.get('.dt-dropdown__list')
|
||||
.as('dropdown-list')
|
||||
.should('be.visible');
|
||||
|
||||
cy.getColumnCell(2).click();
|
||||
|
||||
cy.get('@dropdown-list').should('not.be.visible');
|
||||
});
|
||||
|
||||
it('sort ascending button should work', function () {
|
||||
cy.clickDropdown(2);
|
||||
cy.clickDropdownItem(2, 'Sort Ascending');
|
||||
|
||||
cy.window().then(win => win.datatable.getColumn(2))
|
||||
.its('sortOrder')
|
||||
.should('eq', 'asc');
|
||||
|
||||
cy.window().then(win => win.datatable.datamanager)
|
||||
.its('currentSort.colIndex')
|
||||
.should('eq', 2);
|
||||
|
||||
cy.get('.dt-scrollable .dt-row:first div:nth-of-type(3)')
|
||||
.contains('Airi Satou');
|
||||
|
||||
cy.clickDropdownItem(2, 'Reset sorting');
|
||||
});
|
||||
|
||||
it('removes column using dropdown action', function () {
|
||||
cy.get('.dt-cell--header').should('have.length', 12);
|
||||
|
||||
cy.clickDropdown(5);
|
||||
cy.clickDropdownItem(5, 'Remove column');
|
||||
|
||||
cy.get('.dt-cell--header').should('have.length', 11);
|
||||
});
|
||||
|
||||
it('resize column with mouse drag', function () {
|
||||
cy.get('.dt-cell--header-4 .dt-cell__resize-handle').as('resize-handle');
|
||||
cy
|
||||
.get('@resize-handle')
|
||||
.trigger('mousedown')
|
||||
.trigger('mousemove', { pageX: 700, pageY: 20, which: 1 })
|
||||
.trigger('mouseup');
|
||||
|
||||
cy.getColumnCell(4).invoke('css', 'width').then((width) => {
|
||||
cy.getColumnCell(4)
|
||||
.should('have.css', 'width', width);
|
||||
cy.getCell(4, 1)
|
||||
.should('have.css', 'width', width);
|
||||
});
|
||||
});
|
||||
|
||||
it('resize column using double click', function () {
|
||||
cy.get('.dt-cell--header-4 .dt-cell__resize-handle').trigger('dblclick');
|
||||
cy.getColumnCell(4).should('have.css', 'width')
|
||||
.and('match', /9\dpx/);
|
||||
cy.getCell(4, 1).should('have.css', 'width')
|
||||
.and('match', /9\dpx/);
|
||||
});
|
||||
});
|
||||
165
cypress/integration/datatable_spec.js
Normal file
165
cypress/integration/datatable_spec.js
Normal file
@ -0,0 +1,165 @@
|
||||
describe('DataTable', function () {
|
||||
it('instance is created without any errors', () => {
|
||||
cy.visit('/');
|
||||
|
||||
cy.window()
|
||||
.its('DataTable')
|
||||
.then(DataTable => {
|
||||
// eslint-disable-next-line
|
||||
new DataTable('#datatable2', {
|
||||
columns: ['Name', 'Position'],
|
||||
data: [
|
||||
['Faris', 'Developer']
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
cy.get('#datatable2 .datatable')
|
||||
.contains('Faris');
|
||||
});
|
||||
|
||||
describe('Cell', function () {
|
||||
before(function () {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
it('focuses cell on click', function () {
|
||||
cy.clickCell(2, 0)
|
||||
.should('have.class', 'dt-cell--focus');
|
||||
});
|
||||
|
||||
it('not focuses cell which are not focusable', function () {
|
||||
cy.clickCell(1, 0)
|
||||
.should('not.have.class', 'dt-cell--focus');
|
||||
});
|
||||
|
||||
it('edit cell on enter press', function () {
|
||||
cy.getCell(4, 0).type('{enter}')
|
||||
.should('have.class', 'dt-cell--editing')
|
||||
.type('{enter}')
|
||||
.should('not.have.class', 'dt-cell--editing');
|
||||
});
|
||||
|
||||
it('edit cell on double click', function () {
|
||||
cy.getCell(4, 0)
|
||||
.as('target')
|
||||
.dblclick({ force: true })
|
||||
.should('have.class', 'dt-cell--editing');
|
||||
|
||||
cy.clickCell(3, 0);
|
||||
|
||||
cy.get('@target').should('not.have.class', 'dt-cell--editing');
|
||||
});
|
||||
|
||||
it('edit cell', function () {
|
||||
cy.getCell(4, 1).dblclick();
|
||||
cy.getCell(4, 1).find('input').click();
|
||||
cy.focused().type('{selectall}{del}Test{enter}');
|
||||
cy.getCell(4, 1).contains('Test');
|
||||
});
|
||||
|
||||
it('if editing is false: editing should not activate', function () {
|
||||
cy.getCell(3, 0).dblclick({ force: true })
|
||||
.should('not.have.class', 'dt-cell--editing');
|
||||
});
|
||||
|
||||
it('navigation using arrow keys', function () {
|
||||
cy.clickCell(2, 0)
|
||||
.type('{rightarrow}');
|
||||
|
||||
cy.get('.dt-cell--focus')
|
||||
.should('have.class', 'dt-cell--3-0')
|
||||
.click({ force: true })
|
||||
.type('{downarrow}');
|
||||
|
||||
cy.get('.dt-cell--focus')
|
||||
.should('have.class', 'dt-cell--3-1');
|
||||
// TODO: test navigation over hidden rows
|
||||
});
|
||||
|
||||
it('navigation using ctrl + arrow keys', function () {
|
||||
cy.clickCell(2, 0)
|
||||
.type('{ctrl}{rightarrow}');
|
||||
cy.get('.dt-cell--focus')
|
||||
.should('have.class', 'dt-cell--7-0');
|
||||
});
|
||||
|
||||
it('cell selection using shift + arrow keys', function () {
|
||||
cy.getCell(2, 1)
|
||||
.type('{shift}{rightarrow}{rightarrow}{downarrow}');
|
||||
|
||||
// 6 cells and 2 headers
|
||||
cy.get('.dt-cell--highlight').should('have.length', 6 + 2);
|
||||
|
||||
cy.clickCell(2, 0);
|
||||
});
|
||||
|
||||
it('mouse selection', function () {
|
||||
// TODO
|
||||
// cy.getCell(2, 1)
|
||||
// .trigger('mousedown', { which: 1, pageX: 331, pageY: 207, force: true })
|
||||
// .trigger('mousemove', { which: 1, pageX: 489, pageY: 312 })
|
||||
// .trigger('mouseup');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Column', function () {
|
||||
before(function () {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
it('header dropdown toggles on click', function () {
|
||||
cy.getColumnCell(2)
|
||||
.find('.dt-dropdown__toggle')
|
||||
.as('toggle')
|
||||
.click();
|
||||
cy.getColumnCell(2)
|
||||
.find('.dt-dropdown__list')
|
||||
.as('dropdown-list')
|
||||
.should('be.visible');
|
||||
|
||||
cy.get('@toggle').click();
|
||||
|
||||
cy.get('@dropdown-list').should('not.be.visible');
|
||||
});
|
||||
|
||||
it('sort ascending button should work', function () {
|
||||
cy.clickDropdown(2);
|
||||
cy.clickDropdownItem(2, 'Sort Ascending');
|
||||
|
||||
cy.window().then(win => win.datatable.getColumn(2))
|
||||
.its('sortOrder')
|
||||
.should('eq', 'asc');
|
||||
|
||||
cy.window().then(win => win.datatable.datamanager)
|
||||
.its('currentSort.colIndex')
|
||||
.should('eq', 2);
|
||||
|
||||
cy.get('.dt-body .dt-row:first')
|
||||
.contains('Airi Satou');
|
||||
|
||||
cy.clickDropdownItem(2, 'Reset sorting');
|
||||
});
|
||||
|
||||
it('removes column using dropdown action', function () {
|
||||
cy.get('.dt-cell--header').should('have.length', 8);
|
||||
|
||||
cy.clickDropdown(5);
|
||||
cy.clickDropdownItem(5, 'Remove column');
|
||||
|
||||
cy.get('.dt-cell--header').should('have.length', 7);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Row', function () {
|
||||
it('check / uncheck row', function () {
|
||||
cy.get('.dt-body .dt-row:first')
|
||||
.find('input[type="checkbox"]')
|
||||
.click();
|
||||
|
||||
cy.get('[data-row-index="0"]').should('have.class', 'dt-row--highlight');
|
||||
|
||||
cy.get('.dt-toast').contains('1 row selected');
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,89 +0,0 @@
|
||||
describe('Inline Filters', function () {
|
||||
before(function () {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
cy.get('.dt-filter[data-col-index=4]').as('filterInput4');
|
||||
cy.get('.dt-filter[data-col-index=5]').as('filterInput5');
|
||||
cy.get('.dt-filter[data-col-index=6]').as('filterInput6');
|
||||
cy.get('.dt-row[data-row-index=0]').should('be.visible');
|
||||
});
|
||||
|
||||
it('simple text filter', function () {
|
||||
cy.getCell(4, 0).click().type('{ctrl}f');
|
||||
|
||||
cy.get('@filterInput4').type('edin');
|
||||
cy.get('.dt-row-0').should('be.visible');
|
||||
cy.get('.dt-row-1').should('not.exist');
|
||||
cy.get('@filterInput4').clear();
|
||||
});
|
||||
|
||||
it('simple number filter', function () {
|
||||
cy.get('@filterInput5').type('2360');
|
||||
cy.get('.dt-row[data-row-index=8]').should('be.visible');
|
||||
cy.get('.dt-row[data-row-index=15]').should('not.exist');
|
||||
cy.get('.dt-row[data-row-index=22]').should('not.exist');
|
||||
cy.get('@filterInput5').clear();
|
||||
});
|
||||
|
||||
it('greater than', function () {
|
||||
cy.get('@filterInput5').type('> 6000');
|
||||
cy.get('.dt-row[data-row-index=0]').should('not.exist');
|
||||
cy.get('.dt-row[data-row-index=3]').should('be.visible');
|
||||
cy.get('@filterInput5').clear();
|
||||
});
|
||||
|
||||
it('less than', function () {
|
||||
cy.get('@filterInput5').type('< 2000');
|
||||
cy.get('.dt-row[data-row-index=0]').should('not.exist');
|
||||
cy.get('.dt-row[data-row-index=51]').should('be.visible');
|
||||
cy.get('@filterInput5').clear();
|
||||
});
|
||||
|
||||
it('range', function () {
|
||||
cy.get('@filterInput5').type(' 2000: 5000');
|
||||
cy.get('.dt-row[data-row-index=4]').should('not.exist');
|
||||
cy.get('.dt-row[data-row-index=5]').should('be.visible');
|
||||
cy.get('@filterInput5').clear();
|
||||
});
|
||||
|
||||
it('equals', function () {
|
||||
cy.get('@filterInput5').type('=9608');
|
||||
cy.get('.dt-row-6').should('be.visible');
|
||||
cy.get('@filterInput5').clear();
|
||||
});
|
||||
|
||||
it('multiple filters', function () {
|
||||
cy.get('@filterInput4').type('to');
|
||||
cy.get('@filterInput5').type('54');
|
||||
|
||||
cy.get('.dt-row[data-row-index=4]').should('be.visible');
|
||||
cy.get('.dt-row[data-row-index=1]').should('not.exist');
|
||||
cy.get('@filterInput4').clear();
|
||||
cy.get('@filterInput5').clear();
|
||||
});
|
||||
|
||||
it('greater than for string type filters', function () {
|
||||
cy.get('@filterInput6').type('> 01/07/2011');
|
||||
cy.get('.dt-row[data-row-index=0]').should('not.exist');
|
||||
cy.get('.dt-row[data-row-index=1]').should('be.visible');
|
||||
cy.get('.dt-row[data-row-index=3]').should('be.visible');
|
||||
cy.get('.dt-row[data-row-index=5]').should('be.visible');
|
||||
cy.get('@filterInput6').clear();
|
||||
});
|
||||
|
||||
it('filters with sorting', function () {
|
||||
cy.visit('/');
|
||||
cy.clickDropdown(7);
|
||||
cy.clickDropdownItem(7, 'Sort Descending');
|
||||
cy.get('.dt-filter[data-col-index=5]').as('filterInput5');
|
||||
cy.getCell(5, 24).click().type('{ctrl}f');
|
||||
cy.get('@filterInput5').type('>3000', {delay: 100});
|
||||
|
||||
cy.get('.dt-scrollable .dt-row:first')
|
||||
.should('contain', 'Angelica')
|
||||
.should('have.class', 'dt-row-24');
|
||||
cy.get('@filterInput5').clear();
|
||||
});
|
||||
});
|
||||
@ -1,20 +0,0 @@
|
||||
describe('DataTable init', function () {
|
||||
it('instance is created without any errors', () => {
|
||||
cy.visit('/');
|
||||
|
||||
cy.window()
|
||||
.its('DataTable')
|
||||
.then(DataTable => {
|
||||
// eslint-disable-next-line
|
||||
new DataTable('#datatable2', {
|
||||
columns: ['Name', 'Position'],
|
||||
data: [
|
||||
['Faris', 'Developer']
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
cy.get('#datatable2 .datatable')
|
||||
.contains('Faris');
|
||||
});
|
||||
});
|
||||
@ -1,15 +0,0 @@
|
||||
describe('Row', function () {
|
||||
before(function () {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
it('check / uncheck row', function () {
|
||||
cy.get('.dt-scrollable .dt-row:first')
|
||||
.find('input[type="checkbox"]')
|
||||
.click();
|
||||
|
||||
cy.get('[data-row-index="0"]').should('have.class', 'dt-row--highlight');
|
||||
|
||||
cy.get('.dt-toast').contains('1 row selected');
|
||||
});
|
||||
});
|
||||
@ -12,6 +12,6 @@
|
||||
// the project's config changing)
|
||||
|
||||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
};
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
}
|
||||
|
||||
@ -43,15 +43,7 @@ Cypress.Commands.add('clickDropdown', (col) => {
|
||||
});
|
||||
|
||||
Cypress.Commands.add('clickDropdownItem', (col, item) => {
|
||||
return cy.get(`.dt-dropdown__list-item:contains("${item}")`)
|
||||
return cy.getColumnCell(col)
|
||||
.find(`.dt-dropdown__list-item:contains("${item}")`)
|
||||
.click({ force: true });
|
||||
});
|
||||
|
||||
Cypress.Commands.add('typeTab', (shiftKey, ctrlKey) => {
|
||||
cy.focused().trigger('keydown', {
|
||||
keyCode: 9,
|
||||
which: 9,
|
||||
shiftKey: shiftKey,
|
||||
ctrlKey: ctrlKey
|
||||
});
|
||||
});
|
||||
|
||||
282
dist/frappe-datatable.cjs.css
vendored
Normal file
282
dist/frappe-datatable.cjs.css
vendored
Normal file
@ -0,0 +1,282 @@
|
||||
/* This file is processed by postcss */
|
||||
/* variables */
|
||||
|
||||
.data-table {
|
||||
|
||||
/* styling */
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* resets */
|
||||
|
||||
.data-table *, .data-table *::after, .data-table *::before {
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.data-table button, .data-table input {
|
||||
overflow: visible;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.data-table .input-style {
|
||||
outline: none;
|
||||
width: 100%;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.data-table *, .data-table *:focus {
|
||||
outline: none;
|
||||
border-radius: 0px;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.data-table table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.data-table table td {
|
||||
padding: 0;
|
||||
border: 1px solid #d1d8dd;
|
||||
}
|
||||
|
||||
.data-table thead td {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.data-table .freeze-container {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-pack: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
-ms-flex-line-pack: center;
|
||||
align-content: center;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: #f5f7fa;
|
||||
opacity: 0.5;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.data-table .freeze-container span {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
-webkit-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.data-table .hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.data-table .toast-message {
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
bottom: 1rem;
|
||||
left: 50%;
|
||||
-webkit-transform: translateX(-50%);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.data-table .toast-message span {
|
||||
display: inline-block;
|
||||
background-color: rgba(0, 0, 0, .8);
|
||||
color: #dfe2e5;
|
||||
border-radius: 3px;
|
||||
padding: 8px 16px;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.body-scrollable {
|
||||
max-height: 500px;
|
||||
overflow: auto;
|
||||
border-bottom: 1px solid #d1d8dd;
|
||||
}
|
||||
|
||||
.body-scrollable.row-highlight-all .data-table-row:not(.row-unhighlight) {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.data-table-header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.data-table-header .content span:not(.column-resizer) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.data-table-header .column-resizer {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 4px;
|
||||
width: 0.25rem;
|
||||
height: 100%;
|
||||
background-color: rgb(82, 146, 247);
|
||||
cursor: col-resize;
|
||||
}
|
||||
|
||||
.data-table-header .data-table-dropdown {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
display: -webkit-inline-box;
|
||||
display: -ms-inline-flexbox;
|
||||
display: inline-flex;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.data-table-header .data-table-dropdown.is-active .data-table-dropdown-list {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.data-table-header .data-table-dropdown.is-active .data-table-dropdown-toggle {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.data-table-header .data-table-dropdown-toggle {
|
||||
display: none;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.data-table-header .data-table-dropdown-list {
|
||||
display: none;
|
||||
font-weight: normal;
|
||||
|
||||
position: absolute;
|
||||
min-width: 128px;
|
||||
min-width: 8rem;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
background-color: white;
|
||||
border-radius: 3px;
|
||||
-webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
|
||||
box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
|
||||
padding-bottom: 8px;
|
||||
padding-bottom: 0.5rem;
|
||||
padding-top: 8px;
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
.data-table-header .data-table-dropdown-list> div {
|
||||
padding: 8px 16px;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.data-table-header .data-table-dropdown-list> div:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.data-table-header .data-table-cell.remove-column {
|
||||
background-color: #FD8B8B;
|
||||
-webkit-transition: 300ms background-color ease-in-out;
|
||||
transition: 300ms background-color ease-in-out;
|
||||
}
|
||||
|
||||
.data-table-header .data-table-cell.sortable-chosen {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.data-table-cell {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.data-table-cell .content {
|
||||
padding: 8px;
|
||||
padding: 0.5rem;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.data-table-cell .content.ellipsis {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.data-table-cell .edit-cell {
|
||||
display: none;
|
||||
padding: 8px;
|
||||
padding: 0.5rem;
|
||||
background: #fff;
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.data-table-cell.selected .content {
|
||||
border: 2px solid rgb(82, 146, 247);
|
||||
}
|
||||
|
||||
.data-table-cell.editing .content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.data-table-cell.editing .edit-cell {
|
||||
border: 2px solid rgb(82, 146, 247);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.data-table-cell.highlight {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.data-table-cell:hover .column-resizer {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.data-table-cell:hover .data-table-dropdown-toggle {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.data-table-cell .tree-node {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.data-table-cell .toggle {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
padding: 0 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.data-table-cell .toggle:before {
|
||||
content: '▼';
|
||||
}
|
||||
|
||||
.data-table-cell.tree-close .toggle:before {
|
||||
content: '►';
|
||||
}
|
||||
|
||||
.data-table-row.row-highlight {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.noselect {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
body.data-table-resize {
|
||||
cursor: col-resize;
|
||||
}
|
||||
3764
dist/frappe-datatable.cjs.js
vendored
Normal file
3764
dist/frappe-datatable.cjs.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
257
dist/frappe-datatable.css
vendored
Normal file
257
dist/frappe-datatable.css
vendored
Normal file
@ -0,0 +1,257 @@
|
||||
.datatable *, .datatable *::after, .datatable *::before {
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.datatable {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.dt-header {
|
||||
border-collapse: collapse;
|
||||
border-bottom: 1px solid #d1d8dd;
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.dt-body {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.dt-scrollable {
|
||||
max-height: 40vw;
|
||||
overflow: auto;
|
||||
border-bottom: 1px solid #d1d8dd;
|
||||
}
|
||||
|
||||
.dt-scrollable--highlight-all {
|
||||
background-color: #fffce7;
|
||||
}
|
||||
|
||||
.dt-scrollable__no-data {
|
||||
text-align: center;
|
||||
padding: 16px;
|
||||
padding: 1rem;
|
||||
border-left: 1px solid #d1d8dd;
|
||||
border-right: 1px solid #d1d8dd;
|
||||
}
|
||||
|
||||
.dt-row--highlight {
|
||||
background-color: #fffce7;
|
||||
}
|
||||
|
||||
.dt-row--unhighlight {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.dt-row--hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dt-cell {
|
||||
border: 1px solid #d1d8dd;
|
||||
position: relative;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dt-cell__content {
|
||||
padding: 8px;
|
||||
padding: 0.5rem;
|
||||
border: 2px solid transparent;
|
||||
height: 100%;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dt-cell__edit {
|
||||
display: none;
|
||||
padding: 8px;
|
||||
padding: 0.5rem;
|
||||
background-color: #fff;
|
||||
border: 2px solid rgb(255, 160, 10);
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dt-cell__resize-handle {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: -3px;
|
||||
top: 0;
|
||||
width: 5px;
|
||||
height: 100%;
|
||||
cursor: col-resize;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.dt-cell--editing .dt-cell__content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dt-cell--editing .dt-cell__edit {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dt-cell--focus .dt-cell__content {
|
||||
border-color: rgb(82, 146, 247);
|
||||
}
|
||||
|
||||
.dt-cell--highlight {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.dt-cell--dragging {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.dt-cell--header .dt-cell__content {
|
||||
padding-right: 16px;
|
||||
padding-right: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dt-cell--header:hover .dt-dropdown__toggle {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dt-cell--tree-close .dt-tree-node__toggle:before {
|
||||
content: '►';
|
||||
}
|
||||
|
||||
.dt-dropdown {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
display: -webkit-inline-box;
|
||||
display: -ms-inline-flexbox;
|
||||
display: inline-flex;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dt-dropdown__toggle {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.dt-dropdown__list {
|
||||
display: none;
|
||||
|
||||
position: absolute;
|
||||
min-width: 128px;
|
||||
min-width: 8rem;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
background-color: #fff;
|
||||
border-radius: 3px;
|
||||
padding: 8px 0;
|
||||
padding: 0.5rem 0;
|
||||
-webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
|
||||
box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
|
||||
}
|
||||
|
||||
.dt-dropdown__list-item {
|
||||
padding: 8px 16px;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.dt-dropdown__list-item:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.dt-dropdown--active .dt-dropdown__list {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dt-tree-node {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dt-tree-node__toggle {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
font-size: 10px;
|
||||
padding: 1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dt-tree-node__toggle:before {
|
||||
content: '▼';
|
||||
}
|
||||
|
||||
.dt-toast {
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
bottom: 1rem;
|
||||
left: 50%;
|
||||
-webkit-transform: translateX(-50%);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.dt-toast__message {
|
||||
display: inline-block;
|
||||
background-color: rgba(0, 0, 0, .8);
|
||||
color: #dfe2e5;
|
||||
border-radius: 3px;
|
||||
padding: 8px 16px;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.dt-input {
|
||||
outline: none;
|
||||
width: 100%;
|
||||
border: none;
|
||||
overflow: visible;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dt-freeze {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-pack: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
-ms-flex-line-pack: center;
|
||||
align-content: center;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: #f5f7fa;
|
||||
opacity: 0.5;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.dt-freeze__message {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
-webkit-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.dt-paste-target {
|
||||
position: fixed;
|
||||
left: -999em;
|
||||
}
|
||||
|
||||
body.dt-resize {
|
||||
cursor: col-resize;
|
||||
}
|
||||
3765
dist/frappe-datatable.js
vendored
Normal file
3765
dist/frappe-datatable.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
dist/frappe-datatable.min.css
vendored
Normal file
1
dist/frappe-datatable.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.datatable *,.datatable :after,.datatable :before{-webkit-box-sizing:border-box;box-sizing:border-box}.datatable{position:relative;overflow:auto}.dt-header{border-bottom:1px solid #d1d8dd;position:absolute;top:0;left:0;background-color:#fff}.dt-body,.dt-header{border-collapse:collapse}.dt-scrollable{max-height:40vw;overflow:auto;border-bottom:1px solid #d1d8dd}.dt-scrollable--highlight-all{background-color:#fffce7}.dt-scrollable__no-data{text-align:center;padding:16px;padding:1rem;border-left:1px solid #d1d8dd;border-right:1px solid #d1d8dd}.dt-row--highlight{background-color:#fffce7}.dt-row--unhighlight{background-color:#fff}.dt-row--hide{display:none}.dt-cell{border:1px solid #d1d8dd;position:relative;outline:none;padding:0}.dt-cell__content{border:2px solid transparent;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.dt-cell__content,.dt-cell__edit{padding:8px;padding:.5rem;height:100%}.dt-cell__edit{display:none;background-color:#fff;border:2px solid #ffa00a;z-index:1}.dt-cell__resize-handle{opacity:0;position:absolute;right:-3px;top:0;width:5px;height:100%;cursor:col-resize;z-index:1}.dt-cell--editing .dt-cell__content{display:none}.dt-cell--editing .dt-cell__edit{display:block}.dt-cell--focus .dt-cell__content{border-color:#5292f7}.dt-cell--dragging,.dt-cell--highlight{background-color:#f5f7fa}.dt-cell--header .dt-cell__content{padding-right:16px;padding-right:1rem;font-weight:700}.dt-cell--header:hover .dt-dropdown__toggle{opacity:1}.dt-cell--tree-close .dt-tree-node__toggle:before{content:"►"}.dt-dropdown{position:absolute;right:10px;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;vertical-align:top;text-align:left;font-weight:400;cursor:pointer}.dt-dropdown__toggle{opacity:0}.dt-dropdown__list{display:none;position:absolute;min-width:128px;min-width:8rem;top:100%;right:0;z-index:1;background-color:#fff;border-radius:3px;padding:8px 0;padding:.5rem 0;-webkit-box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1)}.dt-dropdown__list-item{padding:8px 16px;padding:.5rem 1rem}.dt-dropdown__list-item:hover{background-color:#f5f7fa}.dt-dropdown--active .dt-dropdown__list{display:block}.dt-tree-node{display:inline-block;position:relative}.dt-tree-node__toggle{display:inline-block;position:absolute;font-size:10px;padding:0 4px;cursor:pointer}.dt-tree-node__toggle:before{content:"▼"}.dt-toast{position:absolute;bottom:16px;bottom:1rem;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.dt-toast__message{display:inline-block;background-color:rgba(0,0,0,.8);color:#dfe2e5;border-radius:3px;padding:8px 16px;padding:.5rem 1rem}.dt-input{outline:none;width:100%;border:none;overflow:visible;font-family:inherit;font-size:inherit;line-height:inherit;margin:0;padding:0}.dt-freeze{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-ms-flex-line-pack:center;align-content:center;position:absolute;left:0;right:0;top:0;bottom:0;background-color:#f5f7fa;opacity:.5;font-size:2em}.dt-freeze__message{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.dt-paste-target{position:fixed;left:-999em}body.dt-resize{cursor:col-resize}
|
||||
1
dist/frappe-datatable.min.js
vendored
Normal file
1
dist/frappe-datatable.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
257
docs/assets/css/frappe-datatable.css
Normal file
257
docs/assets/css/frappe-datatable.css
Normal file
@ -0,0 +1,257 @@
|
||||
.datatable *, .datatable *::after, .datatable *::before {
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.datatable {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.dt-header {
|
||||
border-collapse: collapse;
|
||||
border-bottom: 1px solid #d1d8dd;
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.dt-body {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.dt-scrollable {
|
||||
max-height: 40vw;
|
||||
overflow: auto;
|
||||
border-bottom: 1px solid #d1d8dd;
|
||||
}
|
||||
|
||||
.dt-scrollable--highlight-all {
|
||||
background-color: #fffce7;
|
||||
}
|
||||
|
||||
.dt-scrollable__no-data {
|
||||
text-align: center;
|
||||
padding: 16px;
|
||||
padding: 1rem;
|
||||
border-left: 1px solid #d1d8dd;
|
||||
border-right: 1px solid #d1d8dd;
|
||||
}
|
||||
|
||||
.dt-row--highlight {
|
||||
background-color: #fffce7;
|
||||
}
|
||||
|
||||
.dt-row--unhighlight {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.dt-row--hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dt-cell {
|
||||
border: 1px solid #d1d8dd;
|
||||
position: relative;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dt-cell__content {
|
||||
padding: 8px;
|
||||
padding: 0.5rem;
|
||||
border: 2px solid transparent;
|
||||
height: 100%;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dt-cell__edit {
|
||||
display: none;
|
||||
padding: 8px;
|
||||
padding: 0.5rem;
|
||||
background-color: #fff;
|
||||
border: 2px solid rgb(255, 160, 10);
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dt-cell__resize-handle {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: -3px;
|
||||
top: 0;
|
||||
width: 5px;
|
||||
height: 100%;
|
||||
cursor: col-resize;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.dt-cell--editing .dt-cell__content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dt-cell--editing .dt-cell__edit {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dt-cell--focus .dt-cell__content {
|
||||
border-color: rgb(82, 146, 247);
|
||||
}
|
||||
|
||||
.dt-cell--highlight {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.dt-cell--dragging {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.dt-cell--header .dt-cell__content {
|
||||
padding-right: 16px;
|
||||
padding-right: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dt-cell--header:hover .dt-dropdown__toggle {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dt-cell--tree-close .dt-tree-node__toggle:before {
|
||||
content: '►';
|
||||
}
|
||||
|
||||
.dt-dropdown {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
display: -webkit-inline-box;
|
||||
display: -ms-inline-flexbox;
|
||||
display: inline-flex;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dt-dropdown__toggle {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.dt-dropdown__list {
|
||||
display: none;
|
||||
|
||||
position: absolute;
|
||||
min-width: 128px;
|
||||
min-width: 8rem;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
background-color: #fff;
|
||||
border-radius: 3px;
|
||||
padding: 8px 0;
|
||||
padding: 0.5rem 0;
|
||||
-webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
|
||||
box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
|
||||
}
|
||||
|
||||
.dt-dropdown__list-item {
|
||||
padding: 8px 16px;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.dt-dropdown__list-item:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.dt-dropdown--active .dt-dropdown__list {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dt-tree-node {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dt-tree-node__toggle {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
font-size: 10px;
|
||||
padding: 0 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dt-tree-node__toggle:before {
|
||||
content: '▼';
|
||||
}
|
||||
|
||||
.dt-toast {
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
bottom: 1rem;
|
||||
left: 50%;
|
||||
-webkit-transform: translateX(-50%);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.dt-toast__message {
|
||||
display: inline-block;
|
||||
background-color: rgba(0, 0, 0, .8);
|
||||
color: #dfe2e5;
|
||||
border-radius: 3px;
|
||||
padding: 8px 16px;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.dt-input {
|
||||
outline: none;
|
||||
width: 100%;
|
||||
border: none;
|
||||
overflow: visible;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dt-freeze {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-pack: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
-ms-flex-line-pack: center;
|
||||
align-content: center;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: #f5f7fa;
|
||||
opacity: 0.5;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.dt-freeze__message {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
-webkit-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.dt-paste-target {
|
||||
position: fixed;
|
||||
left: -999em;
|
||||
}
|
||||
|
||||
body.dt-resize {
|
||||
cursor: col-resize;
|
||||
}
|
||||
99
docs/assets/css/hljs-default.css
Normal file
99
docs/assets/css/hljs-default.css
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
|
||||
Original highlight.js style (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #F0F0F0;
|
||||
}
|
||||
|
||||
|
||||
/* Base color: saturation 0; */
|
||||
|
||||
.hljs,
|
||||
.hljs-subst {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.hljs-comment {
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-attribute,
|
||||
.hljs-selector-tag,
|
||||
.hljs-meta-keyword,
|
||||
.hljs-doctag,
|
||||
.hljs-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
/* User color: hue: 0 */
|
||||
|
||||
.hljs-type,
|
||||
.hljs-string,
|
||||
.hljs-number,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-quote,
|
||||
.hljs-template-tag,
|
||||
.hljs-deletion {
|
||||
color: #880000;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-section {
|
||||
color: #880000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-regexp,
|
||||
.hljs-symbol,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-link,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo {
|
||||
color: #BC6060;
|
||||
}
|
||||
|
||||
|
||||
/* Language color: hue: 90; */
|
||||
|
||||
.hljs-literal {
|
||||
color: #78A960;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-bullet,
|
||||
.hljs-code,
|
||||
.hljs-addition {
|
||||
color: #397300;
|
||||
}
|
||||
|
||||
|
||||
/* Meta color: hue: 200 */
|
||||
|
||||
.hljs-meta {
|
||||
color: #1f7199;
|
||||
}
|
||||
|
||||
.hljs-meta-string {
|
||||
color: #4d99bf;
|
||||
}
|
||||
|
||||
|
||||
/* Misc effects */
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
159
docs/assets/css/index.css
Normal file
159
docs/assets/css/index.css
Normal file
@ -0,0 +1,159 @@
|
||||
@import url('https://fonts.googleapis.com/css?family=Lato:400,700,900');
|
||||
|
||||
:root {
|
||||
--border-color: #d1d8dd;
|
||||
--border: 1px solid var(--border-color);
|
||||
--text-color: #36414C;
|
||||
--navbar-font-size: 1.25rem;
|
||||
--light-bg: #f5f7fa;
|
||||
--primary-color: #7575ff;
|
||||
--primary-color-hover: #5b5be5;
|
||||
}
|
||||
|
||||
*, *::after, *::before {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html, body {
|
||||
/* font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; */
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 12px;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
body {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin: 2rem 0 4rem 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1.25rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding: 1.5rem;
|
||||
background-color: white;
|
||||
z-index: 1;
|
||||
border-bottom: var(--border);
|
||||
}
|
||||
|
||||
.navbar .container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.navbar-links a {
|
||||
color: var(--text-color);
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
font-size: var(--navbar-font-size);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.logo {
|
||||
max-width: 8rem;
|
||||
}
|
||||
|
||||
.footer-logo {
|
||||
max-width: 3rem;
|
||||
max-height: 3rem;
|
||||
padding: 0.5rem;
|
||||
background-color: #fff;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.padding-1 {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.showcase {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
margin: 4rem 0;
|
||||
}
|
||||
|
||||
.showcase.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.features {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
font-size: 1.25rem;
|
||||
margin-top: 1rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
.features ul {
|
||||
padding-left: 2rem;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
[class^="example-"] {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.code {
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.hljs {
|
||||
padding: 2rem;
|
||||
border-left: 2px solid var(--border-color);
|
||||
background-color: var(--light-bg);
|
||||
}
|
||||
|
||||
footer {
|
||||
border-top: 1px solid #d1d8dd;
|
||||
}
|
||||
|
||||
.download-button {
|
||||
color: #fff;
|
||||
background-color: var(--primary-color);
|
||||
border: 0px;
|
||||
border-bottom: 3px solid rgba(0, 0, 0, 0.2);
|
||||
font-size: 1.5rem;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.download-button:hover {
|
||||
cursor: pointer;
|
||||
background-color: var(--primary-color-hover);
|
||||
}
|
||||
|
||||
/* .data-table-header {
|
||||
background: #f7fafc;
|
||||
color: #8d99a6;
|
||||
} */
|
||||
|
||||
22
docs/assets/img/data-table-logo.svg
Normal file
22
docs/assets/img/data-table-logo.svg
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="340.42303" height="330.00024" viewBox="0 0 90.070259 87.312561" version="1.1" id="svg3970" inkscape:version="0.92.2 5c3e80d, 2017-08-06">
|
||||
<defs id="defs3964"/>
|
||||
|
||||
<metadata id="metadata3967">
|
||||
|
||||
</metadata>
|
||||
<g inkscape:label="Layer 1" id="layer1" transform="translate(-7.1255851,-173.21277)">
|
||||
<rect transform="scale(-1,1)" ry="13.229167" rx="13.229167" y="173.21277" x="-94.438156" height="87.312546" width="87.312553" id="rect2004" style="opacity:1;fill:#edfdff;fill-opacity:1;stroke:none;stroke-width:1.74624979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;"/>
|
||||
<path style="opacity:1;fill:#c3f1ff;fill-opacity:1;stroke:none;stroke-width:6.59999943;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 0 96.25 L 0 151.25 L 77.5 151.25 C 80.27 151.25 82.5 149.02 82.5 146.25 L 82.5 101.25 C 82.5 98.48 80.27 96.25 77.5 96.25 L 0 96.25 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect2006"/>
|
||||
<path style="opacity:1;fill:#c3f1ff;fill-opacity:1;stroke:none;stroke-width:6.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 50 0 C 22.299998 0 1.4210855e-14 22.299998 0 50 L 0 54.5 L 78 54.5 C 80.77 54.5 83 52.27 83 49.5 L 83 4.5 C 83 2.503655 81.832377 0.80274423 80.148438 0 L 50 0 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect2008"/>
|
||||
<rect style="opacity:1;fill:#71caff;fill-opacity:1;stroke:none;stroke-width:1.74624979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" id="rect2010" width="57.195782" height="14.552094" x="40.000061" y="198.74506" ry="1.3229167" rx="1.3229166"/>
|
||||
<path style="opacity:1;fill:#c3f1ff;fill-opacity:1;stroke:none;stroke-width:6.59999943;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 0 193 L 0 248 L 77.5 248 C 80.27 248 82.5 245.77 82.5 243 L 82.5 198 C 82.5 195.23 80.27 193 77.5 193 L 0 193 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect2012"/>
|
||||
<path style="fill:#87e34c;fill-opacity:1;stroke:none;stroke-width:1.00000012px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" clip-path="none" d="M 0 219.61328 L 0 284.29688 C 0 309.61626 27.643317 330 61.980469 330 L 268.01953 330 C 300.54576 330 327.04905 311.70603 329.75586 288.25586 L 330 248.5 C 256.7254 233.20714 83.19117 220.5514 0 219.61328 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="path2020"/>
|
||||
<path style="opacity:1;fill:#c3f1ff;fill-opacity:1;stroke:none;stroke-width:6.59999943;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 129.25 193.5 C 126.48 193.5 124.25 195.73 124.25 198.5 L 124.25 243.5 C 124.25 246.27 126.48 248.5 129.25 248.5 L 330 248.5 L 330 193.5 L 129.25 193.5 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect2014"/>
|
||||
<path style="opacity:1;fill:#c3f1ff;fill-opacity:1;stroke:none;stroke-width:6.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 127.10156 0 C 125.41762 0.80274423 124.25 2.503655 124.25 4.5 L 124.25 49.5 C 124.25 52.27 126.48 54.5 129.25 54.5 L 330 54.5 L 330 50 C 330 22.299998 307.7 0 280 0 L 127.10156 0 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect2016"/>
|
||||
<path style="opacity:1;fill:#59b81c;fill-opacity:1;stroke:none;stroke-width:6.59999943;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 129.25 290.5 C 126.48 290.5 124.25 292.73 124.25 295.5 L 124.25 330 L 280 330 C 304.09518 330 324.09664 313.12363 328.89648 290.5 L 129.25 290.5 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect1135"/>
|
||||
<path style="opacity:1;fill:#59b81c;fill-opacity:1;stroke:none;stroke-width:6.59999943;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 0.94921875 289.75 C 5.4614968 312.74994 25.639123 330 50 330 L 82.5 330 L 82.5 294.75 C 82.5 291.98 80.27 289.75 77.5 289.75 L 0.94921875 289.75 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect1139"/>
|
||||
<path style="opacity:1;fill:#59b81c;fill-opacity:1;stroke:none;stroke-width:1.74624979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="m 7.1257773,231.31877 v 7.51065 H 27.630987 c 0.732896,0 1.322917,-0.59002 1.322917,-1.32292 v -5.36505 C 20.620295,231.67818 13.007809,231.3851 7.1257773,231.31877 Z" id="path1264"/>
|
||||
<path style="fill:#59b81c;fill-opacity:1;stroke:none;stroke-width:0.26458335px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" clip-path="none" d="m 40.000259,232.82824 v 4.81056 c 0,0.73289 0.59002,1.32291 1.322916,1.32291 h 53.115108 c -11.982795,-2.50088 -34.095004,-4.73297 -54.438024,-6.13347 z" id="path1163"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
46
docs/assets/img/frappe-bird-grey.svg
Normal file
46
docs/assets/img/frappe-bird-grey.svg
Normal file
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 260 260"
|
||||
version="1.1"
|
||||
xml:space="preserve"
|
||||
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"
|
||||
id="svg16"
|
||||
sodipodi:docname="frappe-bird-grey.svg"
|
||||
inkscape:version="0.92.2 5c3e80d, 2017-08-06"><metadata
|
||||
id="metadata22"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs20" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1029"
|
||||
id="namedview18"
|
||||
showgrid="false"
|
||||
inkscape:snap-global="false"
|
||||
inkscape:zoom="2.5673415"
|
||||
inkscape:cx="125.36812"
|
||||
inkscape:cy="132.66533"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="24"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg16" /><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path846"
|
||||
d="m 39.426993,41.26578 c -0.08308,-0.002 -0.162537,0.01476 -0.244177,0.01847 -0.195406,-0.01104 -0.394663,-0.0078 -0.595581,0.02219 -0.671494,0.09826 -1.359798,0.439289 -1.985268,1.097637 L 36.166705,42.984362 3.911539,86.00785 c -1.4521873,1.95837 -0.089029,5.089411 2.840468,4.758942 l 35.051779,-4.999463 7.486826,52.499391 c 0.143332,1.06941 0.708175,1.87792 1.45651,2.37853 0.06099,0.57446 0.300326,1.15825 0.786491,1.67605 l 54.221237,55.16042 c 0.35241,0.34946 0.76163,0.57974 1.18925,0.71967 l 43.2449,43.9932 c 2.19797,2.06517 5.73881,0.59111 5.73827,-2.44537 l 0.72154,-88.30898 100.76861,-99.226182 c 2.03273,-1.933848 0.52846,-5.096585 -1.98911,-5.01476 L 146.21842,46.315471 c -1.15721,-0.0078 -1.82288,0.589677 -2.08644,0.84183 L 70.489255,119.67376 95.234723,86.666413 c 1.320352,-1.808803 0.561745,-3.846193 -0.6872,-4.787579 L 41.349464,41.92492 C 40.706305,41.485816 40.049657,41.2839 39.427179,41.266357 Z"
|
||||
style="fill:#29344a;fill-opacity:1;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
2
docs/assets/js/Sortable.min.js
vendored
Normal file
2
docs/assets/js/Sortable.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
18
docs/assets/js/clusterize.min.js
vendored
Normal file
18
docs/assets/js/clusterize.min.js
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
Clusterize.js - v0.18.0 - 2017-11-04
|
||||
http://NeXTs.github.com/Clusterize.js/
|
||||
Copyright (c) 2015 Denis Lukov; Licensed GPLv3 */
|
||||
|
||||
;(function(q,n){"undefined"!=typeof module?module.exports=n():"function"==typeof define&&"object"==typeof define.amd?define(n):this[q]=n()})("Clusterize",function(){function q(b,a,c){return a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)}function n(b,a,c){return a.removeEventListener?a.removeEventListener(b,c,!1):a.detachEvent("on"+b,c)}function r(b){return"[object Array]"===Object.prototype.toString.call(b)}function m(b,a){return window.getComputedStyle?window.getComputedStyle(a)[b]:
|
||||
a.currentStyle[b]}var l=function(){for(var b=3,a=document.createElement("b"),c=a.all||[];a.innerHTML="\x3c!--[if gt IE "+ ++b+"]><i><![endif]--\x3e",c[0];);return 4<b?b:document.documentMode}(),x=navigator.platform.toLowerCase().indexOf("mac")+1,p=function(b){if(!(this instanceof p))return new p(b);var a=this,c={rows_in_block:50,blocks_in_cluster:4,tag:null,show_no_data_row:!0,no_data_class:"clusterize-no-data",no_data_text:"No data",keep_parity:!0,callbacks:{}};a.options={};for(var d="rows_in_block blocks_in_cluster show_no_data_row no_data_class no_data_text keep_parity tag callbacks".split(" "),
|
||||
f=0,h;h=d[f];f++)a.options[h]="undefined"!=typeof b[h]&&null!=b[h]?b[h]:c[h];c=["scroll","content"];for(f=0;d=c[f];f++)if(a[d+"_elem"]=b[d+"Id"]?document.getElementById(b[d+"Id"]):b[d+"Elem"],!a[d+"_elem"])throw Error("Error! Could not find "+d+" element");a.content_elem.hasAttribute("tabindex")||a.content_elem.setAttribute("tabindex",0);var e=r(b.rows)?b.rows:a.fetchMarkup(),g={};b=a.scroll_elem.scrollTop;a.insertToDOM(e,g);a.scroll_elem.scrollTop=b;var k=!1,m=0,l=!1,t=function(){x&&(l||(a.content_elem.style.pointerEvents=
|
||||
"none"),l=!0,clearTimeout(m),m=setTimeout(function(){a.content_elem.style.pointerEvents="auto";l=!1},50));k!=(k=a.getClusterNum())&&a.insertToDOM(e,g);a.options.callbacks.scrollingProgress&&a.options.callbacks.scrollingProgress(a.getScrollProgress())},u=0,v=function(){clearTimeout(u);u=setTimeout(a.refresh,100)};q("scroll",a.scroll_elem,t);q("resize",window,v);a.destroy=function(b){n("scroll",a.scroll_elem,t);n("resize",window,v);a.html((b?a.generateEmptyRow():e).join(""))};a.refresh=function(b){(a.getRowsHeight(e)||
|
||||
b)&&a.update(e)};a.update=function(b){e=r(b)?b:[];b=a.scroll_elem.scrollTop;e.length*a.options.item_height<b&&(k=a.scroll_elem.scrollTop=0);a.insertToDOM(e,g);a.scroll_elem.scrollTop=b};a.clear=function(){a.update([])};a.getRowsAmount=function(){return e.length};a.getScrollProgress=function(){return this.options.scroll_top/(e.length*this.options.item_height)*100||0};var w=function(b,c){var d=r(c)?c:[];d.length&&(e="append"==b?e.concat(d):d.concat(e),a.insertToDOM(e,g))};a.append=function(a){w("append",
|
||||
a)};a.prepend=function(a){w("prepend",a)}};p.prototype={constructor:p,fetchMarkup:function(){for(var b=[],a=this.getChildNodes(this.content_elem);a.length;)b.push(a.shift().outerHTML);return b},exploreEnvironment:function(b,a){var c=this.options;c.content_tag=this.content_elem.tagName.toLowerCase();b.length&&(l&&9>=l&&!c.tag&&(c.tag=b[0].match(/<([^>\s/]*)/)[1].toLowerCase()),1>=this.content_elem.children.length&&(a.data=this.html(b[0]+b[0]+b[0])),c.tag||(c.tag=this.content_elem.children[0].tagName.toLowerCase()),
|
||||
this.getRowsHeight(b))},getRowsHeight:function(b){var a=this.options,c=a.item_height;a.cluster_height=0;if(b.length){b=this.content_elem.children;var d=b[Math.floor(b.length/2)];a.item_height=d.offsetHeight;"tr"==a.tag&&"collapse"!=m("borderCollapse",this.content_elem)&&(a.item_height+=parseInt(m("borderSpacing",this.content_elem),10)||0);"tr"!=a.tag&&(b=parseInt(m("marginTop",d),10)||0,d=parseInt(m("marginBottom",d),10)||0,a.item_height+=Math.max(b,d));a.block_height=a.item_height*a.rows_in_block;
|
||||
a.rows_in_cluster=a.blocks_in_cluster*a.rows_in_block;a.cluster_height=a.blocks_in_cluster*a.block_height;return c!=a.item_height}},getClusterNum:function(){this.options.scroll_top=this.scroll_elem.scrollTop;return Math.floor(this.options.scroll_top/(this.options.cluster_height-this.options.block_height))||0},generateEmptyRow:function(){var b=this.options;if(!b.tag||!b.show_no_data_row)return[];var a=document.createElement(b.tag),c=document.createTextNode(b.no_data_text);a.className=b.no_data_class;
|
||||
if("tr"==b.tag){var d=document.createElement("td");d.colSpan=100;d.appendChild(c)}a.appendChild(d||c);return[a.outerHTML]},generate:function(b,a){var c=this.options,d=b.length;if(d<c.rows_in_block)return{top_offset:0,bottom_offset:0,rows_above:0,rows:d?b:this.generateEmptyRow()};var f=Math.max((c.rows_in_cluster-c.rows_in_block)*a,0),h=f+c.rows_in_cluster,e=Math.max(f*c.item_height,0);c=Math.max((d-h)*c.item_height,0);d=[];var g=f;for(1>e&&g++;f<h;f++)b[f]&&d.push(b[f]);return{top_offset:e,bottom_offset:c,
|
||||
rows_above:g,rows:d}},renderExtraTag:function(b,a){var c=document.createElement(this.options.tag);c.className=["clusterize-extra-row","clusterize-"+b].join(" ");a&&(c.style.height=a+"px");return c.outerHTML},insertToDOM:function(b,a){this.options.cluster_height||this.exploreEnvironment(b,a);var c=this.generate(b,this.getClusterNum()),d=c.rows.join(""),f=this.checkChanges("data",d,a),h=this.checkChanges("top",c.top_offset,a),e=this.checkChanges("bottom",c.bottom_offset,a),g=this.options.callbacks,
|
||||
k=[];f||h?(c.top_offset&&(this.options.keep_parity&&k.push(this.renderExtraTag("keep-parity")),k.push(this.renderExtraTag("top-space",c.top_offset))),k.push(d),c.bottom_offset&&k.push(this.renderExtraTag("bottom-space",c.bottom_offset)),g.clusterWillChange&&g.clusterWillChange(),this.html(k.join("")),"ol"==this.options.content_tag&&this.content_elem.setAttribute("start",c.rows_above),this.content_elem.style["counter-increment"]="clusterize-counter "+(c.rows_above-1),g.clusterChanged&&g.clusterChanged()):
|
||||
e&&(this.content_elem.lastChild.style.height=c.bottom_offset+"px")},html:function(b){var a=this.content_elem;if(l&&9>=l&&"tr"==this.options.tag){var c=document.createElement("div");for(c.innerHTML="<table><tbody>"+b+"</tbody></table>";b=a.lastChild;)a.removeChild(b);for(c=this.getChildNodes(c.firstChild.firstChild);c.length;)a.appendChild(c.shift())}else a.innerHTML=b},getChildNodes:function(b){b=b.children;for(var a=[],c=0,d=b.length;c<d;c++)a.push(b[c]);return a},checkChanges:function(b,a,c){var d=
|
||||
a!=c[b];c[b]=a;return d}};return p});
|
||||
3692
docs/assets/js/frappe-datatable.js
Normal file
3692
docs/assets/js/frappe-datatable.js
Normal file
File diff suppressed because it is too large
Load Diff
2
docs/assets/js/highlight.pack.js
Normal file
2
docs/assets/js/highlight.pack.js
Normal file
File diff suppressed because one or more lines are too long
718
docs/assets/js/index.js
Normal file
718
docs/assets/js/index.js
Normal file
@ -0,0 +1,718 @@
|
||||
/* global DataTable */
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
const {
|
||||
columns,
|
||||
data
|
||||
} = getSampleData();
|
||||
|
||||
// Hero
|
||||
let datatable1 = new DataTable('.example-1', {
|
||||
columns,
|
||||
data,
|
||||
checkboxColumn: true
|
||||
});
|
||||
|
||||
// // Formatted Cells
|
||||
// let datatable2 = new DataTable('.example-2', {
|
||||
// columns: ['Name', 'Position', 'Office', 'Extn.', 'Start Date',
|
||||
// { content: 'Salary', format: val => '$' + val, align: 'right' }],
|
||||
// data
|
||||
// });
|
||||
|
||||
// // Inline Filters
|
||||
// let datatable3 = new DataTable('.example-3', {
|
||||
// columns,
|
||||
// data,
|
||||
// enableInlineFilters: true
|
||||
// });
|
||||
// datatable3.showToastMessage('Click on a cell and press Ctrl/Cmd + F');
|
||||
|
||||
// // Keyboard
|
||||
// let datatable4 = new DataTable('.example-4', {
|
||||
// columns,
|
||||
// data
|
||||
// });
|
||||
// datatable4.showToastMessage('Double click to edit');
|
||||
|
||||
// // Tree Structured Rows
|
||||
// let datatable5 = new DataTable('.example-5', getTreeData());
|
||||
// datatable5.showToastMessage('Expand/Collapse tree nodes');
|
||||
|
||||
function getSampleData(multiplier) {
|
||||
let columns = ['Name', 'Position', 'Office', {name: 'Extn.', width: 120}, 'Start Date', 'Salary'];
|
||||
let data = [
|
||||
['Tiger Nixon', 'System Architect', 'Edinburgh', 5421, '2011/04/25', '320,800'],
|
||||
['Garrett Winters', 'Accountant', 'Tokyo', 8422, '2011/07/25', '170,750'],
|
||||
['Ashton Cox', 'Junior Technical Author', 'San Francisco', 1562, '2009/01/12', '86,000'],
|
||||
['Cedric Kelly', 'Senior Javascript Developer', 'Edinburgh', 6224, '2012/03/29', '433,060'],
|
||||
['Airi Satou', 'Accountant', 'Tokyo', 5407, '2008/11/28', '162,700'],
|
||||
['Brielle Williamson', 'Integration Specialist', 'New York', 4804, '2012/12/02', '372,000'],
|
||||
['Herrod Chandler', 'Sales Assistant', 'San Francisco', 9608, '2012/08/06', '137,500'],
|
||||
['Rhona Davidson', 'Integration Specialist', 'Tokyo', 6200, '2010/10/14', '327,900'],
|
||||
['Colleen Hurst', 'Javascript Developer', 'San Francisco', 2360, '2009/09/15', '205,500'],
|
||||
['Sonya Frost', 'Software Engineer', 'Edinburgh', 1667, '2008/12/13', '103,600'],
|
||||
['Jena Gaines', 'Office Manager', 'London', 3814, '2008/12/19', '90,560'],
|
||||
['Quinn Flynn', 'Support Lead', 'Edinburgh', 9497, '2013/03/03', '342,000'],
|
||||
['Charde Marshall', 'Regional Director', 'San Francisco', 6741, '2008/10/16', '470,600'],
|
||||
['Haley Kennedy', 'Senior Marketing Designer', 'London', 3597, '2012/12/18', '313,500'],
|
||||
['Tatyana Fitzpatrick', 'Regional Director', 'London', 1965, '2010/03/17', '385,750'],
|
||||
['Michael Silva', 'Marketing Designer', 'London', 1581, '2012/11/27', '198,500'],
|
||||
['Paul Byrd', 'Chief Financial Officer (CFO)', 'New York', 3059, '2010/06/09', '725,000'],
|
||||
['Gloria Little', 'Systems Administrator', 'New York', 1721, '2009/04/10', '237,500'],
|
||||
['Bradley Greer', 'Software Engineer', 'London', 2558, '2012/10/13', '132,000'],
|
||||
['Dai Rios', 'Personnel Lead', 'Edinburgh', 2290, '2012/09/26', '217,500'],
|
||||
['Jenette Caldwell', 'Development Lead', 'New York', 1937, '2011/09/03', '345,000'],
|
||||
['Yuri Berry', 'Chief Marketing Officer (CMO)', 'New York', 6154, '2009/06/25', '675,000'],
|
||||
['Caesar Vance', 'Pre-Sales Support', 'New York', 8330, '2011/12/12', '106,450'],
|
||||
['Doris Wilder', 'Sales Assistant', 'Sidney', 3023, '2010/09/20', '85,600'],
|
||||
['Angelica Ramos', 'Chief Executive Officer (CEO)', 'London', 5797, '2009/10/09', '1,200,000'],
|
||||
['Gavin Joyce', 'Developer', 'Edinburgh', 8822, '2010/12/22', '92,575'],
|
||||
['Jennifer Chang', 'Regional Director', 'Singapore', 9239, '2010/11/14', '357,650'],
|
||||
['Brenden Wagner', 'Software Engineer', 'San Francisco', 1314, '2011/06/07', '206,850'],
|
||||
['Fiona Green', 'Chief Operating Officer (COO)', 'San Francisco', 2947, '2010/03/11', '850,000'],
|
||||
['Shou Itou', 'Regional Marketing', 'Tokyo', 8899, '2011/08/14', '163,000'],
|
||||
['Michelle House', 'Integration Specialist', 'Sidney', 2769, '2011/06/02', '95,400'],
|
||||
['Suki Burks', 'Developer', 'London', 6832, '2009/10/22', '114,500'],
|
||||
['Prescott Bartlett', 'Technical Author', 'London', 3606, '2011/05/07', '145,000'],
|
||||
['Gavin Cortez', 'Team Leader', 'San Francisco', 2860, '2008/10/26', '235,500'],
|
||||
['Martena Mccray', 'Post-Sales support', 'Edinburgh', 8240, '2011/03/09', '324,050'],
|
||||
['Unity Butler', 'Marketing Designer', 'San Francisco', 5384, '2009/12/09', '85,675'],
|
||||
['Howard Hatfield', 'Office Manager', 'San Francisco', 7031, '2008/12/16', '164,500'],
|
||||
['Hope Fuentes', 'Secretary', 'San Francisco', 6318, '2010/02/12', '109,850'],
|
||||
['Vivian Harrell', 'Financial Controller', 'San Francisco', 9422, '2009/02/14', '452,500'],
|
||||
['Timothy Mooney', 'Office Manager', 'London', 7580, '2008/12/11', '136,200'],
|
||||
['Jackson Bradshaw', 'Director', 'New York', 1042, '2008/09/26', '645,750'],
|
||||
['Olivia Liang', 'Support Engineer', 'Singapore', 2120, '2011/02/03', '234,500'],
|
||||
['Bruno Nash', 'Software Engineer', 'London', 6222, '2011/05/03', '163,500'],
|
||||
['Sakura Yamamoto', 'Support Engineer', 'Tokyo', 9383, '2009/08/19', '139,575'],
|
||||
['Thor Walton', 'Developer', 'New York', 8327, '2013/08/11', '98,540'],
|
||||
['Finn Camacho', 'Support Engineer', 'San Francisco', 2927, '2009/07/07', '87,500'],
|
||||
['Serge Baldwin', 'Data Coordinator', 'Singapore', 8352, '2012/04/09', '138,575'],
|
||||
['Zenaida Frank', 'Software Engineer', 'New York', 7439, '2010/01/04', '125,250'],
|
||||
['Zorita Serrano', 'Software Engineer', 'San Francisco', 4389, '2012/06/01', '115,000'],
|
||||
['Jennifer Acosta', 'Junior Javascript Developer', 'Edinburgh', 3431, '2013/02/01', '75,650'],
|
||||
['Cara Stevens', 'Sales Assistant', 'New York', 3990, '2011/12/06', '145,600'],
|
||||
['Hermione Butler', 'Regional Director', 'London', 1016, '2011/03/21', '356,250'],
|
||||
['Lael Greer', 'Systems Administrator', 'London', 6733, '2009/02/27', '103,500'],
|
||||
['Jonas Alexander', 'Developer', 'San Francisco', 8196, '2010/07/14', '86,500'],
|
||||
['Shad Decker', 'Regional Director', 'Edinburgh', 6373, '2008/11/13', '183,000'],
|
||||
['Michael Bruce', 'Javascript Developer', 'Singapore', 5384, '2011/06/27', '183,000'],
|
||||
['Donna Snider', 'Customer Support', 'New York', 4226, '2011/01/25', '112,000']
|
||||
];
|
||||
|
||||
if (multiplier) {
|
||||
Array.from(new Array(multiplier - 1)).forEach(d => {
|
||||
data = data.concat(data);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
function getTreeData() {
|
||||
return {
|
||||
columns: [{
|
||||
'id': 'account',
|
||||
'content': 'Account'
|
||||
}, {
|
||||
'id': 'opening_debit',
|
||||
'content': 'Opening (Dr)'
|
||||
}, {
|
||||
'id': 'opening_credit',
|
||||
'content': 'Opening (Cr)'
|
||||
},
|
||||
// {
|
||||
// 'id': 'debit',
|
||||
// 'content': 'Debit'
|
||||
// },
|
||||
// {
|
||||
// 'id': 'credit',
|
||||
// 'content': 'Credit'
|
||||
// },
|
||||
{
|
||||
'id': 'closing_debit',
|
||||
'content': 'Closing (Dr)'
|
||||
}, {
|
||||
'id': 'closing_credit',
|
||||
'content': 'Closing (Cr)'
|
||||
}, {
|
||||
'id': 'currency',
|
||||
'content': 'Currency',
|
||||
'hidden': 1
|
||||
}],
|
||||
data: [{
|
||||
'account_name': 'Application of Funds (Assets)',
|
||||
'account': 'Application of Funds (Assets)',
|
||||
'parent_account': null,
|
||||
'indent': 0,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 12023729.54,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 12023729.54,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Current Assets',
|
||||
'account': 'Current Assets',
|
||||
'parent_account': 'Application of Funds (Assets)',
|
||||
'indent': 1,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 13960649.54,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 13960649.54,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Accounts Receivable',
|
||||
'account': 'Accounts Receivable',
|
||||
'parent_account': 'Current Assets',
|
||||
'indent': 2,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 742790.474,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 742790.474,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Debtors',
|
||||
'account': 'Debtors',
|
||||
'parent_account': 'Accounts Receivable',
|
||||
'indent': 3,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 742790.474,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 742790.474,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Bank Accounts',
|
||||
'account': 'Bank Accounts',
|
||||
'parent_account': 'Current Assets',
|
||||
'indent': 2,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 280676.822,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 280676.822,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Corporation Bank',
|
||||
'account': 'Corporation Bank',
|
||||
'parent_account': 'Bank Accounts',
|
||||
'indent': 3,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 290676.822,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 290676.822,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'HDFC Bank',
|
||||
'account': 'HDFC Bank',
|
||||
'parent_account': 'Bank Accounts',
|
||||
'indent': 3,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 10000.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 10000.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Cash In Hand',
|
||||
'account': 'Cash In Hand',
|
||||
'parent_account': 'Current Assets',
|
||||
'indent': 2,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 229904.494,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 229904.494,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Cash',
|
||||
'account': 'Cash',
|
||||
'parent_account': 'Cash In Hand',
|
||||
'indent': 3,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 229904.494,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 229904.494,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Stock Assets',
|
||||
'account': 'Stock Assets',
|
||||
'parent_account': 'Current Assets',
|
||||
'indent': 2,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 12707277.75,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 12707277.75,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'All Warehouses',
|
||||
'account': 'All Warehouses',
|
||||
'parent_account': 'Stock Assets',
|
||||
'indent': 3,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 12707277.75,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 12707277.75,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Finished Goods',
|
||||
'account': 'Finished Goods',
|
||||
'parent_account': 'All Warehouses',
|
||||
'indent': 4,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 87320.3,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 87320.3,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Retail Stores',
|
||||
'account': 'Retail Stores',
|
||||
'parent_account': 'All Warehouses',
|
||||
'indent': 4,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 4540590.0,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 4540590.0,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Bandra Store',
|
||||
'account': 'Bandra Store',
|
||||
'parent_account': 'Retail Stores',
|
||||
'indent': 5,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 3246800.0,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 3246800.0,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Central Warehouse',
|
||||
'account': 'Central Warehouse',
|
||||
'parent_account': 'Retail Stores',
|
||||
'indent': 5,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 1236790.0,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 1236790.0,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Lower Parel Store',
|
||||
'account': 'Lower Parel Store',
|
||||
'parent_account': 'Retail Stores',
|
||||
'indent': 5,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 57000.0,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 57000.0,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Stores',
|
||||
'account': 'Stores',
|
||||
'parent_account': 'All Warehouses',
|
||||
'indent': 4,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 8016525.27,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 8016525.27,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Work In Progress',
|
||||
'account': 'Work In Progress',
|
||||
'parent_account': 'All Warehouses',
|
||||
'indent': 4,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 62842.18,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 62842.18,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Fixed Assets',
|
||||
'account': 'Fixed Assets',
|
||||
'parent_account': 'Application of Funds (Assets)',
|
||||
'indent': 1,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 19920.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 19920.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Electronic Equipments',
|
||||
'account': 'Electronic Equipments',
|
||||
'parent_account': 'Fixed Assets',
|
||||
'indent': 2,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 80.0,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 80.0,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Furnitures and Fixtures',
|
||||
'account': 'Furnitures and Fixtures',
|
||||
'parent_account': 'Fixed Assets',
|
||||
'indent': 2,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 20000.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 20000.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Temporary Accounts',
|
||||
'account': 'Temporary Accounts',
|
||||
'parent_account': 'Application of Funds (Assets)',
|
||||
'indent': 1,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 1917000.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 1917000.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Temporary Opening',
|
||||
'account': 'Temporary Opening',
|
||||
'parent_account': 'Temporary Accounts',
|
||||
'indent': 2,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 1917000.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 1917000.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Source of Funds (Liabilities)',
|
||||
'account': 'Source of Funds (Liabilities)',
|
||||
'parent_account': null,
|
||||
'indent': 0,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 2371628.002,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 2371628.002,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Current Liabilities',
|
||||
'account': 'Current Liabilities',
|
||||
'parent_account': 'Source of Funds (Liabilities)',
|
||||
'indent': 1,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 2371628.002,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 2371628.002,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Accounts Payable',
|
||||
'account': 'Accounts Payable',
|
||||
'parent_account': 'Current Liabilities',
|
||||
'indent': 2,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 368311.85,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 368311.85,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Creditors',
|
||||
'account': 'Creditors',
|
||||
'parent_account': 'Accounts Payable',
|
||||
'indent': 3,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 194871.85,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 194871.85,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Salary Payable',
|
||||
'account': 'Salary Payable',
|
||||
'parent_account': 'Accounts Payable',
|
||||
'indent': 3,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 173440.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 173440.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Duties and Taxes',
|
||||
'account': 'Duties and Taxes',
|
||||
'parent_account': 'Current Liabilities',
|
||||
'indent': 2,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 150146.822,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 150146.822,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'CGST',
|
||||
'account': 'CGST',
|
||||
'parent_account': 'Duties and Taxes',
|
||||
'indent': 3,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 51479.591,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 51479.591,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'IGST',
|
||||
'account': 'IGST',
|
||||
'parent_account': 'Duties and Taxes',
|
||||
'indent': 3,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 1944.0,
|
||||
'opening_credit': 0.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 1944.0,
|
||||
'closing_credit': 0.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'SGST',
|
||||
'account': 'SGST',
|
||||
'parent_account': 'Duties and Taxes',
|
||||
'indent': 3,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 97711.231,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 97711.231,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'UGST',
|
||||
'account': 'UGST',
|
||||
'parent_account': 'Duties and Taxes',
|
||||
'indent': 3,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 2900.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 2900.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Stock Liabilities',
|
||||
'account': 'Stock Liabilities',
|
||||
'parent_account': 'Current Liabilities',
|
||||
'indent': 2,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 1853169.33,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 1853169.33,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Stock Received But Not Billed',
|
||||
'account': 'Stock Received But Not Billed',
|
||||
'parent_account': 'Stock Liabilities',
|
||||
'indent': 3,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 1853169.33,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 1853169.33,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Equity',
|
||||
'account': 'Equity',
|
||||
'parent_account': null,
|
||||
'indent': 0,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 10000.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 10000.0,
|
||||
'has_value': true
|
||||
}, {
|
||||
'account_name': 'Capital Stock',
|
||||
'account': 'Capital Stock',
|
||||
'parent_account': 'Equity',
|
||||
'indent': 1,
|
||||
'from_date': '2018-04-01',
|
||||
'to_date': '2019-03-31',
|
||||
'currency': 'INR',
|
||||
'opening_debit': 0.0,
|
||||
'opening_credit': 10000.0,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 0.0,
|
||||
'closing_credit': 10000.0,
|
||||
'has_value': true
|
||||
}, {}, {
|
||||
'account': 'Total',
|
||||
'account_name': 'Total',
|
||||
'warn_if_negative': true,
|
||||
'opening_debit': 32260956.43,
|
||||
'opening_credit': 22618854.891999997,
|
||||
'debit': 0.0,
|
||||
'credit': 0.0,
|
||||
'closing_debit': 32260956.43,
|
||||
'closing_credit': 22618854.891999997,
|
||||
'parent_account': null,
|
||||
'indent': 0,
|
||||
'has_value': true,
|
||||
'currency': 'INR'
|
||||
}]
|
||||
};
|
||||
}
|
||||
172
docs/index.html
Normal file
172
docs/index.html
Normal file
@ -0,0 +1,172 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Frappe DataTable - A simple, modern datatable library for the web</title>
|
||||
<link href="assets/css/frappe-datatable.css" rel="stylesheet">
|
||||
<link href="assets/css/hljs-default.css" rel="stylesheet">
|
||||
<link href="assets/css/index.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar">
|
||||
<div class="container">
|
||||
<h3>Frappe DataTable</h3>
|
||||
<div class="navbar-links">
|
||||
<a href="https://github.com/frappe/datatable">GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container">
|
||||
|
||||
<section class="showcase align-center">
|
||||
<img class="logo" src="assets/img/data-table-logo.svg" />
|
||||
<h1 class="text-center">A simple, modern and interactive<br> datatable for the web</h1>
|
||||
<div class="example-1"></div>
|
||||
</section>
|
||||
|
||||
<section class="showcase installation">
|
||||
<h3>Installation</h3>
|
||||
<div class="code">
|
||||
<pre><code class="shell"># Install using yarn
|
||||
$ yarn add frappe-datatable
|
||||
|
||||
# or npm
|
||||
$ npm install frappe-datatable</code></pre>
|
||||
</section>
|
||||
|
||||
<section class="showcase installation">
|
||||
<h3>Usage</h3>
|
||||
<div class="code">
|
||||
<pre><code>import DataTable from 'frappe-datatable';
|
||||
|
||||
// or add
|
||||
// <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>
|
||||
276
index.html
276
index.html
File diff suppressed because one or more lines are too long
42
package.json
42
package.json
@ -1,42 +1,29 @@
|
||||
{
|
||||
"name": "frappe-datatable",
|
||||
"version": "0.0.0-development",
|
||||
"version": "0.0.10",
|
||||
"description": "A modern datatable library for the web",
|
||||
"main": "dist/frappe-datatable.cjs.js",
|
||||
"unpkg": "dist/frappe-datatable.min.js",
|
||||
"jsdelivr": "dist/frappe-datatable.min.js",
|
||||
"scripts": {
|
||||
"start": "yarn run dev",
|
||||
"build": "rollup -c && NODE_ENV=production rollup -c",
|
||||
"build": "rollup -c",
|
||||
"production": "rollup -c --production",
|
||||
"build:docs": "rollup -c --docs",
|
||||
"dev": "rollup -c -w",
|
||||
"cy:server": "http-server -p 8989",
|
||||
"cy:open": "cypress open",
|
||||
"cy:run": "cypress run",
|
||||
"test": "start-server-and-test cy:server http://localhost:8989 cy:run",
|
||||
"test-local": "start-server-and-test cy:server http://localhost:8989 cy:open",
|
||||
"travis-deploy-once": "travis-deploy-once",
|
||||
"semantic-release": "semantic-release",
|
||||
"lint": "eslint src",
|
||||
"lint-and-build": "yarn lint && yarn build",
|
||||
"commit": "npx git-cz"
|
||||
"test": "start-server-and-test cy:server http://localhost:8989 cy:run"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"src"
|
||||
],
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^9.0.0",
|
||||
"chai": "3.5.0",
|
||||
"cypress": "^9.2.0",
|
||||
"cz-conventional-changelog": "^2.1.0",
|
||||
"cypress": "3.0.1",
|
||||
"deepmerge": "^2.0.1",
|
||||
"eslint": "^5.0.1",
|
||||
"eslint-config-airbnb": "^16.1.0",
|
||||
"eslint-config-airbnb-base": "^12.1.0",
|
||||
"eslint-plugin-import": "^2.11.0",
|
||||
"http-server": "^0.11.1",
|
||||
"mocha": "3.3.0",
|
||||
"postcss-custom-properties": "^7.0.0",
|
||||
"postcss-cssnext": "^3.1.0",
|
||||
"postcss-nested": "^3.0.0",
|
||||
"rollup": "^0.59.4",
|
||||
"rollup-plugin-commonjs": "^8.3.0",
|
||||
@ -45,9 +32,7 @@
|
||||
"rollup-plugin-node-resolve": "^3.0.3",
|
||||
"rollup-plugin-postcss": "^1.2.8",
|
||||
"rollup-plugin-uglify-es": "^0.0.1",
|
||||
"semantic-release": "^17.1.1",
|
||||
"start-server-and-test": "^1.4.1",
|
||||
"travis-deploy-once": "^5.0.1"
|
||||
"start-server-and-test": "^1.4.1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -64,15 +49,10 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/frappe/datatable/issues"
|
||||
},
|
||||
"homepage": "https://frappe.io/datatable",
|
||||
"homepage": "https://frappe.github.io/datatable",
|
||||
"dependencies": {
|
||||
"hyperlist": "^1.0.0-beta",
|
||||
"clusterize.js": "^0.18.0",
|
||||
"lodash": "^4.17.5",
|
||||
"sortablejs": "^1.7.0"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "cz-conventional-changelog"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,12 +4,11 @@ import nodeResolve from 'rollup-plugin-node-resolve';
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import postcss from 'rollup-plugin-postcss';
|
||||
import nested from 'postcss-nested';
|
||||
import customProperties from 'postcss-custom-properties';
|
||||
import autoprefixer from 'autoprefixer';
|
||||
import cssnext from 'postcss-cssnext';
|
||||
import eslint from 'rollup-plugin-eslint';
|
||||
import merge from 'deepmerge';
|
||||
|
||||
const production = process.env.NODE_ENV === 'production';
|
||||
const production = process.argv[3] === '--production';
|
||||
|
||||
const baseJS = {
|
||||
input: 'src/index.js',
|
||||
@ -41,9 +40,8 @@ const baseCSS = {
|
||||
extract: true,
|
||||
minimize: production,
|
||||
plugins: [
|
||||
customProperties(),
|
||||
nested(),
|
||||
autoprefixer()
|
||||
cssnext()
|
||||
]
|
||||
})
|
||||
]
|
||||
@ -87,8 +85,24 @@ const prodCSS = merge(devCSS, {
|
||||
}
|
||||
});
|
||||
|
||||
// docs
|
||||
const docJS = merge(devIIFE, {
|
||||
output: {
|
||||
file: 'docs/assets/js/frappe-datatable.js'
|
||||
}
|
||||
});
|
||||
|
||||
const docCSS = merge(devCSS, {
|
||||
output: {
|
||||
file: 'docs/assets/css/frappe-datatable.css'
|
||||
}
|
||||
});
|
||||
|
||||
const developmentAssets = [devIIFE, devCjs, devCSS];
|
||||
const documentationAssets = [docJS, docCSS];
|
||||
const productionAssets = [prodIIFE, prodCSS];
|
||||
const assets = production ? productionAssets : developmentAssets;
|
||||
|
||||
const docs = process.argv[3] === '--docs';
|
||||
const assets = docs ? documentationAssets : production ? productionAssets : developmentAssets;
|
||||
|
||||
export default assets;
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import HyperList from 'hyperlist';
|
||||
import Clusterize from 'clusterize.js';
|
||||
import $ from './dom';
|
||||
import { nextTick } from './utils';
|
||||
|
||||
export default class BodyRenderer {
|
||||
constructor(instance) {
|
||||
@ -8,115 +10,60 @@ export default class BodyRenderer {
|
||||
this.rowmanager = instance.rowmanager;
|
||||
this.cellmanager = instance.cellmanager;
|
||||
this.bodyScrollable = instance.bodyScrollable;
|
||||
this.footer = this.instance.footer;
|
||||
this.log = instance.log;
|
||||
}
|
||||
|
||||
renderRows(rows) {
|
||||
this.visibleRows = rows;
|
||||
this.visibleRowIndices = rows.map(row => row.meta.rowIndex);
|
||||
|
||||
if (rows.length === 0) {
|
||||
this.bodyScrollable.innerHTML = this.getNoDataHTML();
|
||||
this.footer.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a temporary set for faster lookups.
|
||||
// We can't change this.visibleRowIndices as it would be breaking for users.
|
||||
let visibleRowIndicesSet = new Set(this.visibleRowIndices);
|
||||
const rowViewOrder = this.datamanager.rowViewOrder.map(index => {
|
||||
if (visibleRowIndicesSet.has(index)) {
|
||||
return index;
|
||||
}
|
||||
return null;
|
||||
}).filter(index => index !== null);
|
||||
|
||||
const computedStyle = getComputedStyle(this.bodyScrollable);
|
||||
|
||||
let config = {
|
||||
width: computedStyle.width,
|
||||
height: computedStyle.height,
|
||||
itemHeight: this.options.cellHeight,
|
||||
total: rows.length,
|
||||
generate: (index) => {
|
||||
const el = document.createElement('div');
|
||||
const rowIndex = rowViewOrder[index];
|
||||
const row = this.datamanager.getRow(rowIndex);
|
||||
const rowHTML = this.rowmanager.getRowHTML(row, row.meta);
|
||||
el.innerHTML = rowHTML;
|
||||
return el.children[0];
|
||||
},
|
||||
afterRender: () => {
|
||||
this.restoreState();
|
||||
}
|
||||
};
|
||||
|
||||
if (!this.hyperlist) {
|
||||
this.hyperlist = new HyperList(this.bodyScrollable, config);
|
||||
} else {
|
||||
this.hyperlist.refresh(this.bodyScrollable, config);
|
||||
}
|
||||
|
||||
this.renderFooter();
|
||||
this.appendRemainingData = nextTick(this.appendRemainingData, this);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.options.clusterize) {
|
||||
this.renderBodyWithClusterize();
|
||||
} else {
|
||||
this.renderBodyHTML();
|
||||
}
|
||||
}
|
||||
|
||||
renderBodyHTML() {
|
||||
const rows = this.datamanager.getRowsForView();
|
||||
this.renderRows(rows);
|
||||
// setDimensions requires atleast 1 row to exist in dom
|
||||
|
||||
this.bodyScrollable.innerHTML = this.getBodyHTML(rows);
|
||||
this.instance.setDimensions();
|
||||
this.restoreState();
|
||||
}
|
||||
|
||||
renderFooter() {
|
||||
if (!this.options.showTotalRow) return;
|
||||
renderBodyWithClusterize() {
|
||||
// first page
|
||||
const rows = this.datamanager.getRowsForView(0, 20);
|
||||
let initialData = this.getDataForClusterize(rows);
|
||||
|
||||
const totalRow = this.getTotalRow();
|
||||
let html = this.rowmanager.getRowHTML(totalRow, { isTotalRow: 1, rowIndex: 'totalRow' });
|
||||
if (initialData.length === 0) {
|
||||
initialData = [this.getNoDataHTML()];
|
||||
}
|
||||
|
||||
this.footer.innerHTML = html;
|
||||
}
|
||||
if (!this.clusterize) {
|
||||
// empty body
|
||||
this.bodyScrollable.innerHTML = this.getBodyHTML([]);
|
||||
|
||||
getTotalRow() {
|
||||
const columns = this.datamanager.getColumns();
|
||||
const totalRowTemplate = columns.map(col => {
|
||||
let content = null;
|
||||
if (['_rowIndex', '_checkbox'].includes(col.id)) {
|
||||
content = '';
|
||||
}
|
||||
return {
|
||||
content,
|
||||
isTotalRow: 1,
|
||||
colIndex: col.colIndex,
|
||||
column: col
|
||||
};
|
||||
});
|
||||
// first 20 rows will appended
|
||||
// rest of them in nextTick
|
||||
this.clusterize = new Clusterize({
|
||||
rows: initialData,
|
||||
scrollElem: this.bodyScrollable,
|
||||
contentElem: $('tbody', this.bodyScrollable),
|
||||
callbacks: {
|
||||
clusterChanged: () => this.restoreState()
|
||||
},
|
||||
/* eslint-disable */
|
||||
show_no_data_row: false,
|
||||
/* eslint-enable */
|
||||
});
|
||||
|
||||
const totalRow = totalRowTemplate.map((cell, i) => {
|
||||
if (cell.content === '') return cell;
|
||||
// setDimensions requires atleast 1 row to exist in dom
|
||||
this.instance.setDimensions();
|
||||
} else {
|
||||
this.clusterize.update(initialData);
|
||||
}
|
||||
|
||||
if (this.options.hooks.columnTotal) {
|
||||
const columnValues = this.visibleRows.map(row => row[i].content);
|
||||
const result = this.options.hooks.columnTotal.call(this.instance, columnValues, cell);
|
||||
if (result != null) {
|
||||
cell.content = result;
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
|
||||
cell.content = this.visibleRows.reduce((acc, prevRow) => {
|
||||
const prevCell = prevRow[i];
|
||||
if (typeof prevCell.content === 'number') {
|
||||
if (acc == null) acc = 0;
|
||||
return acc + prevCell.content;
|
||||
}
|
||||
return acc;
|
||||
}, cell.content);
|
||||
|
||||
return cell;
|
||||
});
|
||||
|
||||
return totalRow;
|
||||
this.appendRemainingData();
|
||||
}
|
||||
|
||||
restoreState() {
|
||||
@ -125,6 +72,12 @@ export default class BodyRenderer {
|
||||
this.cellmanager.focusCellOnClusterChanged();
|
||||
}
|
||||
|
||||
appendRemainingData() {
|
||||
const rows = this.datamanager.getRowsForView(20);
|
||||
const data = this.getDataForClusterize(rows);
|
||||
this.clusterize.append(data);
|
||||
}
|
||||
|
||||
showToastMessage(message, hideAfter) {
|
||||
this.instance.toastMessage.innerHTML = this.getToastMessageHTML(message);
|
||||
|
||||
@ -139,6 +92,20 @@ export default class BodyRenderer {
|
||||
this.instance.toastMessage.innerHTML = '';
|
||||
}
|
||||
|
||||
getDataForClusterize(rows) {
|
||||
return rows.map(row => this.rowmanager.getRowHTML(row, row.meta));
|
||||
}
|
||||
|
||||
getBodyHTML(rows) {
|
||||
return `
|
||||
<table class="dt-body">
|
||||
<tbody>
|
||||
${rows.map(row => this.rowmanager.getRowHTML(row, row.meta)).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
}
|
||||
|
||||
getNoDataHTML() {
|
||||
return `<div class="dt-scrollable__no-data">${this.options.noDataMessage}</div>`;
|
||||
}
|
||||
|
||||
@ -2,11 +2,9 @@ import {
|
||||
copyTextToClipboard,
|
||||
makeDataAttributeString,
|
||||
throttle,
|
||||
linkProperties,
|
||||
escapeHTML,
|
||||
linkProperties
|
||||
} from './utils';
|
||||
import $ from './dom';
|
||||
import icons from './icons';
|
||||
|
||||
export default class CellManager {
|
||||
constructor(instance) {
|
||||
@ -15,18 +13,14 @@ export default class CellManager {
|
||||
'wrapper',
|
||||
'options',
|
||||
'style',
|
||||
'header',
|
||||
'bodyScrollable',
|
||||
'columnmanager',
|
||||
'rowmanager',
|
||||
'datamanager',
|
||||
'keyboard',
|
||||
'footer'
|
||||
'keyboard'
|
||||
]);
|
||||
|
||||
this.bindEvents();
|
||||
this.stickyRowWidth = 0;
|
||||
this.stickyColWitdh = [];
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
@ -55,12 +49,34 @@ export default class CellManager {
|
||||
this.activateEditing(this.$focusedCell);
|
||||
} else if (this.$editingCell) {
|
||||
// enter keypress on editing cell
|
||||
this.submitEditing();
|
||||
this.deactivateEditing();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bindKeyboardNav() {
|
||||
const focusCell = (direction) => {
|
||||
if (!this.$focusedCell || this.$editingCell) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let $cell = this.$focusedCell;
|
||||
|
||||
if (direction === 'left' || direction === 'shift+tab') {
|
||||
$cell = this.getLeftCell$($cell);
|
||||
} else if (direction === 'right' || direction === 'tab') {
|
||||
$cell = this.getRightCell$($cell);
|
||||
} else if (direction === 'up') {
|
||||
$cell = this.getAboveCell$($cell);
|
||||
} else if (direction === 'down') {
|
||||
$cell = this.getBelowCell$($cell);
|
||||
}
|
||||
|
||||
this.focusCell($cell);
|
||||
return true;
|
||||
};
|
||||
|
||||
const focusLastCell = (direction) => {
|
||||
if (!this.$focusedCell || this.$editingCell) {
|
||||
return false;
|
||||
@ -87,13 +103,13 @@ export default class CellManager {
|
||||
};
|
||||
|
||||
['left', 'right', 'up', 'down', 'tab', 'shift+tab']
|
||||
.map(direction => this.keyboard.on(direction, () => this.focusCellInDirection(direction)));
|
||||
.map(direction => this.keyboard.on(direction, () => focusCell(direction)));
|
||||
|
||||
['left', 'right', 'up', 'down']
|
||||
.map(direction => this.keyboard.on(`ctrl+${direction}`, () => focusLastCell(direction)));
|
||||
|
||||
this.keyboard.on('esc', () => {
|
||||
this.deactivateEditing(false);
|
||||
this.deactivateEditing();
|
||||
this.columnmanager.toggleFilter(false);
|
||||
});
|
||||
|
||||
@ -105,10 +121,6 @@ export default class CellManager {
|
||||
this.activateFilter(colIndex);
|
||||
return true;
|
||||
});
|
||||
|
||||
$.on(this.header, 'focusin', '.dt-filter', () => {
|
||||
this.unfocusCell(this.$focusedCell);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,10 +149,7 @@ export default class CellManager {
|
||||
bindCopyCellContents() {
|
||||
this.keyboard.on('ctrl+c', () => {
|
||||
const noOfCellsCopied = this.copyCellContents(this.$focusedCell, this.$selectionCursor);
|
||||
const message = this.instance.translate('{count} cells copied', {
|
||||
count: noOfCellsCopied
|
||||
});
|
||||
|
||||
const message = `${noOfCellsCopied} cell${noOfCellsCopied > 1 ? 's' : ''} copied`;
|
||||
if (noOfCellsCopied) {
|
||||
this.instance.showToastMessage(message, 2);
|
||||
}
|
||||
@ -175,14 +184,6 @@ export default class CellManager {
|
||||
mouseDown = false;
|
||||
});
|
||||
|
||||
if (this.options.showTotalRow) {
|
||||
$.on(this.footer, 'click', '.dt-cell', (e) => {
|
||||
|
||||
this.focusCell($(e.delegatedTarget));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
const selectArea = (e) => {
|
||||
if (!mouseDown) return;
|
||||
this.selectArea($(e.delegatedTarget));
|
||||
@ -205,9 +206,7 @@ export default class CellManager {
|
||||
}
|
||||
|
||||
focusCell($cell, {
|
||||
skipClearSelection = 0,
|
||||
skipDOMFocus = 0,
|
||||
skipScrollToCell = 0
|
||||
skipClearSelection = 0
|
||||
} = {}) {
|
||||
if (!$cell) return;
|
||||
|
||||
@ -227,9 +226,7 @@ export default class CellManager {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!skipScrollToCell) {
|
||||
this.scrollToCell($cell);
|
||||
}
|
||||
this.scrollToCell($cell);
|
||||
|
||||
this.deactivateEditing();
|
||||
if (!skipClearSelection) {
|
||||
@ -243,27 +240,12 @@ export default class CellManager {
|
||||
this.$focusedCell = $cell;
|
||||
$cell.classList.add('dt-cell--focus');
|
||||
|
||||
if (!skipDOMFocus) {
|
||||
// so that keyboard nav works
|
||||
$cell.focus();
|
||||
}
|
||||
// so that keyboard nav works
|
||||
$cell.focus();
|
||||
|
||||
this.highlightRowColumnHeader($cell);
|
||||
}
|
||||
|
||||
unfocusCell($cell) {
|
||||
if (!$cell) return;
|
||||
|
||||
// remove cell border
|
||||
$cell.classList.remove('dt-cell--focus');
|
||||
this.$focusedCell = null;
|
||||
|
||||
// reset header background
|
||||
if (this.lastHeaders) {
|
||||
this.lastHeaders.forEach(header => header && header.classList.remove('dt-cell--highlight'));
|
||||
}
|
||||
}
|
||||
|
||||
highlightRowColumnHeader($cell) {
|
||||
const {
|
||||
colIndex,
|
||||
@ -275,14 +257,14 @@ export default class CellManager {
|
||||
const rowHeaderSelector = `.dt-cell--${srNoColIndex}-${rowIndex}`;
|
||||
|
||||
if (this.lastHeaders) {
|
||||
this.lastHeaders.forEach(header => header && header.classList.remove('dt-cell--highlight'));
|
||||
this.lastHeaders.forEach(header => header.classList.remove('dt-cell--highlight'));
|
||||
}
|
||||
|
||||
const colHeader = $(colHeaderSelector, this.wrapper);
|
||||
const rowHeader = $(rowHeaderSelector, this.wrapper);
|
||||
|
||||
this.lastHeaders = [colHeader, rowHeader];
|
||||
this.lastHeaders.forEach(header => header && header.classList.add('dt-cell--highlight'));
|
||||
this.lastHeaders.forEach(header => header.classList.add('dt-cell--highlight'));
|
||||
}
|
||||
|
||||
selectAreaOnClusterChanged() {
|
||||
@ -312,15 +294,11 @@ export default class CellManager {
|
||||
const $cell = this.getCell$(colIndex, rowIndex);
|
||||
|
||||
if (!$cell) return;
|
||||
// this function is called after hyperlist renders the rows after scroll,
|
||||
// this function is called after selectAreaOnClusterChanged,
|
||||
// focusCell calls clearSelection which resets the area selection
|
||||
// so a flag to skip it
|
||||
// we also skip DOM focus and scroll to cell
|
||||
// because it fights with the user scroll
|
||||
this.focusCell($cell, {
|
||||
skipClearSelection: 1,
|
||||
skipDOMFocus: 1,
|
||||
skipScrollToCell: 1
|
||||
skipClearSelection: 1
|
||||
});
|
||||
}
|
||||
|
||||
@ -456,10 +434,7 @@ export default class CellManager {
|
||||
}
|
||||
}
|
||||
|
||||
deactivateEditing(submitValue = true) {
|
||||
if (submitValue) {
|
||||
this.submitEditing();
|
||||
}
|
||||
deactivateEditing() {
|
||||
// keep focus on the cell so that keyboard navigation works
|
||||
if (this.$focusedCell) this.$focusedCell.focus();
|
||||
|
||||
@ -510,9 +485,7 @@ export default class CellManager {
|
||||
}
|
||||
|
||||
submitEditing() {
|
||||
let promise = Promise.resolve();
|
||||
if (!this.$editingCell) return promise;
|
||||
|
||||
if (!this.$editingCell) return;
|
||||
const $cell = this.$editingCell;
|
||||
const {
|
||||
rowIndex,
|
||||
@ -531,15 +504,12 @@ export default class CellManager {
|
||||
valuePromise = Promise.resolve(valuePromise);
|
||||
}
|
||||
|
||||
promise = valuePromise.then((value) => {
|
||||
valuePromise.then((value) => {
|
||||
const done = editor.setValue(value, rowIndex, col);
|
||||
const oldValue = this.getCell(colIndex, rowIndex).content;
|
||||
|
||||
if (oldValue === value) return false;
|
||||
|
||||
const done = editor.setValue(value, rowIndex, col);
|
||||
|
||||
// update cell immediately
|
||||
this.updateCell(colIndex, rowIndex, value, true);
|
||||
this.updateCell(colIndex, rowIndex, value);
|
||||
$cell.focus();
|
||||
|
||||
if (done && done.then) {
|
||||
@ -549,13 +519,11 @@ export default class CellManager {
|
||||
this.updateCell(colIndex, rowIndex, oldValue);
|
||||
});
|
||||
}
|
||||
return done;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.currentCellEditor = null;
|
||||
return promise;
|
||||
}
|
||||
|
||||
copyCellContents($cell1, $cell2) {
|
||||
@ -563,18 +531,10 @@ export default class CellManager {
|
||||
// copy only focusedCell
|
||||
const {
|
||||
colIndex,
|
||||
rowIndex,
|
||||
isTotalRow
|
||||
rowIndex
|
||||
} = $.data($cell1);
|
||||
let copiedContent = '';
|
||||
if (isTotalRow) {
|
||||
let choosenFooterCell = this.$focusedCell;
|
||||
copiedContent = choosenFooterCell.children[0].title;
|
||||
} else {
|
||||
const cell = this.getCell(colIndex, rowIndex);
|
||||
copiedContent = cell.content;
|
||||
}
|
||||
copyTextToClipboard(copiedContent);
|
||||
const cell = this.getCell(colIndex, rowIndex);
|
||||
copyTextToClipboard(cell.content);
|
||||
return 1;
|
||||
}
|
||||
const cells = this.getCellsInRange($cell1, $cell2);
|
||||
@ -625,7 +585,7 @@ export default class CellManager {
|
||||
let rowIndex = i + focusedCell.rowIndex;
|
||||
row.forEach((cell, j) => {
|
||||
let colIndex = j + focusedCell.colIndex;
|
||||
this.updateCell(colIndex, rowIndex, cell, true);
|
||||
this.updateCell(colIndex, rowIndex, cell);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -636,20 +596,20 @@ export default class CellManager {
|
||||
|
||||
if (!this.columnmanager.isFilterShown) {
|
||||
// put focus back on cell
|
||||
this.$focusedCell && this.$focusedCell.focus();
|
||||
this.$focusedCell.focus();
|
||||
}
|
||||
}
|
||||
|
||||
updateCell(colIndex, rowIndex, value, refreshHtml = false) {
|
||||
updateCell(colIndex, rowIndex, value) {
|
||||
const cell = this.datamanager.updateCell(colIndex, rowIndex, {
|
||||
content: value
|
||||
});
|
||||
this.refreshCell(cell, refreshHtml);
|
||||
this.refreshCell(cell);
|
||||
}
|
||||
|
||||
refreshCell(cell, refreshHtml = false) {
|
||||
refreshCell(cell) {
|
||||
const $cell = $(this.selector(cell.colIndex, cell.rowIndex), this.bodyScrollable);
|
||||
$cell.innerHTML = this.getCellContent(cell, refreshHtml);
|
||||
$cell.innerHTML = this.getCellContent(cell);
|
||||
}
|
||||
|
||||
toggleTreeButton(rowIndex, flag) {
|
||||
@ -665,49 +625,6 @@ export default class CellManager {
|
||||
return colIndex < this.columnmanager.getFirstColumnIndex();
|
||||
}
|
||||
|
||||
focusCellInDirection(direction) {
|
||||
if (!this.$focusedCell || (this.$editingCell && ['left', 'right', 'up', 'down'].includes(direction))) {
|
||||
return false;
|
||||
} else if (this.$editingCell && ['tab', 'shift+tab'].includes(direction)) {
|
||||
this.deactivateEditing();
|
||||
}
|
||||
|
||||
let $cell = this.$focusedCell;
|
||||
|
||||
if (direction === 'left' || direction === 'shift+tab') {
|
||||
$cell = this.getLeftCell$($cell);
|
||||
} else if (direction === 'right' || direction === 'tab') {
|
||||
$cell = this.getRightCell$($cell);
|
||||
} else if (direction === 'up') {
|
||||
$cell = this.getAboveCell$($cell);
|
||||
} else if (direction === 'down') {
|
||||
$cell = this.getBelowCell$($cell);
|
||||
}
|
||||
|
||||
if (!$cell) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const {
|
||||
colIndex
|
||||
} = $.data($cell);
|
||||
const column = this.columnmanager.getColumn(colIndex);
|
||||
|
||||
if (!column.focusable) {
|
||||
let $prevFocusedCell = this.$focusedCell;
|
||||
this.unfocusCell($prevFocusedCell);
|
||||
this.$focusedCell = $cell;
|
||||
let ret = this.focusCellInDirection(direction);
|
||||
if (!ret) {
|
||||
this.focusCell($prevFocusedCell);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
this.focusCell($cell);
|
||||
return true;
|
||||
}
|
||||
|
||||
getCell$(colIndex, rowIndex) {
|
||||
return $(this.selector(colIndex, rowIndex), this.bodyScrollable);
|
||||
}
|
||||
@ -773,7 +690,7 @@ export default class CellManager {
|
||||
}
|
||||
|
||||
scrollToCell($cell) {
|
||||
if ($.inViewport($cell, this.bodyScrollable) || $.inViewport($cell, this.footer)) return false;
|
||||
if ($.inViewport($cell, this.bodyScrollable)) return false;
|
||||
|
||||
const {
|
||||
rowIndex
|
||||
@ -791,54 +708,16 @@ export default class CellManager {
|
||||
rowIndex,
|
||||
colIndex,
|
||||
isHeader,
|
||||
isFilter,
|
||||
isTotalRow
|
||||
isFilter
|
||||
} = cell;
|
||||
const dataAttr = makeDataAttributeString({
|
||||
rowIndex,
|
||||
colIndex,
|
||||
isHeader,
|
||||
isFilter,
|
||||
isTotalRow
|
||||
isFilter
|
||||
});
|
||||
|
||||
let styles = '';
|
||||
|
||||
const row = this.datamanager.getRow(rowIndex);
|
||||
|
||||
const isBodyCell = !(isHeader || isFilter || isTotalRow);
|
||||
|
||||
const serialNoColIndex = !this.options.checkboxColumn && this.options.serialNoColumn ? 0 : 1;
|
||||
|
||||
let sticky = false;
|
||||
|
||||
let checkboxserialNoclass = '';
|
||||
|
||||
if (colIndex === 0 && this.options.checkboxColumn) {
|
||||
if (cell.isHeader && !(cell.id in this.stickyColWitdh)) this.stickyRowWidth = 34;
|
||||
checkboxserialNoclass = 'dt-cell-checkbox';
|
||||
sticky = true;
|
||||
} else if (colIndex === serialNoColIndex && this.options.serialNoColumn) {
|
||||
if (cell.isHeader && !(cell.id in this.stickyColWitdh)) {
|
||||
this.stickyColWitdh[cell.id] = this.stickyRowWidth;
|
||||
this.stickyRowWidth += (cell.width || 37);
|
||||
checkboxserialNoclass = 'dt-cell-serial-no';
|
||||
}
|
||||
styles = `left:${this.stickyColWitdh[isBodyCell ? cell.column.id : cell.id]}px;`;
|
||||
sticky = true;
|
||||
|
||||
} else if (cell.sticky) {
|
||||
if (cell.isHeader && !(cell.id in this.stickyColWitdh)) {
|
||||
this.stickyColWitdh[cell.id] = this.stickyRowWidth;
|
||||
this.stickyRowWidth += ((cell.width || 100) + 1);
|
||||
}
|
||||
styles = `left:${this.stickyColWitdh[cell.id]}px;`;
|
||||
sticky = true;
|
||||
|
||||
} else if ((isBodyCell || isTotalRow) && cell.column.sticky) {
|
||||
styles = `left:${this.stickyColWitdh[cell.column.id]}px;`;
|
||||
sticky = true;
|
||||
}
|
||||
const isBodyCell = !(isHeader || isFilter);
|
||||
|
||||
const className = [
|
||||
'dt-cell',
|
||||
@ -847,20 +726,17 @@ export default class CellManager {
|
||||
isBodyCell ? 'dt-cell--row-' + rowIndex : '',
|
||||
isHeader ? 'dt-cell--header' : '',
|
||||
isHeader ? `dt-cell--header-${colIndex}` : '',
|
||||
isFilter ? 'dt-cell--filter' : '',
|
||||
isBodyCell && (row && row.meta.isTreeNodeClose) ? 'dt-cell--tree-close' : '',
|
||||
sticky ? 'dt-sticky-col' : '',
|
||||
checkboxserialNoclass,
|
||||
isFilter ? 'dt-cell--filter' : ''
|
||||
].join(' ');
|
||||
|
||||
return `
|
||||
<div class="${className}" ${dataAttr} tabindex="0" style="${styles}">
|
||||
<td class="${className}" ${dataAttr} tabindex="0">
|
||||
${this.getCellContent(cell)}
|
||||
</div>
|
||||
</td>
|
||||
`;
|
||||
}
|
||||
|
||||
getCellContent(cell, refreshHtml = false) {
|
||||
getCellContent(cell) {
|
||||
const {
|
||||
isHeader,
|
||||
isFilter,
|
||||
@ -883,41 +759,31 @@ export default class CellManager {
|
||||
const hasDropdown = isHeader && cell.dropdown !== false;
|
||||
const dropdown = hasDropdown ? this.columnmanager.getDropdownHTML() : '';
|
||||
|
||||
let customFormatter = CellManager.getCustomCellFormatter(cell);
|
||||
const customFormatter = cell.format || (cell.column && cell.column.format) || null;
|
||||
|
||||
let contentHTML;
|
||||
if (isHeader || isFilter || !customFormatter) {
|
||||
contentHTML = cell.content;
|
||||
} else {
|
||||
if (!cell.html || refreshHtml) {
|
||||
const row = this.datamanager.getRow(cell.rowIndex);
|
||||
const data = this.datamanager.getData(cell.rowIndex);
|
||||
contentHTML = customFormatter(cell.content, row, cell.column, data);
|
||||
} else {
|
||||
contentHTML = cell.html;
|
||||
}
|
||||
const row = this.datamanager.getRow(cell.rowIndex);
|
||||
const data = this.datamanager.getData(cell.rowIndex);
|
||||
contentHTML = customFormatter(cell.content, row, cell.column, data);
|
||||
}
|
||||
|
||||
cell.html = contentHTML;
|
||||
|
||||
if (this.options.treeView && !(isHeader || isFilter) && cell.indent !== undefined) {
|
||||
const nextRow = this.datamanager.getRow(cell.rowIndex + 1);
|
||||
const addToggle = nextRow && nextRow.meta.indent > cell.indent;
|
||||
const leftPadding = 20;
|
||||
const unit = 'px';
|
||||
|
||||
const leftPadding = 1;
|
||||
|
||||
// Add toggle and indent in the first column
|
||||
const firstColumnIndex = this.datamanager.getColumnIndexById('_rowIndex') + 1;
|
||||
if (firstColumnIndex === cell.colIndex) {
|
||||
const padding = ((cell.indent || 0)) * leftPadding;
|
||||
const padding = ((cell.indent || 0) + 1) * leftPadding;
|
||||
const toggleHTML = addToggle ?
|
||||
`<span class="dt-tree-node__toggle" style="left: ${padding - leftPadding}${unit}">
|
||||
<span class="icon-open">${icons.chevronDown}</span>
|
||||
<span class="icon-close">${icons.chevronRight}</span>
|
||||
</span>` : '';
|
||||
contentHTML = `<span class="dt-tree-node" style="padding-left: ${padding}${unit}">
|
||||
${toggleHTML}
|
||||
<span>${contentHTML}</span>
|
||||
</span>`;
|
||||
`<span class="dt-tree-node__toggle" style="left: ${padding - leftPadding}rem"></span>` : '';
|
||||
contentHTML = `<span class="dt-tree-node" style="padding-left: ${padding}rem">
|
||||
${toggleHTML}${contentHTML}</span>`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -926,7 +792,7 @@ export default class CellManager {
|
||||
isHeader ? `dt-cell__content--header-${colIndex}` : `dt-cell__content--col-${colIndex}`
|
||||
].join(' ');
|
||||
|
||||
let cellContentHTML = `
|
||||
return `
|
||||
<div class="${className}">
|
||||
${contentHTML}
|
||||
${sortIndicator}
|
||||
@ -935,16 +801,6 @@ export default class CellManager {
|
||||
</div>
|
||||
${editCellHTML}
|
||||
`;
|
||||
|
||||
let div = document.createElement('div');
|
||||
div.innerHTML = contentHTML;
|
||||
|
||||
let textContent = div.textContent;
|
||||
textContent = textContent.replace(/\s+/g, ' ').trim();
|
||||
|
||||
cellContentHTML = cellContentHTML.replace('>', ` title="${escapeHTML(textContent)}">`);
|
||||
|
||||
return cellContentHTML;
|
||||
}
|
||||
|
||||
getEditCellHTML(colIndex) {
|
||||
@ -954,8 +810,4 @@ export default class CellManager {
|
||||
selector(colIndex, rowIndex) {
|
||||
return `.dt-cell--${colIndex}-${rowIndex}`;
|
||||
}
|
||||
|
||||
static getCustomCellFormatter(cell) {
|
||||
return cell.format || (cell.column && cell.column.format) || null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,19 +14,17 @@ export default class ColumnManager {
|
||||
'fireEvent',
|
||||
'header',
|
||||
'datamanager',
|
||||
'cellmanager',
|
||||
'style',
|
||||
'wrapper',
|
||||
'rowmanager',
|
||||
'bodyScrollable',
|
||||
'bodyRenderer'
|
||||
'bodyScrollable'
|
||||
]);
|
||||
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
renderHeader() {
|
||||
this.header.innerHTML = '<div></div>';
|
||||
this.header.innerHTML = '<thead></thead>';
|
||||
this.refreshHeader();
|
||||
}
|
||||
|
||||
@ -34,9 +32,9 @@ export default class ColumnManager {
|
||||
const columns = this.datamanager.getColumns();
|
||||
|
||||
// refresh html
|
||||
$('div', this.header).innerHTML = this.getHeaderHTML(columns);
|
||||
$('thead', this.header).innerHTML = this.getHeaderHTML(columns);
|
||||
|
||||
this.$filterRow = $('.dt-row-filter', this.header);
|
||||
this.$filterRow = $('.dt-row[data-is-filter]', this.header);
|
||||
if (this.$filterRow) {
|
||||
$.style(this.$filterRow, { display: 'none' });
|
||||
}
|
||||
@ -60,77 +58,50 @@ export default class ColumnManager {
|
||||
bindEvents() {
|
||||
this.bindDropdown();
|
||||
this.bindResizeColumn();
|
||||
this.bindPerfectColumnWidth();
|
||||
this.bindFilter();
|
||||
}
|
||||
|
||||
bindDropdown() {
|
||||
let $activeDropdown;
|
||||
let activeClass = 'dt-dropdown--active';
|
||||
let toggleClass = '.dt-dropdown__toggle';
|
||||
let dropdownClass = '.dt-dropdown__list';
|
||||
|
||||
// attach the dropdown list to container
|
||||
this.instance.dropdownContainer.innerHTML = this.getDropdownListHTML();
|
||||
this.$dropdownList = this.instance.dropdownContainer.firstElementChild;
|
||||
$.on(this.header, 'click', toggleClass, (e, $button) => {
|
||||
const $dropdown = $.closest('.dt-dropdown', $button);
|
||||
|
||||
$.on(this.header, 'click', toggleClass, e => {
|
||||
this.openDropdown(e);
|
||||
if (!$dropdown.classList.contains(activeClass)) {
|
||||
deactivateDropdown();
|
||||
$dropdown.classList.add(activeClass);
|
||||
$activeDropdown = $dropdown;
|
||||
} else {
|
||||
deactivateDropdown();
|
||||
}
|
||||
});
|
||||
|
||||
const deactivateDropdownOnBodyClick = (e) => {
|
||||
const selector = [
|
||||
toggleClass, toggleClass + ' *',
|
||||
dropdownClass, dropdownClass + ' *'
|
||||
].join(',');
|
||||
if (e.target.matches(selector)) return;
|
||||
$.on(document.body, 'click', (e) => {
|
||||
if (e.target.matches(toggleClass)) return;
|
||||
deactivateDropdown();
|
||||
};
|
||||
$.on(document.body, 'click', deactivateDropdownOnBodyClick);
|
||||
document.addEventListener('scroll', deactivateDropdown, true);
|
||||
|
||||
this.instance.on('onDestroy', () => {
|
||||
$.off(document.body, 'click', deactivateDropdownOnBodyClick);
|
||||
$.off(document, 'scroll', deactivateDropdown);
|
||||
});
|
||||
|
||||
$.on(this.$dropdownList, 'click', '.dt-dropdown__list-item', (e, $item) => {
|
||||
if (!this._dropdownActiveColIndex) return;
|
||||
const dropdownItems = this.options.headerDropdown;
|
||||
const { index } = $.data($item);
|
||||
const colIndex = this._dropdownActiveColIndex;
|
||||
const dropdownItems = this.options.headerDropdown;
|
||||
|
||||
$.on(this.header, 'click', '.dt-dropdown__list-item', (e, $item) => {
|
||||
const $col = $.closest('.dt-cell', $item);
|
||||
const {
|
||||
index
|
||||
} = $.data($item);
|
||||
const {
|
||||
colIndex
|
||||
} = $.data($col);
|
||||
let callback = dropdownItems[index].action;
|
||||
|
||||
callback && callback.call(this.instance, this.getColumn(colIndex));
|
||||
this.hideDropdown();
|
||||
});
|
||||
|
||||
const _this = this;
|
||||
function deactivateDropdown(e) {
|
||||
_this.hideDropdown();
|
||||
$activeDropdown && $activeDropdown.classList.remove(activeClass);
|
||||
$activeDropdown = null;
|
||||
}
|
||||
|
||||
this.hideDropdown();
|
||||
}
|
||||
|
||||
openDropdown(e) {
|
||||
if (!this._dropdownWidth) {
|
||||
$.style(this.$dropdownList, { display: '' });
|
||||
this._dropdownWidth = $.style(this.$dropdownList, 'width');
|
||||
}
|
||||
$.style(this.$dropdownList, {
|
||||
display: '',
|
||||
left: (e.clientX - this._dropdownWidth + 4) + 'px',
|
||||
top: (e.clientY + 4) + 'px'
|
||||
});
|
||||
const $cell = $.closest('.dt-cell', e.target);
|
||||
const { colIndex } = $.data($cell);
|
||||
this._dropdownActiveColIndex = colIndex;
|
||||
}
|
||||
|
||||
hideDropdown() {
|
||||
$.style(this.$dropdownList, {
|
||||
display: 'none'
|
||||
});
|
||||
this._dropdownActiveColIndex = null;
|
||||
}
|
||||
|
||||
bindResizeColumn() {
|
||||
@ -155,7 +126,7 @@ export default class ColumnManager {
|
||||
startX = e.pageX;
|
||||
});
|
||||
|
||||
const onMouseup = (e) => {
|
||||
$.on(document.body, 'mouseup', (e) => {
|
||||
document.body.classList.remove('dt-resize');
|
||||
if (!$resizingCell) return;
|
||||
isDragging = false;
|
||||
@ -166,74 +137,27 @@ export default class ColumnManager {
|
||||
this.setColumnWidth(colIndex);
|
||||
this.style.setBodyStyle();
|
||||
$resizingCell = null;
|
||||
};
|
||||
$.on(document.body, 'mouseup', onMouseup);
|
||||
this.instance.on('onDestroy', () => {
|
||||
$.off(document.body, 'mouseup', onMouseup);
|
||||
});
|
||||
|
||||
const onMouseMove = (e) => {
|
||||
$.on(document.body, 'mousemove', (e) => {
|
||||
if (!isDragging) return;
|
||||
let delta = e.pageX - startX;
|
||||
if (this.options.direction === 'rtl') {
|
||||
delta = -1 * delta;
|
||||
}
|
||||
const finalWidth = startWidth + delta;
|
||||
const finalWidth = startWidth + (e.pageX - startX);
|
||||
const {
|
||||
colIndex
|
||||
} = $.data($resizingCell);
|
||||
|
||||
let columnMinWidth = this.options.minimumColumnWidth;
|
||||
if (columnMinWidth > finalWidth) {
|
||||
// don't resize past 30 pixels
|
||||
if (this.getColumnMinWidth(colIndex) > finalWidth) {
|
||||
// don't resize past minWidth
|
||||
return;
|
||||
}
|
||||
this.datamanager.updateColumn(colIndex, {
|
||||
width: finalWidth
|
||||
});
|
||||
this.setColumnHeaderWidth(colIndex);
|
||||
};
|
||||
$.on(document.body, 'mousemove', onMouseMove);
|
||||
this.instance.on('onDestroy', () => {
|
||||
$.off(document.body, 'mousemove', onMouseMove);
|
||||
});
|
||||
}
|
||||
|
||||
bindPerfectColumnWidth() {
|
||||
$.on(this.header, 'dblclick', '.dt-cell .dt-cell__resize-handle', (e, $handle) => {
|
||||
const $cell = $handle.parentNode.parentNode;
|
||||
const { colIndex } = $.data($cell);
|
||||
|
||||
let longestCell = this.bodyRenderer.visibleRows
|
||||
.map(d => d[colIndex])
|
||||
.reduce((acc, curr) => acc.content.length > curr.content.length ? acc : curr);
|
||||
|
||||
let $longestCellHTML = this.cellmanager.getCellHTML(longestCell);
|
||||
let $div = document.createElement('div');
|
||||
$div.innerHTML = $longestCellHTML;
|
||||
let cellText = $div.querySelector('.dt-cell__content').textContent;
|
||||
|
||||
let {
|
||||
borderLeftWidth,
|
||||
borderRightWidth,
|
||||
paddingLeft,
|
||||
paddingRight
|
||||
} = $.getStyle(this.bodyScrollable.querySelector('.dt-cell__content'));
|
||||
|
||||
let padding = [borderLeftWidth, borderRightWidth, paddingLeft, paddingRight]
|
||||
.map(parseFloat)
|
||||
.reduce((sum, val) => sum + val);
|
||||
|
||||
let width = $.measureTextWidth(cellText) + padding;
|
||||
this.datamanager.updateColumn(colIndex, { width });
|
||||
this.setColumnHeaderWidth(colIndex);
|
||||
this.setColumnWidth(colIndex);
|
||||
});
|
||||
}
|
||||
|
||||
bindMoveColumn() {
|
||||
if (this.options.disableReorderColumn) return;
|
||||
|
||||
const $parent = $('.dt-row', this.header);
|
||||
|
||||
this.sortable = Sortable.create($parent, {
|
||||
@ -304,8 +228,6 @@ export default class ColumnManager {
|
||||
}
|
||||
|
||||
toggleFilter(flag) {
|
||||
if (!this.options.inlineFilters) return;
|
||||
|
||||
let showFilter;
|
||||
if (flag === undefined) {
|
||||
showFilter = !this.isFilterShown;
|
||||
@ -326,38 +248,31 @@ export default class ColumnManager {
|
||||
focusFilter(colIndex) {
|
||||
if (!this.isFilterShown) return;
|
||||
|
||||
const $filterInput = $(`.dt-cell--col-${colIndex} .dt-filter`, this.$filterRow);
|
||||
const $filterInput = $(`[data-col-index="${colIndex}"] .dt-filter`, this.$filterRow);
|
||||
$filterInput.focus();
|
||||
}
|
||||
|
||||
bindFilter() {
|
||||
if (!this.options.inlineFilters) return;
|
||||
const handler = e => {
|
||||
this.applyFilter(this.getAppliedFilters());
|
||||
const $filterCell = $.closest('.dt-cell', e.target);
|
||||
const {
|
||||
colIndex
|
||||
} = $.data($filterCell);
|
||||
const keyword = e.target.value;
|
||||
|
||||
this.datamanager.filterRows(keyword, colIndex)
|
||||
.then(({
|
||||
rowsToHide,
|
||||
rowsToShow
|
||||
}) => {
|
||||
this.rowmanager.hideRows(rowsToHide);
|
||||
this.rowmanager.showRows(rowsToShow);
|
||||
});
|
||||
};
|
||||
$.on(this.header, 'keydown', '.dt-filter', debounce(handler, 300));
|
||||
}
|
||||
|
||||
applyFilter(filters) {
|
||||
this.datamanager.filterRows(filters)
|
||||
.then(({
|
||||
rowsToShow
|
||||
}) => {
|
||||
this.rowmanager.showRows(rowsToShow);
|
||||
});
|
||||
}
|
||||
|
||||
getAppliedFilters() {
|
||||
const filters = {};
|
||||
$.each('.dt-filter', this.header).map((input) => {
|
||||
const value = input.value;
|
||||
if (value) {
|
||||
filters[input.dataset.colIndex] = value;
|
||||
}
|
||||
});
|
||||
return filters;
|
||||
}
|
||||
|
||||
applyDefaultSortOrder() {
|
||||
// sort rows if any 1 column has a default sortOrder set
|
||||
const columnsToSort = this.getColumns().filter(col => col.sortOrder !== 'none');
|
||||
@ -432,24 +347,17 @@ export default class ColumnManager {
|
||||
}
|
||||
|
||||
getDropdownHTML() {
|
||||
const { dropdownButton } = this.options;
|
||||
const { dropdownButton, headerDropdown: dropdownItems } = this.options;
|
||||
|
||||
return `
|
||||
<div class="dt-dropdown">
|
||||
<div class="dt-dropdown__toggle">${dropdownButton}</div>
|
||||
<div class="dt-dropdown__list">
|
||||
${dropdownItems.map((d, i) => `
|
||||
<div class="dt-dropdown__list-item" data-index="${i}">${d.label}</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
getDropdownListHTML() {
|
||||
const { headerDropdown: dropdownItems } = this.options;
|
||||
|
||||
return `
|
||||
<div class="dt-dropdown__list">
|
||||
${dropdownItems.map((d, i) => `
|
||||
<div class="dt-dropdown__list-item" data-index="${i}">${d.label}</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
11
src/dark.css
11
src/dark.css
@ -1,11 +0,0 @@
|
||||
.datatable {
|
||||
--dt-border-color: #424242;
|
||||
--dt-light-bg: #2e3538;
|
||||
--dt-text-color: #dfe2e5;
|
||||
--dt-text-light: #dfe2e5;
|
||||
--dt-cell-bg: #1c1f20;
|
||||
--dt-focus-border-width: 1px;
|
||||
--dt-selection-highlight-color: var(--dt-light-bg);
|
||||
--dt-toast-message-border: 1px solid var(--dt-border-color);
|
||||
--dt-header-cell-bg: #262c2e;
|
||||
}
|
||||
@ -11,7 +11,7 @@ export default class DataManager {
|
||||
this.sortRows = nextTick(this.sortRows, this);
|
||||
this.switchColumn = nextTick(this.switchColumn, this);
|
||||
this.removeColumn = nextTick(this.removeColumn, this);
|
||||
this.options.filterRows = nextTick(this.options.filterRows, this);
|
||||
this.filterRows = nextTick(this.filterRows, this);
|
||||
}
|
||||
|
||||
init(data, columns) {
|
||||
@ -29,11 +29,11 @@ export default class DataManager {
|
||||
this.rows = [];
|
||||
|
||||
this.prepareColumns();
|
||||
this.validateData(this.data);
|
||||
this.rows = this.prepareRows(this.data);
|
||||
this.prepareRows();
|
||||
this.prepareTreeRows();
|
||||
this.prepareRowView();
|
||||
this.prepareNumericColumns();
|
||||
this.prepareGroupBy();
|
||||
}
|
||||
|
||||
// computed property
|
||||
@ -142,8 +142,77 @@ export default class DataManager {
|
||||
});
|
||||
}
|
||||
|
||||
prepareRows(data) {
|
||||
return data.map((d, i) => {
|
||||
prepareGroupBy() {
|
||||
if (!this.options.groupBy) return;
|
||||
|
||||
const groupKey = this.options.groupBy;
|
||||
const column = this.columns.find(column => column.id === groupKey);
|
||||
|
||||
if (!column) {
|
||||
throw new Error('Invalid column id provided in groupBy option');
|
||||
}
|
||||
|
||||
const groups =
|
||||
this.rows
|
||||
// get values
|
||||
.map(row => row[column.colIndex].content)
|
||||
// remove duplicates
|
||||
.filter((value, i, self) => {
|
||||
return self.indexOf(value) === i;
|
||||
})
|
||||
.sort();
|
||||
|
||||
const rowsByGroup = {};
|
||||
|
||||
for (const row of this.rows) {
|
||||
const groupKey = row[column.colIndex].content;
|
||||
rowsByGroup[groupKey] = rowsByGroup[groupKey] || [];
|
||||
rowsByGroup[groupKey].push(row);
|
||||
}
|
||||
|
||||
let rows = [];
|
||||
|
||||
const makeGroupRow = (groupValue) => {
|
||||
const row = this.getColumns().map(c => ({ editable: false }));
|
||||
const firstColumnIndex = this.getStandardColumnCount();
|
||||
row[firstColumnIndex] = groupValue;
|
||||
const meta = {
|
||||
indent: 0
|
||||
};
|
||||
|
||||
return this.prepareRow(row, meta);
|
||||
};
|
||||
|
||||
for (let groupKey of groups) {
|
||||
rowsByGroup[groupKey].forEach(row => {
|
||||
row.meta.indent = 1;
|
||||
});
|
||||
|
||||
rows = [
|
||||
...rows,
|
||||
makeGroupRow(groupKey),
|
||||
...rowsByGroup[groupKey]
|
||||
];
|
||||
}
|
||||
|
||||
this.rows = rows.map((row, i) => {
|
||||
row.meta.rowIndex = i;
|
||||
|
||||
row.forEach(cell => {
|
||||
cell.rowIndex = i;
|
||||
cell.indent = row.meta.indent;
|
||||
});
|
||||
|
||||
row[1].content = i + 1;
|
||||
|
||||
return row;
|
||||
});
|
||||
}
|
||||
|
||||
prepareRows() {
|
||||
this.validateData(this.data);
|
||||
|
||||
this.rows = this.data.map((d, i) => {
|
||||
const index = this._getNextRowCount();
|
||||
|
||||
let row = [];
|
||||
@ -192,7 +261,6 @@ export default class DataManager {
|
||||
row.meta.isLeaf = !nextRow ||
|
||||
notSet(nextRow.meta.indent) ||
|
||||
nextRow.meta.indent <= row.meta.indent;
|
||||
row.meta.isTreeNodeClose = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -242,9 +310,8 @@ export default class DataManager {
|
||||
|
||||
appendRows(rows) {
|
||||
this.validateData(rows);
|
||||
this.rows = this.rows.concat(this.prepareRows(rows));
|
||||
this.prepareTreeRows();
|
||||
this.prepareRowView();
|
||||
|
||||
this.rows.push(...this.prepareRows(rows));
|
||||
}
|
||||
|
||||
sortRows(colIndex, sortOrder = 'none') {
|
||||
@ -280,11 +347,8 @@ export default class DataManager {
|
||||
this.rowViewOrder.sort((a, b) => {
|
||||
const aIndex = a;
|
||||
const bIndex = b;
|
||||
|
||||
let aContent = this.getCell(colIndex, a).content;
|
||||
let bContent = this.getCell(colIndex, b).content;
|
||||
aContent = aContent == null ? '' : aContent;
|
||||
bContent = bContent == null ? '' : bContent;
|
||||
const aContent = this.getCell(colIndex, a).content;
|
||||
const bContent = this.getCell(colIndex, b).content;
|
||||
|
||||
if (sortOrder === 'none') {
|
||||
return aIndex - bIndex;
|
||||
@ -426,37 +490,32 @@ export default class DataManager {
|
||||
return column;
|
||||
}
|
||||
|
||||
filterRows(filters) {
|
||||
return this.options.filterRows(this.rows, filters, this)
|
||||
.then(result => {
|
||||
if (!result) {
|
||||
result = this.getAllRowIndices();
|
||||
}
|
||||
filterRows(keyword, colIndex) {
|
||||
let rowsToHide = [];
|
||||
let rowsToShow = [];
|
||||
const cells = this.rows.map(row => row[colIndex]);
|
||||
|
||||
if (!result.then) {
|
||||
result = Promise.resolve(result);
|
||||
}
|
||||
cells.forEach(cell => {
|
||||
const hay = String(cell.content || '').toLowerCase();
|
||||
const needle = (keyword || '').toLowerCase();
|
||||
|
||||
return result.then(rowsToShow => {
|
||||
this._filteredRows = rowsToShow;
|
||||
if (!needle || hay.includes(needle)) {
|
||||
rowsToShow.push(cell.rowIndex);
|
||||
} else {
|
||||
rowsToHide.push(cell.rowIndex);
|
||||
}
|
||||
});
|
||||
|
||||
const rowsToHide = this.getAllRowIndices()
|
||||
.filter(index => !rowsToShow.includes(index));
|
||||
this._filteredRows = rowsToShow;
|
||||
|
||||
return {
|
||||
rowsToHide,
|
||||
rowsToShow
|
||||
};
|
||||
});
|
||||
});
|
||||
return {
|
||||
rowsToHide,
|
||||
rowsToShow
|
||||
};
|
||||
}
|
||||
|
||||
getFilteredRowIndices() {
|
||||
return this._filteredRows || this.getAllRowIndices();
|
||||
}
|
||||
|
||||
getAllRowIndices() {
|
||||
return this.rows.map(row => row.meta.rowIndex);
|
||||
return this._filteredRows || this.rows.map(row => row.meta.rowIndex);
|
||||
}
|
||||
|
||||
getRowCount() {
|
||||
|
||||
@ -6,18 +6,7 @@ import RowManager from './rowmanager';
|
||||
import BodyRenderer from './body-renderer';
|
||||
import Style from './style';
|
||||
import Keyboard from './keyboard';
|
||||
import TranslationManager from './translationmanager';
|
||||
import getDefaultOptions from './defaults';
|
||||
|
||||
let defaultComponents = {
|
||||
DataManager,
|
||||
CellManager,
|
||||
ColumnManager,
|
||||
RowManager,
|
||||
BodyRenderer,
|
||||
Style,
|
||||
Keyboard
|
||||
};
|
||||
import DEFAULT_OPTIONS from './defaults';
|
||||
|
||||
class DataTable {
|
||||
constructor(wrapper, options) {
|
||||
@ -32,11 +21,16 @@ class DataTable {
|
||||
throw new Error('Invalid argument given for `wrapper`');
|
||||
}
|
||||
|
||||
this.initializeTranslations(options);
|
||||
this.setDefaultOptions();
|
||||
this.buildOptions(options);
|
||||
this.prepare();
|
||||
this.initializeComponents();
|
||||
|
||||
this.style = new Style(this);
|
||||
this.keyboard = new Keyboard(this.wrapper);
|
||||
this.datamanager = new DataManager(this.options);
|
||||
this.rowmanager = new RowManager(this);
|
||||
this.columnmanager = new ColumnManager(this);
|
||||
this.cellmanager = new CellManager(this);
|
||||
this.bodyRenderer = new BodyRenderer(this);
|
||||
|
||||
if (this.options.data) {
|
||||
this.refresh();
|
||||
@ -44,36 +38,23 @@ class DataTable {
|
||||
}
|
||||
}
|
||||
|
||||
initializeTranslations(options) {
|
||||
this.language = options.language || 'en';
|
||||
this.translationManager = new TranslationManager(this.language);
|
||||
|
||||
if (options.translations) {
|
||||
this.translationManager.addTranslations(options.translations);
|
||||
}
|
||||
}
|
||||
|
||||
setDefaultOptions() {
|
||||
this.DEFAULT_OPTIONS = getDefaultOptions(this);
|
||||
}
|
||||
|
||||
buildOptions(options) {
|
||||
this.options = this.options || {};
|
||||
|
||||
this.options = Object.assign(
|
||||
{}, this.DEFAULT_OPTIONS,
|
||||
{}, DEFAULT_OPTIONS,
|
||||
this.options || {}, options
|
||||
);
|
||||
|
||||
options.headerDropdown = options.headerDropdown || [];
|
||||
this.options.headerDropdown = [
|
||||
...this.DEFAULT_OPTIONS.headerDropdown,
|
||||
...DEFAULT_OPTIONS.headerDropdown,
|
||||
...options.headerDropdown
|
||||
];
|
||||
|
||||
// custom user events
|
||||
this.events = Object.assign(
|
||||
{}, this.DEFAULT_OPTIONS.events,
|
||||
{}, DEFAULT_OPTIONS.events,
|
||||
this.options.events || {},
|
||||
options.events || {}
|
||||
);
|
||||
@ -85,34 +66,12 @@ class DataTable {
|
||||
this.unfreeze();
|
||||
}
|
||||
|
||||
initializeComponents() {
|
||||
let components = Object.assign({}, defaultComponents, this.options.overrideComponents);
|
||||
let {
|
||||
Style,
|
||||
Keyboard,
|
||||
DataManager,
|
||||
RowManager,
|
||||
ColumnManager,
|
||||
CellManager,
|
||||
BodyRenderer
|
||||
} = components;
|
||||
|
||||
this.style = new Style(this);
|
||||
this.keyboard = new Keyboard(this.wrapper);
|
||||
this.datamanager = new DataManager(this.options);
|
||||
this.rowmanager = new RowManager(this);
|
||||
this.columnmanager = new ColumnManager(this);
|
||||
this.cellmanager = new CellManager(this);
|
||||
this.bodyRenderer = new BodyRenderer(this);
|
||||
}
|
||||
|
||||
prepareDom() {
|
||||
this.wrapper.innerHTML = `
|
||||
<div class="datatable" dir="${this.options.direction}">
|
||||
<div class="datatable-content">
|
||||
<div class="dt-header"></div>
|
||||
<div class="dt-scrollable"></div>
|
||||
<div class="dt-footer"></div>
|
||||
<div class="datatable">
|
||||
<table class="dt-header">
|
||||
</table>
|
||||
<div class="dt-scrollable">
|
||||
</div>
|
||||
<div class="dt-freeze">
|
||||
<span class="dt-freeze__message">
|
||||
@ -120,19 +79,16 @@ class DataTable {
|
||||
</span>
|
||||
</div>
|
||||
<div class="dt-toast"></div>
|
||||
<div class="dt-dropdown-container"></div>
|
||||
<textarea class="dt-paste-target"></textarea>
|
||||
</div>
|
||||
`;
|
||||
|
||||
this.datatableWrapper = $('.datatable', this.wrapper);
|
||||
this.header = $('.dt-header', this.wrapper);
|
||||
this.footer = $('.dt-footer', this.wrapper);
|
||||
this.bodyScrollable = $('.dt-scrollable', this.wrapper);
|
||||
this.freezeContainer = $('.dt-freeze', this.wrapper);
|
||||
this.toastMessage = $('.dt-toast', this.wrapper);
|
||||
this.pasteTarget = $('.dt-paste-target', this.wrapper);
|
||||
this.dropdownContainer = $('.dt-dropdown-container', this.wrapper);
|
||||
}
|
||||
|
||||
refresh(data, columns) {
|
||||
@ -144,7 +100,6 @@ class DataTable {
|
||||
destroy() {
|
||||
this.wrapper.innerHTML = '';
|
||||
this.style.destroy();
|
||||
this.fireEvent('onDestroy');
|
||||
}
|
||||
|
||||
appendRows(rows) {
|
||||
@ -238,22 +193,7 @@ class DataTable {
|
||||
}
|
||||
|
||||
fireEvent(eventName, ...args) {
|
||||
// fire internalEventHandlers if any
|
||||
// and then user events
|
||||
const handlers = [
|
||||
...(this._internalEventHandlers[eventName] || []),
|
||||
this.events[eventName]
|
||||
].filter(Boolean);
|
||||
|
||||
for (let handler of handlers) {
|
||||
handler.apply(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
on(event, handler) {
|
||||
this._internalEventHandlers = this._internalEventHandlers || {};
|
||||
this._internalEventHandlers[event] = this._internalEventHandlers[event] || [];
|
||||
this._internalEventHandlers[event].push(handler);
|
||||
this.events[eventName].apply(this, args);
|
||||
}
|
||||
|
||||
log() {
|
||||
@ -261,10 +201,6 @@ class DataTable {
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
translate(str, args) {
|
||||
return this.translationManager.translate(str, args);
|
||||
}
|
||||
}
|
||||
|
||||
DataTable.instances = 0;
|
||||
|
||||
120
src/defaults.js
120
src/defaults.js
@ -1,73 +1,57 @@
|
||||
import filterRows from './filterRows';
|
||||
import icons from './icons';
|
||||
|
||||
export default function getDefaultOptions(instance) {
|
||||
return {
|
||||
columns: [],
|
||||
data: [],
|
||||
dropdownButton: icons.chevronDown,
|
||||
headerDropdown: [
|
||||
{
|
||||
label: instance.translate('Sort Ascending'),
|
||||
action: function (column) {
|
||||
this.sortColumn(column.colIndex, 'asc');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: instance.translate('Sort Descending'),
|
||||
action: function (column) {
|
||||
this.sortColumn(column.colIndex, 'desc');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: instance.translate('Reset sorting'),
|
||||
action: function (column) {
|
||||
this.sortColumn(column.colIndex, 'none');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: instance.translate('Remove column'),
|
||||
action: function (column) {
|
||||
this.removeColumn(column.colIndex);
|
||||
}
|
||||
export default {
|
||||
columns: [],
|
||||
data: [],
|
||||
dropdownButton: '▼',
|
||||
headerDropdown: [
|
||||
{
|
||||
label: 'Sort Ascending',
|
||||
action: function (column) {
|
||||
this.sortColumn(column.colIndex, 'asc');
|
||||
}
|
||||
],
|
||||
events: {
|
||||
onRemoveColumn(column) {},
|
||||
onSwitchColumn(column1, column2) {},
|
||||
onSortColumn(column) {},
|
||||
onCheckRow(row) {},
|
||||
onDestroy() {}
|
||||
},
|
||||
hooks: {
|
||||
columnTotal: null
|
||||
{
|
||||
label: 'Sort Descending',
|
||||
action: function (column) {
|
||||
this.sortColumn(column.colIndex, 'desc');
|
||||
}
|
||||
},
|
||||
sortIndicator: {
|
||||
asc: '↑',
|
||||
desc: '↓',
|
||||
none: ''
|
||||
{
|
||||
label: 'Reset sorting',
|
||||
action: function (column) {
|
||||
this.sortColumn(column.colIndex, 'none');
|
||||
}
|
||||
},
|
||||
overrideComponents: {
|
||||
// ColumnManager: CustomColumnManager
|
||||
},
|
||||
filterRows: filterRows,
|
||||
freezeMessage: '',
|
||||
getEditor: null,
|
||||
serialNoColumn: true,
|
||||
checkboxColumn: false,
|
||||
clusterize: true,
|
||||
logs: false,
|
||||
layout: 'fixed', // fixed, fluid, ratio
|
||||
noDataMessage: instance.translate('No Data'),
|
||||
cellHeight: 40,
|
||||
minimumColumnWidth: 30,
|
||||
inlineFilters: false,
|
||||
treeView: false,
|
||||
checkedRowStatus: true,
|
||||
dynamicRowHeight: false,
|
||||
pasteFromClipboard: false,
|
||||
showTotalRow: false,
|
||||
direction: 'ltr',
|
||||
disableReorderColumn: false
|
||||
};
|
||||
{
|
||||
label: 'Remove column',
|
||||
action: function (column) {
|
||||
this.removeColumn(column.colIndex);
|
||||
}
|
||||
}
|
||||
],
|
||||
events: {
|
||||
onRemoveColumn(column) {},
|
||||
onSwitchColumn(column1, column2) {},
|
||||
onSortColumn(column) {},
|
||||
onCheckRow(row) {}
|
||||
},
|
||||
sortIndicator: {
|
||||
asc: '↑',
|
||||
desc: '↓',
|
||||
none: ''
|
||||
},
|
||||
groupBy: '', // column id
|
||||
freezeMessage: '',
|
||||
getEditor: null,
|
||||
serialNoColumn: true,
|
||||
checkboxColumn: false,
|
||||
clusterize: true,
|
||||
logs: false,
|
||||
layout: 'fixed', // fixed, fluid, ratio
|
||||
noDataMessage: 'No Data',
|
||||
cellHeight: null,
|
||||
inlineFilters: false,
|
||||
treeView: false,
|
||||
checkedRowStatus: true,
|
||||
dynamicRowHeight: false,
|
||||
pasteFromClipboard: false
|
||||
};
|
||||
|
||||
35
src/dom.js
35
src/dom.js
@ -138,10 +138,6 @@ $.removeStyle = (elements, styleProps) => {
|
||||
};
|
||||
|
||||
$.getStyle = (element, prop) => {
|
||||
if (!prop) {
|
||||
return getComputedStyle(element);
|
||||
}
|
||||
|
||||
let val = getComputedStyle(element)[prop];
|
||||
|
||||
if (['width', 'height'].includes(prop)) {
|
||||
@ -184,16 +180,7 @@ $.scrollTop = function scrollTop(element, pixels) {
|
||||
});
|
||||
};
|
||||
|
||||
$.scrollbarSize = function scrollbarSize() {
|
||||
if (!$.scrollBarSizeValue) {
|
||||
$.scrollBarSizeValue = getScrollBarSize();
|
||||
}
|
||||
return $.scrollBarSizeValue;
|
||||
};
|
||||
|
||||
function getScrollBarSize() {
|
||||
// assume scrollbar width and height would be the same
|
||||
|
||||
$.scrollbarWidth = function scrollbarWidth() {
|
||||
// Create the measurement node
|
||||
const scrollDiv = document.createElement('div');
|
||||
$.style(scrollDiv, {
|
||||
@ -212,24 +199,4 @@ function getScrollBarSize() {
|
||||
document.body.removeChild(scrollDiv);
|
||||
|
||||
return scrollbarWidth;
|
||||
}
|
||||
|
||||
$.hasVerticalOverflow = function (element) {
|
||||
return element.scrollHeight > element.offsetHeight + 10;
|
||||
};
|
||||
|
||||
$.hasHorizontalOverflow = function (element) {
|
||||
return element.scrollWidth > element.offsetWidth + 10;
|
||||
};
|
||||
|
||||
$.measureTextWidth = function (text) {
|
||||
const div = document.createElement('div');
|
||||
div.style.position = 'absolute';
|
||||
div.style.visibility = 'hidden';
|
||||
div.style.height = 'auto';
|
||||
div.style.width = 'auto';
|
||||
div.style.whiteSpace = 'nowrap';
|
||||
div.innerText = text;
|
||||
document.body.appendChild(div);
|
||||
return div.clientWidth + 1;
|
||||
};
|
||||
|
||||
@ -1,209 +0,0 @@
|
||||
import { isNumber, stripHTML } from './utils';
|
||||
import CellManager from './cellmanager';
|
||||
|
||||
export default function filterRows(rows, filters, data) {
|
||||
let filteredRowIndices = [];
|
||||
|
||||
if (Object.keys(filters).length === 0) {
|
||||
return rows.map(row => row.meta.rowIndex);
|
||||
}
|
||||
|
||||
for (let colIndex in filters) {
|
||||
const keyword = filters[colIndex];
|
||||
|
||||
const filteredRows = filteredRowIndices.length ?
|
||||
filteredRowIndices.map(i => rows[i]) :
|
||||
rows;
|
||||
|
||||
const cells = filteredRows.map(row => row[colIndex]);
|
||||
|
||||
let filter = guessFilter(keyword);
|
||||
let filterMethod = getFilterMethod(rows, data, filter);
|
||||
|
||||
if (filterMethod) {
|
||||
filteredRowIndices = filterMethod(filter.text, cells);
|
||||
} else {
|
||||
filteredRowIndices = cells.map(cell => cell.rowIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return filteredRowIndices;
|
||||
};
|
||||
|
||||
function getFilterMethod(rows, allData, filter) {
|
||||
const getFormattedValue = cell => {
|
||||
let formatter = CellManager.getCustomCellFormatter(cell);
|
||||
let rowData = rows[cell.rowIndex];
|
||||
if (allData && allData.data && allData.data.length) {
|
||||
rowData = allData.data[cell.rowIndex];
|
||||
}
|
||||
if (formatter && cell.content) {
|
||||
cell.html = formatter(cell.content, rows[cell.rowIndex], cell.column, rowData, filter);
|
||||
return stripHTML(cell.html);
|
||||
}
|
||||
return cell.content || '';
|
||||
};
|
||||
|
||||
const stringCompareValue = cell =>
|
||||
String(stripHTML(cell.html || '') || getFormattedValue(cell)).toLowerCase();
|
||||
|
||||
const numberCompareValue = cell => parseFloat(cell.content);
|
||||
|
||||
const getCompareValues = (cell, keyword) => {
|
||||
if (cell.column.compareValue) {
|
||||
const compareValues = cell.column.compareValue(cell, keyword);
|
||||
if (compareValues && Array.isArray(compareValues)) return compareValues;
|
||||
}
|
||||
|
||||
// check if it can be converted to number
|
||||
const float = numberCompareValue(cell);
|
||||
if (!isNaN(float)) {
|
||||
return [float, keyword];
|
||||
}
|
||||
|
||||
return [stringCompareValue(cell), keyword];
|
||||
};
|
||||
|
||||
let filterMethodMap = {
|
||||
contains(keyword, cells) {
|
||||
return cells
|
||||
.filter(cell => {
|
||||
const needle = (keyword || '').toLowerCase();
|
||||
return !needle ||
|
||||
(cell.content || '').toLowerCase().includes(needle) ||
|
||||
stringCompareValue(cell).includes(needle);
|
||||
})
|
||||
.map(cell => cell.rowIndex);
|
||||
},
|
||||
|
||||
greaterThan(keyword, cells) {
|
||||
return cells
|
||||
.filter(cell => {
|
||||
const [compareValue, keywordValue] = getCompareValues(cell, keyword);
|
||||
return compareValue > keywordValue;
|
||||
})
|
||||
.map(cell => cell.rowIndex);
|
||||
},
|
||||
|
||||
lessThan(keyword, cells) {
|
||||
return cells
|
||||
.filter(cell => {
|
||||
const [compareValue, keywordValue] = getCompareValues(cell, keyword);
|
||||
return compareValue < keywordValue;
|
||||
})
|
||||
.map(cell => cell.rowIndex);
|
||||
},
|
||||
|
||||
equals(keyword, cells) {
|
||||
return cells
|
||||
.filter(cell => {
|
||||
const value = parseFloat(cell.content);
|
||||
return value === keyword;
|
||||
})
|
||||
.map(cell => cell.rowIndex);
|
||||
},
|
||||
|
||||
notEquals(keyword, cells) {
|
||||
return cells
|
||||
.filter(cell => {
|
||||
const value = parseFloat(cell.content);
|
||||
return value !== keyword;
|
||||
})
|
||||
.map(cell => cell.rowIndex);
|
||||
},
|
||||
|
||||
range(rangeValues, cells) {
|
||||
return cells
|
||||
.filter(cell => {
|
||||
const values1 = getCompareValues(cell, rangeValues[0]);
|
||||
const values2 = getCompareValues(cell, rangeValues[1]);
|
||||
const value = values1[0];
|
||||
return value >= values1[1] && value <= values2[1];
|
||||
})
|
||||
.map(cell => cell.rowIndex);
|
||||
},
|
||||
|
||||
containsNumber(keyword, cells) {
|
||||
return cells
|
||||
.filter(cell => {
|
||||
let number = parseFloat(keyword, 10);
|
||||
let string = keyword;
|
||||
let hayNumber = numberCompareValue(cell);
|
||||
let hayString = stringCompareValue(cell);
|
||||
|
||||
return number === hayNumber || hayString.includes(string);
|
||||
})
|
||||
.map(cell => cell.rowIndex);
|
||||
}
|
||||
};
|
||||
|
||||
return filterMethodMap[filter.type];
|
||||
}
|
||||
|
||||
function guessFilter(keyword = '') {
|
||||
if (keyword.length === 0) return {};
|
||||
|
||||
let compareString = keyword;
|
||||
|
||||
if (['>', '<', '='].includes(compareString[0])) {
|
||||
compareString = keyword.slice(1);
|
||||
} else if (compareString.startsWith('!=')) {
|
||||
compareString = keyword.slice(2);
|
||||
}
|
||||
|
||||
if (keyword.startsWith('>')) {
|
||||
if (compareString) {
|
||||
return {
|
||||
type: 'greaterThan',
|
||||
text: compareString.trim()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (keyword.startsWith('<')) {
|
||||
if (compareString) {
|
||||
return {
|
||||
type: 'lessThan',
|
||||
text: compareString.trim()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (keyword.startsWith('=')) {
|
||||
if (isNumber(compareString)) {
|
||||
return {
|
||||
type: 'equals',
|
||||
text: Number(keyword.slice(1).trim())
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (isNumber(compareString)) {
|
||||
return {
|
||||
type: 'containsNumber',
|
||||
text: compareString
|
||||
};
|
||||
}
|
||||
|
||||
if (keyword.startsWith('!=')) {
|
||||
if (isNumber(compareString)) {
|
||||
return {
|
||||
type: 'notEquals',
|
||||
text: Number(keyword.slice(2).trim())
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (keyword.split(':').length === 2 && keyword.split(':').every(v => isNumber(v.trim()))) {
|
||||
compareString = keyword.split(':');
|
||||
return {
|
||||
type: 'range',
|
||||
text: compareString.map(v => v.trim())
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'contains',
|
||||
text: compareString.toLowerCase()
|
||||
};
|
||||
}
|
||||
10
src/icons.js
10
src/icons.js
@ -1,10 +0,0 @@
|
||||
/* eslint-disable max-len */
|
||||
|
||||
// Icons from https://feathericons.com/
|
||||
|
||||
let icons = {
|
||||
chevronDown: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg>',
|
||||
chevronRight: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right"><polyline points="9 18 15 12 9 6"></polyline></svg>'
|
||||
};
|
||||
|
||||
export default icons;
|
||||
@ -3,9 +3,7 @@ import {
|
||||
makeDataAttributeString,
|
||||
nextTick,
|
||||
ensureArray,
|
||||
linkProperties,
|
||||
uniq,
|
||||
numberSortAsc
|
||||
linkProperties
|
||||
} from './utils';
|
||||
|
||||
export default class RowManager {
|
||||
@ -16,8 +14,7 @@ export default class RowManager {
|
||||
'fireEvent',
|
||||
'wrapper',
|
||||
'bodyScrollable',
|
||||
'bodyRenderer',
|
||||
'style'
|
||||
'bodyRenderer'
|
||||
]);
|
||||
|
||||
this.bindEvents();
|
||||
@ -42,7 +39,7 @@ export default class RowManager {
|
||||
// map of checked rows
|
||||
this.checkMap = [];
|
||||
|
||||
$.on(this.wrapper, 'click', '.dt-cell--col-0 [type="checkbox"]', (e, $checkbox) => {
|
||||
$.on(this.wrapper, 'click', '.dt-cell[data-col-index="0"] [type="checkbox"]', (e, $checkbox) => {
|
||||
const $cell = $checkbox.closest('.dt-cell');
|
||||
const {
|
||||
rowIndex,
|
||||
@ -67,7 +64,7 @@ export default class RowManager {
|
||||
const _row = this.datamanager.updateRow(row, rowIndex);
|
||||
|
||||
_row.forEach(cell => {
|
||||
this.cellmanager.refreshCell(cell, true);
|
||||
this.cellmanager.refreshCell(cell);
|
||||
});
|
||||
}
|
||||
|
||||
@ -94,7 +91,8 @@ export default class RowManager {
|
||||
|
||||
checkRow(rowIndex, toggle) {
|
||||
const value = toggle ? 1 : 0;
|
||||
const selector = rowIndex => `.dt-cell--0-${rowIndex} [type="checkbox"]`;
|
||||
const selector = rowIndex =>
|
||||
`.dt-cell[data-row-index="${rowIndex}"][data-col-index="0"] [type="checkbox"]`;
|
||||
// update internal map
|
||||
this.checkMap[rowIndex] = value;
|
||||
// set checkbox value explicitly
|
||||
@ -113,25 +111,18 @@ export default class RowManager {
|
||||
|
||||
// update internal map
|
||||
if (toggle) {
|
||||
if (this.datamanager._filteredRows) {
|
||||
this.datamanager._filteredRows.forEach(f => {
|
||||
this.checkRow(f, toggle);
|
||||
});
|
||||
} else {
|
||||
this.checkMap = Array.from(Array(this.getTotalRows())).map(c => value);
|
||||
}
|
||||
this.checkMap = Array.from(Array(this.getTotalRows())).map(c => value);
|
||||
} else {
|
||||
this.checkMap = [];
|
||||
}
|
||||
// set checkbox value
|
||||
$.each('.dt-cell--col-0 [type="checkbox"]', this.bodyScrollable)
|
||||
$.each('.dt-cell[data-col-index="0"] [type="checkbox"]', this.bodyScrollable)
|
||||
.map(input => {
|
||||
input.checked = toggle;
|
||||
});
|
||||
// highlight all
|
||||
this.highlightAll(toggle);
|
||||
this.showCheckStatus();
|
||||
this.fireEvent('onCheckRow');
|
||||
}
|
||||
|
||||
showCheckStatus() {
|
||||
@ -139,10 +130,7 @@ export default class RowManager {
|
||||
const checkedRows = this.getCheckedRows();
|
||||
const count = checkedRows.length;
|
||||
if (count > 0) {
|
||||
let message = this.instance.translate('{count} rows selected', {
|
||||
count: count
|
||||
});
|
||||
this.bodyRenderer.showToastMessage(message);
|
||||
this.bodyRenderer.showToastMessage(`${count} row${count > 1 ? 's' : ''} selected`);
|
||||
} else {
|
||||
this.bodyRenderer.clearToastMessage();
|
||||
}
|
||||
@ -185,104 +173,42 @@ export default class RowManager {
|
||||
}
|
||||
}
|
||||
|
||||
hideRows(rowIndices) {
|
||||
rowIndices = ensureArray(rowIndices);
|
||||
rowIndices.map(rowIndex => {
|
||||
const $tr = this.getRow$(rowIndex);
|
||||
$tr.classList.add('dt-row--hide');
|
||||
});
|
||||
}
|
||||
|
||||
showRows(rowIndices) {
|
||||
rowIndices = ensureArray(rowIndices);
|
||||
const rows = rowIndices.map(rowIndex => this.datamanager.getRow(rowIndex));
|
||||
this.bodyRenderer.renderRows(rows);
|
||||
}
|
||||
|
||||
showAllRows() {
|
||||
const rowIndices = this.datamanager.getAllRowIndices();
|
||||
this.showRows(rowIndices);
|
||||
}
|
||||
|
||||
getChildrenToShowForNode(rowIndex) {
|
||||
const row = this.datamanager.getRow(rowIndex);
|
||||
row.meta.isTreeNodeClose = false;
|
||||
|
||||
return this.datamanager.getImmediateChildren(rowIndex);
|
||||
rowIndices.map(rowIndex => {
|
||||
const $tr = this.getRow$(rowIndex);
|
||||
$tr.classList.remove('dt-row--hide');
|
||||
});
|
||||
}
|
||||
|
||||
openSingleNode(rowIndex) {
|
||||
const childrenToShow = this.getChildrenToShowForNode(rowIndex);
|
||||
const visibleRowIndices = this.bodyRenderer.visibleRowIndices;
|
||||
const rowsToShow = uniq([...childrenToShow, ...visibleRowIndices]).sort(numberSortAsc);
|
||||
|
||||
const rowsToShow = this.datamanager.getImmediateChildren(rowIndex);
|
||||
this.showRows(rowsToShow);
|
||||
}
|
||||
|
||||
getChildrenToHideForNode(rowIndex) {
|
||||
const row = this.datamanager.getRow(rowIndex);
|
||||
row.meta.isTreeNodeClose = true;
|
||||
|
||||
const rowsToHide = this.datamanager.getChildren(rowIndex);
|
||||
rowsToHide.forEach(rowIndex => {
|
||||
const row = this.datamanager.getRow(rowIndex);
|
||||
if (!row.meta.isLeaf) {
|
||||
row.meta.isTreeNodeClose = true;
|
||||
}
|
||||
});
|
||||
|
||||
return rowsToHide;
|
||||
this.cellmanager.toggleTreeButton(rowIndex, true);
|
||||
}
|
||||
|
||||
closeSingleNode(rowIndex) {
|
||||
const rowsToHide = this.getChildrenToHideForNode(rowIndex);
|
||||
const visibleRows = this.bodyRenderer.visibleRowIndices;
|
||||
const rowsToShow = visibleRows
|
||||
.filter(rowIndex => !rowsToHide.includes(rowIndex))
|
||||
.sort(numberSortAsc);
|
||||
|
||||
this.showRows(rowsToShow);
|
||||
}
|
||||
|
||||
expandAllNodes() {
|
||||
let rows = this.datamanager.getRows();
|
||||
let rootNodes = rows.filter(row => !row.meta.isLeaf);
|
||||
|
||||
const childrenToShow = rootNodes.map(row => this.getChildrenToShowForNode(row.meta.rowIndex)).flat();
|
||||
const visibleRowIndices = this.bodyRenderer.visibleRowIndices;
|
||||
const rowsToShow = uniq([...childrenToShow, ...visibleRowIndices]).sort(numberSortAsc);
|
||||
|
||||
this.showRows(rowsToShow);
|
||||
}
|
||||
|
||||
collapseAllNodes() {
|
||||
let rows = this.datamanager.getRows();
|
||||
let rootNodes = rows.filter(row => row.meta.indent === 0);
|
||||
|
||||
const rowsToHide = rootNodes.map(row => this.getChildrenToHideForNode(row.meta.rowIndex)).flat();
|
||||
const visibleRows = this.bodyRenderer.visibleRowIndices;
|
||||
const rowsToShow = visibleRows
|
||||
.filter(rowIndex => !rowsToHide.includes(rowIndex))
|
||||
.sort(numberSortAsc);
|
||||
|
||||
this.showRows(rowsToShow);
|
||||
}
|
||||
|
||||
setTreeDepth(depth) {
|
||||
let rows = this.datamanager.getRows();
|
||||
|
||||
const rowsToOpen = rows.filter(row => row.meta.indent < depth);
|
||||
const rowsToClose = rows.filter(row => row.meta.indent >= depth);
|
||||
const rowsToHide = rowsToClose.filter(row => row.meta.indent > depth);
|
||||
|
||||
rowsToClose.forEach(row => {
|
||||
if (!row.meta.isLeaf) {
|
||||
row.meta.isTreeNodeClose = true;
|
||||
const children = this.datamanager.getImmediateChildren(rowIndex);
|
||||
children.forEach(childIndex => {
|
||||
const row = this.datamanager.getRow(childIndex);
|
||||
if (row.meta.isLeaf) {
|
||||
// close
|
||||
this.hideRows(childIndex);
|
||||
this.cellmanager.toggleTreeButton(childIndex, false);
|
||||
} else {
|
||||
this.closeSingleNode(childIndex);
|
||||
this.hideRows(childIndex);
|
||||
}
|
||||
});
|
||||
rowsToOpen.forEach(row => {
|
||||
if (!row.meta.isLeaf) {
|
||||
row.meta.isTreeNodeClose = false;
|
||||
}
|
||||
});
|
||||
|
||||
const rowsToShow = rows
|
||||
.filter(row => !rowsToHide.includes(row))
|
||||
.map(row => row.meta.rowIndex)
|
||||
.sort(numberSortAsc);
|
||||
this.showRows(rowsToShow);
|
||||
this.cellmanager.toggleTreeButton(rowIndex, false);
|
||||
}
|
||||
|
||||
getRow$(rowIndex) {
|
||||
@ -329,41 +255,31 @@ export default class RowManager {
|
||||
|
||||
getRowHTML(row, props) {
|
||||
const dataAttr = makeDataAttributeString(props);
|
||||
let rowIdentifier = props.rowIndex;
|
||||
|
||||
if (props.isFilter) {
|
||||
row = row.map(cell => (Object.assign({}, cell, {
|
||||
content: this.getFilterInput({
|
||||
colIndex: cell.colIndex,
|
||||
name: cell.name
|
||||
colIndex: cell.colIndex
|
||||
}),
|
||||
isFilter: 1,
|
||||
isHeader: undefined,
|
||||
editable: false
|
||||
})));
|
||||
|
||||
rowIdentifier = 'filter';
|
||||
}
|
||||
|
||||
if (props.isHeader) {
|
||||
rowIdentifier = 'header';
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="dt-row dt-row-${rowIdentifier}" ${dataAttr}>
|
||||
<tr class="dt-row" ${dataAttr}>
|
||||
${row.map(cell => this.cellmanager.getCellHTML(cell)).join('')}
|
||||
</div>
|
||||
</tr>
|
||||
`;
|
||||
}
|
||||
|
||||
getFilterInput(props) {
|
||||
let title = `title="Filter based on ${props.name || 'Index'}"`;
|
||||
const dataAttr = makeDataAttributeString(props);
|
||||
return `<input class="dt-filter dt-input" type="text" ${dataAttr} tabindex="1"
|
||||
${props.colIndex === 0 ? 'disabled' : title} />`;
|
||||
return `<input class="dt-filter dt-input" type="text" ${dataAttr} />`;
|
||||
}
|
||||
|
||||
selector(rowIndex) {
|
||||
return `.dt-row-${rowIndex}`;
|
||||
return `.dt-row[data-row-index="${rowIndex}"]`;
|
||||
}
|
||||
}
|
||||
|
||||
435
src/style.css
435
src/style.css
@ -1,308 +1,259 @@
|
||||
:root {
|
||||
--dt-border-color: #d1d8dd;
|
||||
--dt-primary-color: rgb(82, 146, 247);
|
||||
--dt-light-bg: #f5f7fa;
|
||||
--dt-light-red: #FD8B8B;
|
||||
--dt-light-yellow: #fffce7;
|
||||
--dt-orange: rgb(255, 160, 10);
|
||||
--dt-text-color: #000000;
|
||||
--dt-text-light: #dfe2e5;
|
||||
--dt-spacer-1: 0.25rem;
|
||||
--dt-spacer-2: 0.5rem;
|
||||
--dt-spacer-3: 1rem;
|
||||
--dt-border-radius: 3px;
|
||||
--dt-cell-bg: #fff;
|
||||
--dt-focus-border-width: 2px;
|
||||
--dt-selection-highlight-color: var(--dt-light-yellow);
|
||||
--dt-toast-message-border: none;
|
||||
--dt-header-cell-bg: var(--dt-cell-bg);
|
||||
--border-color: #d1d8dd;
|
||||
--primary-color: rgb(82, 146, 247);
|
||||
--light-bg: #f5f7fa;
|
||||
--light-red: #FD8B8B;
|
||||
--light-yellow: #fffce7;
|
||||
--orange: rgb(255, 160, 10);
|
||||
--text-color: #000000;
|
||||
--text-light: #dfe2e5;
|
||||
|
||||
--spacer-1: 0.25rem;
|
||||
--spacer-2: 0.5rem;
|
||||
--spacer-3: 1rem;
|
||||
|
||||
--border-radius: 3px;
|
||||
|
||||
--cell-bg: #fff;
|
||||
}
|
||||
|
||||
.datatable {
|
||||
*, *::after, *::before {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.datatable {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
.dt-cell--col-0{
|
||||
border-left: 1px solid var(--dt-border-color);
|
||||
}
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.dt-header{
|
||||
border-bottom: 2px solid var(--dt-border-color);
|
||||
.dt-header {
|
||||
border-collapse: collapse;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: var(--cell-bg);
|
||||
}
|
||||
|
||||
.datatable-content{
|
||||
.dt-header{
|
||||
display: flex;
|
||||
}
|
||||
.dt-body {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.dt-scrollable {
|
||||
height: 40vw;
|
||||
max-height: 40vw;
|
||||
overflow: auto;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
|
||||
&--highlight-all {
|
||||
background-color: var(--dt-selection-highlight-color);
|
||||
}
|
||||
&--highlight-all {
|
||||
background-color: var(--light-yellow);
|
||||
}
|
||||
|
||||
&__no-data {
|
||||
text-align: center;
|
||||
padding: var(--dt-spacer-3);
|
||||
border-left: 1px solid var(--dt-border-color);
|
||||
border-right: 1px solid var(--dt-border-color);
|
||||
}
|
||||
&__no-data {
|
||||
text-align: center;
|
||||
padding: var(--spacer-3);
|
||||
border-left: 1px solid var(--border-color);
|
||||
border-right: 1px solid var(--border-color);
|
||||
}
|
||||
}
|
||||
|
||||
.dt-row {
|
||||
display: flex;
|
||||
&--highlight {
|
||||
background-color: var(--light-yellow);
|
||||
}
|
||||
|
||||
&--highlight .dt-cell {
|
||||
background-color: var(--dt-selection-highlight-color);
|
||||
}
|
||||
&--unhighlight {
|
||||
background-color: var(--cell-bg);
|
||||
}
|
||||
|
||||
&--unhighlight .dt-cell {
|
||||
background-color: var(--dt-cell-bg);
|
||||
}
|
||||
|
||||
&--hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:last-child:not(.dt-row-filter) {
|
||||
border-bottom: 1px solid var(--dt-border-color);
|
||||
}
|
||||
&--hide {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.dt-cell {
|
||||
border: 1px solid var(--dt-border-color);
|
||||
border-bottom: none;
|
||||
border-left: none;
|
||||
position: relative;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
background-color: var(--dt-cell-bg);
|
||||
color: var(--dt-text-color);
|
||||
/*
|
||||
Fix for firefox and Edge
|
||||
https://stackoverflow.com/a/16337203
|
||||
firefox paints td background over border
|
||||
*/
|
||||
background-clip: padding-box;
|
||||
user-select: none;
|
||||
border: 1px solid var(--border-color);
|
||||
position: relative;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
|
||||
&__content {
|
||||
padding: var(--dt-spacer-2);
|
||||
border: var(--dt-focus-border-width) solid transparent;
|
||||
height: 100%;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
&__content {
|
||||
padding: var(--spacer-2);
|
||||
border: 2px solid transparent;
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__edit {
|
||||
display: none;
|
||||
padding: var(--dt-spacer-2);
|
||||
background-color: var(--dt-cell-bg);
|
||||
border: var(--dt-focus-border-width) solid var(--dt-orange);
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
}
|
||||
&__edit {
|
||||
display: none;
|
||||
padding: var(--spacer-2);
|
||||
background-color: var(--cell-bg);
|
||||
border: 2px solid var(--orange);
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&__resize-handle {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: -3px;
|
||||
top: 0;
|
||||
width: 5px;
|
||||
height: 100%;
|
||||
cursor: col-resize;
|
||||
z-index: 1;
|
||||
}
|
||||
&__resize-handle {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: -3px;
|
||||
top: 0;
|
||||
width: 5px;
|
||||
height: 100%;
|
||||
cursor: col-resize;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&--editing &__content {
|
||||
display: none;
|
||||
}
|
||||
&--editing &__content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&--editing &__edit {
|
||||
display: block;
|
||||
}
|
||||
&--editing &__edit {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&--focus &__content {
|
||||
border-color: var(--dt-primary-color);
|
||||
}
|
||||
&--focus &__content {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
&--highlight {
|
||||
background-color: var(--dt-light-bg);
|
||||
}
|
||||
&--highlight {
|
||||
background-color: var(--light-bg);
|
||||
}
|
||||
|
||||
&--dragging {
|
||||
background-color: var(--dt-light-bg);
|
||||
}
|
||||
&--dragging {
|
||||
background-color: var(--light-bg);
|
||||
}
|
||||
|
||||
&--header {
|
||||
background-color: var(--dt-header-cell-bg);
|
||||
}
|
||||
&--header &__content {
|
||||
padding-right: var(--spacer-3);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&--header:last-child {
|
||||
border-right: 1px solid var(--dt-border-color);
|
||||
}
|
||||
&--header:hover .dt-dropdown__toggle {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&--header &__content {
|
||||
padding-right: var(--dt-spacer-3);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&--header:hover .dt-dropdown__toggle {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&--tree-close {
|
||||
.icon-open {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.icon-close {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-right: 1px solid var(--dt-border-color);
|
||||
}
|
||||
}
|
||||
|
||||
.datatable[dir=rtl] .dt-cell__resize-handle {
|
||||
right: unset;
|
||||
left: -3px;
|
||||
}
|
||||
|
||||
.icon-open, .icon-close {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.icon-open {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.icon-close {
|
||||
display: none;
|
||||
&--tree-close {
|
||||
.dt-tree-node__toggle:before {
|
||||
content: '►';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dt-dropdown {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
display: inline-flex;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
display: inline-flex;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
|
||||
&__toggle {
|
||||
opacity: 0;
|
||||
background-color: var(--dt-header-cell-bg);
|
||||
}
|
||||
&__toggle {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&__list {
|
||||
position: fixed;
|
||||
min-width: 8rem;
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
background-color: var(--dt-cell-bg);
|
||||
border-radius: var(--dt-border-radius);
|
||||
padding: var(--dt-spacer-2) 0;
|
||||
box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);
|
||||
}
|
||||
&__list {
|
||||
display: none;
|
||||
|
||||
&__list-item {
|
||||
padding: var(--dt-spacer-2) var(--dt-spacer-3);
|
||||
position: absolute;
|
||||
min-width: 8rem;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
background-color: var(--cell-bg);
|
||||
border-radius: var(--border-radius);
|
||||
padding: var(--spacer-2) 0;
|
||||
box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--dt-light-bg);
|
||||
}
|
||||
}
|
||||
&__list-item {
|
||||
padding: var(--spacer-2) var(--spacer-3);
|
||||
|
||||
&--active &__list {
|
||||
display: block;
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--light-bg);
|
||||
}
|
||||
}
|
||||
|
||||
&--active &__list {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.dt-tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
||||
&__toggle {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
margin-right: 0.2rem;
|
||||
}
|
||||
&__toggle {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
font-size: 10px;
|
||||
padding: 1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__toggle:before {
|
||||
content: '▼';
|
||||
}
|
||||
}
|
||||
|
||||
.dt-toast {
|
||||
position: absolute;
|
||||
bottom: var(--dt-spacer-3);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
position: absolute;
|
||||
bottom: var(--spacer-3);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
&__message {
|
||||
display: inline-block;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
color: var(--dt-text-light);
|
||||
border-radius: var(--dt-border-radius);
|
||||
padding: var(--dt-spacer-2) var(--dt-spacer-3);
|
||||
border: var(--dt-toast-message-border);
|
||||
}
|
||||
&__message {
|
||||
display: inline-block;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
color: var(--text-light);
|
||||
border-radius: var(--border-radius);
|
||||
padding: var(--spacer-2) var(--spacer-3);
|
||||
}
|
||||
}
|
||||
|
||||
.dt-input {
|
||||
outline: none;
|
||||
width: 100%;
|
||||
border: none;
|
||||
overflow: visible;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
border: none;
|
||||
overflow: visible;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dt-freeze {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: var(--dt-light-bg);
|
||||
opacity: 0.5;
|
||||
font-size: 2em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: var(--light-bg);
|
||||
opacity: 0.5;
|
||||
font-size: 2em;
|
||||
|
||||
&__message {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
&__message {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.dt-paste-target {
|
||||
position: fixed;
|
||||
left: -999em;
|
||||
position: fixed;
|
||||
left: -999em;
|
||||
}
|
||||
|
||||
body.dt-resize {
|
||||
cursor: col-resize;
|
||||
cursor: col-resize;
|
||||
}
|
||||
.dt-sticky-col {
|
||||
position: sticky;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
}
|
||||
270
src/style.js
270
src/style.js
@ -11,8 +11,8 @@ export default class Style {
|
||||
|
||||
linkProperties(this, this.instance, [
|
||||
'options', 'datamanager', 'columnmanager',
|
||||
'header', 'footer', 'bodyScrollable', 'datatableWrapper',
|
||||
'getColumn', 'bodyRenderer'
|
||||
'header', 'bodyScrollable', 'datatableWrapper',
|
||||
'getColumn'
|
||||
]);
|
||||
|
||||
this.scopeClass = 'dt-instance-' + instance.constructor.instances;
|
||||
@ -23,7 +23,6 @@ export default class Style {
|
||||
this.styleEl = styleEl;
|
||||
|
||||
this.bindResizeWindow();
|
||||
this.bindScrollHeader();
|
||||
}
|
||||
|
||||
get stylesheet() {
|
||||
@ -31,77 +30,18 @@ export default class Style {
|
||||
}
|
||||
|
||||
bindResizeWindow() {
|
||||
this.onWindowResize = this.onWindowResize.bind(this);
|
||||
this.onWindowResize = throttle(this.onWindowResize, 300);
|
||||
|
||||
if (this.options.layout === 'fluid') {
|
||||
$.on(window, 'resize', this.onWindowResize);
|
||||
$.on(window, 'resize', throttle(() => {
|
||||
this.distributeRemainingWidth();
|
||||
this.refreshColumnWidth();
|
||||
this.compensateScrollbarWidth();
|
||||
this.setBodyStyle();
|
||||
}, 300));
|
||||
}
|
||||
}
|
||||
|
||||
bindScrollHeader() {
|
||||
this._settingHeaderPosition = false;
|
||||
|
||||
$.on(this.bodyScrollable, 'scroll', (e) => {
|
||||
|
||||
if (this._settingHeaderPosition) return;
|
||||
|
||||
this._settingHeaderPosition = true;
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
|
||||
const scrollLeft = e.target.scrollLeft;
|
||||
|
||||
// Move non-sticky header and footer cells normally
|
||||
const nonStickyHeaderCells = this.header.querySelectorAll('.dt-cell:not(.dt-sticky-col)');
|
||||
const nonStickyFooterCells = this.footer.querySelectorAll('.dt-cell:not(.dt-sticky-col)');
|
||||
|
||||
nonStickyHeaderCells.forEach(cell => {
|
||||
$.style(cell, { transform: `translateX(${-scrollLeft}px)` });
|
||||
});
|
||||
|
||||
nonStickyFooterCells.forEach(cell => {
|
||||
$.style(cell, { transform: `translateX(${-scrollLeft}px)` });
|
||||
});
|
||||
|
||||
const stickyHeaderCells = this.header.querySelectorAll(
|
||||
'.dt-cell.dt-sticky-col:not(.dt-cell-serial-no):not(.dt-cell-checkbox)'
|
||||
);
|
||||
|
||||
stickyHeaderCells.forEach((headerCell) => {
|
||||
|
||||
const colIndex = headerCell.getAttribute('data-col-index');
|
||||
const bodyCell = this.bodyScrollable.querySelector(`.dt-cell[data-col-index="${colIndex}"]`);
|
||||
const colLeft = parseFloat(headerCell.style.left) || 0; // get left position of the column
|
||||
|
||||
// Find corresponding footer cell
|
||||
const footerCell = this.footer.querySelector(`.dt-cell[data-col-index="${colIndex}"]`);
|
||||
|
||||
if (~~(bodyCell.offsetLeft - scrollLeft) > colLeft) {
|
||||
headerCell.style.transform = `translateX(${-scrollLeft - 1}px)`;
|
||||
if (footerCell) {
|
||||
footerCell.style.transform = `translateX(${scrollLeft ? -scrollLeft - 1 : 0}px)`;
|
||||
}
|
||||
} else {
|
||||
headerCell.style.transform = `translateX(${colLeft - headerCell.offsetLeft}px)`;
|
||||
if (footerCell) footerCell.style.transform = `translateX(${colLeft - footerCell.offsetLeft}px)`;
|
||||
}
|
||||
});
|
||||
|
||||
this._settingHeaderPosition = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onWindowResize() {
|
||||
this.distributeRemainingWidth();
|
||||
this.refreshColumnWidth();
|
||||
this.setBodyStyle();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.styleEl.remove();
|
||||
$.off(window, 'resize', this.onWindowResize);
|
||||
}
|
||||
|
||||
setStyle(selector, styleObject) {
|
||||
@ -114,14 +54,14 @@ export default class Style {
|
||||
return;
|
||||
}
|
||||
|
||||
selector = selector.trim();
|
||||
if (!selector) return;
|
||||
|
||||
this._styleRulesMap = this._styleRulesMap || {};
|
||||
const prefixedSelector = this._getPrefixedSelector(selector);
|
||||
|
||||
if (this._styleRulesMap[prefixedSelector]) {
|
||||
this.removeStyle(selector);
|
||||
// find and remove
|
||||
const index = Array.from(this.stylesheet.cssRules)
|
||||
.findIndex(rule => rule.selectorText === prefixedSelector);
|
||||
this.stylesheet.deleteRule(index);
|
||||
|
||||
// merge with old styleobject
|
||||
styleObject = Object.assign({}, this._styleRulesMap[prefixedSelector], styleObject);
|
||||
@ -134,28 +74,6 @@ export default class Style {
|
||||
this.stylesheet.insertRule(ruleString);
|
||||
}
|
||||
|
||||
removeStyle(selector) {
|
||||
if (selector.includes(',')) {
|
||||
selector.split(',')
|
||||
.map(s => s.trim())
|
||||
.forEach(selector => {
|
||||
this.removeStyle(selector);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
selector = selector.trim();
|
||||
if (!selector) return;
|
||||
|
||||
// find and remove
|
||||
const prefixedSelector = this._getPrefixedSelector(selector);
|
||||
const index = Array.from(this.stylesheet.cssRules)
|
||||
.findIndex(rule => rule.selectorText === prefixedSelector);
|
||||
|
||||
if (index === -1) return;
|
||||
this.stylesheet.deleteRule(index);
|
||||
}
|
||||
|
||||
_getPrefixedSelector(selector) {
|
||||
return `.${this.scopeClass} ${selector}`;
|
||||
}
|
||||
@ -173,23 +91,35 @@ export default class Style {
|
||||
}
|
||||
|
||||
setDimensions() {
|
||||
this.setCellHeight();
|
||||
this.setHeaderStyle();
|
||||
|
||||
this.setupMinWidth();
|
||||
this.setupNaturalColumnWidth();
|
||||
this.setupColumnWidth();
|
||||
this.distributeRemainingWidth();
|
||||
this.setColumnStyle();
|
||||
this.compensateScrollbarWidth();
|
||||
|
||||
this.setDefaultCellHeight();
|
||||
this.setBodyStyle();
|
||||
}
|
||||
|
||||
setCellHeight() {
|
||||
this.setStyle('.dt-cell', {
|
||||
height: this.options.cellHeight + 'px'
|
||||
setHeaderStyle() {
|
||||
if (this.options.layout === 'fluid') {
|
||||
// setting width as 0 will ensure that the
|
||||
// header doesn't take the available space
|
||||
$.style(this.header, {
|
||||
width: 0
|
||||
});
|
||||
}
|
||||
|
||||
$.style(this.header, {
|
||||
margin: 0
|
||||
});
|
||||
}
|
||||
|
||||
setupMinWidth() {
|
||||
$.each('.dt-cell--header', this.header).map(col => {
|
||||
$.each('.dt-cell[data-is-header]', this.header).map(col => {
|
||||
const { colIndex } = $.data(col);
|
||||
const column = this.getColumn(colIndex);
|
||||
|
||||
@ -204,19 +134,8 @@ export default class Style {
|
||||
setupNaturalColumnWidth() {
|
||||
if (!$('.dt-row')) return;
|
||||
|
||||
$.each('.dt-row-header .dt-cell', this.header).map($headerCell => {
|
||||
const { colIndex } = $.data($headerCell);
|
||||
const column = this.datamanager.getColumn(colIndex);
|
||||
let width = $.style($('.dt-cell__content', $headerCell), 'width');
|
||||
if (typeof width === 'number' && width >= this.options.minimumColumnWidth) {
|
||||
column.naturalWidth = width;
|
||||
} else {
|
||||
column.naturalWidth = this.options.minimumColumnWidth;
|
||||
}
|
||||
});
|
||||
|
||||
// set initial width as naturally calculated by table's first row
|
||||
$.each('.dt-row-0 .dt-cell', this.bodyScrollable).map($cell => {
|
||||
$.each('.dt-row[data-row-index="0"] .dt-cell', this.bodyScrollable).map($cell => {
|
||||
const {
|
||||
colIndex
|
||||
} = $.data($cell);
|
||||
@ -224,11 +143,12 @@ export default class Style {
|
||||
|
||||
let naturalWidth = $.style($('.dt-cell__content', $cell), 'width');
|
||||
|
||||
if (typeof naturalWidth === 'number' && naturalWidth >= column.naturalWidth) {
|
||||
column.naturalWidth = naturalWidth;
|
||||
} else {
|
||||
column.naturalWidth = column.naturalWidth;
|
||||
if (column.id === '_rowIndex') {
|
||||
naturalWidth = this.getRowIndexColumnWidth(naturalWidth);
|
||||
column.width = naturalWidth;
|
||||
}
|
||||
|
||||
column.naturalWidth = naturalWidth;
|
||||
});
|
||||
}
|
||||
|
||||
@ -272,32 +192,29 @@ export default class Style {
|
||||
if (!column.width) {
|
||||
column.width = column.naturalWidth;
|
||||
}
|
||||
if (column.id === '_rowIndex') {
|
||||
column.width = this.getRowIndexColumnWidth();
|
||||
}
|
||||
if (column.width < this.options.minimumColumnWidth) {
|
||||
column.width = this.options.minimumColumnWidth;
|
||||
if (column.width < column.minWidth) {
|
||||
column.width = column.minWidth;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
compensateScrollbarWidth() {
|
||||
requestAnimationFrame(() => {
|
||||
const scrollbarWidth = $.scrollbarWidth();
|
||||
const lastCol = this.datamanager.getColumn(-1);
|
||||
const width = lastCol.width - scrollbarWidth;
|
||||
this.columnmanager.setColumnWidth(lastCol.colIndex, width);
|
||||
});
|
||||
}
|
||||
|
||||
distributeRemainingWidth() {
|
||||
if (this.options.layout !== 'fluid') return;
|
||||
|
||||
const wrapperWidth = $.style(this.instance.datatableWrapper, 'width');
|
||||
let firstRow = $('.dt-row', this.bodyScrollable);
|
||||
let firstRowWidth = wrapperWidth;
|
||||
if (!firstRow) {
|
||||
let headerRow = $('.dt-row', this.instance.header);
|
||||
let cellWidths = Array.from(headerRow.children)
|
||||
.map(cell => cell.offsetWidth);
|
||||
firstRowWidth = cellWidths.reduce((sum, a) => sum + a, 0);
|
||||
} else {
|
||||
firstRowWidth = $.style(firstRow, 'width');
|
||||
}
|
||||
const headerWidth = $.style(this.header, 'width');
|
||||
const resizableColumns = this.datamanager.getColumns().filter(col => col.resizable);
|
||||
const deltaWidth = (wrapperWidth - firstRowWidth) / resizableColumns.length;
|
||||
const deltaWidth = (wrapperWidth - headerWidth) / resizableColumns.length;
|
||||
|
||||
resizableColumns.map(col => {
|
||||
const width = $.style(this.getColumnHeaderElement(col.colIndex), 'width');
|
||||
@ -309,6 +226,25 @@ export default class Style {
|
||||
});
|
||||
}
|
||||
|
||||
setDefaultCellHeight() {
|
||||
if (this.options.dynamicRowHeight) return;
|
||||
if (this.__cellHeightSet) return;
|
||||
const $firstCell = $('.dt-cell[data-is-header]', this.instance.header);
|
||||
if (!$firstCell) return;
|
||||
|
||||
const height = this.options.cellHeight || $.style($firstCell, 'height');
|
||||
if (height) {
|
||||
this.setCellHeight(height);
|
||||
this.__cellHeightSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
setCellHeight(height) {
|
||||
this.setStyle('.dt-cell__content, .dt-cell__edit', {
|
||||
height: height + 'px'
|
||||
});
|
||||
}
|
||||
|
||||
setColumnStyle() {
|
||||
// align columns
|
||||
this.datamanager.getColumns()
|
||||
@ -328,6 +264,7 @@ export default class Style {
|
||||
this.columnmanager.setColumnHeaderWidth(column.colIndex);
|
||||
this.columnmanager.setColumnWidth(column.colIndex);
|
||||
});
|
||||
this.setBodyStyle();
|
||||
}
|
||||
|
||||
refreshColumnWidth() {
|
||||
@ -339,67 +276,48 @@ export default class Style {
|
||||
}
|
||||
|
||||
setBodyStyle() {
|
||||
const bodyWidth = $.style(this.datatableWrapper, 'width');
|
||||
const firstRow = $('.dt-row', this.bodyScrollable);
|
||||
if (!firstRow) return;
|
||||
const rowWidth = $.style(firstRow, 'width');
|
||||
requestAnimationFrame(() => {
|
||||
const width = $.style(this.header, 'width');
|
||||
|
||||
let width = bodyWidth > rowWidth ? rowWidth + 10 : bodyWidth;
|
||||
$.style(this.bodyScrollable, {
|
||||
width: width + 'px'
|
||||
});
|
||||
$.style(this.bodyScrollable, {
|
||||
width: width + 'px'
|
||||
});
|
||||
|
||||
// remove the body height, so that it resets to it's original
|
||||
$.removeStyle(this.bodyScrollable, 'height');
|
||||
const $body = $('.dt-body', this.bodyScrollable);
|
||||
|
||||
// when there are less rows than the container
|
||||
// adapt the container height
|
||||
let bodyHeight = $.getStyle(this.bodyScrollable, 'height');
|
||||
const scrollHeight = (this.bodyRenderer.hyperlist || {})._scrollHeight || Infinity;
|
||||
const hasHorizontalOverflow = $.hasHorizontalOverflow(this.bodyScrollable);
|
||||
|
||||
let height;
|
||||
|
||||
if (scrollHeight < bodyHeight) {
|
||||
height = scrollHeight;
|
||||
|
||||
// account for scrollbar size when
|
||||
// there is horizontal overflow
|
||||
if (hasHorizontalOverflow) {
|
||||
height += $.scrollbarSize();
|
||||
if ($body) {
|
||||
$.style($body, {
|
||||
height: '0px'
|
||||
});
|
||||
}
|
||||
|
||||
$.style(this.bodyScrollable, {
|
||||
height: height + 'px'
|
||||
marginTop: $.style(this.header, 'height') + 'px'
|
||||
});
|
||||
}
|
||||
|
||||
const verticalOverflow = this.bodyScrollable.scrollHeight - this.bodyScrollable.offsetHeight;
|
||||
if (verticalOverflow < $.scrollbarSize()) {
|
||||
// if verticalOverflow is less than scrollbar size
|
||||
// then most likely scrollbar is causing the scroll
|
||||
// which is not needed
|
||||
$.style(this.bodyScrollable, {
|
||||
overflowY: 'hidden'
|
||||
$.style($('table', this.bodyScrollable), {
|
||||
margin: 0,
|
||||
width: '100%'
|
||||
});
|
||||
}
|
||||
|
||||
if (this.options.layout === 'fluid') {
|
||||
$.style(this.bodyScrollable, {
|
||||
overflowX: 'hidden'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getColumnHeaderElement(colIndex) {
|
||||
colIndex = +colIndex;
|
||||
if (colIndex < 0) return null;
|
||||
return $(`.dt-cell--col-${colIndex}`, this.header);
|
||||
return $(`.dt-cell[data-col-index="${colIndex}"]`, this.header);
|
||||
}
|
||||
|
||||
getRowIndexColumnWidth() {
|
||||
getRowIndexColumnWidth(baseWidth) {
|
||||
this._rowIndexColumnWidthMap = this._rowIndexColumnWidthMap || {};
|
||||
const rowCount = this.datamanager.getRowCount();
|
||||
const padding = 22;
|
||||
return $.measureTextWidth(rowCount + '') + padding;
|
||||
const digits = (rowCount + '').length;
|
||||
|
||||
if (!this._rowIndexColumnWidthMap[digits]) {
|
||||
// add 8px for each unit
|
||||
this._rowIndexColumnWidthMap[digits] = baseWidth + ((digits - 1) * 8);
|
||||
}
|
||||
|
||||
return this._rowIndexColumnWidthMap[digits];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
import { format } from './utils';
|
||||
import getTranslations from './translations';
|
||||
|
||||
export default class TranslationManager {
|
||||
constructor(language) {
|
||||
this.language = language;
|
||||
this.translations = getTranslations();
|
||||
}
|
||||
|
||||
addTranslations(translations) {
|
||||
this.translations = Object.assign(this.translations, translations);
|
||||
}
|
||||
|
||||
translate(sourceText, args) {
|
||||
let translation = (this.translations[this.language] &&
|
||||
this.translations[this.language][sourceText]) || sourceText;
|
||||
|
||||
if (typeof translation === 'object') {
|
||||
translation = args && args.count ?
|
||||
this.getPluralizedTranslation(translation, args.count) :
|
||||
sourceText;
|
||||
}
|
||||
|
||||
return format(translation, args || {});
|
||||
}
|
||||
|
||||
getPluralizedTranslation(translations, count) {
|
||||
return translations[count] || translations['default'];
|
||||
}
|
||||
};
|
||||
@ -1,15 +0,0 @@
|
||||
{
|
||||
"Sort Ascending": "Aufsteigend sortieren",
|
||||
"Sort Descending": "Absteigend sortieren",
|
||||
"Reset sorting": "Sortierung zurücksetzen",
|
||||
"Remove column": "Spalte entfernen",
|
||||
"No Data": "Keine Daten",
|
||||
"{count} cells copied": {
|
||||
"1": "{count} Zelle kopiert",
|
||||
"default": "{count} Zellen kopiert"
|
||||
},
|
||||
"{count} rows selected": {
|
||||
"1": "{count} Zeile ausgewählt",
|
||||
"default": "{count} Zeilen ausgewählt"
|
||||
}
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
{
|
||||
"Sort Ascending": "Sort Ascending",
|
||||
"Sort Descending": "Sort Descending",
|
||||
"Reset sorting": "Reset sorting",
|
||||
"Remove column": "Remove column",
|
||||
"No Data": "No Data",
|
||||
"{count} cells copied": {
|
||||
"1": "{count} cell copied",
|
||||
"default": "{count} cells copied"
|
||||
},
|
||||
"{count} rows selected": {
|
||||
"1": "{count} row selected",
|
||||
"default": "{count} rows selected"
|
||||
}
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
{
|
||||
"Sort Ascending": "Trier par ordre croissant",
|
||||
"Sort Descending": "Trier par ordre décroissant",
|
||||
"Reset sorting": "Réinitialiser le tri",
|
||||
"Remove column": "Supprimer colonne",
|
||||
"No Data": "Pas de données",
|
||||
"{count} cells copied": {
|
||||
"1": "{count} cellule copiée",
|
||||
"default": "{count} cellules copiées"
|
||||
},
|
||||
"{count} rows selected": {
|
||||
"1": "{count} ligne sélectionnée",
|
||||
"default": "{count} lignes sélectionnées"
|
||||
}
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
import en from './en.json';
|
||||
import de from './de.json';
|
||||
import fr from './fr.json';
|
||||
import it from './it.json';
|
||||
|
||||
export default function getTranslations() {
|
||||
return {
|
||||
en,
|
||||
de,
|
||||
fr,
|
||||
it,
|
||||
};
|
||||
};
|
||||
@ -1,15 +0,0 @@
|
||||
{
|
||||
"Sort Ascending": "Ordinamento ascendente",
|
||||
"Sort Descending": "Ordinamento decrescente",
|
||||
"Reset sorting": "Azzeramento ordinamento",
|
||||
"Remove column": "Rimuovi colonna",
|
||||
"No Data": "Nessun dato",
|
||||
"{count} cells copied": {
|
||||
"1": "Copiato {count} cella",
|
||||
"default": "{count} celle copiate"
|
||||
},
|
||||
"{count} rows selected": {
|
||||
"1": "{count} linea selezionata",
|
||||
"default": "{count} linee selezionate"
|
||||
}
|
||||
}
|
||||
40
src/utils.js
40
src/utils.js
@ -1,6 +1,5 @@
|
||||
import _throttle from 'lodash/throttle';
|
||||
import _debounce from 'lodash/debounce';
|
||||
import _uniq from 'lodash/uniq';
|
||||
|
||||
export function camelCaseToDash(str) {
|
||||
return str.replace(/([A-Z])/g, (g) => `-${g[0].toLowerCase()}`);
|
||||
@ -126,42 +125,3 @@ export function ensureArray(val) {
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
export function uniq(arr) {
|
||||
return _uniq(arr);
|
||||
}
|
||||
|
||||
export function numberSortAsc(a, b) {
|
||||
return a - b;
|
||||
};
|
||||
|
||||
export function stripHTML(html) {
|
||||
return html.replace(/<[^>]*>/g, '');
|
||||
};
|
||||
|
||||
export function format(str, args) {
|
||||
if (!str) return str;
|
||||
|
||||
Object.keys(args).forEach(arg => {
|
||||
let regex = new RegExp(`{(${arg})}`, 'g');
|
||||
str = str.replace(regex, args[arg]);
|
||||
});
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
export function escapeHTML(txt) {
|
||||
if (!txt) return '';
|
||||
let escapeHtmlMapping = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
'/': '/',
|
||||
'`': '`',
|
||||
'=': '=',
|
||||
};
|
||||
|
||||
return String(txt).replace(/[&<>"'`=/]/g, (char) => escapeHtmlMapping[char] || char);
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user