commit
54a3c670c5
@ -1 +1,2 @@
|
||||
dist
|
||||
dist
|
||||
docs/frappe-datatable.js
|
||||
@ -55,7 +55,7 @@
|
||||
"generator-star-spacing": [2, "both"],
|
||||
"guard-for-in": 0,
|
||||
"handle-callback-err": [2, "^(err|error|anySpecificError)$" ],
|
||||
"indent": [2, 2, { "SwitchCase": 1 }],
|
||||
"indent": [2, 4, { "SwitchCase": 1 }],
|
||||
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
|
||||
"keyword-spacing": [2, {"before": true, "after": true}],
|
||||
"linebreak-style": 0,
|
||||
|
||||
255
dist/frappe-datatable.cjs.css
vendored
Normal file
255
dist/frappe-datatable.cjs.css
vendored
Normal file
@ -0,0 +1,255 @@
|
||||
/* This file is processed by postcss */
|
||||
/* variables */
|
||||
|
||||
.data-table {
|
||||
|
||||
/* styling */
|
||||
width: 100%;
|
||||
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 .trash-container {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 30%;
|
||||
right: 30%;
|
||||
height: 70px;
|
||||
background: palevioletred;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.data-table .hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.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-col.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-col.sortable-chosen {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.data-table-col {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.data-table-col .content {
|
||||
padding: 8px;
|
||||
padding: 0.5rem;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.data-table-col .content.ellipsis {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.data-table-col .edit-cell {
|
||||
display: none;
|
||||
padding: 8px;
|
||||
padding: 0.5rem;
|
||||
background: #fff;
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.data-table-col.selected .content {
|
||||
border: 2px solid rgb(82, 146, 247);
|
||||
}
|
||||
|
||||
.data-table-col.editing .content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.data-table-col.editing .edit-cell {
|
||||
border: 2px solid rgb(82, 146, 247);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.data-table-col.highlight {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.data-table-col:hover .column-resizer {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.data-table-col:hover .data-table-dropdown-toggle {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
4612
dist/frappe-datatable.cjs.js
vendored
4612
dist/frappe-datatable.cjs.js
vendored
File diff suppressed because it is too large
Load Diff
1
dist/frappe-datatable.css
vendored
1
dist/frappe-datatable.css
vendored
@ -1,3 +1,4 @@
|
||||
/* This file is processed by postcss */
|
||||
/* variables */
|
||||
|
||||
.data-table {
|
||||
|
||||
4612
dist/frappe-datatable.js
vendored
4612
dist/frappe-datatable.js
vendored
File diff suppressed because it is too large
Load Diff
255
docs/frappe-datatable.css
Normal file
255
docs/frappe-datatable.css
Normal file
@ -0,0 +1,255 @@
|
||||
/* This file is processed by postcss */
|
||||
/* variables */
|
||||
|
||||
.data-table {
|
||||
|
||||
/* styling */
|
||||
width: 100%;
|
||||
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 .trash-container {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 30%;
|
||||
right: 30%;
|
||||
height: 70px;
|
||||
background: palevioletred;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.data-table .hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.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-col.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-col.sortable-chosen {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.data-table-col {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.data-table-col .content {
|
||||
padding: 8px;
|
||||
padding: 0.5rem;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.data-table-col .content.ellipsis {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.data-table-col .edit-cell {
|
||||
display: none;
|
||||
padding: 8px;
|
||||
padding: 0.5rem;
|
||||
background: #fff;
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.data-table-col.selected .content {
|
||||
border: 2px solid rgb(82, 146, 247);
|
||||
}
|
||||
|
||||
.data-table-col.editing .content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.data-table-col.editing .edit-cell {
|
||||
border: 2px solid rgb(82, 146, 247);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.data-table-col.highlight {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.data-table-col:hover .column-resizer {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.data-table-col:hover .data-table-dropdown-toggle {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
3332
docs/frappe-datatable.js
Normal file
3332
docs/frappe-datatable.js
Normal file
File diff suppressed because it is too large
Load Diff
40
docs/index.css
Normal file
40
docs/index.css
Normal file
@ -0,0 +1,40 @@
|
||||
*, *::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-size: 14px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.showcase {
|
||||
margin: 0 auto;
|
||||
padding: 4rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.showcase p {
|
||||
font-size: 2rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.datatable-1, .datatable-2 {
|
||||
width: 735px;
|
||||
margin: 0 auto;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.datatable-2 {
|
||||
width: 934px;
|
||||
}
|
||||
27
docs/index.html
Normal file
27
docs/index.html
Normal file
@ -0,0 +1,27 @@
|
||||
<!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="frappe-datatable.css" rel="stylesheet">
|
||||
<link href="index.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<section class="hero showcase">
|
||||
<h1>Frappe DataTable</h1>
|
||||
<p>A simple, modern and interactive datatable for the web</p>
|
||||
<div class="datatable-1"></div>
|
||||
</section>
|
||||
<section class="showcase">
|
||||
<h2>Tree Structure</h2>
|
||||
<p>Show tree structured rows in your table</p>
|
||||
<div class="datatable-2"></div>
|
||||
</section>
|
||||
<script src="../node_modules/clusterize.js/clusterize.js"></script>
|
||||
<script src="../node_modules/sortablejs/Sortable.js"></script>
|
||||
<script src="frappe-datatable.js"></script>
|
||||
<script src="index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
691
docs/index.js
Normal file
691
docs/index.js
Normal file
@ -0,0 +1,691 @@
|
||||
/* global DataTable */
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
const {
|
||||
columns,
|
||||
data
|
||||
} = getSampleData();
|
||||
|
||||
let datatable1 = new DataTable('.datatable-1', {
|
||||
columns,
|
||||
data
|
||||
});
|
||||
|
||||
let datatable2 = new DataTable('.datatable-2', Object.assign(getTreeData(), {
|
||||
enableInlineFilters: true
|
||||
}));
|
||||
|
||||
function getSampleData(multiplier) {
|
||||
let columns = ['Name', 'Position', 'Office', 'Extn.', '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) - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Application of Funds (Assets) - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Current Assets - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Accounts Receivable - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Current Assets - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Bank Accounts - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Bank Accounts - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Current Assets - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Cash In Hand - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Current Assets - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Stock Assets - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'All Warehouses - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'All Warehouses - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Retail Stores - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Retail Stores - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Retail Stores - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'All Warehouses - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'All Warehouses - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Application of Funds (Assets) - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Fixed Assets - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Fixed Assets - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Application of Funds (Assets) - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Temporary Accounts - GTPL',
|
||||
'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) - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Source of Funds (Liabilities) - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Current Liabilities - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Accounts Payable - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Accounts Payable - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Current Liabilities - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Duties and Taxes - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Duties and Taxes - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Duties and Taxes - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Duties and Taxes - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Current Liabilities - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Stock Liabilities - GTPL',
|
||||
'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 - GTPL',
|
||||
'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 - GTPL',
|
||||
'parent_account': 'Equity - GTPL',
|
||||
'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'
|
||||
}]
|
||||
};
|
||||
}
|
||||
@ -8,34 +8,45 @@ import cssnext from 'postcss-cssnext';
|
||||
// import cssnano from 'cssnano';
|
||||
|
||||
const dev = {
|
||||
input: 'src/index.js',
|
||||
output: {
|
||||
file: 'dist/frappe-datatable.js',
|
||||
format: 'iife',
|
||||
name: 'DataTable',
|
||||
globals: {
|
||||
sortablejs: 'Sortable',
|
||||
'clusterize.js': 'Clusterize'
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
json(),
|
||||
nodeResolve(),
|
||||
commonjs(),
|
||||
postcss({
|
||||
extract: 'dist/frappe-datatable.css',
|
||||
plugins: [
|
||||
nested(),
|
||||
cssnext()
|
||||
]
|
||||
})
|
||||
],
|
||||
external: ['sortablejs', 'clusterize.js']
|
||||
input: 'src/index.js',
|
||||
output: [{
|
||||
file: 'dist/frappe-datatable.js',
|
||||
format: 'iife',
|
||||
name: 'DataTable',
|
||||
globals: {
|
||||
sortablejs: 'Sortable',
|
||||
'clusterize.js': 'Clusterize'
|
||||
}
|
||||
}, {
|
||||
file: 'docs/frappe-datatable.js',
|
||||
format: 'iife',
|
||||
name: 'DataTable',
|
||||
globals: {
|
||||
sortablejs: 'Sortable',
|
||||
'clusterize.js': 'Clusterize'
|
||||
}
|
||||
}],
|
||||
plugins: [
|
||||
json(),
|
||||
nodeResolve(),
|
||||
commonjs(),
|
||||
postcss({
|
||||
extract: ['dist/frappe-datatable.css', 'docs/frappe-datatable.css'],
|
||||
plugins: [
|
||||
nested(),
|
||||
cssnext()
|
||||
]
|
||||
})
|
||||
],
|
||||
external: ['sortablejs', 'clusterize.js']
|
||||
};
|
||||
|
||||
export default [dev, Object.assign({}, dev, {
|
||||
output: {
|
||||
format: 'cjs',
|
||||
file: 'dist/frappe-datatable.cjs.js'
|
||||
}
|
||||
})];
|
||||
export default [
|
||||
dev,
|
||||
Object.assign({}, dev, {
|
||||
output: {
|
||||
format: 'cjs',
|
||||
file: 'dist/frappe-datatable.cjs.js'
|
||||
}
|
||||
})
|
||||
];
|
||||
|
||||
@ -1,99 +1,101 @@
|
||||
import $ from './dom';
|
||||
import Clusterize from 'clusterize.js';
|
||||
import { promisify } from './utils';
|
||||
import {
|
||||
promisify
|
||||
} from './utils';
|
||||
|
||||
export default class BodyRenderer {
|
||||
constructor(instance) {
|
||||
this.instance = instance;
|
||||
this.options = instance.options;
|
||||
this.datamanager = instance.datamanager;
|
||||
this.rowmanager = instance.rowmanager;
|
||||
this.cellmanager = instance.cellmanager;
|
||||
this.bodyScrollable = instance.bodyScrollable;
|
||||
this.log = instance.log;
|
||||
this.appendRemainingData = promisify(this.appendRemainingData, this);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.options.enableClusterize) {
|
||||
this.renderBodyWithClusterize();
|
||||
} else {
|
||||
this.renderBodyHTML();
|
||||
}
|
||||
}
|
||||
|
||||
renderBodyHTML() {
|
||||
const rows = this.datamanager.getRows();
|
||||
|
||||
this.bodyScrollable.innerHTML = `
|
||||
<table class="data-table-body">
|
||||
${getBodyHTML(rows)}
|
||||
</table>
|
||||
`;
|
||||
this.instance.setDimensions();
|
||||
this.restoreState();
|
||||
}
|
||||
|
||||
renderBodyWithClusterize() {
|
||||
// first page
|
||||
const rows = this.datamanager.getRows(0, 20);
|
||||
const initialData = this.getDataForClusterize(rows);
|
||||
|
||||
if (!this.clusterize) {
|
||||
// empty body
|
||||
this.bodyScrollable.innerHTML = `
|
||||
<table class="data-table-body">
|
||||
${getBodyHTML([])}
|
||||
</table>
|
||||
`;
|
||||
|
||||
// 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 */
|
||||
no_data_text: this.options.noDataMessage,
|
||||
no_data_class: 'empty-state'
|
||||
/* eslint-enable */
|
||||
});
|
||||
|
||||
// setDimensions requires atleast 1 row to exist in dom
|
||||
this.instance.setDimensions();
|
||||
} else {
|
||||
this.clusterize.update(initialData);
|
||||
constructor(instance) {
|
||||
this.instance = instance;
|
||||
this.options = instance.options;
|
||||
this.datamanager = instance.datamanager;
|
||||
this.rowmanager = instance.rowmanager;
|
||||
this.cellmanager = instance.cellmanager;
|
||||
this.bodyScrollable = instance.bodyScrollable;
|
||||
this.log = instance.log;
|
||||
this.appendRemainingData = promisify(this.appendRemainingData, this);
|
||||
}
|
||||
|
||||
this.appendRemainingData();
|
||||
}
|
||||
render() {
|
||||
if (this.options.enableClusterize) {
|
||||
this.renderBodyWithClusterize();
|
||||
} else {
|
||||
this.renderBodyHTML();
|
||||
}
|
||||
}
|
||||
|
||||
restoreState() {
|
||||
this.rowmanager.highlightCheckedRows();
|
||||
this.cellmanager.selectAreaOnClusterChanged();
|
||||
this.cellmanager.focusCellOnClusterChanged();
|
||||
}
|
||||
renderBodyHTML() {
|
||||
const rows = this.datamanager.getRows();
|
||||
|
||||
appendRemainingData() {
|
||||
const rows = this.datamanager.getRows(20);
|
||||
const data = this.getDataForClusterize(rows);
|
||||
this.clusterize.append(data);
|
||||
}
|
||||
this.bodyScrollable.innerHTML = `
|
||||
<table class="data-table-body">
|
||||
${this.getBodyHTML(rows)}
|
||||
</table>
|
||||
`;
|
||||
this.instance.setDimensions();
|
||||
this.restoreState();
|
||||
}
|
||||
|
||||
getDataForClusterize(rows) {
|
||||
return rows.map((row) => this.rowmanager.getRowHTML(row, { rowIndex: row[0].rowIndex }));
|
||||
}
|
||||
renderBodyWithClusterize() {
|
||||
// first page
|
||||
const rows = this.datamanager.getRows(0, 20);
|
||||
const initialData = this.getDataForClusterize(rows);
|
||||
|
||||
if (!this.clusterize) {
|
||||
// empty body
|
||||
this.bodyScrollable.innerHTML = `
|
||||
<table class="data-table-body">
|
||||
${this.getBodyHTML([])}
|
||||
</table>
|
||||
`;
|
||||
|
||||
// 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 */
|
||||
no_data_text: this.options.noDataMessage,
|
||||
no_data_class: 'empty-state'
|
||||
/* eslint-enable */
|
||||
});
|
||||
|
||||
// setDimensions requires atleast 1 row to exist in dom
|
||||
this.instance.setDimensions();
|
||||
} else {
|
||||
this.clusterize.update(initialData);
|
||||
}
|
||||
|
||||
this.appendRemainingData();
|
||||
}
|
||||
|
||||
restoreState() {
|
||||
this.rowmanager.highlightCheckedRows();
|
||||
this.cellmanager.selectAreaOnClusterChanged();
|
||||
this.cellmanager.focusCellOnClusterChanged();
|
||||
}
|
||||
|
||||
appendRemainingData() {
|
||||
const rows = this.datamanager.getRows(20);
|
||||
const data = this.getDataForClusterize(rows);
|
||||
this.clusterize.append(data);
|
||||
}
|
||||
|
||||
getDataForClusterize(rows) {
|
||||
return rows.map((row) => this.rowmanager.getRowHTML(row, row.meta));
|
||||
}
|
||||
|
||||
getBodyHTML(rows) {
|
||||
return `
|
||||
<tbody>
|
||||
${rows.map(row => this.rowmanager.getRowHTML(row, row.meta)).join('')}
|
||||
</tbody>
|
||||
`;
|
||||
}
|
||||
};
|
||||
|
||||
export function getBodyHTML(rows) {
|
||||
return `
|
||||
<tbody>
|
||||
${rows.map(row => this.rowmanager.getRowHTML(row, { rowIndex: row[0].rowIndex })).join('')}
|
||||
</tbody>
|
||||
`;
|
||||
}
|
||||
|
||||
1166
src/cellmanager.js
1166
src/cellmanager.js
File diff suppressed because it is too large
Load Diff
@ -1,413 +1,452 @@
|
||||
import $ from './dom';
|
||||
import Sortable from 'sortablejs';
|
||||
import { getDefault, linkProperties, debounce } from './utils';
|
||||
import {
|
||||
getDefault,
|
||||
linkProperties,
|
||||
debounce
|
||||
} from './utils';
|
||||
|
||||
export default class ColumnManager {
|
||||
constructor(instance) {
|
||||
this.instance = instance;
|
||||
constructor(instance) {
|
||||
this.instance = instance;
|
||||
|
||||
linkProperties(this, this.instance, [
|
||||
'options',
|
||||
'fireEvent',
|
||||
'header',
|
||||
'datamanager',
|
||||
'style',
|
||||
'wrapper',
|
||||
'rowmanager',
|
||||
'bodyScrollable'
|
||||
]);
|
||||
linkProperties(this, this.instance, [
|
||||
'options',
|
||||
'fireEvent',
|
||||
'header',
|
||||
'datamanager',
|
||||
'style',
|
||||
'wrapper',
|
||||
'rowmanager',
|
||||
'bodyScrollable'
|
||||
]);
|
||||
|
||||
this.bindEvents();
|
||||
getDropdownHTML = getDropdownHTML.bind(this, this.options.dropdownButton);
|
||||
}
|
||||
this.bindEvents();
|
||||
getDropdownHTML = getDropdownHTML.bind(this, this.options.dropdownButton);
|
||||
}
|
||||
|
||||
renderHeader() {
|
||||
this.header.innerHTML = '<thead></thead>';
|
||||
this.refreshHeader();
|
||||
}
|
||||
renderHeader() {
|
||||
this.header.innerHTML = '<thead></thead>';
|
||||
this.refreshHeader();
|
||||
}
|
||||
|
||||
refreshHeader() {
|
||||
const columns = this.datamanager.getColumns();
|
||||
refreshHeader() {
|
||||
const columns = this.datamanager.getColumns();
|
||||
|
||||
if (!$('.data-table-col', this.header)) {
|
||||
// insert html
|
||||
if (!$('.data-table-col', this.header)) {
|
||||
// insert html
|
||||
|
||||
let html = this.rowmanager.getRowHTML(columns, { isHeader: 1 });
|
||||
if (this.options.enableInlineFilters) {
|
||||
html += this.rowmanager.getRowHTML(columns, { isFilter: 1 });
|
||||
}
|
||||
let html = this.rowmanager.getRowHTML(columns, {
|
||||
isHeader: 1
|
||||
});
|
||||
if (this.options.enableInlineFilters) {
|
||||
html += this.rowmanager.getRowHTML(columns, {
|
||||
isFilter: 1
|
||||
});
|
||||
}
|
||||
|
||||
$('thead', this.header).innerHTML = html;
|
||||
$('thead', this.header).innerHTML = html;
|
||||
|
||||
this.$filterRow = $('.data-table-row[data-is-filter]', this.header);
|
||||
this.$filterRow = $('.data-table-row[data-is-filter]', this.header);
|
||||
|
||||
if (this.$filterRow) {
|
||||
// hide filter row immediately, so it doesn't disturb layout
|
||||
$.style(this.$filterRow, {
|
||||
display: 'none'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// refresh dom state
|
||||
const $cols = $.each('.data-table-col', this.header);
|
||||
if (columns.length < $cols.length) {
|
||||
// deleted column
|
||||
$('thead', this.header).innerHTML = this.rowmanager.getRowHTML(columns, { isHeader: 1 });
|
||||
return;
|
||||
}
|
||||
if (this.$filterRow) {
|
||||
// hide filter row immediately, so it doesn't disturb layout
|
||||
$.style(this.$filterRow, {
|
||||
display: 'none'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// refresh dom state
|
||||
const $cols = $.each('.data-table-col', this.header);
|
||||
if (columns.length < $cols.length) {
|
||||
// deleted column
|
||||
$('thead', this.header).innerHTML = this.rowmanager.getRowHTML(columns, {
|
||||
isHeader: 1
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
$cols.map(($col, i) => {
|
||||
const column = columns[i];
|
||||
// column sorted or order changed
|
||||
// update colIndex of each header cell
|
||||
$.data($col, {
|
||||
colIndex: column.colIndex
|
||||
});
|
||||
$cols.map(($col, i) => {
|
||||
const column = columns[i];
|
||||
// column sorted or order changed
|
||||
// update colIndex of each header cell
|
||||
$.data($col, {
|
||||
colIndex: column.colIndex
|
||||
});
|
||||
|
||||
// refresh sort indicator
|
||||
const sortIndicator = $('.sort-indicator', $col);
|
||||
if (sortIndicator) {
|
||||
sortIndicator.innerHTML = this.options.sortIndicator[column.sortOrder];
|
||||
// refresh sort indicator
|
||||
const sortIndicator = $('.sort-indicator', $col);
|
||||
if (sortIndicator) {
|
||||
sortIndicator.innerHTML = this.options.sortIndicator[column.sortOrder];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// reset columnMap
|
||||
this.$columnMap = [];
|
||||
}
|
||||
// reset columnMap
|
||||
this.$columnMap = [];
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
this.bindDropdown();
|
||||
this.bindResizeColumn();
|
||||
this.bindMoveColumn();
|
||||
this.bindFilter();
|
||||
}
|
||||
|
||||
bindDropdown() {
|
||||
let $activeDropdown;
|
||||
$.on(this.header, 'click', '.data-table-dropdown-toggle', (e, $button) => {
|
||||
const $dropdown = $.closest('.data-table-dropdown', $button);
|
||||
|
||||
if (!$dropdown.classList.contains('is-active')) {
|
||||
deactivateDropdown();
|
||||
$dropdown.classList.add('is-active');
|
||||
$activeDropdown = $dropdown;
|
||||
} else {
|
||||
deactivateDropdown();
|
||||
}
|
||||
});
|
||||
|
||||
$.on(document.body, 'click', (e) => {
|
||||
if (e.target.matches('.data-table-dropdown-toggle')) return;
|
||||
deactivateDropdown();
|
||||
});
|
||||
|
||||
const dropdownItems = this.options.headerDropdown;
|
||||
|
||||
$.on(this.header, 'click', '.data-table-dropdown-list > div', (e, $item) => {
|
||||
const $col = $.closest('.data-table-col', $item);
|
||||
const { index } = $.data($item);
|
||||
const { colIndex } = $.data($col);
|
||||
let callback = dropdownItems[index].action;
|
||||
|
||||
callback && callback.call(this.instance, this.getColumn(colIndex));
|
||||
});
|
||||
|
||||
function deactivateDropdown(e) {
|
||||
$activeDropdown && $activeDropdown.classList.remove('is-active');
|
||||
$activeDropdown = null;
|
||||
bindEvents() {
|
||||
this.bindDropdown();
|
||||
this.bindResizeColumn();
|
||||
this.bindMoveColumn();
|
||||
this.bindFilter();
|
||||
}
|
||||
}
|
||||
|
||||
bindResizeColumn() {
|
||||
let isDragging = false;
|
||||
let $resizingCell, startWidth, startX;
|
||||
bindDropdown() {
|
||||
let $activeDropdown;
|
||||
$.on(this.header, 'click', '.data-table-dropdown-toggle', (e, $button) => {
|
||||
const $dropdown = $.closest('.data-table-dropdown', $button);
|
||||
|
||||
$.on(this.header, 'mousedown', '.data-table-col .column-resizer', (e, $handle) => {
|
||||
document.body.classList.add('data-table-resize');
|
||||
const $cell = $handle.parentNode.parentNode;
|
||||
$resizingCell = $cell;
|
||||
const { colIndex } = $.data($resizingCell);
|
||||
const col = this.getColumn(colIndex);
|
||||
|
||||
if (col && col.resizable === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
isDragging = true;
|
||||
startWidth = $.style($('.content', $resizingCell), 'width');
|
||||
startX = e.pageX;
|
||||
});
|
||||
|
||||
$.on(document.body, 'mouseup', (e) => {
|
||||
document.body.classList.remove('data-table-resize');
|
||||
if (!$resizingCell) return;
|
||||
isDragging = false;
|
||||
|
||||
const { colIndex } = $.data($resizingCell);
|
||||
this.setColumnWidth(colIndex);
|
||||
this.style.setBodyStyle();
|
||||
$resizingCell = null;
|
||||
});
|
||||
|
||||
$.on(document.body, 'mousemove', (e) => {
|
||||
if (!isDragging) return;
|
||||
const finalWidth = startWidth + (e.pageX - startX);
|
||||
const { colIndex } = $.data($resizingCell);
|
||||
|
||||
if (this.getColumnMinWidth(colIndex) > finalWidth) {
|
||||
// don't resize past minWidth
|
||||
return;
|
||||
}
|
||||
this.datamanager.updateColumn(colIndex, { width: finalWidth });
|
||||
this.setColumnHeaderWidth(colIndex);
|
||||
});
|
||||
}
|
||||
|
||||
bindMoveColumn() {
|
||||
let initialized;
|
||||
|
||||
const initialize = () => {
|
||||
if (initialized) {
|
||||
$.off(document.body, 'mousemove', initialize);
|
||||
return;
|
||||
}
|
||||
const ready = $('.data-table-col', this.header);
|
||||
if (!ready) return;
|
||||
|
||||
const $parent = $('.data-table-row', this.header);
|
||||
|
||||
this.sortable = Sortable.create($parent, {
|
||||
onEnd: (e) => {
|
||||
const { oldIndex, newIndex } = e;
|
||||
const $draggedCell = e.item;
|
||||
const { colIndex } = $.data($draggedCell);
|
||||
if (+colIndex === newIndex) return;
|
||||
|
||||
this.switchColumn(oldIndex, newIndex);
|
||||
},
|
||||
preventOnFilter: false,
|
||||
filter: '.column-resizer, .data-table-dropdown',
|
||||
animation: 150
|
||||
});
|
||||
};
|
||||
|
||||
$.on(document.body, 'mousemove', initialize);
|
||||
}
|
||||
|
||||
bindSortColumn() {
|
||||
|
||||
$.on(this.header, 'click', '.data-table-col .column-title', (e, span) => {
|
||||
const $cell = span.closest('.data-table-col');
|
||||
let { colIndex, sortOrder } = $.data($cell);
|
||||
sortOrder = getDefault(sortOrder, 'none');
|
||||
const col = this.getColumn(colIndex);
|
||||
|
||||
if (col && col.sortable === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// reset sort indicator
|
||||
$('.sort-indicator', this.header).textContent = '';
|
||||
$.each('.data-table-col', this.header).map($cell => {
|
||||
$.data($cell, {
|
||||
sortOrder: 'none'
|
||||
if (!$dropdown.classList.contains('is-active')) {
|
||||
deactivateDropdown();
|
||||
$dropdown.classList.add('is-active');
|
||||
$activeDropdown = $dropdown;
|
||||
} else {
|
||||
deactivateDropdown();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let nextSortOrder, textContent;
|
||||
if (sortOrder === 'none') {
|
||||
nextSortOrder = 'asc';
|
||||
textContent = '▲';
|
||||
} else if (sortOrder === 'asc') {
|
||||
nextSortOrder = 'desc';
|
||||
textContent = '▼';
|
||||
} else if (sortOrder === 'desc') {
|
||||
nextSortOrder = 'none';
|
||||
textContent = '';
|
||||
}
|
||||
|
||||
$.data($cell, {
|
||||
sortOrder: nextSortOrder
|
||||
});
|
||||
$('.sort-indicator', $cell).textContent = textContent;
|
||||
|
||||
this.sortColumn(colIndex, nextSortOrder);
|
||||
});
|
||||
}
|
||||
|
||||
sortColumn(colIndex, nextSortOrder) {
|
||||
this.instance.freeze();
|
||||
this.sortRows(colIndex, nextSortOrder)
|
||||
.then(() => {
|
||||
this.refreshHeader();
|
||||
return this.rowmanager.refreshRows();
|
||||
})
|
||||
.then(() => this.instance.unfreeze())
|
||||
.then(() => {
|
||||
this.fireEvent('onSortColumn', this.getColumn(colIndex));
|
||||
});
|
||||
}
|
||||
|
||||
removeColumn(colIndex) {
|
||||
const removedCol = this.getColumn(colIndex);
|
||||
this.instance.freeze();
|
||||
this.datamanager.removeColumn(colIndex)
|
||||
.then(() => {
|
||||
this.refreshHeader();
|
||||
return this.rowmanager.refreshRows();
|
||||
})
|
||||
.then(() => this.instance.unfreeze())
|
||||
.then(() => {
|
||||
this.fireEvent('onRemoveColumn', removedCol);
|
||||
});
|
||||
}
|
||||
|
||||
switchColumn(oldIndex, newIndex) {
|
||||
this.instance.freeze();
|
||||
this.datamanager.switchColumn(oldIndex, newIndex)
|
||||
.then(() => {
|
||||
this.refreshHeader();
|
||||
return this.rowmanager.refreshRows();
|
||||
})
|
||||
.then(() => {
|
||||
this.setColumnWidth(oldIndex);
|
||||
this.setColumnWidth(newIndex);
|
||||
this.instance.unfreeze();
|
||||
})
|
||||
.then(() => {
|
||||
this.fireEvent('onSwitchColumn',
|
||||
this.getColumn(oldIndex), this.getColumn(newIndex)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
toggleFilter() {
|
||||
this.isFilterShown = this.isFilterShown || false;
|
||||
|
||||
if (this.isFilterShown) {
|
||||
$.style(this.$filterRow, {
|
||||
display: 'none'
|
||||
});
|
||||
} else {
|
||||
$.style(this.$filterRow, {
|
||||
display: ''
|
||||
});
|
||||
}
|
||||
|
||||
this.isFilterShown = !this.isFilterShown;
|
||||
this.style.setBodyStyle();
|
||||
}
|
||||
|
||||
focusFilter(colIndex) {
|
||||
if (!this.isFilterShown) return;
|
||||
|
||||
const $filterInput = $(`[data-col-index="${colIndex}"] .data-table-filter`, this.$filterRow);
|
||||
$filterInput.focus();
|
||||
}
|
||||
|
||||
bindFilter() {
|
||||
if (!this.options.enableInlineFilters) return;
|
||||
const handler = e => {
|
||||
const $filterCell = $.closest('.data-table-col', e.target);
|
||||
const { colIndex } = $.data($filterCell);
|
||||
const keyword = e.target.value;
|
||||
|
||||
this.datamanager.filterRows(keyword, colIndex)
|
||||
.then(({ rowsToHide, rowsToShow }) => {
|
||||
rowsToHide.map(rowIndex => {
|
||||
const $tr = $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable);
|
||||
$tr.classList.add('hide');
|
||||
});
|
||||
rowsToShow.map(rowIndex => {
|
||||
const $tr = $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable);
|
||||
$tr.classList.remove('hide');
|
||||
});
|
||||
$.on(document.body, 'click', (e) => {
|
||||
if (e.target.matches('.data-table-dropdown-toggle')) return;
|
||||
deactivateDropdown();
|
||||
});
|
||||
};
|
||||
$.on(this.header, 'keydown', '.data-table-filter', debounce(handler, 300));
|
||||
}
|
||||
|
||||
sortRows(colIndex, sortOrder) {
|
||||
return this.datamanager.sortRows(colIndex, sortOrder);
|
||||
}
|
||||
const dropdownItems = this.options.headerDropdown;
|
||||
|
||||
getColumn(colIndex) {
|
||||
return this.datamanager.getColumn(colIndex);
|
||||
}
|
||||
$.on(this.header, 'click', '.data-table-dropdown-list > div', (e, $item) => {
|
||||
const $col = $.closest('.data-table-col', $item);
|
||||
const {
|
||||
index
|
||||
} = $.data($item);
|
||||
const {
|
||||
colIndex
|
||||
} = $.data($col);
|
||||
let callback = dropdownItems[index].action;
|
||||
|
||||
getColumns() {
|
||||
return this.datamanager.getColumns();
|
||||
}
|
||||
callback && callback.call(this.instance, this.getColumn(colIndex));
|
||||
});
|
||||
|
||||
setColumnWidth(colIndex) {
|
||||
colIndex = +colIndex;
|
||||
this._columnWidthMap = this._columnWidthMap || [];
|
||||
|
||||
const { width } = this.getColumn(colIndex);
|
||||
|
||||
let index = this._columnWidthMap[colIndex];
|
||||
const selector = `[data-col-index="${colIndex}"] .content, [data-col-index="${colIndex}"] .edit-cell`;
|
||||
const styles = {
|
||||
width: width + 'px'
|
||||
};
|
||||
|
||||
index = this.style.setStyle(selector, styles, index);
|
||||
this._columnWidthMap[colIndex] = index;
|
||||
}
|
||||
|
||||
setColumnHeaderWidth(colIndex) {
|
||||
colIndex = +colIndex;
|
||||
this.$columnMap = this.$columnMap || [];
|
||||
const selector = `.data-table-header [data-col-index="${colIndex}"] .content`;
|
||||
const { width } = this.getColumn(colIndex);
|
||||
|
||||
let $column = this.$columnMap[colIndex];
|
||||
if (!$column) {
|
||||
$column = this.header.querySelector(selector);
|
||||
this.$columnMap[colIndex] = $column;
|
||||
function deactivateDropdown(e) {
|
||||
$activeDropdown && $activeDropdown.classList.remove('is-active');
|
||||
$activeDropdown = null;
|
||||
}
|
||||
}
|
||||
|
||||
$column.style.width = width + 'px';
|
||||
}
|
||||
bindResizeColumn() {
|
||||
let isDragging = false;
|
||||
let $resizingCell, startWidth, startX;
|
||||
|
||||
getColumnMinWidth(colIndex) {
|
||||
colIndex = +colIndex;
|
||||
return this.getColumn(colIndex).minWidth || 24;
|
||||
}
|
||||
$.on(this.header, 'mousedown', '.data-table-col .column-resizer', (e, $handle) => {
|
||||
document.body.classList.add('data-table-resize');
|
||||
const $cell = $handle.parentNode.parentNode;
|
||||
$resizingCell = $cell;
|
||||
const {
|
||||
colIndex
|
||||
} = $.data($resizingCell);
|
||||
const col = this.getColumn(colIndex);
|
||||
|
||||
getFirstColumnIndex() {
|
||||
if (this.options.addCheckboxColumn && this.options.addSerialNoColumn) {
|
||||
return 2;
|
||||
if (col && col.resizable === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
isDragging = true;
|
||||
startWidth = $.style($('.content', $resizingCell), 'width');
|
||||
startX = e.pageX;
|
||||
});
|
||||
|
||||
$.on(document.body, 'mouseup', (e) => {
|
||||
document.body.classList.remove('data-table-resize');
|
||||
if (!$resizingCell) return;
|
||||
isDragging = false;
|
||||
|
||||
const {
|
||||
colIndex
|
||||
} = $.data($resizingCell);
|
||||
this.setColumnWidth(colIndex);
|
||||
this.style.setBodyStyle();
|
||||
$resizingCell = null;
|
||||
});
|
||||
|
||||
$.on(document.body, 'mousemove', (e) => {
|
||||
if (!isDragging) return;
|
||||
const finalWidth = startWidth + (e.pageX - startX);
|
||||
const {
|
||||
colIndex
|
||||
} = $.data($resizingCell);
|
||||
|
||||
if (this.getColumnMinWidth(colIndex) > finalWidth) {
|
||||
// don't resize past minWidth
|
||||
return;
|
||||
}
|
||||
this.datamanager.updateColumn(colIndex, {
|
||||
width: finalWidth
|
||||
});
|
||||
this.setColumnHeaderWidth(colIndex);
|
||||
});
|
||||
}
|
||||
|
||||
if (this.options.addCheckboxColumn || this.options.addSerialNoColumn) {
|
||||
return 1;
|
||||
bindMoveColumn() {
|
||||
let initialized;
|
||||
|
||||
const initialize = () => {
|
||||
if (initialized) {
|
||||
$.off(document.body, 'mousemove', initialize);
|
||||
return;
|
||||
}
|
||||
const ready = $('.data-table-col', this.header);
|
||||
if (!ready) return;
|
||||
|
||||
const $parent = $('.data-table-row', this.header);
|
||||
|
||||
this.sortable = Sortable.create($parent, {
|
||||
onEnd: (e) => {
|
||||
const {
|
||||
oldIndex,
|
||||
newIndex
|
||||
} = e;
|
||||
const $draggedCell = e.item;
|
||||
const {
|
||||
colIndex
|
||||
} = $.data($draggedCell);
|
||||
if (+colIndex === newIndex) return;
|
||||
|
||||
this.switchColumn(oldIndex, newIndex);
|
||||
},
|
||||
preventOnFilter: false,
|
||||
filter: '.column-resizer, .data-table-dropdown',
|
||||
animation: 150
|
||||
});
|
||||
};
|
||||
|
||||
$.on(document.body, 'mousemove', initialize);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
bindSortColumn() {
|
||||
|
||||
getHeaderCell$(colIndex) {
|
||||
return $(`.data-table-col[data-col-index="${colIndex}"]`, this.header);
|
||||
}
|
||||
$.on(this.header, 'click', '.data-table-col .column-title', (e, span) => {
|
||||
const $cell = span.closest('.data-table-col');
|
||||
let {
|
||||
colIndex,
|
||||
sortOrder
|
||||
} = $.data($cell);
|
||||
sortOrder = getDefault(sortOrder, 'none');
|
||||
const col = this.getColumn(colIndex);
|
||||
|
||||
getLastColumnIndex() {
|
||||
return this.datamanager.getColumnCount() - 1;
|
||||
}
|
||||
if (col && col.sortable === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
getSerialColumnIndex() {
|
||||
const columns = this.datamanager.getColumns();
|
||||
// reset sort indicator
|
||||
$('.sort-indicator', this.header).textContent = '';
|
||||
$.each('.data-table-col', this.header).map($cell => {
|
||||
$.data($cell, {
|
||||
sortOrder: 'none'
|
||||
});
|
||||
});
|
||||
|
||||
return columns.findIndex(column => column.content.includes('Sr. No'));
|
||||
}
|
||||
let nextSortOrder, textContent;
|
||||
if (sortOrder === 'none') {
|
||||
nextSortOrder = 'asc';
|
||||
textContent = '▲';
|
||||
} else if (sortOrder === 'asc') {
|
||||
nextSortOrder = 'desc';
|
||||
textContent = '▼';
|
||||
} else if (sortOrder === 'desc') {
|
||||
nextSortOrder = 'none';
|
||||
textContent = '';
|
||||
}
|
||||
|
||||
$.data($cell, {
|
||||
sortOrder: nextSortOrder
|
||||
});
|
||||
$('.sort-indicator', $cell).textContent = textContent;
|
||||
|
||||
this.sortColumn(colIndex, nextSortOrder);
|
||||
});
|
||||
}
|
||||
|
||||
sortColumn(colIndex, nextSortOrder) {
|
||||
this.instance.freeze();
|
||||
this.sortRows(colIndex, nextSortOrder)
|
||||
.then(() => {
|
||||
this.refreshHeader();
|
||||
return this.rowmanager.refreshRows();
|
||||
})
|
||||
.then(() => this.instance.unfreeze())
|
||||
.then(() => {
|
||||
this.fireEvent('onSortColumn', this.getColumn(colIndex));
|
||||
});
|
||||
}
|
||||
|
||||
removeColumn(colIndex) {
|
||||
const removedCol = this.getColumn(colIndex);
|
||||
this.instance.freeze();
|
||||
this.datamanager.removeColumn(colIndex)
|
||||
.then(() => {
|
||||
this.refreshHeader();
|
||||
return this.rowmanager.refreshRows();
|
||||
})
|
||||
.then(() => this.instance.unfreeze())
|
||||
.then(() => {
|
||||
this.fireEvent('onRemoveColumn', removedCol);
|
||||
});
|
||||
}
|
||||
|
||||
switchColumn(oldIndex, newIndex) {
|
||||
this.instance.freeze();
|
||||
this.datamanager.switchColumn(oldIndex, newIndex)
|
||||
.then(() => {
|
||||
this.refreshHeader();
|
||||
return this.rowmanager.refreshRows();
|
||||
})
|
||||
.then(() => {
|
||||
this.setColumnWidth(oldIndex);
|
||||
this.setColumnWidth(newIndex);
|
||||
this.instance.unfreeze();
|
||||
})
|
||||
.then(() => {
|
||||
this.fireEvent('onSwitchColumn',
|
||||
this.getColumn(oldIndex), this.getColumn(newIndex)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
toggleFilter() {
|
||||
this.isFilterShown = this.isFilterShown || false;
|
||||
|
||||
if (this.isFilterShown) {
|
||||
$.style(this.$filterRow, {
|
||||
display: 'none'
|
||||
});
|
||||
} else {
|
||||
$.style(this.$filterRow, {
|
||||
display: ''
|
||||
});
|
||||
}
|
||||
|
||||
this.isFilterShown = !this.isFilterShown;
|
||||
this.style.setBodyStyle();
|
||||
}
|
||||
|
||||
focusFilter(colIndex) {
|
||||
if (!this.isFilterShown) return;
|
||||
|
||||
const $filterInput = $(`[data-col-index="${colIndex}"] .data-table-filter`, this.$filterRow);
|
||||
$filterInput.focus();
|
||||
}
|
||||
|
||||
bindFilter() {
|
||||
if (!this.options.enableInlineFilters) return;
|
||||
const handler = e => {
|
||||
const $filterCell = $.closest('.data-table-col', e.target);
|
||||
const {
|
||||
colIndex
|
||||
} = $.data($filterCell);
|
||||
const keyword = e.target.value;
|
||||
|
||||
this.datamanager.filterRows(keyword, colIndex)
|
||||
.then(({
|
||||
rowsToHide,
|
||||
rowsToShow
|
||||
}) => {
|
||||
rowsToHide.map(rowIndex => {
|
||||
const $tr = $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable);
|
||||
$tr.classList.add('hide');
|
||||
});
|
||||
rowsToShow.map(rowIndex => {
|
||||
const $tr = $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable);
|
||||
$tr.classList.remove('hide');
|
||||
});
|
||||
});
|
||||
};
|
||||
$.on(this.header, 'keydown', '.data-table-filter', debounce(handler, 300));
|
||||
}
|
||||
|
||||
sortRows(colIndex, sortOrder) {
|
||||
return this.datamanager.sortRows(colIndex, sortOrder);
|
||||
}
|
||||
|
||||
getColumn(colIndex) {
|
||||
return this.datamanager.getColumn(colIndex);
|
||||
}
|
||||
|
||||
getColumns() {
|
||||
return this.datamanager.getColumns();
|
||||
}
|
||||
|
||||
setColumnWidth(colIndex) {
|
||||
colIndex = +colIndex;
|
||||
this._columnWidthMap = this._columnWidthMap || [];
|
||||
|
||||
const {
|
||||
width
|
||||
} = this.getColumn(colIndex);
|
||||
|
||||
let index = this._columnWidthMap[colIndex];
|
||||
const selector = `[data-col-index="${colIndex}"] .content, [data-col-index="${colIndex}"] .edit-cell`;
|
||||
const styles = {
|
||||
width: width + 'px'
|
||||
};
|
||||
|
||||
index = this.style.setStyle(selector, styles, index);
|
||||
this._columnWidthMap[colIndex] = index;
|
||||
}
|
||||
|
||||
setColumnHeaderWidth(colIndex) {
|
||||
colIndex = +colIndex;
|
||||
this.$columnMap = this.$columnMap || [];
|
||||
const selector = `.data-table-header [data-col-index="${colIndex}"] .content`;
|
||||
const {
|
||||
width
|
||||
} = this.getColumn(colIndex);
|
||||
|
||||
let $column = this.$columnMap[colIndex];
|
||||
if (!$column) {
|
||||
$column = this.header.querySelector(selector);
|
||||
this.$columnMap[colIndex] = $column;
|
||||
}
|
||||
|
||||
$column.style.width = width + 'px';
|
||||
}
|
||||
|
||||
getColumnMinWidth(colIndex) {
|
||||
colIndex = +colIndex;
|
||||
return this.getColumn(colIndex).minWidth || 24;
|
||||
}
|
||||
|
||||
getFirstColumnIndex() {
|
||||
if (this.options.addCheckboxColumn && this.options.addSerialNoColumn) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (this.options.addCheckboxColumn || this.options.addSerialNoColumn) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
getHeaderCell$(colIndex) {
|
||||
return $(`.data-table-col[data-col-index="${colIndex}"]`, this.header);
|
||||
}
|
||||
|
||||
getLastColumnIndex() {
|
||||
return this.datamanager.getColumnCount() - 1;
|
||||
}
|
||||
|
||||
getSerialColumnIndex() {
|
||||
const columns = this.datamanager.getColumns();
|
||||
|
||||
return columns.findIndex(column => column.content.includes('Sr. No'));
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
var getDropdownHTML = function getDropdownHTML(dropdownButton = 'v') {
|
||||
// add dropdown buttons
|
||||
const dropdownItems = this.options.headerDropdown;
|
||||
// add dropdown buttons
|
||||
const dropdownItems = this.options.headerDropdown;
|
||||
|
||||
return `<div class="data-table-dropdown-toggle">${dropdownButton}</div>
|
||||
return `<div class="data-table-dropdown-toggle">${dropdownButton}</div>
|
||||
<div class="data-table-dropdown-list">
|
||||
${dropdownItems.map((d, i) => `<div data-index="${i}">${d.label}</div>`).join('')}
|
||||
</div>
|
||||
@ -415,5 +454,5 @@ var getDropdownHTML = function getDropdownHTML(dropdownButton = 'v') {
|
||||
};
|
||||
|
||||
export {
|
||||
getDropdownHTML
|
||||
getDropdownHTML
|
||||
};
|
||||
|
||||
@ -1,500 +1,517 @@
|
||||
import { isNumeric, promisify } from './utils';
|
||||
import {
|
||||
isNumeric,
|
||||
promisify
|
||||
} from './utils';
|
||||
|
||||
export default class DataManager {
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
this.sortRows = promisify(this.sortRows, this);
|
||||
this.switchColumn = promisify(this.switchColumn, this);
|
||||
this.removeColumn = promisify(this.removeColumn, this);
|
||||
this.filterRows = promisify(this.filterRows, this);
|
||||
}
|
||||
|
||||
init(data) {
|
||||
if (!data) {
|
||||
data = this.options.data;
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
this.sortRows = promisify(this.sortRows, this);
|
||||
this.switchColumn = promisify(this.switchColumn, this);
|
||||
this.removeColumn = promisify(this.removeColumn, this);
|
||||
this.filterRows = promisify(this.filterRows, this);
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
|
||||
this.rowCount = 0;
|
||||
this.columns = [];
|
||||
this.rows = [];
|
||||
|
||||
this.prepareColumns();
|
||||
this.prepareRows();
|
||||
|
||||
this.prepareNumericColumns();
|
||||
}
|
||||
|
||||
// computed property
|
||||
get currentSort() {
|
||||
const col = this.columns.find(col => col.sortOrder !== 'none');
|
||||
return col || {
|
||||
colIndex: -1,
|
||||
sortOrder: 'none'
|
||||
};
|
||||
}
|
||||
|
||||
prepareColumns() {
|
||||
this.columns = [];
|
||||
this.validateColumns();
|
||||
this.prepareDefaultColumns();
|
||||
this.prepareHeader();
|
||||
}
|
||||
|
||||
prepareDefaultColumns() {
|
||||
if (this.options.addCheckboxColumn && !this.hasColumnById('_checkbox')) {
|
||||
const cell = {
|
||||
id: '_checkbox',
|
||||
content: this.getCheckboxHTML(),
|
||||
editable: false,
|
||||
resizable: false,
|
||||
sortable: false,
|
||||
focusable: false,
|
||||
dropdown: false,
|
||||
width: 25
|
||||
};
|
||||
this.columns.push(cell);
|
||||
}
|
||||
|
||||
if (this.options.addSerialNoColumn && !this.hasColumnById('_rowIndex')) {
|
||||
let cell = {
|
||||
id: '_rowIndex',
|
||||
content: '',
|
||||
align: 'center',
|
||||
editable: false,
|
||||
resizable: false,
|
||||
focusable: false,
|
||||
dropdown: false
|
||||
};
|
||||
|
||||
this.columns.push(cell);
|
||||
}
|
||||
}
|
||||
|
||||
prepareRow(row, i) {
|
||||
const baseRowCell = {
|
||||
rowIndex: i
|
||||
};
|
||||
|
||||
return row
|
||||
.map((cell, i) => this.prepareCell(cell, i))
|
||||
.map(cell => Object.assign({}, baseRowCell, cell));
|
||||
}
|
||||
|
||||
prepareHeader() {
|
||||
let columns = this.columns.concat(this.options.columns);
|
||||
const baseCell = {
|
||||
isHeader: 1,
|
||||
editable: true,
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
focusable: true,
|
||||
dropdown: true,
|
||||
width: null,
|
||||
format: (value) => {
|
||||
if (value === null || value === undefined) {
|
||||
return '';
|
||||
}
|
||||
return value + '';
|
||||
}
|
||||
};
|
||||
|
||||
this.columns = columns
|
||||
.map((cell, i) => this.prepareCell(cell, i))
|
||||
.map(col => Object.assign({}, baseCell, col))
|
||||
.map(col => {
|
||||
col.id = col.id || col.content;
|
||||
return col;
|
||||
});
|
||||
}
|
||||
|
||||
prepareCell(content, i) {
|
||||
const cell = {
|
||||
content: '',
|
||||
align: 'left',
|
||||
sortOrder: 'none',
|
||||
colIndex: i,
|
||||
column: this.columns[i]
|
||||
};
|
||||
|
||||
if (content !== null && typeof content === 'object') {
|
||||
// passed as column/header
|
||||
Object.assign(cell, content);
|
||||
} else {
|
||||
cell.content = content;
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
prepareNumericColumns() {
|
||||
const row0 = this.getRow(0);
|
||||
if (!row0) return;
|
||||
this.columns = this.columns.map((column, i) => {
|
||||
|
||||
const cellValue = row0[i].content;
|
||||
if (!column.align && cellValue && isNumeric(cellValue)) {
|
||||
column.align = 'right';
|
||||
}
|
||||
|
||||
return column;
|
||||
});
|
||||
}
|
||||
|
||||
prepareRows() {
|
||||
this.validateData(this.data);
|
||||
|
||||
this.rows = this.data.map((d, i) => {
|
||||
const index = this._getNextRowCount();
|
||||
|
||||
let row = [];
|
||||
|
||||
if (Array.isArray(d)) {
|
||||
// row is an array
|
||||
if (this.options.addCheckboxColumn) {
|
||||
row.push(this.getCheckboxHTML());
|
||||
}
|
||||
if (this.options.addSerialNoColumn) {
|
||||
row.push((index + 1) + '');
|
||||
}
|
||||
row = row.concat(d);
|
||||
|
||||
while (row.length < this.columns.length) {
|
||||
row.push('');
|
||||
init(data) {
|
||||
if (!data) {
|
||||
data = this.options.data;
|
||||
}
|
||||
|
||||
} else {
|
||||
// row is a dict
|
||||
for (let col of this.columns) {
|
||||
if (col.id === '_checkbox') {
|
||||
row.push(this.getCheckboxHTML());
|
||||
} else if (col.id === '_rowIndex') {
|
||||
row.push((index + 1) + '');
|
||||
} else {
|
||||
row.push(d[col.id]);
|
||||
}
|
||||
this.data = data;
|
||||
|
||||
this.rowCount = 0;
|
||||
this.columns = [];
|
||||
this.rows = [];
|
||||
|
||||
this.prepareColumns();
|
||||
this.prepareRows();
|
||||
|
||||
this.prepareNumericColumns();
|
||||
}
|
||||
|
||||
// computed property
|
||||
get currentSort() {
|
||||
const col = this.columns.find(col => col.sortOrder !== 'none');
|
||||
return col || {
|
||||
colIndex: -1,
|
||||
sortOrder: 'none'
|
||||
};
|
||||
}
|
||||
|
||||
prepareColumns() {
|
||||
this.columns = [];
|
||||
this.validateColumns();
|
||||
this.prepareDefaultColumns();
|
||||
this.prepareHeader();
|
||||
}
|
||||
|
||||
prepareDefaultColumns() {
|
||||
if (this.options.addCheckboxColumn && !this.hasColumnById('_checkbox')) {
|
||||
const cell = {
|
||||
id: '_checkbox',
|
||||
content: this.getCheckboxHTML(),
|
||||
editable: false,
|
||||
resizable: false,
|
||||
sortable: false,
|
||||
focusable: false,
|
||||
dropdown: false,
|
||||
width: 25
|
||||
};
|
||||
this.columns.push(cell);
|
||||
}
|
||||
}
|
||||
|
||||
return this.prepareRow(row, index);
|
||||
});
|
||||
}
|
||||
if (this.options.addSerialNoColumn && !this.hasColumnById('_rowIndex')) {
|
||||
let cell = {
|
||||
id: '_rowIndex',
|
||||
content: '',
|
||||
align: 'center',
|
||||
editable: false,
|
||||
resizable: false,
|
||||
focusable: false,
|
||||
dropdown: false
|
||||
};
|
||||
|
||||
validateColumns() {
|
||||
const columns = this.options.columns;
|
||||
if (!Array.isArray(columns)) {
|
||||
throw new DataError('`columns` must be an array');
|
||||
this.columns.push(cell);
|
||||
}
|
||||
}
|
||||
|
||||
columns.forEach((column, i) => {
|
||||
if (typeof column !== 'string' && typeof column !== 'object') {
|
||||
throw new DataError(`column "${i}" must be a string or an object`);
|
||||
}
|
||||
});
|
||||
}
|
||||
prepareHeader() {
|
||||
let columns = this.columns.concat(this.options.columns);
|
||||
const baseCell = {
|
||||
isHeader: 1,
|
||||
editable: true,
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
focusable: true,
|
||||
dropdown: true,
|
||||
width: null,
|
||||
format: (value) => {
|
||||
if (value === null || value === undefined) {
|
||||
return '';
|
||||
}
|
||||
return value + '';
|
||||
}
|
||||
};
|
||||
|
||||
validateData(data) {
|
||||
if (Array.isArray(data) &&
|
||||
(data.length === 0 || Array.isArray(data[0]) || typeof data[0] === 'object')) {
|
||||
return true;
|
||||
this.columns = columns
|
||||
.map((cell, i) => this.prepareCell(cell, i))
|
||||
.map(col => Object.assign({}, baseCell, col))
|
||||
.map(col => {
|
||||
col.id = col.id || col.content;
|
||||
return col;
|
||||
});
|
||||
}
|
||||
throw new DataError('`data` must be an array of arrays or objects');
|
||||
}
|
||||
|
||||
appendRows(rows) {
|
||||
this.validateData(rows);
|
||||
prepareCell(content, i) {
|
||||
const cell = {
|
||||
content: '',
|
||||
align: 'left',
|
||||
sortOrder: 'none',
|
||||
colIndex: i,
|
||||
column: this.columns[i]
|
||||
};
|
||||
|
||||
this.rows = this.rows.concat(this.prepareRows(rows));
|
||||
}
|
||||
|
||||
sortRows(colIndex, sortOrder = 'none') {
|
||||
colIndex = +colIndex;
|
||||
|
||||
// reset sortOrder and update for colIndex
|
||||
this.getColumns()
|
||||
.map(col => {
|
||||
if (col.colIndex === colIndex) {
|
||||
col.sortOrder = sortOrder;
|
||||
if (content !== null && typeof content === 'object') {
|
||||
// passed as column/header
|
||||
Object.assign(cell, content);
|
||||
} else {
|
||||
col.sortOrder = 'none';
|
||||
cell.content = content;
|
||||
}
|
||||
});
|
||||
|
||||
this._sortRows(colIndex, sortOrder);
|
||||
}
|
||||
|
||||
_sortRows(colIndex, sortOrder) {
|
||||
|
||||
if (this.currentSort.colIndex === colIndex) {
|
||||
// reverse the array if only sortOrder changed
|
||||
if (
|
||||
(this.currentSort.sortOrder === 'asc' && sortOrder === 'desc') ||
|
||||
(this.currentSort.sortOrder === 'desc' && sortOrder === 'asc')
|
||||
) {
|
||||
this.reverseArray(this.rows);
|
||||
this.currentSort.sortOrder = sortOrder;
|
||||
return;
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
this.rows.sort((a, b) => {
|
||||
const _aIndex = a[0].rowIndex;
|
||||
const _bIndex = b[0].rowIndex;
|
||||
const _a = a[colIndex].content;
|
||||
const _b = b[colIndex].content;
|
||||
prepareNumericColumns() {
|
||||
const row0 = this.getRow(0);
|
||||
if (!row0) return;
|
||||
this.columns = this.columns.map((column, i) => {
|
||||
|
||||
if (sortOrder === 'none') {
|
||||
return _aIndex - _bIndex;
|
||||
} else if (sortOrder === 'asc') {
|
||||
if (_a < _b) return -1;
|
||||
if (_a > _b) return 1;
|
||||
if (_a === _b) return 0;
|
||||
} else if (sortOrder === 'desc') {
|
||||
if (_a < _b) return 1;
|
||||
if (_a > _b) return -1;
|
||||
if (_a === _b) return 0;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
const cellValue = row0[i].content;
|
||||
if (!column.align && cellValue && isNumeric(cellValue)) {
|
||||
column.align = 'right';
|
||||
}
|
||||
|
||||
if (this.hasColumnById('_rowIndex')) {
|
||||
// update row index
|
||||
const srNoColIndex = this.getColumnIndexById('_rowIndex');
|
||||
this.rows = this.rows.map((row, index) => {
|
||||
return row.map(cell => {
|
||||
if (cell.colIndex === srNoColIndex) {
|
||||
cell.content = (index + 1) + '';
|
||||
}
|
||||
return cell;
|
||||
return column;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
reverseArray(array) {
|
||||
let left = null;
|
||||
let right = null;
|
||||
let length = array.length;
|
||||
|
||||
for (left = 0, right = length - 1; left < right; left += 1, right -= 1) {
|
||||
const temporary = array[left];
|
||||
|
||||
array[left] = array[right];
|
||||
array[right] = temporary;
|
||||
}
|
||||
}
|
||||
|
||||
switchColumn(index1, index2) {
|
||||
// update columns
|
||||
const temp = this.columns[index1];
|
||||
this.columns[index1] = this.columns[index2];
|
||||
this.columns[index2] = temp;
|
||||
|
||||
this.columns[index1].colIndex = index1;
|
||||
this.columns[index2].colIndex = index2;
|
||||
|
||||
// update rows
|
||||
this.rows = this.rows.map(row => {
|
||||
const newCell1 = Object.assign({}, row[index1], { colIndex: index2 });
|
||||
const newCell2 = Object.assign({}, row[index2], { colIndex: index1 });
|
||||
|
||||
let newRow = row.map(cell => {
|
||||
// make object copy
|
||||
return Object.assign({}, cell);
|
||||
});
|
||||
|
||||
newRow[index2] = newCell1;
|
||||
newRow[index1] = newCell2;
|
||||
|
||||
return newRow;
|
||||
});
|
||||
}
|
||||
|
||||
removeColumn(index) {
|
||||
index = +index;
|
||||
const filter = cell => cell.colIndex !== index;
|
||||
const map = (cell, i) => Object.assign({}, cell, { colIndex: i });
|
||||
// update columns
|
||||
this.columns = this.columns
|
||||
.filter(filter)
|
||||
.map(map);
|
||||
|
||||
// update rows
|
||||
this.rows = this.rows.map(row => {
|
||||
const newRow = row
|
||||
.filter(filter)
|
||||
.map(map);
|
||||
|
||||
return newRow;
|
||||
});
|
||||
}
|
||||
|
||||
updateRow(row, rowIndex) {
|
||||
if (row.length < this.columns.length) {
|
||||
if (this.hasColumnById('_rowIndex')) {
|
||||
const val = (rowIndex + 1) + '';
|
||||
|
||||
row = [val].concat(row);
|
||||
}
|
||||
|
||||
if (this.hasColumnById('_checkbox')) {
|
||||
const val = '<input type="checkbox" />';
|
||||
|
||||
row = [val].concat(row);
|
||||
}
|
||||
}
|
||||
|
||||
const _row = this.prepareRow(row, rowIndex);
|
||||
const index = this.rows.findIndex(row => row[0].rowIndex === rowIndex);
|
||||
this.rows[index] = _row;
|
||||
prepareRows() {
|
||||
this.validateData(this.data);
|
||||
|
||||
return _row;
|
||||
}
|
||||
this.rows = this.data.map((d, i) => {
|
||||
const index = this._getNextRowCount();
|
||||
|
||||
updateCell(colIndex, rowIndex, options) {
|
||||
let cell;
|
||||
if (typeof colIndex === 'object') {
|
||||
// cell object was passed,
|
||||
// must have colIndex, rowIndex
|
||||
cell = colIndex;
|
||||
colIndex = cell.colIndex;
|
||||
rowIndex = cell.rowIndex;
|
||||
// the object passed must be merged with original cell
|
||||
options = cell;
|
||||
}
|
||||
cell = this.getCell(colIndex, rowIndex);
|
||||
let row = [];
|
||||
|
||||
// mutate object directly
|
||||
for (let key in options) {
|
||||
const newVal = options[key];
|
||||
if (newVal !== undefined) {
|
||||
cell[key] = newVal;
|
||||
}
|
||||
if (Array.isArray(d)) {
|
||||
// row is an array
|
||||
if (this.options.addCheckboxColumn) {
|
||||
row.push(this.getCheckboxHTML());
|
||||
}
|
||||
if (this.options.addSerialNoColumn) {
|
||||
row.push((index + 1) + '');
|
||||
}
|
||||
row = row.concat(d);
|
||||
|
||||
while (row.length < this.columns.length) {
|
||||
row.push('');
|
||||
}
|
||||
|
||||
} else {
|
||||
// row is an object
|
||||
for (let col of this.columns) {
|
||||
if (col.id === '_checkbox') {
|
||||
row.push(this.getCheckboxHTML());
|
||||
} else if (col.id === '_rowIndex') {
|
||||
row.push((index + 1) + '');
|
||||
} else {
|
||||
row.push(d[col.id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.prepareRow(row, {
|
||||
rowIndex: index
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
prepareRow(row, props) {
|
||||
const baseRowCell = {
|
||||
rowIndex: props.rowIndex
|
||||
};
|
||||
|
||||
updateColumn(colIndex, keyValPairs) {
|
||||
const column = this.getColumn(colIndex);
|
||||
for (let key in keyValPairs) {
|
||||
const newVal = keyValPairs[key];
|
||||
if (newVal !== undefined) {
|
||||
column[key] = newVal;
|
||||
}
|
||||
}
|
||||
return column;
|
||||
}
|
||||
row = row
|
||||
.map((cell, i) => this.prepareCell(cell, i))
|
||||
.map(cell => Object.assign({}, baseRowCell, cell));
|
||||
|
||||
filterRows(keyword, colIndex) {
|
||||
let rowsToHide = [];
|
||||
let rowsToShow = [];
|
||||
const cells = this.rows.map(row => row[colIndex]);
|
||||
|
||||
cells.forEach(cell => {
|
||||
const hay = cell.content.toLowerCase();
|
||||
const needle = (keyword || '').toLowerCase();
|
||||
|
||||
if (!needle || hay.includes(needle)) {
|
||||
rowsToShow.push(cell.rowIndex);
|
||||
} else {
|
||||
rowsToHide.push(cell.rowIndex);
|
||||
}
|
||||
});
|
||||
|
||||
return {rowsToHide, rowsToShow};
|
||||
}
|
||||
|
||||
getRowCount() {
|
||||
return this.rowCount;
|
||||
}
|
||||
|
||||
_getNextRowCount() {
|
||||
const val = this.rowCount;
|
||||
|
||||
this.rowCount++;
|
||||
return val;
|
||||
}
|
||||
|
||||
getRows(start, end) {
|
||||
return this.rows.slice(start, end);
|
||||
}
|
||||
|
||||
getColumns(skipStandardColumns) {
|
||||
let columns = this.columns;
|
||||
|
||||
if (skipStandardColumns) {
|
||||
columns = columns.slice(this.getStandardColumnCount());
|
||||
// monkey patched in array object
|
||||
row.meta = props;
|
||||
return row;
|
||||
}
|
||||
|
||||
return columns;
|
||||
}
|
||||
validateColumns() {
|
||||
const columns = this.options.columns;
|
||||
if (!Array.isArray(columns)) {
|
||||
throw new DataError('`columns` must be an array');
|
||||
}
|
||||
|
||||
getStandardColumnCount() {
|
||||
if (this.options.addCheckboxColumn && this.options.addSerialNoColumn) {
|
||||
return 2;
|
||||
columns.forEach((column, i) => {
|
||||
if (typeof column !== 'string' && typeof column !== 'object') {
|
||||
throw new DataError(`column "${i}" must be a string or an object`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.options.addCheckboxColumn || this.options.addSerialNoColumn) {
|
||||
return 1;
|
||||
validateData(data) {
|
||||
if (Array.isArray(data) &&
|
||||
(data.length === 0 || Array.isArray(data[0]) || typeof data[0] === 'object')) {
|
||||
return true;
|
||||
}
|
||||
throw new DataError('`data` must be an array of arrays or objects');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
appendRows(rows) {
|
||||
this.validateData(rows);
|
||||
|
||||
getColumnCount(skipStandardColumns) {
|
||||
let val = this.columns.length;
|
||||
|
||||
if (skipStandardColumns) {
|
||||
val = val - this.getStandardColumnCount();
|
||||
this.rows = this.rows.concat(this.prepareRows(rows));
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
sortRows(colIndex, sortOrder = 'none') {
|
||||
colIndex = +colIndex;
|
||||
|
||||
getColumn(colIndex) {
|
||||
colIndex = +colIndex;
|
||||
return this.columns.find(col => col.colIndex === colIndex);
|
||||
}
|
||||
// reset sortOrder and update for colIndex
|
||||
this.getColumns()
|
||||
.map(col => {
|
||||
if (col.colIndex === colIndex) {
|
||||
col.sortOrder = sortOrder;
|
||||
} else {
|
||||
col.sortOrder = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
getRow(rowIndex) {
|
||||
rowIndex = +rowIndex;
|
||||
return this.rows.find(row => row[0].rowIndex === rowIndex);
|
||||
}
|
||||
this._sortRows(colIndex, sortOrder);
|
||||
}
|
||||
|
||||
getCell(colIndex, rowIndex) {
|
||||
rowIndex = +rowIndex;
|
||||
colIndex = +colIndex;
|
||||
return this.rows.find(row => row[0].rowIndex === rowIndex)[colIndex];
|
||||
}
|
||||
_sortRows(colIndex, sortOrder) {
|
||||
|
||||
get() {
|
||||
return {
|
||||
columns: this.columns,
|
||||
rows: this.rows
|
||||
};
|
||||
}
|
||||
if (this.currentSort.colIndex === colIndex) {
|
||||
// reverse the array if only sortOrder changed
|
||||
if (
|
||||
(this.currentSort.sortOrder === 'asc' && sortOrder === 'desc') ||
|
||||
(this.currentSort.sortOrder === 'desc' && sortOrder === 'asc')
|
||||
) {
|
||||
this.reverseArray(this.rows);
|
||||
this.currentSort.sortOrder = sortOrder;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
hasColumn(name) {
|
||||
return Boolean(this.columns.find(col => col.content === name));
|
||||
}
|
||||
this.rows.sort((a, b) => {
|
||||
const _aIndex = a[0].rowIndex;
|
||||
const _bIndex = b[0].rowIndex;
|
||||
const _a = a[colIndex].content;
|
||||
const _b = b[colIndex].content;
|
||||
|
||||
hasColumnById(id) {
|
||||
return Boolean(this.columns.find(col => col.id === id));
|
||||
}
|
||||
if (sortOrder === 'none') {
|
||||
return _aIndex - _bIndex;
|
||||
} else if (sortOrder === 'asc') {
|
||||
if (_a < _b) return -1;
|
||||
if (_a > _b) return 1;
|
||||
if (_a === _b) return 0;
|
||||
} else if (sortOrder === 'desc') {
|
||||
if (_a < _b) return 1;
|
||||
if (_a > _b) return -1;
|
||||
if (_a === _b) return 0;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
getColumnIndex(name) {
|
||||
return this.columns.findIndex(col => col.content === name);
|
||||
}
|
||||
if (this.hasColumnById('_rowIndex')) {
|
||||
// update row index
|
||||
const srNoColIndex = this.getColumnIndexById('_rowIndex');
|
||||
this.rows.forEach((row, index) => {
|
||||
row.forEach(cell => {
|
||||
if (cell.colIndex === srNoColIndex) {
|
||||
cell.content = (index + 1) + '';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getColumnIndexById(id) {
|
||||
return this.columns.findIndex(col => col.id === id);
|
||||
}
|
||||
reverseArray(array) {
|
||||
let left = null;
|
||||
let right = null;
|
||||
let length = array.length;
|
||||
|
||||
getCheckboxHTML() {
|
||||
return '<input type="checkbox" />';
|
||||
}
|
||||
for (left = 0, right = length - 1; left < right; left += 1, right -= 1) {
|
||||
const temporary = array[left];
|
||||
|
||||
array[left] = array[right];
|
||||
array[right] = temporary;
|
||||
}
|
||||
}
|
||||
|
||||
switchColumn(index1, index2) {
|
||||
// update columns
|
||||
const temp = this.columns[index1];
|
||||
this.columns[index1] = this.columns[index2];
|
||||
this.columns[index2] = temp;
|
||||
|
||||
this.columns[index1].colIndex = index1;
|
||||
this.columns[index2].colIndex = index2;
|
||||
|
||||
// update rows
|
||||
this.rows = this.rows.map(row => {
|
||||
const newCell1 = Object.assign({}, row[index1], {
|
||||
colIndex: index2
|
||||
});
|
||||
const newCell2 = Object.assign({}, row[index2], {
|
||||
colIndex: index1
|
||||
});
|
||||
|
||||
let newRow = row.map(cell => {
|
||||
// make object copy
|
||||
return Object.assign({}, cell);
|
||||
});
|
||||
|
||||
newRow[index2] = newCell1;
|
||||
newRow[index1] = newCell2;
|
||||
|
||||
return newRow;
|
||||
});
|
||||
}
|
||||
|
||||
removeColumn(index) {
|
||||
index = +index;
|
||||
const filter = cell => cell.colIndex !== index;
|
||||
const map = (cell, i) => Object.assign({}, cell, {
|
||||
colIndex: i
|
||||
});
|
||||
// update columns
|
||||
this.columns = this.columns
|
||||
.filter(filter)
|
||||
.map(map);
|
||||
|
||||
// update rows
|
||||
this.rows = this.rows.map(row => {
|
||||
const newRow = row
|
||||
.filter(filter)
|
||||
.map(map);
|
||||
|
||||
return newRow;
|
||||
});
|
||||
}
|
||||
|
||||
updateRow(row, rowIndex) {
|
||||
if (row.length < this.columns.length) {
|
||||
if (this.hasColumnById('_rowIndex')) {
|
||||
const val = (rowIndex + 1) + '';
|
||||
|
||||
row = [val].concat(row);
|
||||
}
|
||||
|
||||
if (this.hasColumnById('_checkbox')) {
|
||||
const val = '<input type="checkbox" />';
|
||||
|
||||
row = [val].concat(row);
|
||||
}
|
||||
}
|
||||
|
||||
const _row = this.prepareRow(row, rowIndex);
|
||||
const index = this.rows.findIndex(row => row[0].rowIndex === rowIndex);
|
||||
this.rows[index] = _row;
|
||||
|
||||
return _row;
|
||||
}
|
||||
|
||||
updateCell(colIndex, rowIndex, options) {
|
||||
let cell;
|
||||
if (typeof colIndex === 'object') {
|
||||
// cell object was passed,
|
||||
// must have colIndex, rowIndex
|
||||
cell = colIndex;
|
||||
colIndex = cell.colIndex;
|
||||
rowIndex = cell.rowIndex;
|
||||
// the object passed must be merged with original cell
|
||||
options = cell;
|
||||
}
|
||||
cell = this.getCell(colIndex, rowIndex);
|
||||
|
||||
// mutate object directly
|
||||
for (let key in options) {
|
||||
const newVal = options[key];
|
||||
if (newVal !== undefined) {
|
||||
cell[key] = newVal;
|
||||
}
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
updateColumn(colIndex, keyValPairs) {
|
||||
const column = this.getColumn(colIndex);
|
||||
for (let key in keyValPairs) {
|
||||
const newVal = keyValPairs[key];
|
||||
if (newVal !== undefined) {
|
||||
column[key] = newVal;
|
||||
}
|
||||
}
|
||||
return column;
|
||||
}
|
||||
|
||||
filterRows(keyword, colIndex) {
|
||||
let rowsToHide = [];
|
||||
let rowsToShow = [];
|
||||
const cells = this.rows.map(row => row[colIndex]);
|
||||
|
||||
cells.forEach(cell => {
|
||||
const hay = cell.content.toLowerCase();
|
||||
const needle = (keyword || '').toLowerCase();
|
||||
|
||||
if (!needle || hay.includes(needle)) {
|
||||
rowsToShow.push(cell.rowIndex);
|
||||
} else {
|
||||
rowsToHide.push(cell.rowIndex);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
rowsToHide,
|
||||
rowsToShow
|
||||
};
|
||||
}
|
||||
|
||||
getRowCount() {
|
||||
return this.rowCount;
|
||||
}
|
||||
|
||||
_getNextRowCount() {
|
||||
const val = this.rowCount;
|
||||
|
||||
this.rowCount++;
|
||||
return val;
|
||||
}
|
||||
|
||||
getRows(start, end) {
|
||||
return this.rows.slice(start, end);
|
||||
}
|
||||
|
||||
getColumns(skipStandardColumns) {
|
||||
let columns = this.columns;
|
||||
|
||||
if (skipStandardColumns) {
|
||||
columns = columns.slice(this.getStandardColumnCount());
|
||||
}
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
||||
getStandardColumnCount() {
|
||||
if (this.options.addCheckboxColumn && this.options.addSerialNoColumn) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (this.options.addCheckboxColumn || this.options.addSerialNoColumn) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
getColumnCount(skipStandardColumns) {
|
||||
let val = this.columns.length;
|
||||
|
||||
if (skipStandardColumns) {
|
||||
val = val - this.getStandardColumnCount();
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
getColumn(colIndex) {
|
||||
colIndex = +colIndex;
|
||||
return this.columns.find(col => col.colIndex === colIndex);
|
||||
}
|
||||
|
||||
getRow(rowIndex) {
|
||||
rowIndex = +rowIndex;
|
||||
return this.rows.find(row => row[0].rowIndex === rowIndex);
|
||||
}
|
||||
|
||||
getCell(colIndex, rowIndex) {
|
||||
rowIndex = +rowIndex;
|
||||
colIndex = +colIndex;
|
||||
return this.rows.find(row => row[0].rowIndex === rowIndex)[colIndex];
|
||||
}
|
||||
|
||||
get() {
|
||||
return {
|
||||
columns: this.columns,
|
||||
rows: this.rows
|
||||
};
|
||||
}
|
||||
|
||||
hasColumn(name) {
|
||||
return Boolean(this.columns.find(col => col.content === name));
|
||||
}
|
||||
|
||||
hasColumnById(id) {
|
||||
return Boolean(this.columns.find(col => col.id === id));
|
||||
}
|
||||
|
||||
getColumnIndex(name) {
|
||||
return this.columns.findIndex(col => col.content === name);
|
||||
}
|
||||
|
||||
getColumnIndexById(id) {
|
||||
return this.columns.findIndex(col => col.id === id);
|
||||
}
|
||||
|
||||
getCheckboxHTML() {
|
||||
return '<input type="checkbox" />';
|
||||
}
|
||||
}
|
||||
|
||||
// Custom Errors
|
||||
|
||||
278
src/datatable.js
278
src/datatable.js
@ -10,50 +10,48 @@ import DEFAULT_OPTIONS from './defaults';
|
||||
import './style.css';
|
||||
|
||||
class DataTable {
|
||||
constructor(wrapper, options) {
|
||||
DataTable.instances++;
|
||||
constructor(wrapper, options) {
|
||||
DataTable.instances++;
|
||||
|
||||
if (typeof wrapper === 'string') {
|
||||
// css selector
|
||||
wrapper = document.querySelector(wrapper);
|
||||
}
|
||||
this.wrapper = wrapper;
|
||||
if (!(this.wrapper instanceof HTMLElement)) {
|
||||
throw new Error('Invalid argument given for `wrapper`');
|
||||
if (typeof wrapper === 'string') {
|
||||
// css selector
|
||||
wrapper = document.querySelector(wrapper);
|
||||
}
|
||||
this.wrapper = wrapper;
|
||||
if (!(this.wrapper instanceof HTMLElement)) {
|
||||
throw new Error('Invalid argument given for `wrapper`');
|
||||
}
|
||||
|
||||
this.options = Object.assign({}, DEFAULT_OPTIONS, options);
|
||||
this.options.headerDropdown =
|
||||
DEFAULT_OPTIONS.headerDropdown
|
||||
.concat(options.headerDropdown || []);
|
||||
// custom user events
|
||||
this.events = Object.assign({}, DEFAULT_OPTIONS.events, options.events || {});
|
||||
this.fireEvent = this.fireEvent.bind(this);
|
||||
|
||||
this.prepare();
|
||||
|
||||
this.style = new Style(this);
|
||||
this.keyboard = new Keyboard(this.wrapper);
|
||||
this.datamanager = new DataManager(this.options);
|
||||
this.rowmanager = new RowManager(this);
|
||||
this.columnmanager = new ColumnManager(this);
|
||||
this.cellmanager = new CellManager(this);
|
||||
this.bodyRenderer = new BodyRenderer(this);
|
||||
|
||||
if (this.options.data) {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
this.options = Object.assign({}, DEFAULT_OPTIONS, options);
|
||||
this.options.headerDropdown =
|
||||
DEFAULT_OPTIONS.headerDropdown
|
||||
.concat(options.headerDropdown || []);
|
||||
// custom user events
|
||||
this.events = Object.assign(
|
||||
{}, DEFAULT_OPTIONS.events, options.events || {}
|
||||
);
|
||||
this.fireEvent = this.fireEvent.bind(this);
|
||||
|
||||
this.prepare();
|
||||
|
||||
this.style = new Style(this);
|
||||
this.keyboard = new Keyboard(this.wrapper);
|
||||
this.datamanager = new DataManager(this.options);
|
||||
this.rowmanager = new RowManager(this);
|
||||
this.columnmanager = new ColumnManager(this);
|
||||
this.cellmanager = new CellManager(this);
|
||||
this.bodyRenderer = new BodyRenderer(this);
|
||||
|
||||
if (this.options.data) {
|
||||
this.refresh();
|
||||
prepare() {
|
||||
this.prepareDom();
|
||||
this.unfreeze();
|
||||
}
|
||||
}
|
||||
|
||||
prepare() {
|
||||
this.prepareDom();
|
||||
this.unfreeze();
|
||||
}
|
||||
|
||||
prepareDom() {
|
||||
this.wrapper.innerHTML = `
|
||||
prepareDom() {
|
||||
this.wrapper.innerHTML = `
|
||||
<div class="data-table">
|
||||
<table class="data-table-header">
|
||||
</table>
|
||||
@ -67,110 +65,110 @@ class DataTable {
|
||||
</div>
|
||||
`;
|
||||
|
||||
this.datatableWrapper = $('.data-table', this.wrapper);
|
||||
this.header = $('.data-table-header', this.wrapper);
|
||||
this.bodyScrollable = $('.body-scrollable', this.wrapper);
|
||||
this.freezeContainer = $('.freeze-container', this.wrapper);
|
||||
}
|
||||
|
||||
refresh(data) {
|
||||
this.datamanager.init(data);
|
||||
this.render();
|
||||
this.setDimensions();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.wrapper.innerHTML = '';
|
||||
this.style.destroy();
|
||||
}
|
||||
|
||||
appendRows(rows) {
|
||||
this.datamanager.appendRows(rows);
|
||||
this.rowmanager.refreshRows();
|
||||
}
|
||||
|
||||
refreshRow(row, rowIndex) {
|
||||
this.rowmanager.refreshRow(row, rowIndex);
|
||||
}
|
||||
|
||||
render() {
|
||||
this.renderHeader();
|
||||
this.renderBody();
|
||||
}
|
||||
|
||||
renderHeader() {
|
||||
this.columnmanager.renderHeader();
|
||||
}
|
||||
|
||||
renderBody() {
|
||||
this.bodyRenderer.render();
|
||||
}
|
||||
|
||||
setDimensions() {
|
||||
this.style.setDimensions();
|
||||
}
|
||||
|
||||
getColumn(colIndex) {
|
||||
return this.datamanager.getColumn(colIndex);
|
||||
}
|
||||
|
||||
getColumns() {
|
||||
return this.datamanager.getColumns();
|
||||
}
|
||||
|
||||
getRows() {
|
||||
return this.datamanager.getRows();
|
||||
}
|
||||
|
||||
getCell(colIndex, rowIndex) {
|
||||
return this.datamanager.getCell(colIndex, rowIndex);
|
||||
}
|
||||
|
||||
getColumnHeaderElement(colIndex) {
|
||||
return this.columnmanager.getColumnHeaderElement(colIndex);
|
||||
}
|
||||
|
||||
getViewportHeight() {
|
||||
if (!this.viewportHeight) {
|
||||
this.viewportHeight = $.style(this.bodyScrollable, 'height');
|
||||
this.datatableWrapper = $('.data-table', this.wrapper);
|
||||
this.header = $('.data-table-header', this.wrapper);
|
||||
this.bodyScrollable = $('.body-scrollable', this.wrapper);
|
||||
this.freezeContainer = $('.freeze-container', this.wrapper);
|
||||
}
|
||||
|
||||
return this.viewportHeight;
|
||||
}
|
||||
|
||||
sortColumn(colIndex, sortOrder) {
|
||||
this.columnmanager.sortColumn(colIndex, sortOrder);
|
||||
}
|
||||
|
||||
removeColumn(colIndex) {
|
||||
this.columnmanager.removeColumn(colIndex);
|
||||
}
|
||||
|
||||
scrollToLastColumn() {
|
||||
this.datatableWrapper.scrollLeft = 9999;
|
||||
}
|
||||
|
||||
freeze() {
|
||||
$.style(this.freezeContainer, {
|
||||
display: ''
|
||||
});
|
||||
}
|
||||
|
||||
unfreeze() {
|
||||
$.style(this.freezeContainer, {
|
||||
display: 'none'
|
||||
});
|
||||
}
|
||||
|
||||
fireEvent(eventName, ...args) {
|
||||
this.events[eventName].apply(this, args);
|
||||
}
|
||||
|
||||
log() {
|
||||
if (this.options.enableLogs) {
|
||||
console.log.apply(console, arguments);
|
||||
refresh(data) {
|
||||
this.datamanager.init(data);
|
||||
this.render();
|
||||
this.setDimensions();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.wrapper.innerHTML = '';
|
||||
this.style.destroy();
|
||||
}
|
||||
|
||||
appendRows(rows) {
|
||||
this.datamanager.appendRows(rows);
|
||||
this.rowmanager.refreshRows();
|
||||
}
|
||||
|
||||
refreshRow(row, rowIndex) {
|
||||
this.rowmanager.refreshRow(row, rowIndex);
|
||||
}
|
||||
|
||||
render() {
|
||||
this.renderHeader();
|
||||
this.renderBody();
|
||||
}
|
||||
|
||||
renderHeader() {
|
||||
this.columnmanager.renderHeader();
|
||||
}
|
||||
|
||||
renderBody() {
|
||||
this.bodyRenderer.render();
|
||||
}
|
||||
|
||||
setDimensions() {
|
||||
this.style.setDimensions();
|
||||
}
|
||||
|
||||
getColumn(colIndex) {
|
||||
return this.datamanager.getColumn(colIndex);
|
||||
}
|
||||
|
||||
getColumns() {
|
||||
return this.datamanager.getColumns();
|
||||
}
|
||||
|
||||
getRows() {
|
||||
return this.datamanager.getRows();
|
||||
}
|
||||
|
||||
getCell(colIndex, rowIndex) {
|
||||
return this.datamanager.getCell(colIndex, rowIndex);
|
||||
}
|
||||
|
||||
getColumnHeaderElement(colIndex) {
|
||||
return this.columnmanager.getColumnHeaderElement(colIndex);
|
||||
}
|
||||
|
||||
getViewportHeight() {
|
||||
if (!this.viewportHeight) {
|
||||
this.viewportHeight = $.style(this.bodyScrollable, 'height');
|
||||
}
|
||||
|
||||
return this.viewportHeight;
|
||||
}
|
||||
|
||||
sortColumn(colIndex, sortOrder) {
|
||||
this.columnmanager.sortColumn(colIndex, sortOrder);
|
||||
}
|
||||
|
||||
removeColumn(colIndex) {
|
||||
this.columnmanager.removeColumn(colIndex);
|
||||
}
|
||||
|
||||
scrollToLastColumn() {
|
||||
this.datatableWrapper.scrollLeft = 9999;
|
||||
}
|
||||
|
||||
freeze() {
|
||||
$.style(this.freezeContainer, {
|
||||
display: ''
|
||||
});
|
||||
}
|
||||
|
||||
unfreeze() {
|
||||
$.style(this.freezeContainer, {
|
||||
display: 'none'
|
||||
});
|
||||
}
|
||||
|
||||
fireEvent(eventName, ...args) {
|
||||
this.events[eventName].apply(this, args);
|
||||
}
|
||||
|
||||
log() {
|
||||
if (this.options.enableLogs) {
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DataTable.instances = 0;
|
||||
|
||||
@ -1,51 +1,51 @@
|
||||
export default {
|
||||
columns: [],
|
||||
data: [],
|
||||
dropdownButton: '▼',
|
||||
headerDropdown: [
|
||||
{
|
||||
label: 'Sort Ascending',
|
||||
action: function (column) {
|
||||
this.sortColumn(column.colIndex, 'asc');
|
||||
}
|
||||
columns: [],
|
||||
data: [],
|
||||
dropdownButton: '▼',
|
||||
headerDropdown: [
|
||||
{
|
||||
label: 'Sort Ascending',
|
||||
action: function (column) {
|
||||
this.sortColumn(column.colIndex, 'asc');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Sort Descending',
|
||||
action: function (column) {
|
||||
this.sortColumn(column.colIndex, 'desc');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Reset sorting',
|
||||
action: function (column) {
|
||||
this.sortColumn(column.colIndex, 'none');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Remove column',
|
||||
action: function (column) {
|
||||
this.removeColumn(column.colIndex);
|
||||
}
|
||||
}
|
||||
],
|
||||
events: {
|
||||
onRemoveColumn(column) {},
|
||||
onSwitchColumn(column1, column2) {},
|
||||
onSortColumn(column) {}
|
||||
},
|
||||
{
|
||||
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');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Remove column',
|
||||
action: function (column) {
|
||||
this.removeColumn(column.colIndex);
|
||||
}
|
||||
}
|
||||
],
|
||||
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
|
||||
freezeMessage: '',
|
||||
getEditor: () => {},
|
||||
addSerialNoColumn: true,
|
||||
addCheckboxColumn: false,
|
||||
enableClusterize: true,
|
||||
enableLogs: false,
|
||||
layout: 'fixed', // fixed, fluid
|
||||
noDataMessage: 'No Data',
|
||||
cellHeight: null,
|
||||
enableInlineFilters: false
|
||||
};
|
||||
|
||||
219
src/dom.js
219
src/dom.js
@ -1,172 +1,181 @@
|
||||
|
||||
export default function $(expr, con) {
|
||||
return typeof expr === 'string' ?
|
||||
(con || document).querySelector(expr) :
|
||||
expr || null;
|
||||
return typeof expr === 'string' ?
|
||||
(con || document).querySelector(expr) :
|
||||
expr || null;
|
||||
}
|
||||
|
||||
$.each = (expr, con) => {
|
||||
return typeof expr === 'string' ?
|
||||
Array.from((con || document).querySelectorAll(expr)) :
|
||||
expr || null;
|
||||
return typeof expr === 'string' ?
|
||||
Array.from((con || document).querySelectorAll(expr)) :
|
||||
expr || null;
|
||||
};
|
||||
|
||||
$.create = (tag, o) => {
|
||||
let element = document.createElement(tag);
|
||||
let element = document.createElement(tag);
|
||||
|
||||
for (let i in o) {
|
||||
let val = o[i];
|
||||
for (let i in o) {
|
||||
let val = o[i];
|
||||
|
||||
if (i === 'inside') {
|
||||
$(val).appendChild(element);
|
||||
} else
|
||||
if (i === 'around') {
|
||||
let ref = $(val);
|
||||
ref.parentNode.insertBefore(element, ref);
|
||||
element.appendChild(ref);
|
||||
} else
|
||||
if (i === 'styles') {
|
||||
if (typeof val === 'object') {
|
||||
Object.keys(val).map(prop => {
|
||||
element.style[prop] = val[prop];
|
||||
});
|
||||
}
|
||||
if (i === 'inside') {
|
||||
$(val).appendChild(element);
|
||||
} else
|
||||
if (i in element) {
|
||||
if (i === 'around') {
|
||||
let ref = $(val);
|
||||
ref.parentNode.insertBefore(element, ref);
|
||||
element.appendChild(ref);
|
||||
} else
|
||||
if (i === 'styles') {
|
||||
if (typeof val === 'object') {
|
||||
Object.keys(val).map(prop => {
|
||||
element.style[prop] = val[prop];
|
||||
});
|
||||
}
|
||||
} else
|
||||
if (i in element) {
|
||||
element[i] = val;
|
||||
} else {
|
||||
} else {
|
||||
element.setAttribute(i, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return element;
|
||||
return element;
|
||||
};
|
||||
|
||||
$.on = (element, event, selector, callback) => {
|
||||
if (!callback) {
|
||||
callback = selector;
|
||||
$.bind(element, event, callback);
|
||||
} else {
|
||||
$.delegate(element, event, selector, callback);
|
||||
}
|
||||
if (!callback) {
|
||||
callback = selector;
|
||||
$.bind(element, event, callback);
|
||||
} else {
|
||||
$.delegate(element, event, selector, callback);
|
||||
}
|
||||
};
|
||||
|
||||
$.off = (element, event, handler) => {
|
||||
element.removeEventListener(event, handler);
|
||||
element.removeEventListener(event, handler);
|
||||
};
|
||||
|
||||
$.bind = (element, event, callback) => {
|
||||
event.split(/\s+/).forEach(function (event) {
|
||||
element.addEventListener(event, callback);
|
||||
});
|
||||
event.split(/\s+/).forEach(function (event) {
|
||||
element.addEventListener(event, callback);
|
||||
});
|
||||
};
|
||||
|
||||
$.delegate = (element, event, selector, callback) => {
|
||||
element.addEventListener(event, function (e) {
|
||||
const delegatedTarget = e.target.closest(selector);
|
||||
if (delegatedTarget) {
|
||||
e.delegatedTarget = delegatedTarget;
|
||||
callback.call(this, e, delegatedTarget);
|
||||
}
|
||||
});
|
||||
element.addEventListener(event, function (e) {
|
||||
const delegatedTarget = e.target.closest(selector);
|
||||
if (delegatedTarget) {
|
||||
e.delegatedTarget = delegatedTarget;
|
||||
callback.call(this, e, delegatedTarget);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$.unbind = (element, o) => {
|
||||
if (element) {
|
||||
for (let event in o) {
|
||||
let callback = o[event];
|
||||
if (element) {
|
||||
for (let event in o) {
|
||||
let callback = o[event];
|
||||
|
||||
event.split(/\s+/).forEach(function (event) {
|
||||
element.removeEventListener(event, callback);
|
||||
});
|
||||
event.split(/\s+/).forEach(function (event) {
|
||||
element.removeEventListener(event, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$.fire = (target, type, properties) => {
|
||||
let evt = document.createEvent('HTMLEvents');
|
||||
let evt = document.createEvent('HTMLEvents');
|
||||
|
||||
evt.initEvent(type, true, true);
|
||||
evt.initEvent(type, true, true);
|
||||
|
||||
for (let j in properties) {
|
||||
evt[j] = properties[j];
|
||||
}
|
||||
for (let j in properties) {
|
||||
evt[j] = properties[j];
|
||||
}
|
||||
|
||||
return target.dispatchEvent(evt);
|
||||
return target.dispatchEvent(evt);
|
||||
};
|
||||
|
||||
$.data = (element, attrs) => { // eslint-disable-line
|
||||
if (!attrs) {
|
||||
return element.dataset;
|
||||
}
|
||||
if (!attrs) {
|
||||
return element.dataset;
|
||||
}
|
||||
|
||||
for (const attr in attrs) {
|
||||
element.dataset[attr] = attrs[attr];
|
||||
}
|
||||
for (const attr in attrs) {
|
||||
element.dataset[attr] = attrs[attr];
|
||||
}
|
||||
};
|
||||
|
||||
$.style = (elements, styleMap) => { // eslint-disable-line
|
||||
|
||||
if (typeof styleMap === 'string') {
|
||||
return $.getStyle(elements, styleMap);
|
||||
}
|
||||
|
||||
if (!Array.isArray(elements)) {
|
||||
elements = [elements];
|
||||
}
|
||||
|
||||
elements.map(element => {
|
||||
for (const prop in styleMap) {
|
||||
element.style[prop] = styleMap[prop];
|
||||
if (typeof styleMap === 'string') {
|
||||
return $.getStyle(elements, styleMap);
|
||||
}
|
||||
});
|
||||
|
||||
if (!Array.isArray(elements)) {
|
||||
elements = [elements];
|
||||
}
|
||||
|
||||
elements.map(element => {
|
||||
for (const prop in styleMap) {
|
||||
element.style[prop] = styleMap[prop];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$.removeStyle = (elements, styleProps) => {
|
||||
if (!Array.isArray(elements)) {
|
||||
elements = [elements];
|
||||
}
|
||||
|
||||
if (!Array.isArray(styleProps)) {
|
||||
styleProps = [styleProps];
|
||||
}
|
||||
|
||||
elements.map(element => {
|
||||
for (const prop of styleProps) {
|
||||
element.style[prop] = '';
|
||||
if (!Array.isArray(elements)) {
|
||||
elements = [elements];
|
||||
}
|
||||
});
|
||||
|
||||
if (!Array.isArray(styleProps)) {
|
||||
styleProps = [styleProps];
|
||||
}
|
||||
|
||||
elements.map(element => {
|
||||
for (const prop of styleProps) {
|
||||
element.style[prop] = '';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$.getStyle = (element, prop) => {
|
||||
let val = getComputedStyle(element)[prop];
|
||||
let val = getComputedStyle(element)[prop];
|
||||
|
||||
if (['width', 'height'].includes(prop)) {
|
||||
val = parseFloat(val);
|
||||
}
|
||||
if (['width', 'height'].includes(prop)) {
|
||||
val = parseFloat(val);
|
||||
}
|
||||
|
||||
return val;
|
||||
return val;
|
||||
};
|
||||
|
||||
$.closest = (selector, element) => {
|
||||
if (!element) return null;
|
||||
if (!element) return null;
|
||||
|
||||
if (element.matches(selector)) {
|
||||
return element;
|
||||
}
|
||||
if (element.matches(selector)) {
|
||||
return element;
|
||||
}
|
||||
|
||||
return $.closest(selector, element.parentNode);
|
||||
return $.closest(selector, element.parentNode);
|
||||
};
|
||||
|
||||
$.inViewport = (el, parentEl) => {
|
||||
const { top, left, bottom, right } = el.getBoundingClientRect();
|
||||
const { top: pTop, left: pLeft, bottom: pBottom, right: pRight } = parentEl.getBoundingClientRect();
|
||||
const {
|
||||
top,
|
||||
left,
|
||||
bottom,
|
||||
right
|
||||
} = el.getBoundingClientRect();
|
||||
const {
|
||||
top: pTop,
|
||||
left: pLeft,
|
||||
bottom: pBottom,
|
||||
right: pRight
|
||||
} = parentEl.getBoundingClientRect();
|
||||
|
||||
return top >= pTop && left >= pLeft && bottom <= pBottom && right <= pRight;
|
||||
return top >= pTop && left >= pLeft && bottom <= pBottom && right <= pRight;
|
||||
};
|
||||
|
||||
$.scrollTop = function scrollTop(element, pixels) {
|
||||
requestAnimationFrame(() => {
|
||||
element.scrollTop = pixels;
|
||||
});
|
||||
requestAnimationFrame(() => {
|
||||
element.scrollTop = pixels;
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,56 +1,56 @@
|
||||
import $ from './dom';
|
||||
|
||||
const KEYCODES = {
|
||||
13: 'enter',
|
||||
91: 'meta',
|
||||
16: 'shift',
|
||||
17: 'ctrl',
|
||||
18: 'alt',
|
||||
37: 'left',
|
||||
38: 'up',
|
||||
39: 'right',
|
||||
40: 'down',
|
||||
9: 'tab',
|
||||
27: 'esc',
|
||||
67: 'c',
|
||||
70: 'f'
|
||||
13: 'enter',
|
||||
91: 'meta',
|
||||
16: 'shift',
|
||||
17: 'ctrl',
|
||||
18: 'alt',
|
||||
37: 'left',
|
||||
38: 'up',
|
||||
39: 'right',
|
||||
40: 'down',
|
||||
9: 'tab',
|
||||
27: 'esc',
|
||||
67: 'c',
|
||||
70: 'f'
|
||||
};
|
||||
|
||||
export default class Keyboard {
|
||||
constructor(element) {
|
||||
this.listeners = {};
|
||||
$.on(element, 'keydown', this.handler.bind(this));
|
||||
}
|
||||
|
||||
handler(e) {
|
||||
let key = KEYCODES[e.keyCode];
|
||||
|
||||
if (e.shiftKey && key !== 'shift') {
|
||||
key = 'shift+' + key;
|
||||
constructor(element) {
|
||||
this.listeners = {};
|
||||
$.on(element, 'keydown', this.handler.bind(this));
|
||||
}
|
||||
|
||||
if ((e.ctrlKey && key !== 'ctrl') || (e.metaKey && key !== 'meta')) {
|
||||
key = 'ctrl+' + key;
|
||||
}
|
||||
handler(e) {
|
||||
let key = KEYCODES[e.keyCode];
|
||||
|
||||
const listeners = this.listeners[key];
|
||||
|
||||
if (listeners && listeners.length > 0) {
|
||||
for (let listener of listeners) {
|
||||
const preventBubbling = listener(e);
|
||||
if (preventBubbling === undefined || preventBubbling === true) {
|
||||
e.preventDefault();
|
||||
if (e.shiftKey && key !== 'shift') {
|
||||
key = 'shift+' + key;
|
||||
}
|
||||
|
||||
if ((e.ctrlKey && key !== 'ctrl') || (e.metaKey && key !== 'meta')) {
|
||||
key = 'ctrl+' + key;
|
||||
}
|
||||
|
||||
const listeners = this.listeners[key];
|
||||
|
||||
if (listeners && listeners.length > 0) {
|
||||
for (let listener of listeners) {
|
||||
const preventBubbling = listener(e);
|
||||
if (preventBubbling === undefined || preventBubbling === true) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
on(key, listener) {
|
||||
const keys = key.split(',').map(k => k.trim());
|
||||
on(key, listener) {
|
||||
const keys = key.split(',').map(k => k.trim());
|
||||
|
||||
keys.map(key => {
|
||||
this.listeners[key] = this.listeners[key] || [];
|
||||
this.listeners[key].push(listener);
|
||||
});
|
||||
}
|
||||
keys.map(key => {
|
||||
this.listeners[key] = this.listeners[key] || [];
|
||||
this.listeners[key].push(listener);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
class Performance {
|
||||
start() {
|
||||
this._start = window.performance.now();
|
||||
}
|
||||
|
||||
end() {
|
||||
this._end = window.performance.now();
|
||||
console.log(this._end - this._start);
|
||||
}
|
||||
}
|
||||
|
||||
let perf = new Performance();
|
||||
|
||||
export default perf;
|
||||
@ -1,210 +1,224 @@
|
||||
import $ from './dom';
|
||||
import { makeDataAttributeString, promisify } from './utils';
|
||||
import {
|
||||
makeDataAttributeString,
|
||||
promisify
|
||||
} from './utils';
|
||||
|
||||
export default class RowManager {
|
||||
constructor(instance) {
|
||||
this.instance = instance;
|
||||
this.options = this.instance.options;
|
||||
this.wrapper = this.instance.wrapper;
|
||||
this.bodyScrollable = this.instance.bodyScrollable;
|
||||
constructor(instance) {
|
||||
this.instance = instance;
|
||||
this.options = this.instance.options;
|
||||
this.wrapper = this.instance.wrapper;
|
||||
this.bodyScrollable = this.instance.bodyScrollable;
|
||||
|
||||
this.bindEvents();
|
||||
this.refreshRows = promisify(this.refreshRows, this);
|
||||
}
|
||||
|
||||
get datamanager() {
|
||||
return this.instance.datamanager;
|
||||
}
|
||||
|
||||
get cellmanager() {
|
||||
return this.instance.cellmanager;
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
this.bindCheckbox();
|
||||
}
|
||||
|
||||
bindCheckbox() {
|
||||
if (!this.options.addCheckboxColumn) return;
|
||||
|
||||
// map of checked rows
|
||||
this.checkMap = [];
|
||||
|
||||
$.on(this.wrapper, 'click', '.data-table-col[data-col-index="0"] [type="checkbox"]', (e, $checkbox) => {
|
||||
const $cell = $checkbox.closest('.data-table-col');
|
||||
const { rowIndex, isHeader } = $.data($cell);
|
||||
const checked = $checkbox.checked;
|
||||
|
||||
if (isHeader) {
|
||||
this.checkAll(checked);
|
||||
} else {
|
||||
this.checkRow(rowIndex, checked);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
refreshRows() {
|
||||
this.instance.renderBody();
|
||||
this.instance.setDimensions();
|
||||
}
|
||||
|
||||
refreshRow(row, rowIndex) {
|
||||
const _row = this.datamanager.updateRow(row, rowIndex);
|
||||
|
||||
_row.forEach(cell => {
|
||||
this.cellmanager.refreshCell(cell);
|
||||
});
|
||||
}
|
||||
|
||||
getCheckedRows() {
|
||||
if (!this.checkMap) {
|
||||
return [];
|
||||
this.bindEvents();
|
||||
this.refreshRows = promisify(this.refreshRows, this);
|
||||
}
|
||||
|
||||
return this.checkMap
|
||||
.map((c, rowIndex) => {
|
||||
if (c) {
|
||||
return rowIndex;
|
||||
get datamanager() {
|
||||
return this.instance.datamanager;
|
||||
}
|
||||
|
||||
get cellmanager() {
|
||||
return this.instance.cellmanager;
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
this.bindCheckbox();
|
||||
}
|
||||
|
||||
bindCheckbox() {
|
||||
if (!this.options.addCheckboxColumn) return;
|
||||
|
||||
// map of checked rows
|
||||
this.checkMap = [];
|
||||
|
||||
$.on(this.wrapper, 'click', '.data-table-col[data-col-index="0"] [type="checkbox"]', (e, $checkbox) => {
|
||||
const $cell = $checkbox.closest('.data-table-col');
|
||||
const {
|
||||
rowIndex,
|
||||
isHeader
|
||||
} = $.data($cell);
|
||||
const checked = $checkbox.checked;
|
||||
|
||||
if (isHeader) {
|
||||
this.checkAll(checked);
|
||||
} else {
|
||||
this.checkRow(rowIndex, checked);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
refreshRows() {
|
||||
this.instance.renderBody();
|
||||
this.instance.setDimensions();
|
||||
}
|
||||
|
||||
refreshRow(row, rowIndex) {
|
||||
const _row = this.datamanager.updateRow(row, rowIndex);
|
||||
|
||||
_row.forEach(cell => {
|
||||
this.cellmanager.refreshCell(cell);
|
||||
});
|
||||
}
|
||||
|
||||
getCheckedRows() {
|
||||
if (!this.checkMap) {
|
||||
return [];
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(c => {
|
||||
return c !== null || c !== undefined;
|
||||
});
|
||||
}
|
||||
|
||||
highlightCheckedRows() {
|
||||
this.getCheckedRows()
|
||||
.map(rowIndex => this.checkRow(rowIndex, true));
|
||||
}
|
||||
|
||||
checkRow(rowIndex, toggle) {
|
||||
const value = toggle ? 1 : 0;
|
||||
|
||||
// update internal map
|
||||
this.checkMap[rowIndex] = value;
|
||||
// set checkbox value explicitly
|
||||
$.each(`.data-table-col[data-row-index="${rowIndex}"][data-col-index="0"] [type="checkbox"]`, this.bodyScrollable)
|
||||
.map(input => {
|
||||
input.checked = toggle;
|
||||
});
|
||||
// highlight row
|
||||
this.highlightRow(rowIndex, toggle);
|
||||
}
|
||||
|
||||
checkAll(toggle) {
|
||||
const value = toggle ? 1 : 0;
|
||||
|
||||
// update internal map
|
||||
if (toggle) {
|
||||
this.checkMap = Array.from(Array(this.getTotalRows())).map(c => value);
|
||||
} else {
|
||||
this.checkMap = [];
|
||||
}
|
||||
// set checkbox value
|
||||
$.each('.data-table-col[data-col-index="0"] [type="checkbox"]', this.bodyScrollable)
|
||||
.map(input => {
|
||||
input.checked = toggle;
|
||||
});
|
||||
// highlight all
|
||||
this.highlightAll(toggle);
|
||||
}
|
||||
|
||||
highlightRow(rowIndex, toggle = true) {
|
||||
const $row = this.getRow$(rowIndex);
|
||||
if (!$row) return;
|
||||
|
||||
if (!toggle && this.bodyScrollable.classList.contains('row-highlight-all')) {
|
||||
$row.classList.add('row-unhighlight');
|
||||
return;
|
||||
return this.checkMap
|
||||
.map((c, rowIndex) => {
|
||||
if (c) {
|
||||
return rowIndex;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(c => {
|
||||
return c !== null || c !== undefined;
|
||||
});
|
||||
}
|
||||
|
||||
if (toggle && $row.classList.contains('row-unhighlight')) {
|
||||
$row.classList.remove('row-unhighlight');
|
||||
highlightCheckedRows() {
|
||||
this.getCheckedRows()
|
||||
.map(rowIndex => this.checkRow(rowIndex, true));
|
||||
}
|
||||
|
||||
this._highlightedRows = this._highlightedRows || {};
|
||||
|
||||
if (toggle) {
|
||||
$row.classList.add('row-highlight');
|
||||
this._highlightedRows[rowIndex] = $row;
|
||||
} else {
|
||||
$row.classList.remove('row-highlight');
|
||||
delete this._highlightedRows[rowIndex];
|
||||
}
|
||||
}
|
||||
|
||||
highlightAll(toggle = true) {
|
||||
if (toggle) {
|
||||
this.bodyScrollable.classList.add('row-highlight-all');
|
||||
} else {
|
||||
this.bodyScrollable.classList.remove('row-highlight-all');
|
||||
for (const rowIndex in this._highlightedRows) {
|
||||
const $row = this._highlightedRows[rowIndex];
|
||||
$row.classList.remove('row-highlight');
|
||||
}
|
||||
this._highlightedRows = {};
|
||||
}
|
||||
}
|
||||
|
||||
getRow$(rowIndex) {
|
||||
return $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable);
|
||||
}
|
||||
|
||||
getTotalRows() {
|
||||
return this.datamanager.getRowCount();
|
||||
}
|
||||
|
||||
getFirstRowIndex() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
getLastRowIndex() {
|
||||
return this.datamanager.getRowCount() - 1;
|
||||
}
|
||||
|
||||
scrollToRow(rowIndex) {
|
||||
rowIndex = +rowIndex;
|
||||
this._lastScrollTo = this._lastScrollTo || 0;
|
||||
const $row = this.getRow$(rowIndex);
|
||||
if ($.inViewport($row, this.bodyScrollable)) return;
|
||||
|
||||
const { height } = $row.getBoundingClientRect();
|
||||
const { top, bottom } = this.bodyScrollable.getBoundingClientRect();
|
||||
const rowsInView = Math.floor((bottom - top) / height);
|
||||
|
||||
let offset = 0;
|
||||
if (rowIndex > this._lastScrollTo) {
|
||||
offset = height * ((rowIndex + 1) - rowsInView);
|
||||
} else {
|
||||
offset = height * ((rowIndex + 1) - 1);
|
||||
checkRow(rowIndex, toggle) {
|
||||
const value = toggle ? 1 : 0;
|
||||
const selector = rowIndex =>
|
||||
`.data-table-col[data-row-index="${rowIndex}"][data-col-index="0"] [type="checkbox"]`;
|
||||
// update internal map
|
||||
this.checkMap[rowIndex] = value;
|
||||
// set checkbox value explicitly
|
||||
$.each(selector(rowIndex), this.bodyScrollable)
|
||||
.map(input => {
|
||||
input.checked = toggle;
|
||||
});
|
||||
// highlight row
|
||||
this.highlightRow(rowIndex, toggle);
|
||||
}
|
||||
|
||||
this._lastScrollTo = rowIndex;
|
||||
$.scrollTop(this.bodyScrollable, offset);
|
||||
}
|
||||
checkAll(toggle) {
|
||||
const value = toggle ? 1 : 0;
|
||||
|
||||
getRowHTML(row, props) {
|
||||
const dataAttr = makeDataAttributeString(props);
|
||||
|
||||
if (props.isFilter) {
|
||||
row = row.map(cell => (Object.assign(cell, {
|
||||
content: this.getFilterInput({ colIndex: cell.colIndex }),
|
||||
isFilter: 1,
|
||||
isHeader: undefined,
|
||||
editable: false
|
||||
})));
|
||||
// update internal map
|
||||
if (toggle) {
|
||||
this.checkMap = Array.from(Array(this.getTotalRows())).map(c => value);
|
||||
} else {
|
||||
this.checkMap = [];
|
||||
}
|
||||
// set checkbox value
|
||||
$.each('.data-table-col[data-col-index="0"] [type="checkbox"]', this.bodyScrollable)
|
||||
.map(input => {
|
||||
input.checked = toggle;
|
||||
});
|
||||
// highlight all
|
||||
this.highlightAll(toggle);
|
||||
}
|
||||
|
||||
return `
|
||||
<tr class="data-table-row" ${dataAttr}>
|
||||
${row.map(cell => this.cellmanager.getCellHTML(cell)).join('')}
|
||||
</tr>
|
||||
`;
|
||||
}
|
||||
highlightRow(rowIndex, toggle = true) {
|
||||
const $row = this.getRow$(rowIndex);
|
||||
if (!$row) return;
|
||||
|
||||
getFilterInput(props) {
|
||||
const dataAttr = makeDataAttributeString(props);
|
||||
return `<input class="data-table-filter input-style" type="text" ${dataAttr} />`;
|
||||
}
|
||||
if (!toggle && this.bodyScrollable.classList.contains('row-highlight-all')) {
|
||||
$row.classList.add('row-unhighlight');
|
||||
return;
|
||||
}
|
||||
|
||||
if (toggle && $row.classList.contains('row-unhighlight')) {
|
||||
$row.classList.remove('row-unhighlight');
|
||||
}
|
||||
|
||||
this._highlightedRows = this._highlightedRows || {};
|
||||
|
||||
if (toggle) {
|
||||
$row.classList.add('row-highlight');
|
||||
this._highlightedRows[rowIndex] = $row;
|
||||
} else {
|
||||
$row.classList.remove('row-highlight');
|
||||
delete this._highlightedRows[rowIndex];
|
||||
}
|
||||
}
|
||||
|
||||
highlightAll(toggle = true) {
|
||||
if (toggle) {
|
||||
this.bodyScrollable.classList.add('row-highlight-all');
|
||||
} else {
|
||||
this.bodyScrollable.classList.remove('row-highlight-all');
|
||||
for (const rowIndex in this._highlightedRows) {
|
||||
const $row = this._highlightedRows[rowIndex];
|
||||
$row.classList.remove('row-highlight');
|
||||
}
|
||||
this._highlightedRows = {};
|
||||
}
|
||||
}
|
||||
|
||||
getRow$(rowIndex) {
|
||||
return $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable);
|
||||
}
|
||||
|
||||
getTotalRows() {
|
||||
return this.datamanager.getRowCount();
|
||||
}
|
||||
|
||||
getFirstRowIndex() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
getLastRowIndex() {
|
||||
return this.datamanager.getRowCount() - 1;
|
||||
}
|
||||
|
||||
scrollToRow(rowIndex) {
|
||||
rowIndex = +rowIndex;
|
||||
this._lastScrollTo = this._lastScrollTo || 0;
|
||||
const $row = this.getRow$(rowIndex);
|
||||
if ($.inViewport($row, this.bodyScrollable)) return;
|
||||
|
||||
const {
|
||||
height
|
||||
} = $row.getBoundingClientRect();
|
||||
const {
|
||||
top,
|
||||
bottom
|
||||
} = this.bodyScrollable.getBoundingClientRect();
|
||||
const rowsInView = Math.floor((bottom - top) / height);
|
||||
|
||||
let offset = 0;
|
||||
if (rowIndex > this._lastScrollTo) {
|
||||
offset = height * ((rowIndex + 1) - rowsInView);
|
||||
} else {
|
||||
offset = height * ((rowIndex + 1) - 1);
|
||||
}
|
||||
|
||||
this._lastScrollTo = rowIndex;
|
||||
$.scrollTop(this.bodyScrollable, offset);
|
||||
}
|
||||
|
||||
getRowHTML(row, props) {
|
||||
const dataAttr = makeDataAttributeString(props);
|
||||
|
||||
if (props.isFilter) {
|
||||
row = row.map(cell => (Object.assign(cell, {
|
||||
content: this.getFilterInput({
|
||||
colIndex: cell.colIndex
|
||||
}),
|
||||
isFilter: 1,
|
||||
isHeader: undefined,
|
||||
editable: false
|
||||
})));
|
||||
}
|
||||
|
||||
return `
|
||||
<tr class="data-table-row" ${dataAttr}>
|
||||
${row.map(cell => this.cellmanager.getCellHTML(cell)).join('')}
|
||||
</tr>
|
||||
`;
|
||||
}
|
||||
|
||||
getFilterInput(props) {
|
||||
const dataAttr = makeDataAttributeString(props);
|
||||
return `<input class="data-table-filter input-style" type="text" ${dataAttr} />`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/* This file is processed by postcss */
|
||||
/* variables */
|
||||
|
||||
:root {
|
||||
|
||||
414
src/style.js
414
src/style.js
@ -1,235 +1,241 @@
|
||||
import $ from './dom';
|
||||
import {
|
||||
camelCaseToDash,
|
||||
linkProperties,
|
||||
throttle
|
||||
camelCaseToDash,
|
||||
linkProperties,
|
||||
throttle
|
||||
} from './utils';
|
||||
|
||||
export default class Style {
|
||||
constructor(instance) {
|
||||
this.instance = instance;
|
||||
constructor(instance) {
|
||||
this.instance = instance;
|
||||
|
||||
linkProperties(this, this.instance, [
|
||||
'options', 'datamanager', 'columnmanager',
|
||||
'header', 'bodyScrollable', 'getColumn'
|
||||
]);
|
||||
linkProperties(this, this.instance, [
|
||||
'options', 'datamanager', 'columnmanager',
|
||||
'header', 'bodyScrollable', 'getColumn'
|
||||
]);
|
||||
|
||||
this.scopeClass = 'datatable-instance-' + instance.constructor.instances;
|
||||
instance.datatableWrapper.classList.add(this.scopeClass);
|
||||
this.scopeClass = 'datatable-instance-' + instance.constructor.instances;
|
||||
instance.datatableWrapper.classList.add(this.scopeClass);
|
||||
|
||||
const styleEl = document.createElement('style');
|
||||
instance.wrapper.insertBefore(styleEl, instance.datatableWrapper);
|
||||
this.styleEl = styleEl;
|
||||
this.styleSheet = styleEl.sheet;
|
||||
const styleEl = document.createElement('style');
|
||||
instance.wrapper.insertBefore(styleEl, instance.datatableWrapper);
|
||||
this.styleEl = styleEl;
|
||||
this.styleSheet = styleEl.sheet;
|
||||
|
||||
this.bindResizeWindow();
|
||||
}
|
||||
this.bindResizeWindow();
|
||||
}
|
||||
|
||||
bindResizeWindow() {
|
||||
if (this.options.layout === 'fluid') {
|
||||
$.on(window, 'resize', throttle(() => {
|
||||
this.distributeRemainingWidth();
|
||||
this.refreshColumnWidth();
|
||||
this.setBodyStyle();
|
||||
}, 300));
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.styleEl.remove();
|
||||
}
|
||||
|
||||
setStyle(selector, styleMap, index = -1) {
|
||||
const styles = Object.keys(styleMap)
|
||||
.map(prop => {
|
||||
if (!prop.includes('-')) {
|
||||
prop = camelCaseToDash(prop);
|
||||
}
|
||||
return `${prop}:${styleMap[prop]};`;
|
||||
})
|
||||
.join('');
|
||||
let prefixedSelector = selector
|
||||
.split(',')
|
||||
.map(r => `.${this.scopeClass} ${r}`)
|
||||
.join(',');
|
||||
|
||||
let ruleString = `${prefixedSelector} { ${styles} }`;
|
||||
|
||||
let _index = this.styleSheet.cssRules.length;
|
||||
if (index !== -1) {
|
||||
this.styleSheet.deleteRule(index);
|
||||
_index = index;
|
||||
}
|
||||
|
||||
this.styleSheet.insertRule(ruleString, _index);
|
||||
return _index;
|
||||
}
|
||||
|
||||
setDimensions() {
|
||||
this.setHeaderStyle();
|
||||
|
||||
this.setupMinWidth();
|
||||
this.setupNaturalColumnWidth();
|
||||
this.setupColumnWidth();
|
||||
|
||||
bindResizeWindow() {
|
||||
if (this.options.layout === 'fluid') {
|
||||
$.on(window, 'resize', throttle(() => {
|
||||
this.distributeRemainingWidth();
|
||||
this.refreshColumnWidth();
|
||||
this.setColumnStyle();
|
||||
this.setDefaultCellHeight();
|
||||
this.setBodyStyle();
|
||||
}, 300));
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.styleEl.remove();
|
||||
}
|
||||
|
||||
setStyle(rule, styleMap, index = -1) {
|
||||
const styles = Object.keys(styleMap)
|
||||
.map(prop => {
|
||||
if (!prop.includes('-')) {
|
||||
prop = camelCaseToDash(prop);
|
||||
}
|
||||
return `${prop}:${styleMap[prop]};`;
|
||||
})
|
||||
.join('');
|
||||
let ruleString = `.${this.scopeClass} ${rule} { ${styles} }`;
|
||||
|
||||
let _index = this.styleSheet.cssRules.length;
|
||||
if (index !== -1) {
|
||||
this.styleSheet.deleteRule(index);
|
||||
_index = index;
|
||||
}
|
||||
|
||||
this.styleSheet.insertRule(ruleString, _index);
|
||||
return _index;
|
||||
}
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
setDimensions() {
|
||||
this.setHeaderStyle();
|
||||
$.style(this.header, {
|
||||
margin: 0
|
||||
});
|
||||
|
||||
this.setupMinWidth();
|
||||
this.setupNaturalColumnWidth();
|
||||
this.setupColumnWidth();
|
||||
// don't show resize cursor on nonResizable columns
|
||||
const nonResizableColumnsSelector = this.datamanager.getColumns()
|
||||
.filter(col => col.resizable === false)
|
||||
.map(col => col.colIndex)
|
||||
.map(i => `.data-table-header [data-col-index="${i}"]`)
|
||||
.join();
|
||||
|
||||
this.distributeRemainingWidth();
|
||||
this.setColumnStyle();
|
||||
this.setDefaultCellHeight();
|
||||
this.setBodyStyle();
|
||||
}
|
||||
|
||||
setHeaderStyle() {
|
||||
if (this.options.layout === 'fluid') {
|
||||
// setting width as 0 will ensure that the
|
||||
// header doesn't take the available space
|
||||
$.style(this.header, {
|
||||
width: 0
|
||||
});
|
||||
this.setStyle(nonResizableColumnsSelector, {
|
||||
cursor: 'pointer'
|
||||
});
|
||||
}
|
||||
|
||||
$.style(this.header, {
|
||||
margin: 0
|
||||
});
|
||||
setupMinWidth() {
|
||||
$.each('.data-table-col[data-is-header]', this.header).map(col => {
|
||||
const width = $.style($('.content', col), 'width');
|
||||
const {
|
||||
colIndex
|
||||
} = $.data(col);
|
||||
const column = this.getColumn(colIndex);
|
||||
|
||||
// don't show resize cursor on nonResizable columns
|
||||
const nonResizableColumnsSelector = this.datamanager.getColumns()
|
||||
.filter(col => col.resizable === false)
|
||||
.map(col => col.colIndex)
|
||||
.map(i => `.data-table-header [data-col-index="${i}"]`)
|
||||
.join();
|
||||
|
||||
this.setStyle(nonResizableColumnsSelector, {
|
||||
cursor: 'pointer'
|
||||
});
|
||||
}
|
||||
|
||||
setupMinWidth() {
|
||||
$.each('.data-table-col[data-is-header]', this.header).map(col => {
|
||||
const width = $.style($('.content', col), 'width');
|
||||
const {
|
||||
colIndex
|
||||
} = $.data(col);
|
||||
const column = this.getColumn(colIndex);
|
||||
|
||||
if (!column.minWidth) {
|
||||
// only set this once
|
||||
column.minWidth = width;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setupNaturalColumnWidth() {
|
||||
if (!$('.data-table-row')) return;
|
||||
|
||||
// set initial width as naturally calculated by table's first row
|
||||
$.each('.data-table-row[data-row-index="0"] .data-table-col', this.bodyScrollable).map($cell => {
|
||||
const {
|
||||
colIndex
|
||||
} = $.data($cell);
|
||||
const column = this.datamanager.getColumn(colIndex);
|
||||
|
||||
let naturalWidth = $.style($('.content', $cell), 'width');
|
||||
|
||||
if (column.id === '_rowIndex') {
|
||||
// width based on rowCount
|
||||
const rowCount = this.datamanager.getRowCount();
|
||||
const digits = (rowCount + '').length;
|
||||
if (digits > 2) {
|
||||
naturalWidth = naturalWidth + ((digits - 2) * 8);
|
||||
}
|
||||
}
|
||||
|
||||
column.naturalWidth = naturalWidth;
|
||||
});
|
||||
}
|
||||
|
||||
setupColumnWidth() {
|
||||
this.datamanager.getColumns()
|
||||
.map(column => {
|
||||
if (!column.width) {
|
||||
column.width = column.naturalWidth;
|
||||
}
|
||||
if (column.width < column.minWidth) {
|
||||
column.width = column.minWidth;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
distributeRemainingWidth() {
|
||||
if (this.options.layout !== 'fluid') return;
|
||||
|
||||
const wrapperWidth = $.style(this.instance.datatableWrapper, 'width');
|
||||
const headerWidth = $.style(this.header, 'width');
|
||||
const resizableColumns = this.datamanager.getColumns().filter(col => col.resizable);
|
||||
const deltaWidth = (wrapperWidth - headerWidth) / resizableColumns.length;
|
||||
|
||||
resizableColumns.map(col => {
|
||||
const width = $.style(this.getColumnHeaderElement(col.colIndex), 'width');
|
||||
let finalWidth = Math.floor(width + deltaWidth) - 2;
|
||||
|
||||
this.datamanager.updateColumn(col.colIndex, {
|
||||
width: finalWidth
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setDefaultCellHeight() {
|
||||
if (this.__cellHeightSet) return;
|
||||
const height = this.options.cellHeight || $.style($('.data-table-col', this.instance.datatableWrapper), 'height');
|
||||
if (height) {
|
||||
this.setCellHeight(height);
|
||||
this.__cellHeightSet = true;
|
||||
if (!column.minWidth) {
|
||||
// only set this once
|
||||
column.minWidth = width;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setCellHeight(height) {
|
||||
this.setStyle('.data-table-col .content', {
|
||||
height: height + 'px'
|
||||
});
|
||||
this.setStyle('.data-table-col .edit-cell', {
|
||||
height: height + 'px'
|
||||
});
|
||||
}
|
||||
setupNaturalColumnWidth() {
|
||||
if (!$('.data-table-row')) return;
|
||||
|
||||
setColumnStyle() {
|
||||
// align columns
|
||||
this.datamanager.getColumns()
|
||||
.map(column => {
|
||||
// alignment
|
||||
if (['left', 'center', 'right'].includes(column.align)) {
|
||||
this.setStyle(`[data-col-index="${column.colIndex}"]`, {
|
||||
'text-align': column.align
|
||||
});
|
||||
// set initial width as naturally calculated by table's first row
|
||||
$.each('.data-table-row[data-row-index="0"] .data-table-col', this.bodyScrollable).map($cell => {
|
||||
const {
|
||||
colIndex
|
||||
} = $.data($cell);
|
||||
const column = this.datamanager.getColumn(colIndex);
|
||||
|
||||
let naturalWidth = $.style($('.content', $cell), 'width');
|
||||
|
||||
if (column.id === '_rowIndex') {
|
||||
// width based on rowCount
|
||||
const rowCount = this.datamanager.getRowCount();
|
||||
const digits = (rowCount + '').length;
|
||||
if (digits > 1) {
|
||||
naturalWidth = naturalWidth + ((digits - 1) * 8);
|
||||
}
|
||||
}
|
||||
|
||||
column.naturalWidth = naturalWidth;
|
||||
});
|
||||
}
|
||||
|
||||
setupColumnWidth() {
|
||||
this.datamanager.getColumns()
|
||||
.map(column => {
|
||||
if (!column.width) {
|
||||
column.width = column.naturalWidth;
|
||||
}
|
||||
if (column.width < column.minWidth) {
|
||||
column.width = column.minWidth;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
distributeRemainingWidth() {
|
||||
if (this.options.layout !== 'fluid') return;
|
||||
|
||||
const wrapperWidth = $.style(this.instance.datatableWrapper, 'width');
|
||||
const headerWidth = $.style(this.header, 'width');
|
||||
const resizableColumns = this.datamanager.getColumns().filter(col => col.resizable);
|
||||
const deltaWidth = (wrapperWidth - headerWidth) / resizableColumns.length;
|
||||
|
||||
resizableColumns.map(col => {
|
||||
const width = $.style(this.getColumnHeaderElement(col.colIndex), 'width');
|
||||
let finalWidth = Math.floor(width + deltaWidth) - 2;
|
||||
|
||||
this.datamanager.updateColumn(col.colIndex, {
|
||||
width: finalWidth
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setDefaultCellHeight() {
|
||||
if (this.__cellHeightSet) return;
|
||||
const height = this.options.cellHeight ||
|
||||
$.style($('.data-table-col', this.instance.datatableWrapper), 'height');
|
||||
if (height) {
|
||||
this.setCellHeight(height);
|
||||
this.__cellHeightSet = true;
|
||||
}
|
||||
// width
|
||||
this.columnmanager.setColumnHeaderWidth(column.colIndex);
|
||||
this.columnmanager.setColumnWidth(column.colIndex);
|
||||
});
|
||||
this.setBodyStyle();
|
||||
}
|
||||
}
|
||||
|
||||
refreshColumnWidth() {
|
||||
this.datamanager.getColumns()
|
||||
.map(column => {
|
||||
this.columnmanager.setColumnHeaderWidth(column.colIndex);
|
||||
this.columnmanager.setColumnWidth(column.colIndex);
|
||||
});
|
||||
}
|
||||
setCellHeight(height) {
|
||||
this.setStyle('.data-table-col .content', {
|
||||
height: height + 'px'
|
||||
});
|
||||
this.setStyle('.data-table-col .edit-cell', {
|
||||
height: height + 'px'
|
||||
});
|
||||
}
|
||||
|
||||
setBodyStyle() {
|
||||
const width = $.style(this.header, 'width');
|
||||
setColumnStyle() {
|
||||
// align columns
|
||||
this.datamanager.getColumns()
|
||||
.map(column => {
|
||||
// alignment
|
||||
if (['left', 'center', 'right'].includes(column.align)) {
|
||||
this.setStyle(`[data-col-index="${column.colIndex}"]`, {
|
||||
'text-align': column.align
|
||||
});
|
||||
}
|
||||
// width
|
||||
this.columnmanager.setColumnHeaderWidth(column.colIndex);
|
||||
this.columnmanager.setColumnWidth(column.colIndex);
|
||||
});
|
||||
this.setBodyStyle();
|
||||
}
|
||||
|
||||
$.style(this.bodyScrollable, {
|
||||
width: width + 'px'
|
||||
});
|
||||
refreshColumnWidth() {
|
||||
this.datamanager.getColumns()
|
||||
.map(column => {
|
||||
this.columnmanager.setColumnHeaderWidth(column.colIndex);
|
||||
this.columnmanager.setColumnWidth(column.colIndex);
|
||||
});
|
||||
}
|
||||
|
||||
$.style(this.bodyScrollable, {
|
||||
marginTop: $.style(this.header, 'height') + 'px'
|
||||
});
|
||||
setBodyStyle() {
|
||||
const width = $.style(this.header, 'width');
|
||||
|
||||
$.style($('table', this.bodyScrollable), {
|
||||
margin: 0
|
||||
});
|
||||
}
|
||||
$.style(this.bodyScrollable, {
|
||||
width: width + 'px'
|
||||
});
|
||||
|
||||
getColumnHeaderElement(colIndex) {
|
||||
colIndex = +colIndex;
|
||||
if (colIndex < 0) return null;
|
||||
return $(`.data-table-col[data-col-index="${colIndex}"]`, this.header);
|
||||
}
|
||||
$.style(this.bodyScrollable, {
|
||||
marginTop: $.style(this.header, 'height') + 'px'
|
||||
});
|
||||
|
||||
$.style($('table', this.bodyScrollable), {
|
||||
margin: 0
|
||||
});
|
||||
}
|
||||
|
||||
getColumnHeaderElement(colIndex) {
|
||||
colIndex = +colIndex;
|
||||
if (colIndex < 0) return null;
|
||||
return $(`.data-table-col[data-col-index="${colIndex}"]`, this.header);
|
||||
}
|
||||
}
|
||||
|
||||
232
src/utils.js
232
src/utils.js
@ -2,153 +2,153 @@ import _throttle from 'lodash/throttle';
|
||||
import _debounce from 'lodash/debounce';
|
||||
|
||||
export function camelCaseToDash(str) {
|
||||
return str.replace(/([A-Z])/g, (g) => `-${g[0].toLowerCase()}`);
|
||||
return str.replace(/([A-Z])/g, (g) => `-${g[0].toLowerCase()}`);
|
||||
}
|
||||
|
||||
export function makeDataAttributeString(props) {
|
||||
const keys = Object.keys(props);
|
||||
const keys = Object.keys(props);
|
||||
|
||||
return keys
|
||||
.map((key) => {
|
||||
const _key = camelCaseToDash(key);
|
||||
const val = props[key];
|
||||
return keys
|
||||
.map((key) => {
|
||||
const _key = camelCaseToDash(key);
|
||||
const val = props[key];
|
||||
|
||||
if (val === undefined) return '';
|
||||
return `data-${_key}="${val}" `;
|
||||
})
|
||||
.join('')
|
||||
.trim();
|
||||
if (val === undefined) return '';
|
||||
return `data-${_key}="${val}" `;
|
||||
})
|
||||
.join('')
|
||||
.trim();
|
||||
}
|
||||
|
||||
export function getDefault(a, b) {
|
||||
return a !== undefined ? a : b;
|
||||
return a !== undefined ? a : b;
|
||||
}
|
||||
|
||||
export function escapeRegExp(str) {
|
||||
// https://stackoverflow.com/a/6969486
|
||||
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
|
||||
// https://stackoverflow.com/a/6969486
|
||||
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
|
||||
}
|
||||
|
||||
export function getCSSString(styleMap) {
|
||||
let style = '';
|
||||
let style = '';
|
||||
|
||||
for (const prop in styleMap) {
|
||||
if (styleMap.hasOwnProperty(prop)) {
|
||||
style += `${prop}: ${styleMap[prop]}; `;
|
||||
for (const prop in styleMap) {
|
||||
if (styleMap.hasOwnProperty(prop)) {
|
||||
style += `${prop}: ${styleMap[prop]}; `;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return style.trim();
|
||||
return style.trim();
|
||||
}
|
||||
|
||||
export function getCSSRuleBlock(rule, styleMap) {
|
||||
return `${rule} { ${getCSSString(styleMap)} }`;
|
||||
return `${rule} { ${getCSSString(styleMap)} }`;
|
||||
}
|
||||
|
||||
export function buildCSSRule(rule, styleMap, cssRulesString = '') {
|
||||
// build css rules efficiently,
|
||||
// append new rule if doesnt exist,
|
||||
// update existing ones
|
||||
// build css rules efficiently,
|
||||
// append new rule if doesnt exist,
|
||||
// update existing ones
|
||||
|
||||
const rulePatternStr = `${escapeRegExp(rule)} {([^}]*)}`;
|
||||
const rulePattern = new RegExp(rulePatternStr, 'g');
|
||||
const rulePatternStr = `${escapeRegExp(rule)} {([^}]*)}`;
|
||||
const rulePattern = new RegExp(rulePatternStr, 'g');
|
||||
|
||||
if (cssRulesString && cssRulesString.match(rulePattern)) {
|
||||
for (const property in styleMap) {
|
||||
const value = styleMap[property];
|
||||
const propPattern = new RegExp(`${escapeRegExp(property)}:([^;]*);`);
|
||||
if (cssRulesString && cssRulesString.match(rulePattern)) {
|
||||
for (const property in styleMap) {
|
||||
const value = styleMap[property];
|
||||
const propPattern = new RegExp(`${escapeRegExp(property)}:([^;]*);`);
|
||||
|
||||
cssRulesString = cssRulesString.replace(rulePattern, function (match, propertyStr) {
|
||||
if (propertyStr.match(propPattern)) {
|
||||
// property exists, replace value with new value
|
||||
propertyStr = propertyStr.replace(propPattern, (match, valueStr) => {
|
||||
return `${property}: ${value};`;
|
||||
});
|
||||
cssRulesString = cssRulesString.replace(rulePattern, function (match, propertyStr) {
|
||||
if (propertyStr.match(propPattern)) {
|
||||
// property exists, replace value with new value
|
||||
propertyStr = propertyStr.replace(propPattern, (match, valueStr) => {
|
||||
return `${property}: ${value};`;
|
||||
});
|
||||
}
|
||||
propertyStr = propertyStr.trim();
|
||||
|
||||
const replacer =
|
||||
`${rule} { ${propertyStr} }`;
|
||||
|
||||
return replacer;
|
||||
});
|
||||
}
|
||||
propertyStr = propertyStr.trim();
|
||||
|
||||
const replacer =
|
||||
`${rule} { ${propertyStr} }`;
|
||||
|
||||
return replacer;
|
||||
});
|
||||
return cssRulesString;
|
||||
}
|
||||
|
||||
return cssRulesString;
|
||||
}
|
||||
// no match, append new rule block
|
||||
return `${cssRulesString}${getCSSRuleBlock(rule, styleMap)}`;
|
||||
// no match, append new rule block
|
||||
return `${cssRulesString}${getCSSRuleBlock(rule, styleMap)}`;
|
||||
}
|
||||
|
||||
export function removeCSSRule(rule, cssRulesString = '') {
|
||||
const rulePatternStr = `${escapeRegExp(rule)} {([^}]*)}`;
|
||||
const rulePattern = new RegExp(rulePatternStr, 'g');
|
||||
let output = cssRulesString;
|
||||
const rulePatternStr = `${escapeRegExp(rule)} {([^}]*)}`;
|
||||
const rulePattern = new RegExp(rulePatternStr, 'g');
|
||||
let output = cssRulesString;
|
||||
|
||||
if (cssRulesString && cssRulesString.match(rulePattern)) {
|
||||
output = cssRulesString.replace(rulePattern, '');
|
||||
}
|
||||
if (cssRulesString && cssRulesString.match(rulePattern)) {
|
||||
output = cssRulesString.replace(rulePattern, '');
|
||||
}
|
||||
|
||||
return output.trim();
|
||||
return output.trim();
|
||||
}
|
||||
|
||||
export function copyTextToClipboard(text) {
|
||||
// https://stackoverflow.com/a/30810322/5353542
|
||||
var textArea = document.createElement('textarea');
|
||||
// https://stackoverflow.com/a/30810322/5353542
|
||||
var textArea = document.createElement('textarea');
|
||||
|
||||
//
|
||||
// *** This styling is an extra step which is likely not required. ***
|
||||
//
|
||||
// Why is it here? To ensure:
|
||||
// 1. the element is able to have focus and selection.
|
||||
// 2. if element was to flash render it has minimal visual impact.
|
||||
// 3. less flakyness with selection and copying which **might** occur if
|
||||
// the textarea element is not visible.
|
||||
//
|
||||
// The likelihood is the element won't even render, not even a flash,
|
||||
// so some of these are just precautions. However in IE the element
|
||||
// is visible whilst the popup box asking the user for permission for
|
||||
// the web page to copy to the clipboard.
|
||||
//
|
||||
//
|
||||
// *** This styling is an extra step which is likely not required. ***
|
||||
//
|
||||
// Why is it here? To ensure:
|
||||
// 1. the element is able to have focus and selection.
|
||||
// 2. if element was to flash render it has minimal visual impact.
|
||||
// 3. less flakyness with selection and copying which **might** occur if
|
||||
// the textarea element is not visible.
|
||||
//
|
||||
// The likelihood is the element won't even render, not even a flash,
|
||||
// so some of these are just precautions. However in IE the element
|
||||
// is visible whilst the popup box asking the user for permission for
|
||||
// the web page to copy to the clipboard.
|
||||
//
|
||||
|
||||
// Place in top-left corner of screen regardless of scroll position.
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.top = 0;
|
||||
textArea.style.left = 0;
|
||||
// Place in top-left corner of screen regardless of scroll position.
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.top = 0;
|
||||
textArea.style.left = 0;
|
||||
|
||||
// Ensure it has a small width and height. Setting to 1px / 1em
|
||||
// doesn't work as this gives a negative w/h on some browsers.
|
||||
textArea.style.width = '2em';
|
||||
textArea.style.height = '2em';
|
||||
// Ensure it has a small width and height. Setting to 1px / 1em
|
||||
// doesn't work as this gives a negative w/h on some browsers.
|
||||
textArea.style.width = '2em';
|
||||
textArea.style.height = '2em';
|
||||
|
||||
// We don't need padding, reducing the size if it does flash render.
|
||||
textArea.style.padding = 0;
|
||||
// We don't need padding, reducing the size if it does flash render.
|
||||
textArea.style.padding = 0;
|
||||
|
||||
// Clean up any borders.
|
||||
textArea.style.border = 'none';
|
||||
textArea.style.outline = 'none';
|
||||
textArea.style.boxShadow = 'none';
|
||||
// Clean up any borders.
|
||||
textArea.style.border = 'none';
|
||||
textArea.style.outline = 'none';
|
||||
textArea.style.boxShadow = 'none';
|
||||
|
||||
// Avoid flash of white box if rendered for any reason.
|
||||
textArea.style.background = 'transparent';
|
||||
// Avoid flash of white box if rendered for any reason.
|
||||
textArea.style.background = 'transparent';
|
||||
|
||||
textArea.value = text;
|
||||
textArea.value = text;
|
||||
|
||||
document.body.appendChild(textArea);
|
||||
document.body.appendChild(textArea);
|
||||
|
||||
textArea.select();
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
} catch (err) {
|
||||
console.log('Oops, unable to copy');
|
||||
}
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
} catch (err) {
|
||||
console.log('Oops, unable to copy');
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
|
||||
export function isNumeric(val) {
|
||||
return !isNaN(val);
|
||||
return !isNaN(val);
|
||||
}
|
||||
|
||||
export let throttle = _throttle;
|
||||
@ -156,30 +156,30 @@ export let throttle = _throttle;
|
||||
export let debounce = _debounce;
|
||||
|
||||
export function promisify(fn, context = null) {
|
||||
return (...args) => {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
const out = fn.apply(context, args);
|
||||
resolve(out);
|
||||
}, 0);
|
||||
});
|
||||
};
|
||||
return (...args) => {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
const out = fn.apply(context, args);
|
||||
resolve(out);
|
||||
}, 0);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function chainPromises(promises) {
|
||||
return promises.reduce(
|
||||
(prev, cur) => prev.then(cur), Promise.resolve()
|
||||
);
|
||||
return promises.reduce(
|
||||
(prev, cur) => prev.then(cur), Promise.resolve()
|
||||
);
|
||||
};
|
||||
|
||||
export function linkProperties(target, source, properties) {
|
||||
const props = properties.reduce((acc, prop) => {
|
||||
acc[prop] = {
|
||||
get() {
|
||||
return source[prop];
|
||||
}
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
Object.defineProperties(target, props);
|
||||
const props = properties.reduce((acc, prop) => {
|
||||
acc[prop] = {
|
||||
get() {
|
||||
return source[prop];
|
||||
}
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
Object.defineProperties(target, props);
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user