feat: 🎸 Use HyperList instead of Clusterize to render rows
Clusterize failed to load rows if they were above 1000, this may be a problem of datatable itself, since it has a complex DOM structure, but hyperlist works better in it's case.
This commit is contained in:
parent
f790a6729a
commit
51b112687e
@ -13,6 +13,7 @@
|
||||
"cy:open": "cypress open",
|
||||
"cy:run": "cypress run",
|
||||
"test": "start-server-and-test cy:server http://localhost:8989 cy:run",
|
||||
"test-local": "start-server-and-test cy:server http://localhost:8989 cy:open",
|
||||
"travis-deploy-once": "travis-deploy-once",
|
||||
"semantic-release": "semantic-release",
|
||||
"lint": "eslint src",
|
||||
@ -64,7 +65,7 @@
|
||||
},
|
||||
"homepage": "https://frappe.github.io/datatable",
|
||||
"dependencies": {
|
||||
"clusterize.js": "^0.18.0",
|
||||
"hyperlist": "^1.0.0-beta",
|
||||
"lodash": "^4.17.5",
|
||||
"sortablejs": "^1.7.0"
|
||||
},
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import Clusterize from 'clusterize.js';
|
||||
import HyperList from 'hyperlist';
|
||||
import $ from './dom';
|
||||
import { nextTick } from './utils';
|
||||
|
||||
export default class BodyRenderer {
|
||||
constructor(instance) {
|
||||
@ -11,59 +10,45 @@ export default class BodyRenderer {
|
||||
this.cellmanager = instance.cellmanager;
|
||||
this.bodyScrollable = instance.bodyScrollable;
|
||||
this.log = instance.log;
|
||||
this.appendRemainingData = nextTick(this.appendRemainingData, this);
|
||||
}
|
||||
|
||||
renderRows(rows) {
|
||||
let config = {
|
||||
itemHeight: 40,
|
||||
total: rows.length,
|
||||
generate: (index) => {
|
||||
const el = document.createElement('div');
|
||||
const rowHTML = this.rowmanager.getRowHTML(rows[index], rows[index].meta);
|
||||
el.innerHTML = rowHTML;
|
||||
return el.children[0];
|
||||
}
|
||||
};
|
||||
this.hyperlist.refresh($('.dt-body', this.bodyScrollable), config);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.options.clusterize) {
|
||||
this.renderBodyWithClusterize();
|
||||
} else {
|
||||
this.renderBodyHTML();
|
||||
}
|
||||
}
|
||||
|
||||
renderBodyHTML() {
|
||||
const rows = this.datamanager.getRowsForView();
|
||||
|
||||
this.bodyScrollable.innerHTML = this.getBodyHTML(rows);
|
||||
this.instance.setDimensions();
|
||||
this.restoreState();
|
||||
}
|
||||
let config = {
|
||||
itemHeight: 40,
|
||||
total: rows.length,
|
||||
generate: (index) => {
|
||||
const el = document.createElement('div');
|
||||
const rowHTML = this.rowmanager.getRowHTML(rows[index], rows[index].meta);
|
||||
el.innerHTML = rowHTML;
|
||||
return el.children[0];
|
||||
}
|
||||
};
|
||||
|
||||
renderBodyWithClusterize() {
|
||||
// first page
|
||||
const rows = this.datamanager.getRowsForView(0, 20);
|
||||
let initialData = this.getDataForClusterize(rows);
|
||||
|
||||
if (initialData.length === 0) {
|
||||
initialData = [this.getNoDataHTML()];
|
||||
}
|
||||
|
||||
if (!this.clusterize) {
|
||||
// empty body
|
||||
this.bodyScrollable.innerHTML = this.getBodyHTML([]);
|
||||
|
||||
// first 20 rows will appended
|
||||
// rest of them in nextTick
|
||||
this.clusterize = new Clusterize({
|
||||
rows: initialData,
|
||||
scrollElem: this.bodyScrollable,
|
||||
contentElem: $('tbody', this.bodyScrollable),
|
||||
callbacks: {
|
||||
clusterChanged: () => this.restoreState()
|
||||
},
|
||||
/* eslint-disable */
|
||||
show_no_data_row: false,
|
||||
/* eslint-enable */
|
||||
});
|
||||
|
||||
// setDimensions requires atleast 1 row to exist in dom
|
||||
this.instance.setDimensions();
|
||||
if (!this.hyperlist) {
|
||||
this.bodyScrollable.innerHTML = '<div class="dt-body"></div>';
|
||||
this.hyperlist = new HyperList($('.dt-body', this.bodyScrollable), config);
|
||||
} else {
|
||||
this.clusterize.update(initialData);
|
||||
this.renderRows(rows);
|
||||
}
|
||||
|
||||
this.appendRemainingData();
|
||||
// setDimensions requires atleast 1 row to exist in dom
|
||||
this.instance.setDimensions();
|
||||
}
|
||||
|
||||
restoreState() {
|
||||
@ -73,12 +58,6 @@ export default class BodyRenderer {
|
||||
this.cellmanager.focusCellOnClusterChanged();
|
||||
}
|
||||
|
||||
appendRemainingData() {
|
||||
const rows = this.datamanager.getRowsForView(20);
|
||||
const data = this.getDataForClusterize(rows);
|
||||
this.clusterize.append(data);
|
||||
}
|
||||
|
||||
showToastMessage(message, hideAfter) {
|
||||
this.instance.toastMessage.innerHTML = this.getToastMessageHTML(message);
|
||||
|
||||
@ -99,11 +78,11 @@ export default class BodyRenderer {
|
||||
|
||||
getBodyHTML(rows) {
|
||||
return `
|
||||
<table class="dt-body">
|
||||
<tbody>
|
||||
<div class="dt-body">
|
||||
<div>
|
||||
${rows.map(row => this.rowmanager.getRowHTML(row, row.meta)).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@ -749,9 +749,9 @@ export default class CellManager {
|
||||
].join(' ');
|
||||
|
||||
return `
|
||||
<td class="${className}" ${dataAttr} tabindex="0">
|
||||
<div class="${className}" ${dataAttr} tabindex="0">
|
||||
${this.getCellContent(cell)}
|
||||
</td>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@ -17,14 +17,15 @@ export default class ColumnManager {
|
||||
'style',
|
||||
'wrapper',
|
||||
'rowmanager',
|
||||
'bodyScrollable'
|
||||
'bodyScrollable',
|
||||
'bodyRenderer'
|
||||
]);
|
||||
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
renderHeader() {
|
||||
this.header.innerHTML = '<thead></thead>';
|
||||
this.header.innerHTML = '<div></div>';
|
||||
this.refreshHeader();
|
||||
}
|
||||
|
||||
@ -32,7 +33,7 @@ export default class ColumnManager {
|
||||
const columns = this.datamanager.getColumns();
|
||||
|
||||
// refresh html
|
||||
$('thead', this.header).innerHTML = this.getHeaderHTML(columns);
|
||||
$('div', this.header).innerHTML = this.getHeaderHTML(columns);
|
||||
|
||||
this.$filterRow = $('.dt-row-filter', this.header);
|
||||
if (this.$filterRow) {
|
||||
@ -282,11 +283,10 @@ export default class ColumnManager {
|
||||
applyFilter(keyword, colIndex) {
|
||||
this.datamanager.filterRows(keyword, colIndex)
|
||||
.then(({
|
||||
rowsToHide,
|
||||
rowsToShow
|
||||
}) => {
|
||||
this.rowmanager.hideRows(rowsToHide);
|
||||
this.rowmanager.showRows(rowsToShow);
|
||||
const rows = rowsToShow.map(rowIndex => this.datamanager.getRow(rowIndex));
|
||||
this.bodyRenderer.renderRows(rows);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -69,8 +69,8 @@ class DataTable {
|
||||
prepareDom() {
|
||||
this.wrapper.innerHTML = `
|
||||
<div class="datatable">
|
||||
<table class="dt-header">
|
||||
</table>
|
||||
<div class="dt-header">
|
||||
</div>
|
||||
<div class="dt-scrollable">
|
||||
</div>
|
||||
<div class="dt-freeze">
|
||||
|
||||
@ -279,9 +279,9 @@ export default class RowManager {
|
||||
}
|
||||
|
||||
return `
|
||||
<tr class="dt-row dt-row-${rowIdentifier}" ${dataAttr}>
|
||||
<div class="dt-row dt-row-${rowIdentifier}" ${dataAttr}>
|
||||
${row.map(cell => this.cellmanager.getCellHTML(cell)).join('')}
|
||||
</tr>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@
|
||||
}
|
||||
|
||||
.dt-scrollable {
|
||||
max-height: 40vw;
|
||||
height: 40vw;
|
||||
overflow: auto;
|
||||
border-bottom: 1px solid var(--dt-border-color);
|
||||
|
||||
@ -60,6 +60,8 @@
|
||||
}
|
||||
|
||||
.dt-row {
|
||||
display: flex;
|
||||
|
||||
&--highlight .dt-cell {
|
||||
background-color: var(--dt-selection-highlight-color);
|
||||
}
|
||||
@ -75,6 +77,8 @@
|
||||
|
||||
.dt-cell {
|
||||
border: 1px solid var(--dt-border-color);
|
||||
border-bottom: none;
|
||||
border-right: none;
|
||||
position: relative;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
@ -141,6 +145,10 @@
|
||||
background-color: var(--dt-header-cell-bg);
|
||||
}
|
||||
|
||||
&--header:last-child {
|
||||
border-right: 1px solid var(--dt-border-color);
|
||||
}
|
||||
|
||||
&--header &__content {
|
||||
padding-right: var(--dt-spacer-3);
|
||||
font-weight: bold;
|
||||
|
||||
36
src/style.js
36
src/style.js
@ -119,33 +119,16 @@ export default class Style {
|
||||
}
|
||||
|
||||
setDimensions() {
|
||||
this.setHeaderStyle();
|
||||
|
||||
this.setupMinWidth();
|
||||
this.setupNaturalColumnWidth();
|
||||
this.setupColumnWidth();
|
||||
this.distributeRemainingWidth();
|
||||
this.setColumnStyle();
|
||||
this.compensateScrollbarWidth();
|
||||
|
||||
this.setDefaultCellHeight();
|
||||
this.setBodyStyle();
|
||||
}
|
||||
|
||||
setHeaderStyle() {
|
||||
if (this.options.layout === 'fluid') {
|
||||
// setting width as 0 will ensure that the
|
||||
// header doesn't take the available space
|
||||
$.style(this.header, {
|
||||
width: 0
|
||||
});
|
||||
}
|
||||
|
||||
$.style(this.header, {
|
||||
margin: 0
|
||||
});
|
||||
}
|
||||
|
||||
setupMinWidth() {
|
||||
$.each('.dt-cell--header', this.header).map(col => {
|
||||
const { colIndex } = $.data(col);
|
||||
@ -228,7 +211,7 @@ export default class Style {
|
||||
}
|
||||
|
||||
compensateScrollbarWidth() {
|
||||
if (!$.hasVerticalOverflow(this.bodyScrollable)) return;
|
||||
if (!$.hasVerticalOverflow($('.dt-body', this.bodyScrollable))) return;
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const scrollbarWidth = $.scrollbarWidth();
|
||||
@ -313,26 +296,9 @@ export default class Style {
|
||||
width: width + 'px'
|
||||
});
|
||||
|
||||
const $body = $('.dt-body', this.bodyScrollable);
|
||||
|
||||
if ($body) {
|
||||
$.style($body, {
|
||||
height: '0px'
|
||||
});
|
||||
|
||||
$.style($('tbody', $body), {
|
||||
height: '100%'
|
||||
});
|
||||
}
|
||||
|
||||
$.style(this.bodyScrollable, {
|
||||
marginTop: $.style(this.header, 'height') + 'px'
|
||||
});
|
||||
|
||||
$.style($('table', this.bodyScrollable), {
|
||||
margin: 0,
|
||||
width: '100%'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
10
yarn.lock
10
yarn.lock
@ -1493,11 +1493,6 @@ clone@^1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.3.tgz#298d7e2231660f40c003c2ed3140decf3f53085f"
|
||||
integrity sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=
|
||||
|
||||
clusterize.js@^0.18.0:
|
||||
version "0.18.0"
|
||||
resolved "https://registry.yarnpkg.com/clusterize.js/-/clusterize.js-0.18.0.tgz#32dce7267c5e934bfb205ba65a98760005041331"
|
||||
integrity sha512-13AqtsPStx5OawyggucdPawJaJmPl56HbG32Urya79VE39T7ZlpgVUGIOPwr9TIwf/duZAXjhXDQxC2eiMe6AA==
|
||||
|
||||
co@^4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
|
||||
@ -3331,6 +3326,11 @@ https-proxy-agent@^2.2.0, https-proxy-agent@^2.2.1:
|
||||
agent-base "^4.1.0"
|
||||
debug "^3.1.0"
|
||||
|
||||
hyperlist@^1.0.0-beta:
|
||||
version "1.0.0-beta"
|
||||
resolved "https://registry.yarnpkg.com/hyperlist/-/hyperlist-1.0.0-beta.tgz#2cbbd77f4498c2ecc290b7f3c6745b3f0288247e"
|
||||
integrity sha1-LLvXf0SYwuzCkLfzxnRbPwKIJH4=
|
||||
|
||||
iconv-lite@^0.4.17:
|
||||
version "0.4.21"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.21.tgz#c47f8733d02171189ebc4a400f3218d348094798"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user