Merge pull request #17 from frappe/docs-new

Add docs folder
This commit is contained in:
Faris Ansari 2018-02-27 12:48:19 +05:30 committed by GitHub
commit 54a3c670c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 11815 additions and 6842 deletions

View File

@ -1 +1,2 @@
dist dist
docs/frappe-datatable.js

View File

@ -55,7 +55,7 @@
"generator-star-spacing": [2, "both"], "generator-star-spacing": [2, "both"],
"guard-for-in": 0, "guard-for-in": 0,
"handle-callback-err": [2, "^(err|error|anySpecificError)$" ], "handle-callback-err": [2, "^(err|error|anySpecificError)$" ],
"indent": [2, 2, { "SwitchCase": 1 }], "indent": [2, 4, { "SwitchCase": 1 }],
"key-spacing": [2, { "beforeColon": false, "afterColon": true }], "key-spacing": [2, { "beforeColon": false, "afterColon": true }],
"keyword-spacing": [2, {"before": true, "after": true}], "keyword-spacing": [2, {"before": true, "after": true}],
"linebreak-style": 0, "linebreak-style": 0,

255
dist/frappe-datatable.cjs.css vendored Normal file
View 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;
}

View File

@ -165,8 +165,18 @@ $.closest = (selector, element) => {
}; };
$.inViewport = (el, parentEl) => { $.inViewport = (el, parentEl) => {
const { top, left, bottom, right } = el.getBoundingClientRect(); const {
const { top: pTop, left: pLeft, bottom: pBottom, right: pRight } = parentEl.getBoundingClientRect(); 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;
}; };
@ -912,16 +922,6 @@ class DataManager {
} }
} }
prepareRow(row, i) {
const baseRowCell = {
rowIndex: i
};
return row
.map((cell, i) => this.prepareCell(cell, i))
.map(cell => Object.assign({}, baseRowCell, cell));
}
prepareHeader() { prepareHeader() {
let columns = this.columns.concat(this.options.columns); let columns = this.columns.concat(this.options.columns);
const baseCell = { const baseCell = {
@ -1005,7 +1005,7 @@ class DataManager {
} }
} else { } else {
// row is a dict // row is an object
for (let col of this.columns) { for (let col of this.columns) {
if (col.id === '_checkbox') { if (col.id === '_checkbox') {
row.push(this.getCheckboxHTML()); row.push(this.getCheckboxHTML());
@ -1017,8 +1017,24 @@ class DataManager {
} }
} }
return this.prepareRow(row, index); return this.prepareRow(row, {
rowIndex: index
}); });
});
}
prepareRow(row, props) {
const baseRowCell = {
rowIndex: props.rowIndex
};
row = row
.map((cell, i) => this.prepareCell(cell, i))
.map(cell => Object.assign({}, baseRowCell, cell));
// monkey patched in array object
row.meta = props;
return row;
} }
validateColumns() { validateColumns() {
@ -1101,12 +1117,11 @@ class DataManager {
if (this.hasColumnById('_rowIndex')) { if (this.hasColumnById('_rowIndex')) {
// update row index // update row index
const srNoColIndex = this.getColumnIndexById('_rowIndex'); const srNoColIndex = this.getColumnIndexById('_rowIndex');
this.rows = this.rows.map((row, index) => { this.rows.forEach((row, index) => {
return row.map(cell => { row.forEach(cell => {
if (cell.colIndex === srNoColIndex) { if (cell.colIndex === srNoColIndex) {
cell.content = (index + 1) + ''; cell.content = (index + 1) + '';
} }
return cell;
}); });
}); });
} }
@ -1136,8 +1151,12 @@ class DataManager {
// update rows // update rows
this.rows = this.rows.map(row => { this.rows = this.rows.map(row => {
const newCell1 = Object.assign({}, row[index1], { colIndex: index2 }); const newCell1 = Object.assign({}, row[index1], {
const newCell2 = Object.assign({}, row[index2], { colIndex: index1 }); colIndex: index2
});
const newCell2 = Object.assign({}, row[index2], {
colIndex: index1
});
let newRow = row.map(cell => { let newRow = row.map(cell => {
// make object copy // make object copy
@ -1154,7 +1173,9 @@ class DataManager {
removeColumn(index) { removeColumn(index) {
index = +index; index = +index;
const filter = cell => cell.colIndex !== index; const filter = cell => cell.colIndex !== index;
const map = (cell, i) => Object.assign({}, cell, { colIndex: i }); const map = (cell, i) => Object.assign({}, cell, {
colIndex: i
});
// update columns // update columns
this.columns = this.columns this.columns = this.columns
.filter(filter) .filter(filter)
@ -1243,7 +1264,10 @@ class DataManager {
} }
}); });
return {rowsToHide, rowsToShow}; return {
rowsToHide,
rowsToShow
};
} }
getRowCount() { getRowCount() {
@ -1370,9 +1394,13 @@ class ColumnManager {
if (!$('.data-table-col', this.header)) { if (!$('.data-table-col', this.header)) {
// insert html // insert html
let html = this.rowmanager.getRowHTML(columns, { isHeader: 1 }); let html = this.rowmanager.getRowHTML(columns, {
isHeader: 1
});
if (this.options.enableInlineFilters) { if (this.options.enableInlineFilters) {
html += this.rowmanager.getRowHTML(columns, { isFilter: 1 }); html += this.rowmanager.getRowHTML(columns, {
isFilter: 1
});
} }
$('thead', this.header).innerHTML = html; $('thead', this.header).innerHTML = html;
@ -1390,7 +1418,9 @@ class ColumnManager {
const $cols = $.each('.data-table-col', this.header); const $cols = $.each('.data-table-col', this.header);
if (columns.length < $cols.length) { if (columns.length < $cols.length) {
// deleted column // deleted column
$('thead', this.header).innerHTML = this.rowmanager.getRowHTML(columns, { isHeader: 1 }); $('thead', this.header).innerHTML = this.rowmanager.getRowHTML(columns, {
isHeader: 1
});
return; return;
} }
@ -1443,8 +1473,12 @@ class ColumnManager {
$.on(this.header, 'click', '.data-table-dropdown-list > div', (e, $item) => { $.on(this.header, 'click', '.data-table-dropdown-list > div', (e, $item) => {
const $col = $.closest('.data-table-col', $item); const $col = $.closest('.data-table-col', $item);
const { index } = $.data($item); const {
const { colIndex } = $.data($col); index
} = $.data($item);
const {
colIndex
} = $.data($col);
let callback = dropdownItems[index].action; let callback = dropdownItems[index].action;
callback && callback.call(this.instance, this.getColumn(colIndex)); callback && callback.call(this.instance, this.getColumn(colIndex));
@ -1464,7 +1498,9 @@ class ColumnManager {
document.body.classList.add('data-table-resize'); document.body.classList.add('data-table-resize');
const $cell = $handle.parentNode.parentNode; const $cell = $handle.parentNode.parentNode;
$resizingCell = $cell; $resizingCell = $cell;
const { colIndex } = $.data($resizingCell); const {
colIndex
} = $.data($resizingCell);
const col = this.getColumn(colIndex); const col = this.getColumn(colIndex);
if (col && col.resizable === false) { if (col && col.resizable === false) {
@ -1481,7 +1517,9 @@ class ColumnManager {
if (!$resizingCell) return; if (!$resizingCell) return;
isDragging = false; isDragging = false;
const { colIndex } = $.data($resizingCell); const {
colIndex
} = $.data($resizingCell);
this.setColumnWidth(colIndex); this.setColumnWidth(colIndex);
this.style.setBodyStyle(); this.style.setBodyStyle();
$resizingCell = null; $resizingCell = null;
@ -1490,13 +1528,17 @@ class ColumnManager {
$.on(document.body, 'mousemove', (e) => { $.on(document.body, 'mousemove', (e) => {
if (!isDragging) return; if (!isDragging) return;
const finalWidth = startWidth + (e.pageX - startX); const finalWidth = startWidth + (e.pageX - startX);
const { colIndex } = $.data($resizingCell); const {
colIndex
} = $.data($resizingCell);
if (this.getColumnMinWidth(colIndex) > finalWidth) { if (this.getColumnMinWidth(colIndex) > finalWidth) {
// don't resize past minWidth // don't resize past minWidth
return; return;
} }
this.datamanager.updateColumn(colIndex, { width: finalWidth }); this.datamanager.updateColumn(colIndex, {
width: finalWidth
});
this.setColumnHeaderWidth(colIndex); this.setColumnHeaderWidth(colIndex);
}); });
} }
@ -1516,9 +1558,14 @@ class ColumnManager {
this.sortable = Sortable.create($parent, { this.sortable = Sortable.create($parent, {
onEnd: (e) => { onEnd: (e) => {
const { oldIndex, newIndex } = e; const {
oldIndex,
newIndex
} = e;
const $draggedCell = e.item; const $draggedCell = e.item;
const { colIndex } = $.data($draggedCell); const {
colIndex
} = $.data($draggedCell);
if (+colIndex === newIndex) return; if (+colIndex === newIndex) return;
this.switchColumn(oldIndex, newIndex); this.switchColumn(oldIndex, newIndex);
@ -1536,7 +1583,10 @@ class ColumnManager {
$.on(this.header, 'click', '.data-table-col .column-title', (e, span) => { $.on(this.header, 'click', '.data-table-col .column-title', (e, span) => {
const $cell = span.closest('.data-table-col'); const $cell = span.closest('.data-table-col');
let { colIndex, sortOrder } = $.data($cell); let {
colIndex,
sortOrder
} = $.data($cell);
sortOrder = getDefault(sortOrder, 'none'); sortOrder = getDefault(sortOrder, 'none');
const col = this.getColumn(colIndex); const col = this.getColumn(colIndex);
@ -1647,11 +1697,16 @@ class ColumnManager {
if (!this.options.enableInlineFilters) return; if (!this.options.enableInlineFilters) return;
const handler = e => { const handler = e => {
const $filterCell = $.closest('.data-table-col', e.target); const $filterCell = $.closest('.data-table-col', e.target);
const { colIndex } = $.data($filterCell); const {
colIndex
} = $.data($filterCell);
const keyword = e.target.value; const keyword = e.target.value;
this.datamanager.filterRows(keyword, colIndex) this.datamanager.filterRows(keyword, colIndex)
.then(({ rowsToHide, rowsToShow }) => { .then(({
rowsToHide,
rowsToShow
}) => {
rowsToHide.map(rowIndex => { rowsToHide.map(rowIndex => {
const $tr = $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable); const $tr = $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable);
$tr.classList.add('hide'); $tr.classList.add('hide');
@ -1681,7 +1736,9 @@ class ColumnManager {
colIndex = +colIndex; colIndex = +colIndex;
this._columnWidthMap = this._columnWidthMap || []; this._columnWidthMap = this._columnWidthMap || [];
const { width } = this.getColumn(colIndex); const {
width
} = this.getColumn(colIndex);
let index = this._columnWidthMap[colIndex]; let index = this._columnWidthMap[colIndex];
const selector = `[data-col-index="${colIndex}"] .content, [data-col-index="${colIndex}"] .edit-cell`; const selector = `[data-col-index="${colIndex}"] .content, [data-col-index="${colIndex}"] .edit-cell`;
@ -1697,7 +1754,9 @@ class ColumnManager {
colIndex = +colIndex; colIndex = +colIndex;
this.$columnMap = this.$columnMap || []; this.$columnMap = this.$columnMap || [];
const selector = `.data-table-header [data-col-index="${colIndex}"] .content`; const selector = `.data-table-header [data-col-index="${colIndex}"] .content`;
const { width } = this.getColumn(colIndex); const {
width
} = this.getColumn(colIndex);
let $column = this.$columnMap[colIndex]; let $column = this.$columnMap[colIndex];
if (!$column) { if (!$column) {
@ -1826,7 +1885,10 @@ class CellManager {
} }
let $cell = this.$focusedCell; let $cell = this.$focusedCell;
const { rowIndex, colIndex } = $.data($cell); const {
rowIndex,
colIndex
} = $.data($cell);
if (direction === 'left') { if (direction === 'left') {
$cell = this.getLeftMostCell$(rowIndex); $cell = this.getLeftMostCell$(rowIndex);
@ -1857,7 +1919,9 @@ class CellManager {
if (this.options.enableInlineFilters) { if (this.options.enableInlineFilters) {
this.keyboard.on('ctrl+f', (e) => { this.keyboard.on('ctrl+f', (e) => {
const $cell = $.closest('.data-table-col', e.target); const $cell = $.closest('.data-table-col', e.target);
let { colIndex } = $.data($cell); let {
colIndex
} = $.data($cell);
this.activateFilter(colIndex); this.activateFilter(colIndex);
return true; return true;
@ -1914,13 +1978,18 @@ class CellManager {
$.on(this.bodyScrollable, 'mousemove', '.data-table-col', throttle$1(selectArea, 50)); $.on(this.bodyScrollable, 'mousemove', '.data-table-col', throttle$1(selectArea, 50));
} }
focusCell($cell, { skipClearSelection = 0 } = {}) { focusCell($cell, {
skipClearSelection = 0
} = {}) {
if (!$cell) return; if (!$cell) return;
// don't focus if already editing cell // don't focus if already editing cell
if ($cell === this.$editingCell) return; if ($cell === this.$editingCell) return;
const { colIndex, isHeader } = $.data($cell); const {
colIndex,
isHeader
} = $.data($cell);
if (isHeader) { if (isHeader) {
return; return;
} }
@ -1951,7 +2020,10 @@ class CellManager {
} }
highlightRowColumnHeader($cell) { highlightRowColumnHeader($cell) {
const { colIndex, rowIndex } = $.data($cell); const {
colIndex,
rowIndex
} = $.data($cell);
const _colIndex = this.datamanager.getColumnIndexById('_rowIndex'); const _colIndex = this.datamanager.getColumnIndexById('_rowIndex');
const colHeaderSelector = `.data-table-header .data-table-col[data-col-index="${colIndex}"]`; const colHeaderSelector = `.data-table-header .data-table-col[data-col-index="${colIndex}"]`;
const rowHeaderSelector = `.data-table-col[data-row-index="${rowIndex}"][data-col-index="${_colIndex}"]`; const rowHeaderSelector = `.data-table-col[data-row-index="${rowIndex}"][data-col-index="${_colIndex}"]`;
@ -1972,7 +2044,10 @@ class CellManager {
selectAreaOnClusterChanged() { selectAreaOnClusterChanged() {
if (!(this.$focusedCell && this.$selectionCursor)) return; if (!(this.$focusedCell && this.$selectionCursor)) return;
const { colIndex, rowIndex } = $.data(this.$selectionCursor); const {
colIndex,
rowIndex
} = $.data(this.$selectionCursor);
const $cell = this.getCell$(colIndex, rowIndex); const $cell = this.getCell$(colIndex, rowIndex);
if (!$cell || $cell === this.$selectionCursor) return; if (!$cell || $cell === this.$selectionCursor) return;
@ -1987,14 +2062,19 @@ class CellManager {
focusCellOnClusterChanged() { focusCellOnClusterChanged() {
if (!this.$focusedCell) return; if (!this.$focusedCell) return;
const { colIndex, rowIndex } = $.data(this.$focusedCell); const {
colIndex,
rowIndex
} = $.data(this.$focusedCell);
const $cell = this.getCell$(colIndex, rowIndex); const $cell = this.getCell$(colIndex, rowIndex);
if (!$cell) return; if (!$cell) return;
// this function is called after selectAreaOnClusterChanged, // this function is called after selectAreaOnClusterChanged,
// focusCell calls clearSelection which resets the area selection // focusCell calls clearSelection which resets the area selection
// so a flag to skip it // so a flag to skip it
this.focusCell($cell, { skipClearSelection: 1 }); this.focusCell($cell, {
skipClearSelection: 1
});
} }
selectArea($selectionCursor) { selectArea($selectionCursor) {
@ -2084,7 +2164,10 @@ class CellManager {
activateEditing($cell) { activateEditing($cell) {
this.focusCell($cell); this.focusCell($cell);
const { rowIndex, colIndex } = $.data($cell); const {
rowIndex,
colIndex
} = $.data($cell);
const col = this.columnmanager.getColumn(colIndex); const col = this.columnmanager.getColumn(colIndex);
if (col && (col.editable === false || col.focusable === false)) { if (col && (col.editable === false || col.focusable === false)) {
@ -2097,7 +2180,10 @@ class CellManager {
} }
if (this.$editingCell) { if (this.$editingCell) {
const { _rowIndex, _colIndex } = $.data(this.$editingCell); const {
_rowIndex,
_colIndex
} = $.data(this.$editingCell);
if (rowIndex === _rowIndex && colIndex === _colIndex) { if (rowIndex === _rowIndex && colIndex === _colIndex) {
// editing the same cell // editing the same cell
@ -2158,7 +2244,10 @@ class CellManager {
submitEditing() { submitEditing() {
if (!this.$editingCell) return; if (!this.$editingCell) return;
const $cell = this.$editingCell; const $cell = this.$editingCell;
const { rowIndex, colIndex } = $.data($cell); const {
rowIndex,
colIndex
} = $.data($cell);
const col = this.datamanager.getColumn(colIndex); const col = this.datamanager.getColumn(colIndex);
if ($cell) { if ($cell) {
@ -2189,7 +2278,10 @@ class CellManager {
copyCellContents($cell1, $cell2) { copyCellContents($cell1, $cell2) {
if (!$cell2 && $cell1) { if (!$cell2 && $cell1) {
// copy only focusedCell // copy only focusedCell
const { colIndex, rowIndex } = $.data($cell1); const {
colIndex,
rowIndex
} = $.data($cell1);
const cell = this.getCell(colIndex, rowIndex); const cell = this.getCell(colIndex, rowIndex);
copyTextToClipboard(cell.content); copyTextToClipboard(cell.content);
return; return;
@ -2250,14 +2342,18 @@ class CellManager {
} }
getAboveCell$($cell) { getAboveCell$($cell) {
const { colIndex } = $.data($cell); const {
colIndex
} = $.data($cell);
const $aboveRow = $cell.parentElement.previousElementSibling; const $aboveRow = $cell.parentElement.previousElementSibling;
return $(`[data-col-index="${colIndex}"]`, $aboveRow); return $(`[data-col-index="${colIndex}"]`, $aboveRow);
} }
getBelowCell$($cell) { getBelowCell$($cell) {
const { colIndex } = $.data($cell); const {
colIndex
} = $.data($cell);
const $belowRow = $cell.parentElement.nextElementSibling; const $belowRow = $cell.parentElement.nextElementSibling;
return $(`[data-col-index="${colIndex}"]`, $belowRow); return $(`[data-col-index="${colIndex}"]`, $belowRow);
@ -2302,7 +2398,9 @@ class CellManager {
scrollToCell($cell) { scrollToCell($cell) {
if ($.inViewport($cell, this.bodyScrollable)) return false; if ($.inViewport($cell, this.bodyScrollable)) return false;
const { rowIndex } = $.data($cell); const {
rowIndex
} = $.data($cell);
this.rowmanager.scrollToRow(rowIndex); this.rowmanager.scrollToRow(rowIndex);
return false; return false;
} }
@ -2312,7 +2410,12 @@ class CellManager {
} }
getCellHTML(cell) { getCellHTML(cell) {
const { rowIndex, colIndex, isHeader, isFilter } = cell; const {
rowIndex,
colIndex,
isHeader,
isFilter
} = cell;
const dataAttr = makeDataAttributeString({ const dataAttr = makeDataAttributeString({
rowIndex, rowIndex,
colIndex, colIndex,
@ -2328,7 +2431,9 @@ class CellManager {
} }
getCellContent(cell) { getCellContent(cell) {
const { isHeader } = cell; const {
isHeader
} = cell;
const editable = !isHeader && cell.editable !== false; const editable = !isHeader && cell.editable !== false;
const editCellHTML = editable ? this.getEditCellHTML() : ''; const editCellHTML = editable ? this.getEditCellHTML() : '';
@ -2402,7 +2507,10 @@ class RowManager {
$.on(this.wrapper, 'click', '.data-table-col[data-col-index="0"] [type="checkbox"]', (e, $checkbox) => { $.on(this.wrapper, 'click', '.data-table-col[data-col-index="0"] [type="checkbox"]', (e, $checkbox) => {
const $cell = $checkbox.closest('.data-table-col'); const $cell = $checkbox.closest('.data-table-col');
const { rowIndex, isHeader } = $.data($cell); const {
rowIndex,
isHeader
} = $.data($cell);
const checked = $checkbox.checked; const checked = $checkbox.checked;
if (isHeader) { if (isHeader) {
@ -2450,11 +2558,12 @@ class RowManager {
checkRow(rowIndex, toggle) { checkRow(rowIndex, toggle) {
const value = toggle ? 1 : 0; const value = toggle ? 1 : 0;
const selector = rowIndex =>
`.data-table-col[data-row-index="${rowIndex}"][data-col-index="0"] [type="checkbox"]`;
// update internal map // update internal map
this.checkMap[rowIndex] = value; this.checkMap[rowIndex] = value;
// set checkbox value explicitly // set checkbox value explicitly
$.each(`.data-table-col[data-row-index="${rowIndex}"][data-col-index="0"] [type="checkbox"]`, this.bodyScrollable) $.each(selector(rowIndex), this.bodyScrollable)
.map(input => { .map(input => {
input.checked = toggle; input.checked = toggle;
}); });
@ -2539,8 +2648,13 @@ class RowManager {
const $row = this.getRow$(rowIndex); const $row = this.getRow$(rowIndex);
if ($.inViewport($row, this.bodyScrollable)) return; if ($.inViewport($row, this.bodyScrollable)) return;
const { height } = $row.getBoundingClientRect(); const {
const { top, bottom } = this.bodyScrollable.getBoundingClientRect(); height
} = $row.getBoundingClientRect();
const {
top,
bottom
} = this.bodyScrollable.getBoundingClientRect();
const rowsInView = Math.floor((bottom - top) / height); const rowsInView = Math.floor((bottom - top) / height);
let offset = 0; let offset = 0;
@ -2559,7 +2673,9 @@ class RowManager {
if (props.isFilter) { if (props.isFilter) {
row = row.map(cell => (Object.assign(cell, { row = row.map(cell => (Object.assign(cell, {
content: this.getFilterInput({ colIndex: cell.colIndex }), content: this.getFilterInput({
colIndex: cell.colIndex
}),
isFilter: 1, isFilter: 1,
isHeader: undefined, isHeader: undefined,
editable: false editable: false
@ -2604,7 +2720,7 @@ class BodyRenderer {
this.bodyScrollable.innerHTML = ` this.bodyScrollable.innerHTML = `
<table class="data-table-body"> <table class="data-table-body">
${getBodyHTML(rows)} ${this.getBodyHTML(rows)}
</table> </table>
`; `;
this.instance.setDimensions(); this.instance.setDimensions();
@ -2620,7 +2736,7 @@ class BodyRenderer {
// empty body // empty body
this.bodyScrollable.innerHTML = ` this.bodyScrollable.innerHTML = `
<table class="data-table-body"> <table class="data-table-body">
${getBodyHTML([])} ${this.getBodyHTML([])}
</table> </table>
`; `;
@ -2663,16 +2779,16 @@ class BodyRenderer {
} }
getDataForClusterize(rows) { getDataForClusterize(rows) {
return rows.map((row) => this.rowmanager.getRowHTML(row, { rowIndex: row[0].rowIndex })); return rows.map((row) => this.rowmanager.getRowHTML(row, row.meta));
} }
}
function getBodyHTML(rows) { getBodyHTML(rows) {
return ` return `
<tbody> <tbody>
${rows.map(row => this.rowmanager.getRowHTML(row, { rowIndex: row[0].rowIndex })).join('')} ${rows.map(row => this.rowmanager.getRowHTML(row, row.meta)).join('')}
</tbody> </tbody>
`; `;
}
} }
class Style { class Style {
@ -2709,7 +2825,7 @@ class Style {
this.styleEl.remove(); this.styleEl.remove();
} }
setStyle(rule, styleMap, index = -1) { setStyle(selector, styleMap, index = -1) {
const styles = Object.keys(styleMap) const styles = Object.keys(styleMap)
.map(prop => { .map(prop => {
if (!prop.includes('-')) { if (!prop.includes('-')) {
@ -2718,7 +2834,12 @@ class Style {
return `${prop}:${styleMap[prop]};`; return `${prop}:${styleMap[prop]};`;
}) })
.join(''); .join('');
let ruleString = `.${this.scopeClass} ${rule} { ${styles} }`; let prefixedSelector = selector
.split(',')
.map(r => `.${this.scopeClass} ${r}`)
.join(',');
let ruleString = `${prefixedSelector} { ${styles} }`;
let _index = this.styleSheet.cssRules.length; let _index = this.styleSheet.cssRules.length;
if (index !== -1) { if (index !== -1) {
@ -2799,8 +2920,8 @@ class Style {
// width based on rowCount // width based on rowCount
const rowCount = this.datamanager.getRowCount(); const rowCount = this.datamanager.getRowCount();
const digits = (rowCount + '').length; const digits = (rowCount + '').length;
if (digits > 2) { if (digits > 1) {
naturalWidth = naturalWidth + ((digits - 2) * 8); naturalWidth = naturalWidth + ((digits - 1) * 8);
} }
} }
@ -2840,7 +2961,8 @@ class Style {
setDefaultCellHeight() { setDefaultCellHeight() {
if (this.__cellHeightSet) return; if (this.__cellHeightSet) return;
const height = this.options.cellHeight || $.style($('.data-table-col', this.instance.datatableWrapper), 'height'); const height = this.options.cellHeight ||
$.style($('.data-table-col', this.instance.datatableWrapper), 'height');
if (height) { if (height) {
this.setCellHeight(height); this.setCellHeight(height);
this.__cellHeightSet = true; this.__cellHeightSet = true;
@ -3029,9 +3151,7 @@ class DataTable {
DEFAULT_OPTIONS.headerDropdown DEFAULT_OPTIONS.headerDropdown
.concat(options.headerDropdown || []); .concat(options.headerDropdown || []);
// custom user events // custom user events
this.events = Object.assign( this.events = Object.assign({}, DEFAULT_OPTIONS.events, options.events || {});
{}, DEFAULT_OPTIONS.events, options.events || {}
);
this.fireEvent = this.fireEvent.bind(this); this.fireEvent = this.fireEvent.bind(this);
this.prepare(); this.prepare();

View File

@ -1,3 +1,4 @@
/* This file is processed by postcss */
/* variables */ /* variables */
.data-table { .data-table {

View File

@ -164,8 +164,18 @@ $.closest = (selector, element) => {
}; };
$.inViewport = (el, parentEl) => { $.inViewport = (el, parentEl) => {
const { top, left, bottom, right } = el.getBoundingClientRect(); const {
const { top: pTop, left: pLeft, bottom: pBottom, right: pRight } = parentEl.getBoundingClientRect(); 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;
}; };
@ -911,16 +921,6 @@ class DataManager {
} }
} }
prepareRow(row, i) {
const baseRowCell = {
rowIndex: i
};
return row
.map((cell, i) => this.prepareCell(cell, i))
.map(cell => Object.assign({}, baseRowCell, cell));
}
prepareHeader() { prepareHeader() {
let columns = this.columns.concat(this.options.columns); let columns = this.columns.concat(this.options.columns);
const baseCell = { const baseCell = {
@ -1004,7 +1004,7 @@ class DataManager {
} }
} else { } else {
// row is a dict // row is an object
for (let col of this.columns) { for (let col of this.columns) {
if (col.id === '_checkbox') { if (col.id === '_checkbox') {
row.push(this.getCheckboxHTML()); row.push(this.getCheckboxHTML());
@ -1016,8 +1016,24 @@ class DataManager {
} }
} }
return this.prepareRow(row, index); return this.prepareRow(row, {
rowIndex: index
}); });
});
}
prepareRow(row, props) {
const baseRowCell = {
rowIndex: props.rowIndex
};
row = row
.map((cell, i) => this.prepareCell(cell, i))
.map(cell => Object.assign({}, baseRowCell, cell));
// monkey patched in array object
row.meta = props;
return row;
} }
validateColumns() { validateColumns() {
@ -1100,12 +1116,11 @@ class DataManager {
if (this.hasColumnById('_rowIndex')) { if (this.hasColumnById('_rowIndex')) {
// update row index // update row index
const srNoColIndex = this.getColumnIndexById('_rowIndex'); const srNoColIndex = this.getColumnIndexById('_rowIndex');
this.rows = this.rows.map((row, index) => { this.rows.forEach((row, index) => {
return row.map(cell => { row.forEach(cell => {
if (cell.colIndex === srNoColIndex) { if (cell.colIndex === srNoColIndex) {
cell.content = (index + 1) + ''; cell.content = (index + 1) + '';
} }
return cell;
}); });
}); });
} }
@ -1135,8 +1150,12 @@ class DataManager {
// update rows // update rows
this.rows = this.rows.map(row => { this.rows = this.rows.map(row => {
const newCell1 = Object.assign({}, row[index1], { colIndex: index2 }); const newCell1 = Object.assign({}, row[index1], {
const newCell2 = Object.assign({}, row[index2], { colIndex: index1 }); colIndex: index2
});
const newCell2 = Object.assign({}, row[index2], {
colIndex: index1
});
let newRow = row.map(cell => { let newRow = row.map(cell => {
// make object copy // make object copy
@ -1153,7 +1172,9 @@ class DataManager {
removeColumn(index) { removeColumn(index) {
index = +index; index = +index;
const filter = cell => cell.colIndex !== index; const filter = cell => cell.colIndex !== index;
const map = (cell, i) => Object.assign({}, cell, { colIndex: i }); const map = (cell, i) => Object.assign({}, cell, {
colIndex: i
});
// update columns // update columns
this.columns = this.columns this.columns = this.columns
.filter(filter) .filter(filter)
@ -1242,7 +1263,10 @@ class DataManager {
} }
}); });
return {rowsToHide, rowsToShow}; return {
rowsToHide,
rowsToShow
};
} }
getRowCount() { getRowCount() {
@ -1369,9 +1393,13 @@ class ColumnManager {
if (!$('.data-table-col', this.header)) { if (!$('.data-table-col', this.header)) {
// insert html // insert html
let html = this.rowmanager.getRowHTML(columns, { isHeader: 1 }); let html = this.rowmanager.getRowHTML(columns, {
isHeader: 1
});
if (this.options.enableInlineFilters) { if (this.options.enableInlineFilters) {
html += this.rowmanager.getRowHTML(columns, { isFilter: 1 }); html += this.rowmanager.getRowHTML(columns, {
isFilter: 1
});
} }
$('thead', this.header).innerHTML = html; $('thead', this.header).innerHTML = html;
@ -1389,7 +1417,9 @@ class ColumnManager {
const $cols = $.each('.data-table-col', this.header); const $cols = $.each('.data-table-col', this.header);
if (columns.length < $cols.length) { if (columns.length < $cols.length) {
// deleted column // deleted column
$('thead', this.header).innerHTML = this.rowmanager.getRowHTML(columns, { isHeader: 1 }); $('thead', this.header).innerHTML = this.rowmanager.getRowHTML(columns, {
isHeader: 1
});
return; return;
} }
@ -1442,8 +1472,12 @@ class ColumnManager {
$.on(this.header, 'click', '.data-table-dropdown-list > div', (e, $item) => { $.on(this.header, 'click', '.data-table-dropdown-list > div', (e, $item) => {
const $col = $.closest('.data-table-col', $item); const $col = $.closest('.data-table-col', $item);
const { index } = $.data($item); const {
const { colIndex } = $.data($col); index
} = $.data($item);
const {
colIndex
} = $.data($col);
let callback = dropdownItems[index].action; let callback = dropdownItems[index].action;
callback && callback.call(this.instance, this.getColumn(colIndex)); callback && callback.call(this.instance, this.getColumn(colIndex));
@ -1463,7 +1497,9 @@ class ColumnManager {
document.body.classList.add('data-table-resize'); document.body.classList.add('data-table-resize');
const $cell = $handle.parentNode.parentNode; const $cell = $handle.parentNode.parentNode;
$resizingCell = $cell; $resizingCell = $cell;
const { colIndex } = $.data($resizingCell); const {
colIndex
} = $.data($resizingCell);
const col = this.getColumn(colIndex); const col = this.getColumn(colIndex);
if (col && col.resizable === false) { if (col && col.resizable === false) {
@ -1480,7 +1516,9 @@ class ColumnManager {
if (!$resizingCell) return; if (!$resizingCell) return;
isDragging = false; isDragging = false;
const { colIndex } = $.data($resizingCell); const {
colIndex
} = $.data($resizingCell);
this.setColumnWidth(colIndex); this.setColumnWidth(colIndex);
this.style.setBodyStyle(); this.style.setBodyStyle();
$resizingCell = null; $resizingCell = null;
@ -1489,13 +1527,17 @@ class ColumnManager {
$.on(document.body, 'mousemove', (e) => { $.on(document.body, 'mousemove', (e) => {
if (!isDragging) return; if (!isDragging) return;
const finalWidth = startWidth + (e.pageX - startX); const finalWidth = startWidth + (e.pageX - startX);
const { colIndex } = $.data($resizingCell); const {
colIndex
} = $.data($resizingCell);
if (this.getColumnMinWidth(colIndex) > finalWidth) { if (this.getColumnMinWidth(colIndex) > finalWidth) {
// don't resize past minWidth // don't resize past minWidth
return; return;
} }
this.datamanager.updateColumn(colIndex, { width: finalWidth }); this.datamanager.updateColumn(colIndex, {
width: finalWidth
});
this.setColumnHeaderWidth(colIndex); this.setColumnHeaderWidth(colIndex);
}); });
} }
@ -1515,9 +1557,14 @@ class ColumnManager {
this.sortable = Sortable.create($parent, { this.sortable = Sortable.create($parent, {
onEnd: (e) => { onEnd: (e) => {
const { oldIndex, newIndex } = e; const {
oldIndex,
newIndex
} = e;
const $draggedCell = e.item; const $draggedCell = e.item;
const { colIndex } = $.data($draggedCell); const {
colIndex
} = $.data($draggedCell);
if (+colIndex === newIndex) return; if (+colIndex === newIndex) return;
this.switchColumn(oldIndex, newIndex); this.switchColumn(oldIndex, newIndex);
@ -1535,7 +1582,10 @@ class ColumnManager {
$.on(this.header, 'click', '.data-table-col .column-title', (e, span) => { $.on(this.header, 'click', '.data-table-col .column-title', (e, span) => {
const $cell = span.closest('.data-table-col'); const $cell = span.closest('.data-table-col');
let { colIndex, sortOrder } = $.data($cell); let {
colIndex,
sortOrder
} = $.data($cell);
sortOrder = getDefault(sortOrder, 'none'); sortOrder = getDefault(sortOrder, 'none');
const col = this.getColumn(colIndex); const col = this.getColumn(colIndex);
@ -1646,11 +1696,16 @@ class ColumnManager {
if (!this.options.enableInlineFilters) return; if (!this.options.enableInlineFilters) return;
const handler = e => { const handler = e => {
const $filterCell = $.closest('.data-table-col', e.target); const $filterCell = $.closest('.data-table-col', e.target);
const { colIndex } = $.data($filterCell); const {
colIndex
} = $.data($filterCell);
const keyword = e.target.value; const keyword = e.target.value;
this.datamanager.filterRows(keyword, colIndex) this.datamanager.filterRows(keyword, colIndex)
.then(({ rowsToHide, rowsToShow }) => { .then(({
rowsToHide,
rowsToShow
}) => {
rowsToHide.map(rowIndex => { rowsToHide.map(rowIndex => {
const $tr = $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable); const $tr = $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable);
$tr.classList.add('hide'); $tr.classList.add('hide');
@ -1680,7 +1735,9 @@ class ColumnManager {
colIndex = +colIndex; colIndex = +colIndex;
this._columnWidthMap = this._columnWidthMap || []; this._columnWidthMap = this._columnWidthMap || [];
const { width } = this.getColumn(colIndex); const {
width
} = this.getColumn(colIndex);
let index = this._columnWidthMap[colIndex]; let index = this._columnWidthMap[colIndex];
const selector = `[data-col-index="${colIndex}"] .content, [data-col-index="${colIndex}"] .edit-cell`; const selector = `[data-col-index="${colIndex}"] .content, [data-col-index="${colIndex}"] .edit-cell`;
@ -1696,7 +1753,9 @@ class ColumnManager {
colIndex = +colIndex; colIndex = +colIndex;
this.$columnMap = this.$columnMap || []; this.$columnMap = this.$columnMap || [];
const selector = `.data-table-header [data-col-index="${colIndex}"] .content`; const selector = `.data-table-header [data-col-index="${colIndex}"] .content`;
const { width } = this.getColumn(colIndex); const {
width
} = this.getColumn(colIndex);
let $column = this.$columnMap[colIndex]; let $column = this.$columnMap[colIndex];
if (!$column) { if (!$column) {
@ -1825,7 +1884,10 @@ class CellManager {
} }
let $cell = this.$focusedCell; let $cell = this.$focusedCell;
const { rowIndex, colIndex } = $.data($cell); const {
rowIndex,
colIndex
} = $.data($cell);
if (direction === 'left') { if (direction === 'left') {
$cell = this.getLeftMostCell$(rowIndex); $cell = this.getLeftMostCell$(rowIndex);
@ -1856,7 +1918,9 @@ class CellManager {
if (this.options.enableInlineFilters) { if (this.options.enableInlineFilters) {
this.keyboard.on('ctrl+f', (e) => { this.keyboard.on('ctrl+f', (e) => {
const $cell = $.closest('.data-table-col', e.target); const $cell = $.closest('.data-table-col', e.target);
let { colIndex } = $.data($cell); let {
colIndex
} = $.data($cell);
this.activateFilter(colIndex); this.activateFilter(colIndex);
return true; return true;
@ -1913,13 +1977,18 @@ class CellManager {
$.on(this.bodyScrollable, 'mousemove', '.data-table-col', throttle$1(selectArea, 50)); $.on(this.bodyScrollable, 'mousemove', '.data-table-col', throttle$1(selectArea, 50));
} }
focusCell($cell, { skipClearSelection = 0 } = {}) { focusCell($cell, {
skipClearSelection = 0
} = {}) {
if (!$cell) return; if (!$cell) return;
// don't focus if already editing cell // don't focus if already editing cell
if ($cell === this.$editingCell) return; if ($cell === this.$editingCell) return;
const { colIndex, isHeader } = $.data($cell); const {
colIndex,
isHeader
} = $.data($cell);
if (isHeader) { if (isHeader) {
return; return;
} }
@ -1950,7 +2019,10 @@ class CellManager {
} }
highlightRowColumnHeader($cell) { highlightRowColumnHeader($cell) {
const { colIndex, rowIndex } = $.data($cell); const {
colIndex,
rowIndex
} = $.data($cell);
const _colIndex = this.datamanager.getColumnIndexById('_rowIndex'); const _colIndex = this.datamanager.getColumnIndexById('_rowIndex');
const colHeaderSelector = `.data-table-header .data-table-col[data-col-index="${colIndex}"]`; const colHeaderSelector = `.data-table-header .data-table-col[data-col-index="${colIndex}"]`;
const rowHeaderSelector = `.data-table-col[data-row-index="${rowIndex}"][data-col-index="${_colIndex}"]`; const rowHeaderSelector = `.data-table-col[data-row-index="${rowIndex}"][data-col-index="${_colIndex}"]`;
@ -1971,7 +2043,10 @@ class CellManager {
selectAreaOnClusterChanged() { selectAreaOnClusterChanged() {
if (!(this.$focusedCell && this.$selectionCursor)) return; if (!(this.$focusedCell && this.$selectionCursor)) return;
const { colIndex, rowIndex } = $.data(this.$selectionCursor); const {
colIndex,
rowIndex
} = $.data(this.$selectionCursor);
const $cell = this.getCell$(colIndex, rowIndex); const $cell = this.getCell$(colIndex, rowIndex);
if (!$cell || $cell === this.$selectionCursor) return; if (!$cell || $cell === this.$selectionCursor) return;
@ -1986,14 +2061,19 @@ class CellManager {
focusCellOnClusterChanged() { focusCellOnClusterChanged() {
if (!this.$focusedCell) return; if (!this.$focusedCell) return;
const { colIndex, rowIndex } = $.data(this.$focusedCell); const {
colIndex,
rowIndex
} = $.data(this.$focusedCell);
const $cell = this.getCell$(colIndex, rowIndex); const $cell = this.getCell$(colIndex, rowIndex);
if (!$cell) return; if (!$cell) return;
// this function is called after selectAreaOnClusterChanged, // this function is called after selectAreaOnClusterChanged,
// focusCell calls clearSelection which resets the area selection // focusCell calls clearSelection which resets the area selection
// so a flag to skip it // so a flag to skip it
this.focusCell($cell, { skipClearSelection: 1 }); this.focusCell($cell, {
skipClearSelection: 1
});
} }
selectArea($selectionCursor) { selectArea($selectionCursor) {
@ -2083,7 +2163,10 @@ class CellManager {
activateEditing($cell) { activateEditing($cell) {
this.focusCell($cell); this.focusCell($cell);
const { rowIndex, colIndex } = $.data($cell); const {
rowIndex,
colIndex
} = $.data($cell);
const col = this.columnmanager.getColumn(colIndex); const col = this.columnmanager.getColumn(colIndex);
if (col && (col.editable === false || col.focusable === false)) { if (col && (col.editable === false || col.focusable === false)) {
@ -2096,7 +2179,10 @@ class CellManager {
} }
if (this.$editingCell) { if (this.$editingCell) {
const { _rowIndex, _colIndex } = $.data(this.$editingCell); const {
_rowIndex,
_colIndex
} = $.data(this.$editingCell);
if (rowIndex === _rowIndex && colIndex === _colIndex) { if (rowIndex === _rowIndex && colIndex === _colIndex) {
// editing the same cell // editing the same cell
@ -2157,7 +2243,10 @@ class CellManager {
submitEditing() { submitEditing() {
if (!this.$editingCell) return; if (!this.$editingCell) return;
const $cell = this.$editingCell; const $cell = this.$editingCell;
const { rowIndex, colIndex } = $.data($cell); const {
rowIndex,
colIndex
} = $.data($cell);
const col = this.datamanager.getColumn(colIndex); const col = this.datamanager.getColumn(colIndex);
if ($cell) { if ($cell) {
@ -2188,7 +2277,10 @@ class CellManager {
copyCellContents($cell1, $cell2) { copyCellContents($cell1, $cell2) {
if (!$cell2 && $cell1) { if (!$cell2 && $cell1) {
// copy only focusedCell // copy only focusedCell
const { colIndex, rowIndex } = $.data($cell1); const {
colIndex,
rowIndex
} = $.data($cell1);
const cell = this.getCell(colIndex, rowIndex); const cell = this.getCell(colIndex, rowIndex);
copyTextToClipboard(cell.content); copyTextToClipboard(cell.content);
return; return;
@ -2249,14 +2341,18 @@ class CellManager {
} }
getAboveCell$($cell) { getAboveCell$($cell) {
const { colIndex } = $.data($cell); const {
colIndex
} = $.data($cell);
const $aboveRow = $cell.parentElement.previousElementSibling; const $aboveRow = $cell.parentElement.previousElementSibling;
return $(`[data-col-index="${colIndex}"]`, $aboveRow); return $(`[data-col-index="${colIndex}"]`, $aboveRow);
} }
getBelowCell$($cell) { getBelowCell$($cell) {
const { colIndex } = $.data($cell); const {
colIndex
} = $.data($cell);
const $belowRow = $cell.parentElement.nextElementSibling; const $belowRow = $cell.parentElement.nextElementSibling;
return $(`[data-col-index="${colIndex}"]`, $belowRow); return $(`[data-col-index="${colIndex}"]`, $belowRow);
@ -2301,7 +2397,9 @@ class CellManager {
scrollToCell($cell) { scrollToCell($cell) {
if ($.inViewport($cell, this.bodyScrollable)) return false; if ($.inViewport($cell, this.bodyScrollable)) return false;
const { rowIndex } = $.data($cell); const {
rowIndex
} = $.data($cell);
this.rowmanager.scrollToRow(rowIndex); this.rowmanager.scrollToRow(rowIndex);
return false; return false;
} }
@ -2311,7 +2409,12 @@ class CellManager {
} }
getCellHTML(cell) { getCellHTML(cell) {
const { rowIndex, colIndex, isHeader, isFilter } = cell; const {
rowIndex,
colIndex,
isHeader,
isFilter
} = cell;
const dataAttr = makeDataAttributeString({ const dataAttr = makeDataAttributeString({
rowIndex, rowIndex,
colIndex, colIndex,
@ -2327,7 +2430,9 @@ class CellManager {
} }
getCellContent(cell) { getCellContent(cell) {
const { isHeader } = cell; const {
isHeader
} = cell;
const editable = !isHeader && cell.editable !== false; const editable = !isHeader && cell.editable !== false;
const editCellHTML = editable ? this.getEditCellHTML() : ''; const editCellHTML = editable ? this.getEditCellHTML() : '';
@ -2401,7 +2506,10 @@ class RowManager {
$.on(this.wrapper, 'click', '.data-table-col[data-col-index="0"] [type="checkbox"]', (e, $checkbox) => { $.on(this.wrapper, 'click', '.data-table-col[data-col-index="0"] [type="checkbox"]', (e, $checkbox) => {
const $cell = $checkbox.closest('.data-table-col'); const $cell = $checkbox.closest('.data-table-col');
const { rowIndex, isHeader } = $.data($cell); const {
rowIndex,
isHeader
} = $.data($cell);
const checked = $checkbox.checked; const checked = $checkbox.checked;
if (isHeader) { if (isHeader) {
@ -2449,11 +2557,12 @@ class RowManager {
checkRow(rowIndex, toggle) { checkRow(rowIndex, toggle) {
const value = toggle ? 1 : 0; const value = toggle ? 1 : 0;
const selector = rowIndex =>
`.data-table-col[data-row-index="${rowIndex}"][data-col-index="0"] [type="checkbox"]`;
// update internal map // update internal map
this.checkMap[rowIndex] = value; this.checkMap[rowIndex] = value;
// set checkbox value explicitly // set checkbox value explicitly
$.each(`.data-table-col[data-row-index="${rowIndex}"][data-col-index="0"] [type="checkbox"]`, this.bodyScrollable) $.each(selector(rowIndex), this.bodyScrollable)
.map(input => { .map(input => {
input.checked = toggle; input.checked = toggle;
}); });
@ -2538,8 +2647,13 @@ class RowManager {
const $row = this.getRow$(rowIndex); const $row = this.getRow$(rowIndex);
if ($.inViewport($row, this.bodyScrollable)) return; if ($.inViewport($row, this.bodyScrollable)) return;
const { height } = $row.getBoundingClientRect(); const {
const { top, bottom } = this.bodyScrollable.getBoundingClientRect(); height
} = $row.getBoundingClientRect();
const {
top,
bottom
} = this.bodyScrollable.getBoundingClientRect();
const rowsInView = Math.floor((bottom - top) / height); const rowsInView = Math.floor((bottom - top) / height);
let offset = 0; let offset = 0;
@ -2558,7 +2672,9 @@ class RowManager {
if (props.isFilter) { if (props.isFilter) {
row = row.map(cell => (Object.assign(cell, { row = row.map(cell => (Object.assign(cell, {
content: this.getFilterInput({ colIndex: cell.colIndex }), content: this.getFilterInput({
colIndex: cell.colIndex
}),
isFilter: 1, isFilter: 1,
isHeader: undefined, isHeader: undefined,
editable: false editable: false
@ -2603,7 +2719,7 @@ class BodyRenderer {
this.bodyScrollable.innerHTML = ` this.bodyScrollable.innerHTML = `
<table class="data-table-body"> <table class="data-table-body">
${getBodyHTML(rows)} ${this.getBodyHTML(rows)}
</table> </table>
`; `;
this.instance.setDimensions(); this.instance.setDimensions();
@ -2619,7 +2735,7 @@ class BodyRenderer {
// empty body // empty body
this.bodyScrollable.innerHTML = ` this.bodyScrollable.innerHTML = `
<table class="data-table-body"> <table class="data-table-body">
${getBodyHTML([])} ${this.getBodyHTML([])}
</table> </table>
`; `;
@ -2662,16 +2778,16 @@ class BodyRenderer {
} }
getDataForClusterize(rows) { getDataForClusterize(rows) {
return rows.map((row) => this.rowmanager.getRowHTML(row, { rowIndex: row[0].rowIndex })); return rows.map((row) => this.rowmanager.getRowHTML(row, row.meta));
} }
}
function getBodyHTML(rows) { getBodyHTML(rows) {
return ` return `
<tbody> <tbody>
${rows.map(row => this.rowmanager.getRowHTML(row, { rowIndex: row[0].rowIndex })).join('')} ${rows.map(row => this.rowmanager.getRowHTML(row, row.meta)).join('')}
</tbody> </tbody>
`; `;
}
} }
class Style { class Style {
@ -2708,7 +2824,7 @@ class Style {
this.styleEl.remove(); this.styleEl.remove();
} }
setStyle(rule, styleMap, index = -1) { setStyle(selector, styleMap, index = -1) {
const styles = Object.keys(styleMap) const styles = Object.keys(styleMap)
.map(prop => { .map(prop => {
if (!prop.includes('-')) { if (!prop.includes('-')) {
@ -2717,7 +2833,12 @@ class Style {
return `${prop}:${styleMap[prop]};`; return `${prop}:${styleMap[prop]};`;
}) })
.join(''); .join('');
let ruleString = `.${this.scopeClass} ${rule} { ${styles} }`; let prefixedSelector = selector
.split(',')
.map(r => `.${this.scopeClass} ${r}`)
.join(',');
let ruleString = `${prefixedSelector} { ${styles} }`;
let _index = this.styleSheet.cssRules.length; let _index = this.styleSheet.cssRules.length;
if (index !== -1) { if (index !== -1) {
@ -2798,8 +2919,8 @@ class Style {
// width based on rowCount // width based on rowCount
const rowCount = this.datamanager.getRowCount(); const rowCount = this.datamanager.getRowCount();
const digits = (rowCount + '').length; const digits = (rowCount + '').length;
if (digits > 2) { if (digits > 1) {
naturalWidth = naturalWidth + ((digits - 2) * 8); naturalWidth = naturalWidth + ((digits - 1) * 8);
} }
} }
@ -2839,7 +2960,8 @@ class Style {
setDefaultCellHeight() { setDefaultCellHeight() {
if (this.__cellHeightSet) return; if (this.__cellHeightSet) return;
const height = this.options.cellHeight || $.style($('.data-table-col', this.instance.datatableWrapper), 'height'); const height = this.options.cellHeight ||
$.style($('.data-table-col', this.instance.datatableWrapper), 'height');
if (height) { if (height) {
this.setCellHeight(height); this.setCellHeight(height);
this.__cellHeightSet = true; this.__cellHeightSet = true;
@ -3028,9 +3150,7 @@ class DataTable {
DEFAULT_OPTIONS.headerDropdown DEFAULT_OPTIONS.headerDropdown
.concat(options.headerDropdown || []); .concat(options.headerDropdown || []);
// custom user events // custom user events
this.events = Object.assign( this.events = Object.assign({}, DEFAULT_OPTIONS.events, options.events || {});
{}, DEFAULT_OPTIONS.events, options.events || {}
);
this.fireEvent = this.fireEvent.bind(this); this.fireEvent = this.fireEvent.bind(this);
this.prepare(); this.prepare();

255
docs/frappe-datatable.css Normal file
View 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

File diff suppressed because it is too large Load Diff

40
docs/index.css Normal file
View 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
View 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
View 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'
}]
};
}

View File

@ -9,7 +9,7 @@ import cssnext from 'postcss-cssnext';
const dev = { const dev = {
input: 'src/index.js', input: 'src/index.js',
output: { output: [{
file: 'dist/frappe-datatable.js', file: 'dist/frappe-datatable.js',
format: 'iife', format: 'iife',
name: 'DataTable', name: 'DataTable',
@ -17,13 +17,21 @@ const dev = {
sortablejs: 'Sortable', sortablejs: 'Sortable',
'clusterize.js': 'Clusterize' 'clusterize.js': 'Clusterize'
} }
}, }, {
file: 'docs/frappe-datatable.js',
format: 'iife',
name: 'DataTable',
globals: {
sortablejs: 'Sortable',
'clusterize.js': 'Clusterize'
}
}],
plugins: [ plugins: [
json(), json(),
nodeResolve(), nodeResolve(),
commonjs(), commonjs(),
postcss({ postcss({
extract: 'dist/frappe-datatable.css', extract: ['dist/frappe-datatable.css', 'docs/frappe-datatable.css'],
plugins: [ plugins: [
nested(), nested(),
cssnext() cssnext()
@ -33,9 +41,12 @@ const dev = {
external: ['sortablejs', 'clusterize.js'] external: ['sortablejs', 'clusterize.js']
}; };
export default [dev, Object.assign({}, dev, { export default [
dev,
Object.assign({}, dev, {
output: { output: {
format: 'cjs', format: 'cjs',
file: 'dist/frappe-datatable.cjs.js' file: 'dist/frappe-datatable.cjs.js'
} }
})]; })
];

View File

@ -1,6 +1,8 @@
import $ from './dom'; import $ from './dom';
import Clusterize from 'clusterize.js'; import Clusterize from 'clusterize.js';
import { promisify } from './utils'; import {
promisify
} from './utils';
export default class BodyRenderer { export default class BodyRenderer {
constructor(instance) { constructor(instance) {
@ -27,7 +29,7 @@ export default class BodyRenderer {
this.bodyScrollable.innerHTML = ` this.bodyScrollable.innerHTML = `
<table class="data-table-body"> <table class="data-table-body">
${getBodyHTML(rows)} ${this.getBodyHTML(rows)}
</table> </table>
`; `;
this.instance.setDimensions(); this.instance.setDimensions();
@ -43,7 +45,7 @@ export default class BodyRenderer {
// empty body // empty body
this.bodyScrollable.innerHTML = ` this.bodyScrollable.innerHTML = `
<table class="data-table-body"> <table class="data-table-body">
${getBodyHTML([])} ${this.getBodyHTML([])}
</table> </table>
`; `;
@ -86,14 +88,14 @@ export default class BodyRenderer {
} }
getDataForClusterize(rows) { getDataForClusterize(rows) {
return rows.map((row) => this.rowmanager.getRowHTML(row, { rowIndex: row[0].rowIndex })); return rows.map((row) => this.rowmanager.getRowHTML(row, row.meta));
} }
};
export function getBodyHTML(rows) { getBodyHTML(rows) {
return ` return `
<tbody> <tbody>
${rows.map(row => this.rowmanager.getRowHTML(row, { rowIndex: row[0].rowIndex })).join('')} ${rows.map(row => this.rowmanager.getRowHTML(row, row.meta)).join('')}
</tbody> </tbody>
`; `;
} }
};

View File

@ -4,7 +4,9 @@ import {
throttle throttle
} from './utils'; } from './utils';
import $ from './dom'; import $ from './dom';
import { getDropdownHTML } from './columnmanager'; import {
getDropdownHTML
} from './columnmanager';
export default class CellManager { export default class CellManager {
constructor(instance) { constructor(instance) {
@ -80,7 +82,10 @@ export default class CellManager {
} }
let $cell = this.$focusedCell; let $cell = this.$focusedCell;
const { rowIndex, colIndex } = $.data($cell); const {
rowIndex,
colIndex
} = $.data($cell);
if (direction === 'left') { if (direction === 'left') {
$cell = this.getLeftMostCell$(rowIndex); $cell = this.getLeftMostCell$(rowIndex);
@ -111,7 +116,9 @@ export default class CellManager {
if (this.options.enableInlineFilters) { if (this.options.enableInlineFilters) {
this.keyboard.on('ctrl+f', (e) => { this.keyboard.on('ctrl+f', (e) => {
const $cell = $.closest('.data-table-col', e.target); const $cell = $.closest('.data-table-col', e.target);
let { colIndex } = $.data($cell); let {
colIndex
} = $.data($cell);
this.activateFilter(colIndex); this.activateFilter(colIndex);
return true; return true;
@ -168,13 +175,18 @@ export default class CellManager {
$.on(this.bodyScrollable, 'mousemove', '.data-table-col', throttle(selectArea, 50)); $.on(this.bodyScrollable, 'mousemove', '.data-table-col', throttle(selectArea, 50));
} }
focusCell($cell, { skipClearSelection = 0 } = {}) { focusCell($cell, {
skipClearSelection = 0
} = {}) {
if (!$cell) return; if (!$cell) return;
// don't focus if already editing cell // don't focus if already editing cell
if ($cell === this.$editingCell) return; if ($cell === this.$editingCell) return;
const { colIndex, isHeader } = $.data($cell); const {
colIndex,
isHeader
} = $.data($cell);
if (isHeader) { if (isHeader) {
return; return;
} }
@ -205,7 +217,10 @@ export default class CellManager {
} }
highlightRowColumnHeader($cell) { highlightRowColumnHeader($cell) {
const { colIndex, rowIndex } = $.data($cell); const {
colIndex,
rowIndex
} = $.data($cell);
const _colIndex = this.datamanager.getColumnIndexById('_rowIndex'); const _colIndex = this.datamanager.getColumnIndexById('_rowIndex');
const colHeaderSelector = `.data-table-header .data-table-col[data-col-index="${colIndex}"]`; const colHeaderSelector = `.data-table-header .data-table-col[data-col-index="${colIndex}"]`;
const rowHeaderSelector = `.data-table-col[data-row-index="${rowIndex}"][data-col-index="${_colIndex}"]`; const rowHeaderSelector = `.data-table-col[data-row-index="${rowIndex}"][data-col-index="${_colIndex}"]`;
@ -226,7 +241,10 @@ export default class CellManager {
selectAreaOnClusterChanged() { selectAreaOnClusterChanged() {
if (!(this.$focusedCell && this.$selectionCursor)) return; if (!(this.$focusedCell && this.$selectionCursor)) return;
const { colIndex, rowIndex } = $.data(this.$selectionCursor); const {
colIndex,
rowIndex
} = $.data(this.$selectionCursor);
const $cell = this.getCell$(colIndex, rowIndex); const $cell = this.getCell$(colIndex, rowIndex);
if (!$cell || $cell === this.$selectionCursor) return; if (!$cell || $cell === this.$selectionCursor) return;
@ -241,14 +259,19 @@ export default class CellManager {
focusCellOnClusterChanged() { focusCellOnClusterChanged() {
if (!this.$focusedCell) return; if (!this.$focusedCell) return;
const { colIndex, rowIndex } = $.data(this.$focusedCell); const {
colIndex,
rowIndex
} = $.data(this.$focusedCell);
const $cell = this.getCell$(colIndex, rowIndex); const $cell = this.getCell$(colIndex, rowIndex);
if (!$cell) return; if (!$cell) return;
// this function is called after selectAreaOnClusterChanged, // this function is called after selectAreaOnClusterChanged,
// focusCell calls clearSelection which resets the area selection // focusCell calls clearSelection which resets the area selection
// so a flag to skip it // so a flag to skip it
this.focusCell($cell, { skipClearSelection: 1 }); this.focusCell($cell, {
skipClearSelection: 1
});
} }
selectArea($selectionCursor) { selectArea($selectionCursor) {
@ -338,7 +361,10 @@ export default class CellManager {
activateEditing($cell) { activateEditing($cell) {
this.focusCell($cell); this.focusCell($cell);
const { rowIndex, colIndex } = $.data($cell); const {
rowIndex,
colIndex
} = $.data($cell);
const col = this.columnmanager.getColumn(colIndex); const col = this.columnmanager.getColumn(colIndex);
if (col && (col.editable === false || col.focusable === false)) { if (col && (col.editable === false || col.focusable === false)) {
@ -351,7 +377,10 @@ export default class CellManager {
} }
if (this.$editingCell) { if (this.$editingCell) {
const { _rowIndex, _colIndex } = $.data(this.$editingCell); const {
_rowIndex,
_colIndex
} = $.data(this.$editingCell);
if (rowIndex === _rowIndex && colIndex === _colIndex) { if (rowIndex === _rowIndex && colIndex === _colIndex) {
// editing the same cell // editing the same cell
@ -412,7 +441,10 @@ export default class CellManager {
submitEditing() { submitEditing() {
if (!this.$editingCell) return; if (!this.$editingCell) return;
const $cell = this.$editingCell; const $cell = this.$editingCell;
const { rowIndex, colIndex } = $.data($cell); const {
rowIndex,
colIndex
} = $.data($cell);
const col = this.datamanager.getColumn(colIndex); const col = this.datamanager.getColumn(colIndex);
if ($cell) { if ($cell) {
@ -443,7 +475,10 @@ export default class CellManager {
copyCellContents($cell1, $cell2) { copyCellContents($cell1, $cell2) {
if (!$cell2 && $cell1) { if (!$cell2 && $cell1) {
// copy only focusedCell // copy only focusedCell
const { colIndex, rowIndex } = $.data($cell1); const {
colIndex,
rowIndex
} = $.data($cell1);
const cell = this.getCell(colIndex, rowIndex); const cell = this.getCell(colIndex, rowIndex);
copyTextToClipboard(cell.content); copyTextToClipboard(cell.content);
return; return;
@ -504,14 +539,18 @@ export default class CellManager {
} }
getAboveCell$($cell) { getAboveCell$($cell) {
const { colIndex } = $.data($cell); const {
colIndex
} = $.data($cell);
const $aboveRow = $cell.parentElement.previousElementSibling; const $aboveRow = $cell.parentElement.previousElementSibling;
return $(`[data-col-index="${colIndex}"]`, $aboveRow); return $(`[data-col-index="${colIndex}"]`, $aboveRow);
} }
getBelowCell$($cell) { getBelowCell$($cell) {
const { colIndex } = $.data($cell); const {
colIndex
} = $.data($cell);
const $belowRow = $cell.parentElement.nextElementSibling; const $belowRow = $cell.parentElement.nextElementSibling;
return $(`[data-col-index="${colIndex}"]`, $belowRow); return $(`[data-col-index="${colIndex}"]`, $belowRow);
@ -556,7 +595,9 @@ export default class CellManager {
scrollToCell($cell) { scrollToCell($cell) {
if ($.inViewport($cell, this.bodyScrollable)) return false; if ($.inViewport($cell, this.bodyScrollable)) return false;
const { rowIndex } = $.data($cell); const {
rowIndex
} = $.data($cell);
this.rowmanager.scrollToRow(rowIndex); this.rowmanager.scrollToRow(rowIndex);
return false; return false;
} }
@ -566,7 +607,12 @@ export default class CellManager {
} }
getCellHTML(cell) { getCellHTML(cell) {
const { rowIndex, colIndex, isHeader, isFilter } = cell; const {
rowIndex,
colIndex,
isHeader,
isFilter
} = cell;
const dataAttr = makeDataAttributeString({ const dataAttr = makeDataAttributeString({
rowIndex, rowIndex,
colIndex, colIndex,
@ -582,7 +628,9 @@ export default class CellManager {
} }
getCellContent(cell) { getCellContent(cell) {
const { isHeader } = cell; const {
isHeader
} = cell;
const editable = !isHeader && cell.editable !== false; const editable = !isHeader && cell.editable !== false;
const editCellHTML = editable ? this.getEditCellHTML() : ''; const editCellHTML = editable ? this.getEditCellHTML() : '';

View File

@ -1,6 +1,10 @@
import $ from './dom'; import $ from './dom';
import Sortable from 'sortablejs'; import Sortable from 'sortablejs';
import { getDefault, linkProperties, debounce } from './utils'; import {
getDefault,
linkProperties,
debounce
} from './utils';
export default class ColumnManager { export default class ColumnManager {
constructor(instance) { constructor(instance) {
@ -32,9 +36,13 @@ export default class ColumnManager {
if (!$('.data-table-col', this.header)) { if (!$('.data-table-col', this.header)) {
// insert html // insert html
let html = this.rowmanager.getRowHTML(columns, { isHeader: 1 }); let html = this.rowmanager.getRowHTML(columns, {
isHeader: 1
});
if (this.options.enableInlineFilters) { if (this.options.enableInlineFilters) {
html += this.rowmanager.getRowHTML(columns, { isFilter: 1 }); html += this.rowmanager.getRowHTML(columns, {
isFilter: 1
});
} }
$('thead', this.header).innerHTML = html; $('thead', this.header).innerHTML = html;
@ -52,7 +60,9 @@ export default class ColumnManager {
const $cols = $.each('.data-table-col', this.header); const $cols = $.each('.data-table-col', this.header);
if (columns.length < $cols.length) { if (columns.length < $cols.length) {
// deleted column // deleted column
$('thead', this.header).innerHTML = this.rowmanager.getRowHTML(columns, { isHeader: 1 }); $('thead', this.header).innerHTML = this.rowmanager.getRowHTML(columns, {
isHeader: 1
});
return; return;
} }
@ -105,8 +115,12 @@ export default class ColumnManager {
$.on(this.header, 'click', '.data-table-dropdown-list > div', (e, $item) => { $.on(this.header, 'click', '.data-table-dropdown-list > div', (e, $item) => {
const $col = $.closest('.data-table-col', $item); const $col = $.closest('.data-table-col', $item);
const { index } = $.data($item); const {
const { colIndex } = $.data($col); index
} = $.data($item);
const {
colIndex
} = $.data($col);
let callback = dropdownItems[index].action; let callback = dropdownItems[index].action;
callback && callback.call(this.instance, this.getColumn(colIndex)); callback && callback.call(this.instance, this.getColumn(colIndex));
@ -126,7 +140,9 @@ export default class ColumnManager {
document.body.classList.add('data-table-resize'); document.body.classList.add('data-table-resize');
const $cell = $handle.parentNode.parentNode; const $cell = $handle.parentNode.parentNode;
$resizingCell = $cell; $resizingCell = $cell;
const { colIndex } = $.data($resizingCell); const {
colIndex
} = $.data($resizingCell);
const col = this.getColumn(colIndex); const col = this.getColumn(colIndex);
if (col && col.resizable === false) { if (col && col.resizable === false) {
@ -143,7 +159,9 @@ export default class ColumnManager {
if (!$resizingCell) return; if (!$resizingCell) return;
isDragging = false; isDragging = false;
const { colIndex } = $.data($resizingCell); const {
colIndex
} = $.data($resizingCell);
this.setColumnWidth(colIndex); this.setColumnWidth(colIndex);
this.style.setBodyStyle(); this.style.setBodyStyle();
$resizingCell = null; $resizingCell = null;
@ -152,13 +170,17 @@ export default class ColumnManager {
$.on(document.body, 'mousemove', (e) => { $.on(document.body, 'mousemove', (e) => {
if (!isDragging) return; if (!isDragging) return;
const finalWidth = startWidth + (e.pageX - startX); const finalWidth = startWidth + (e.pageX - startX);
const { colIndex } = $.data($resizingCell); const {
colIndex
} = $.data($resizingCell);
if (this.getColumnMinWidth(colIndex) > finalWidth) { if (this.getColumnMinWidth(colIndex) > finalWidth) {
// don't resize past minWidth // don't resize past minWidth
return; return;
} }
this.datamanager.updateColumn(colIndex, { width: finalWidth }); this.datamanager.updateColumn(colIndex, {
width: finalWidth
});
this.setColumnHeaderWidth(colIndex); this.setColumnHeaderWidth(colIndex);
}); });
} }
@ -178,9 +200,14 @@ export default class ColumnManager {
this.sortable = Sortable.create($parent, { this.sortable = Sortable.create($parent, {
onEnd: (e) => { onEnd: (e) => {
const { oldIndex, newIndex } = e; const {
oldIndex,
newIndex
} = e;
const $draggedCell = e.item; const $draggedCell = e.item;
const { colIndex } = $.data($draggedCell); const {
colIndex
} = $.data($draggedCell);
if (+colIndex === newIndex) return; if (+colIndex === newIndex) return;
this.switchColumn(oldIndex, newIndex); this.switchColumn(oldIndex, newIndex);
@ -198,7 +225,10 @@ export default class ColumnManager {
$.on(this.header, 'click', '.data-table-col .column-title', (e, span) => { $.on(this.header, 'click', '.data-table-col .column-title', (e, span) => {
const $cell = span.closest('.data-table-col'); const $cell = span.closest('.data-table-col');
let { colIndex, sortOrder } = $.data($cell); let {
colIndex,
sortOrder
} = $.data($cell);
sortOrder = getDefault(sortOrder, 'none'); sortOrder = getDefault(sortOrder, 'none');
const col = this.getColumn(colIndex); const col = this.getColumn(colIndex);
@ -309,11 +339,16 @@ export default class ColumnManager {
if (!this.options.enableInlineFilters) return; if (!this.options.enableInlineFilters) return;
const handler = e => { const handler = e => {
const $filterCell = $.closest('.data-table-col', e.target); const $filterCell = $.closest('.data-table-col', e.target);
const { colIndex } = $.data($filterCell); const {
colIndex
} = $.data($filterCell);
const keyword = e.target.value; const keyword = e.target.value;
this.datamanager.filterRows(keyword, colIndex) this.datamanager.filterRows(keyword, colIndex)
.then(({ rowsToHide, rowsToShow }) => { .then(({
rowsToHide,
rowsToShow
}) => {
rowsToHide.map(rowIndex => { rowsToHide.map(rowIndex => {
const $tr = $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable); const $tr = $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable);
$tr.classList.add('hide'); $tr.classList.add('hide');
@ -343,7 +378,9 @@ export default class ColumnManager {
colIndex = +colIndex; colIndex = +colIndex;
this._columnWidthMap = this._columnWidthMap || []; this._columnWidthMap = this._columnWidthMap || [];
const { width } = this.getColumn(colIndex); const {
width
} = this.getColumn(colIndex);
let index = this._columnWidthMap[colIndex]; let index = this._columnWidthMap[colIndex];
const selector = `[data-col-index="${colIndex}"] .content, [data-col-index="${colIndex}"] .edit-cell`; const selector = `[data-col-index="${colIndex}"] .content, [data-col-index="${colIndex}"] .edit-cell`;
@ -359,7 +396,9 @@ export default class ColumnManager {
colIndex = +colIndex; colIndex = +colIndex;
this.$columnMap = this.$columnMap || []; this.$columnMap = this.$columnMap || [];
const selector = `.data-table-header [data-col-index="${colIndex}"] .content`; const selector = `.data-table-header [data-col-index="${colIndex}"] .content`;
const { width } = this.getColumn(colIndex); const {
width
} = this.getColumn(colIndex);
let $column = this.$columnMap[colIndex]; let $column = this.$columnMap[colIndex];
if (!$column) { if (!$column) {

View File

@ -1,4 +1,7 @@
import { isNumeric, promisify } from './utils'; import {
isNumeric,
promisify
} from './utils';
export default class DataManager { export default class DataManager {
constructor(options) { constructor(options) {
@ -72,16 +75,6 @@ export default class DataManager {
} }
} }
prepareRow(row, i) {
const baseRowCell = {
rowIndex: i
};
return row
.map((cell, i) => this.prepareCell(cell, i))
.map(cell => Object.assign({}, baseRowCell, cell));
}
prepareHeader() { prepareHeader() {
let columns = this.columns.concat(this.options.columns); let columns = this.columns.concat(this.options.columns);
const baseCell = { const baseCell = {
@ -165,7 +158,7 @@ export default class DataManager {
} }
} else { } else {
// row is a dict // row is an object
for (let col of this.columns) { for (let col of this.columns) {
if (col.id === '_checkbox') { if (col.id === '_checkbox') {
row.push(this.getCheckboxHTML()); row.push(this.getCheckboxHTML());
@ -177,8 +170,24 @@ export default class DataManager {
} }
} }
return this.prepareRow(row, index); return this.prepareRow(row, {
rowIndex: index
}); });
});
}
prepareRow(row, props) {
const baseRowCell = {
rowIndex: props.rowIndex
};
row = row
.map((cell, i) => this.prepareCell(cell, i))
.map(cell => Object.assign({}, baseRowCell, cell));
// monkey patched in array object
row.meta = props;
return row;
} }
validateColumns() { validateColumns() {
@ -261,12 +270,11 @@ export default class DataManager {
if (this.hasColumnById('_rowIndex')) { if (this.hasColumnById('_rowIndex')) {
// update row index // update row index
const srNoColIndex = this.getColumnIndexById('_rowIndex'); const srNoColIndex = this.getColumnIndexById('_rowIndex');
this.rows = this.rows.map((row, index) => { this.rows.forEach((row, index) => {
return row.map(cell => { row.forEach(cell => {
if (cell.colIndex === srNoColIndex) { if (cell.colIndex === srNoColIndex) {
cell.content = (index + 1) + ''; cell.content = (index + 1) + '';
} }
return cell;
}); });
}); });
} }
@ -296,8 +304,12 @@ export default class DataManager {
// update rows // update rows
this.rows = this.rows.map(row => { this.rows = this.rows.map(row => {
const newCell1 = Object.assign({}, row[index1], { colIndex: index2 }); const newCell1 = Object.assign({}, row[index1], {
const newCell2 = Object.assign({}, row[index2], { colIndex: index1 }); colIndex: index2
});
const newCell2 = Object.assign({}, row[index2], {
colIndex: index1
});
let newRow = row.map(cell => { let newRow = row.map(cell => {
// make object copy // make object copy
@ -314,7 +326,9 @@ export default class DataManager {
removeColumn(index) { removeColumn(index) {
index = +index; index = +index;
const filter = cell => cell.colIndex !== index; const filter = cell => cell.colIndex !== index;
const map = (cell, i) => Object.assign({}, cell, { colIndex: i }); const map = (cell, i) => Object.assign({}, cell, {
colIndex: i
});
// update columns // update columns
this.columns = this.columns this.columns = this.columns
.filter(filter) .filter(filter)
@ -403,7 +417,10 @@ export default class DataManager {
} }
}); });
return {rowsToHide, rowsToShow}; return {
rowsToHide,
rowsToShow
};
} }
getRowCount() { getRowCount() {

View File

@ -27,9 +27,7 @@ class DataTable {
DEFAULT_OPTIONS.headerDropdown DEFAULT_OPTIONS.headerDropdown
.concat(options.headerDropdown || []); .concat(options.headerDropdown || []);
// custom user events // custom user events
this.events = Object.assign( this.events = Object.assign({}, DEFAULT_OPTIONS.events, options.events || {});
{}, DEFAULT_OPTIONS.events, options.events || {}
);
this.fireEvent = this.fireEvent.bind(this); this.fireEvent = this.fireEvent.bind(this);
this.prepare(); this.prepare();

View File

@ -1,4 +1,3 @@
export default function $(expr, con) { export default function $(expr, con) {
return typeof expr === 'string' ? return typeof expr === 'string' ?
(con || document).querySelector(expr) : (con || document).querySelector(expr) :
@ -159,8 +158,18 @@ $.closest = (selector, element) => {
}; };
$.inViewport = (el, parentEl) => { $.inViewport = (el, parentEl) => {
const { top, left, bottom, right } = el.getBoundingClientRect(); const {
const { top: pTop, left: pLeft, bottom: pBottom, right: pRight } = parentEl.getBoundingClientRect(); 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;
}; };

View File

@ -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;

View File

@ -1,5 +1,8 @@
import $ from './dom'; import $ from './dom';
import { makeDataAttributeString, promisify } from './utils'; import {
makeDataAttributeString,
promisify
} from './utils';
export default class RowManager { export default class RowManager {
constructor(instance) { constructor(instance) {
@ -32,7 +35,10 @@ export default class RowManager {
$.on(this.wrapper, 'click', '.data-table-col[data-col-index="0"] [type="checkbox"]', (e, $checkbox) => { $.on(this.wrapper, 'click', '.data-table-col[data-col-index="0"] [type="checkbox"]', (e, $checkbox) => {
const $cell = $checkbox.closest('.data-table-col'); const $cell = $checkbox.closest('.data-table-col');
const { rowIndex, isHeader } = $.data($cell); const {
rowIndex,
isHeader
} = $.data($cell);
const checked = $checkbox.checked; const checked = $checkbox.checked;
if (isHeader) { if (isHeader) {
@ -80,11 +86,12 @@ export default class RowManager {
checkRow(rowIndex, toggle) { checkRow(rowIndex, toggle) {
const value = toggle ? 1 : 0; const value = toggle ? 1 : 0;
const selector = rowIndex =>
`.data-table-col[data-row-index="${rowIndex}"][data-col-index="0"] [type="checkbox"]`;
// update internal map // update internal map
this.checkMap[rowIndex] = value; this.checkMap[rowIndex] = value;
// set checkbox value explicitly // set checkbox value explicitly
$.each(`.data-table-col[data-row-index="${rowIndex}"][data-col-index="0"] [type="checkbox"]`, this.bodyScrollable) $.each(selector(rowIndex), this.bodyScrollable)
.map(input => { .map(input => {
input.checked = toggle; input.checked = toggle;
}); });
@ -169,8 +176,13 @@ export default class RowManager {
const $row = this.getRow$(rowIndex); const $row = this.getRow$(rowIndex);
if ($.inViewport($row, this.bodyScrollable)) return; if ($.inViewport($row, this.bodyScrollable)) return;
const { height } = $row.getBoundingClientRect(); const {
const { top, bottom } = this.bodyScrollable.getBoundingClientRect(); height
} = $row.getBoundingClientRect();
const {
top,
bottom
} = this.bodyScrollable.getBoundingClientRect();
const rowsInView = Math.floor((bottom - top) / height); const rowsInView = Math.floor((bottom - top) / height);
let offset = 0; let offset = 0;
@ -189,7 +201,9 @@ export default class RowManager {
if (props.isFilter) { if (props.isFilter) {
row = row.map(cell => (Object.assign(cell, { row = row.map(cell => (Object.assign(cell, {
content: this.getFilterInput({ colIndex: cell.colIndex }), content: this.getFilterInput({
colIndex: cell.colIndex
}),
isFilter: 1, isFilter: 1,
isHeader: undefined, isHeader: undefined,
editable: false editable: false

View File

@ -1,3 +1,4 @@
/* This file is processed by postcss */
/* variables */ /* variables */
:root { :root {

View File

@ -39,7 +39,7 @@ export default class Style {
this.styleEl.remove(); this.styleEl.remove();
} }
setStyle(rule, styleMap, index = -1) { setStyle(selector, styleMap, index = -1) {
const styles = Object.keys(styleMap) const styles = Object.keys(styleMap)
.map(prop => { .map(prop => {
if (!prop.includes('-')) { if (!prop.includes('-')) {
@ -48,7 +48,12 @@ export default class Style {
return `${prop}:${styleMap[prop]};`; return `${prop}:${styleMap[prop]};`;
}) })
.join(''); .join('');
let ruleString = `.${this.scopeClass} ${rule} { ${styles} }`; let prefixedSelector = selector
.split(',')
.map(r => `.${this.scopeClass} ${r}`)
.join(',');
let ruleString = `${prefixedSelector} { ${styles} }`;
let _index = this.styleSheet.cssRules.length; let _index = this.styleSheet.cssRules.length;
if (index !== -1) { if (index !== -1) {
@ -129,8 +134,8 @@ export default class Style {
// width based on rowCount // width based on rowCount
const rowCount = this.datamanager.getRowCount(); const rowCount = this.datamanager.getRowCount();
const digits = (rowCount + '').length; const digits = (rowCount + '').length;
if (digits > 2) { if (digits > 1) {
naturalWidth = naturalWidth + ((digits - 2) * 8); naturalWidth = naturalWidth + ((digits - 1) * 8);
} }
} }
@ -170,7 +175,8 @@ export default class Style {
setDefaultCellHeight() { setDefaultCellHeight() {
if (this.__cellHeightSet) return; if (this.__cellHeightSet) return;
const height = this.options.cellHeight || $.style($('.data-table-col', this.instance.datatableWrapper), 'height'); const height = this.options.cellHeight ||
$.style($('.data-table-col', this.instance.datatableWrapper), 'height');
if (height) { if (height) {
this.setCellHeight(height); this.setCellHeight(height);
this.__cellHeightSet = true; this.__cellHeightSet = true;