frappe-datatable
Version:
A modern datatable library for the web
297 lines (249 loc) • 8.16 kB
JavaScript
import $ from './dom';
import DataManager from './datamanager';
import CellManager from './cellmanager';
import ColumnManager from './columnmanager';
import RowManager from './rowmanager';
import BodyRenderer from './body-renderer';
import Style from './style';
import Keyboard from './keyboard';
import TranslationManager from './translationmanager';
import getDefaultOptions from './defaults';
let defaultComponents = {
DataManager,
CellManager,
ColumnManager,
RowManager,
BodyRenderer,
Style,
Keyboard
};
class DataTable {
constructor(wrapper, options) {
DataTable.instances++;
if (typeof wrapper === 'string') {
// css selector
wrapper = document.querySelector(wrapper);
}
this.wrapper = wrapper;
if (!(this.wrapper instanceof HTMLElement)) {
throw new Error('Invalid argument given for `wrapper`');
}
this.initializeTranslations(options);
this.setDefaultOptions();
this.buildOptions(options);
this.prepare();
this.initializeComponents();
if (this.options.data) {
this.refresh();
this.columnmanager.applyDefaultSortOrder();
if (this.options.saveSorting) {
this.setupSaveSorting();
this.columnmanager.applySavedSortOrder();
}
}
}
initializeTranslations(options) {
this.language = options.language || 'en';
this.translationManager = new TranslationManager(this.language);
if (options.translations) {
this.translationManager.addTranslations(options.translations);
}
}
setDefaultOptions() {
this.DEFAULT_OPTIONS = getDefaultOptions(this);
}
buildOptions(options) {
this.options = this.options || {};
this.options = Object.assign(
{}, this.DEFAULT_OPTIONS,
this.options || {}, options
);
options.headerDropdown = options.headerDropdown || [];
this.options.headerDropdown = [
...this.DEFAULT_OPTIONS.headerDropdown,
...options.headerDropdown
];
// custom user events
this.events = Object.assign(
{}, this.DEFAULT_OPTIONS.events,
this.options.events || {},
options.events || {}
);
this.fireEvent = this.fireEvent.bind(this);
}
prepare() {
this.prepareDom();
this.unfreeze();
}
initializeComponents() {
let components = Object.assign({}, defaultComponents, this.options.overrideComponents);
let {
Style,
Keyboard,
DataManager,
RowManager,
ColumnManager,
CellManager,
BodyRenderer
} = components;
this.style = new Style(this);
this.keyboard = new Keyboard(this.wrapper);
this.datamanager = new DataManager(this.options);
this.rowmanager = new RowManager(this);
this.columnmanager = new ColumnManager(this);
this.cellmanager = new CellManager(this);
this.bodyRenderer = new BodyRenderer(this);
}
prepareDom() {
this.wrapper.innerHTML = `
<div class="datatable" dir="${this.options.direction}">
<div class="dt-header"></div>
<div class="dt-scrollable"></div>
<div class="dt-footer"></div>
<div class="dt-freeze">
<span class="dt-freeze__message">
${this.options.freezeMessage}
</span>
</div>
<div class="dt-toast"></div>
<div class="dt-dropdown-container"></div>
<textarea class="dt-paste-target"></textarea>
</div>
`;
this.datatableWrapper = $('.datatable', this.wrapper);
this.header = $('.dt-header', this.wrapper);
this.footer = $('.dt-footer', this.wrapper);
this.bodyScrollable = $('.dt-scrollable', this.wrapper);
this.freezeContainer = $('.dt-freeze', this.wrapper);
this.toastMessage = $('.dt-toast', this.wrapper);
this.pasteTarget = $('.dt-paste-target', this.wrapper);
this.dropdownContainer = $('.dt-dropdown-container', this.wrapper);
}
refresh(data, columns) {
this.datamanager.init(data, columns);
this.render();
this.setDimensions();
}
destroy() {
this.wrapper.innerHTML = '';
this.style.destroy();
this.fireEvent('onDestroy');
}
appendRows(rows) {
this.datamanager.appendRows(rows);
this.rowmanager.refreshRows();
}
refreshRow(row, rowIndex) {
this.rowmanager.refreshRow(row, rowIndex);
}
render() {
this.renderHeader();
this.renderBody();
}
renderHeader() {
this.columnmanager.renderHeader();
}
renderBody() {
this.bodyRenderer.render();
}
setDimensions() {
this.style.setDimensions();
}
showToastMessage(message, hideAfter) {
this.bodyRenderer.showToastMessage(message, hideAfter);
}
clearToastMessage() {
this.bodyRenderer.clearToastMessage();
}
getColumn(colIndex) {
return this.datamanager.getColumn(colIndex);
}
getColumns() {
return this.datamanager.getColumns();
}
getRows() {
return this.datamanager.getRows();
}
getCell(colIndex, rowIndex) {
return this.datamanager.getCell(colIndex, rowIndex);
}
getColumnHeaderElement(colIndex) {
return this.columnmanager.getColumnHeaderElement(colIndex);
}
getViewportHeight() {
if (!this.viewportHeight) {
this.viewportHeight = $.style(this.bodyScrollable, 'height');
}
return this.viewportHeight;
}
sortColumn(colIndex, sortOrder) {
this.columnmanager.sortColumn(colIndex, sortOrder);
}
saveSorting(colIndex, nextSortOrder) {
this.columnmanager.saveSorting(colIndex, nextSortOrder);
}
removeColumn(colIndex) {
this.columnmanager.removeColumn(colIndex);
}
scrollToLastColumn() {
this.datatableWrapper.scrollLeft = 9999;
}
freeze() {
$.style(this.freezeContainer, {
display: ''
});
}
unfreeze() {
$.style(this.freezeContainer, {
display: 'none'
});
}
updateOptions(options) {
this.buildOptions(options);
}
fireEvent(eventName, ...args) {
// fire internalEventHandlers if any
// and then user events
const handlers = [
...(this._internalEventHandlers[eventName] || []),
this.events[eventName]
].filter(Boolean);
for (let handler of handlers) {
handler.apply(this, args);
}
}
on(event, handler) {
this._internalEventHandlers = this._internalEventHandlers || {};
this._internalEventHandlers[event] = this._internalEventHandlers[event] || [];
this._internalEventHandlers[event].push(handler);
}
log() {
if (this.options.logs) {
console.log.apply(console, arguments);
}
}
translate(str, args) {
return this.translationManager.translate(str, args);
}
setupSaveSorting() {
// add options in default headerdropdown
let action = {
label: this.translate('Save Sorting'),
action: function (column) {
this.saveSorting(column.colIndex, column.sotOrder);
},
display: 'hidden'
};
this.options.headerDropdown.push(action);
this.columnmanager.bindDropdown();
// add events for onSortColumn
this.on('onSortColumn', function (column) {
this.columnmanager.toggleDropdownItem(4);
if (column.sortOrder === 'none') {
localStorage.removeItem(this.columnmanager.sortingKey);
}
});
}
}
DataTable.instances = 0;
export default DataTable;