Indent using 4 spaces

This commit is contained in:
Faris Ansari 2018-02-27 12:36:12 +05:30
parent d8fb48ba91
commit 6ee51038d0
13 changed files with 2237 additions and 2117 deletions

View File

@ -1,99 +1,101 @@
import $ from './dom';
import Clusterize from 'clusterize.js';
import { promisify } from './utils';
import {
promisify
} from './utils';
export default class BodyRenderer {
constructor(instance) {
this.instance = instance;
this.options = instance.options;
this.datamanager = instance.datamanager;
this.rowmanager = instance.rowmanager;
this.cellmanager = instance.cellmanager;
this.bodyScrollable = instance.bodyScrollable;
this.log = instance.log;
this.appendRemainingData = promisify(this.appendRemainingData, this);
}
render() {
if (this.options.enableClusterize) {
this.renderBodyWithClusterize();
} else {
this.renderBodyHTML();
}
}
renderBodyHTML() {
const rows = this.datamanager.getRows();
this.bodyScrollable.innerHTML = `
<table class="data-table-body">
${getBodyHTML(rows)}
</table>
`;
this.instance.setDimensions();
this.restoreState();
}
renderBodyWithClusterize() {
// first page
const rows = this.datamanager.getRows(0, 20);
const initialData = this.getDataForClusterize(rows);
if (!this.clusterize) {
// empty body
this.bodyScrollable.innerHTML = `
<table class="data-table-body">
${getBodyHTML([])}
</table>
`;
// first 20 rows will appended
// rest of them in nextTick
this.clusterize = new Clusterize({
rows: initialData,
scrollElem: this.bodyScrollable,
contentElem: $('tbody', this.bodyScrollable),
callbacks: {
clusterChanged: () => {
this.restoreState();
}
},
/* eslint-disable */
no_data_text: this.options.noDataMessage,
no_data_class: 'empty-state'
/* eslint-enable */
});
// setDimensions requires atleast 1 row to exist in dom
this.instance.setDimensions();
} else {
this.clusterize.update(initialData);
constructor(instance) {
this.instance = instance;
this.options = instance.options;
this.datamanager = instance.datamanager;
this.rowmanager = instance.rowmanager;
this.cellmanager = instance.cellmanager;
this.bodyScrollable = instance.bodyScrollable;
this.log = instance.log;
this.appendRemainingData = promisify(this.appendRemainingData, this);
}
this.appendRemainingData();
}
render() {
if (this.options.enableClusterize) {
this.renderBodyWithClusterize();
} else {
this.renderBodyHTML();
}
}
restoreState() {
this.rowmanager.highlightCheckedRows();
this.cellmanager.selectAreaOnClusterChanged();
this.cellmanager.focusCellOnClusterChanged();
}
renderBodyHTML() {
const rows = this.datamanager.getRows();
appendRemainingData() {
const rows = this.datamanager.getRows(20);
const data = this.getDataForClusterize(rows);
this.clusterize.append(data);
}
this.bodyScrollable.innerHTML = `
<table class="data-table-body">
${this.getBodyHTML(rows)}
</table>
`;
this.instance.setDimensions();
this.restoreState();
}
getDataForClusterize(rows) {
return rows.map((row) => this.rowmanager.getRowHTML(row, { rowIndex: row[0].rowIndex }));
}
renderBodyWithClusterize() {
// first page
const rows = this.datamanager.getRows(0, 20);
const initialData = this.getDataForClusterize(rows);
if (!this.clusterize) {
// empty body
this.bodyScrollable.innerHTML = `
<table class="data-table-body">
${this.getBodyHTML([])}
</table>
`;
// first 20 rows will appended
// rest of them in nextTick
this.clusterize = new Clusterize({
rows: initialData,
scrollElem: this.bodyScrollable,
contentElem: $('tbody', this.bodyScrollable),
callbacks: {
clusterChanged: () => {
this.restoreState();
}
},
/* eslint-disable */
no_data_text: this.options.noDataMessage,
no_data_class: 'empty-state'
/* eslint-enable */
});
// setDimensions requires atleast 1 row to exist in dom
this.instance.setDimensions();
} else {
this.clusterize.update(initialData);
}
this.appendRemainingData();
}
restoreState() {
this.rowmanager.highlightCheckedRows();
this.cellmanager.selectAreaOnClusterChanged();
this.cellmanager.focusCellOnClusterChanged();
}
appendRemainingData() {
const rows = this.datamanager.getRows(20);
const data = this.getDataForClusterize(rows);
this.clusterize.append(data);
}
getDataForClusterize(rows) {
return rows.map((row) => this.rowmanager.getRowHTML(row, row.meta));
}
getBodyHTML(rows) {
return `
<tbody>
${rows.map(row => this.rowmanager.getRowHTML(row, row.meta)).join('')}
</tbody>
`;
}
};
export function getBodyHTML(rows) {
return `
<tbody>
${rows.map(row => this.rowmanager.getRowHTML(row, { rowIndex: row[0].rowIndex })).join('')}
</tbody>
`;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,413 +1,452 @@
import $ from './dom';
import Sortable from 'sortablejs';
import { getDefault, linkProperties, debounce } from './utils';
import {
getDefault,
linkProperties,
debounce
} from './utils';
export default class ColumnManager {
constructor(instance) {
this.instance = instance;
constructor(instance) {
this.instance = instance;
linkProperties(this, this.instance, [
'options',
'fireEvent',
'header',
'datamanager',
'style',
'wrapper',
'rowmanager',
'bodyScrollable'
]);
linkProperties(this, this.instance, [
'options',
'fireEvent',
'header',
'datamanager',
'style',
'wrapper',
'rowmanager',
'bodyScrollable'
]);
this.bindEvents();
getDropdownHTML = getDropdownHTML.bind(this, this.options.dropdownButton);
}
this.bindEvents();
getDropdownHTML = getDropdownHTML.bind(this, this.options.dropdownButton);
}
renderHeader() {
this.header.innerHTML = '<thead></thead>';
this.refreshHeader();
}
renderHeader() {
this.header.innerHTML = '<thead></thead>';
this.refreshHeader();
}
refreshHeader() {
const columns = this.datamanager.getColumns();
refreshHeader() {
const columns = this.datamanager.getColumns();
if (!$('.data-table-col', this.header)) {
// insert html
if (!$('.data-table-col', this.header)) {
// insert html
let html = this.rowmanager.getRowHTML(columns, { isHeader: 1 });
if (this.options.enableInlineFilters) {
html += this.rowmanager.getRowHTML(columns, { isFilter: 1 });
}
let html = this.rowmanager.getRowHTML(columns, {
isHeader: 1
});
if (this.options.enableInlineFilters) {
html += this.rowmanager.getRowHTML(columns, {
isFilter: 1
});
}
$('thead', this.header).innerHTML = html;
$('thead', this.header).innerHTML = html;
this.$filterRow = $('.data-table-row[data-is-filter]', this.header);
this.$filterRow = $('.data-table-row[data-is-filter]', this.header);
if (this.$filterRow) {
// hide filter row immediately, so it doesn't disturb layout
$.style(this.$filterRow, {
display: 'none'
});
}
} else {
// refresh dom state
const $cols = $.each('.data-table-col', this.header);
if (columns.length < $cols.length) {
// deleted column
$('thead', this.header).innerHTML = this.rowmanager.getRowHTML(columns, { isHeader: 1 });
return;
}
if (this.$filterRow) {
// hide filter row immediately, so it doesn't disturb layout
$.style(this.$filterRow, {
display: 'none'
});
}
} else {
// refresh dom state
const $cols = $.each('.data-table-col', this.header);
if (columns.length < $cols.length) {
// deleted column
$('thead', this.header).innerHTML = this.rowmanager.getRowHTML(columns, {
isHeader: 1
});
return;
}
$cols.map(($col, i) => {
const column = columns[i];
// column sorted or order changed
// update colIndex of each header cell
$.data($col, {
colIndex: column.colIndex
});
$cols.map(($col, i) => {
const column = columns[i];
// column sorted or order changed
// update colIndex of each header cell
$.data($col, {
colIndex: column.colIndex
});
// refresh sort indicator
const sortIndicator = $('.sort-indicator', $col);
if (sortIndicator) {
sortIndicator.innerHTML = this.options.sortIndicator[column.sortOrder];
// refresh sort indicator
const sortIndicator = $('.sort-indicator', $col);
if (sortIndicator) {
sortIndicator.innerHTML = this.options.sortIndicator[column.sortOrder];
}
});
}
});
// reset columnMap
this.$columnMap = [];
}
// reset columnMap
this.$columnMap = [];
}
bindEvents() {
this.bindDropdown();
this.bindResizeColumn();
this.bindMoveColumn();
this.bindFilter();
}
bindDropdown() {
let $activeDropdown;
$.on(this.header, 'click', '.data-table-dropdown-toggle', (e, $button) => {
const $dropdown = $.closest('.data-table-dropdown', $button);
if (!$dropdown.classList.contains('is-active')) {
deactivateDropdown();
$dropdown.classList.add('is-active');
$activeDropdown = $dropdown;
} else {
deactivateDropdown();
}
});
$.on(document.body, 'click', (e) => {
if (e.target.matches('.data-table-dropdown-toggle')) return;
deactivateDropdown();
});
const dropdownItems = this.options.headerDropdown;
$.on(this.header, 'click', '.data-table-dropdown-list > div', (e, $item) => {
const $col = $.closest('.data-table-col', $item);
const { index } = $.data($item);
const { colIndex } = $.data($col);
let callback = dropdownItems[index].action;
callback && callback.call(this.instance, this.getColumn(colIndex));
});
function deactivateDropdown(e) {
$activeDropdown && $activeDropdown.classList.remove('is-active');
$activeDropdown = null;
bindEvents() {
this.bindDropdown();
this.bindResizeColumn();
this.bindMoveColumn();
this.bindFilter();
}
}
bindResizeColumn() {
let isDragging = false;
let $resizingCell, startWidth, startX;
bindDropdown() {
let $activeDropdown;
$.on(this.header, 'click', '.data-table-dropdown-toggle', (e, $button) => {
const $dropdown = $.closest('.data-table-dropdown', $button);
$.on(this.header, 'mousedown', '.data-table-col .column-resizer', (e, $handle) => {
document.body.classList.add('data-table-resize');
const $cell = $handle.parentNode.parentNode;
$resizingCell = $cell;
const { colIndex } = $.data($resizingCell);
const col = this.getColumn(colIndex);
if (col && col.resizable === false) {
return;
}
isDragging = true;
startWidth = $.style($('.content', $resizingCell), 'width');
startX = e.pageX;
});
$.on(document.body, 'mouseup', (e) => {
document.body.classList.remove('data-table-resize');
if (!$resizingCell) return;
isDragging = false;
const { colIndex } = $.data($resizingCell);
this.setColumnWidth(colIndex);
this.style.setBodyStyle();
$resizingCell = null;
});
$.on(document.body, 'mousemove', (e) => {
if (!isDragging) return;
const finalWidth = startWidth + (e.pageX - startX);
const { colIndex } = $.data($resizingCell);
if (this.getColumnMinWidth(colIndex) > finalWidth) {
// don't resize past minWidth
return;
}
this.datamanager.updateColumn(colIndex, { width: finalWidth });
this.setColumnHeaderWidth(colIndex);
});
}
bindMoveColumn() {
let initialized;
const initialize = () => {
if (initialized) {
$.off(document.body, 'mousemove', initialize);
return;
}
const ready = $('.data-table-col', this.header);
if (!ready) return;
const $parent = $('.data-table-row', this.header);
this.sortable = Sortable.create($parent, {
onEnd: (e) => {
const { oldIndex, newIndex } = e;
const $draggedCell = e.item;
const { colIndex } = $.data($draggedCell);
if (+colIndex === newIndex) return;
this.switchColumn(oldIndex, newIndex);
},
preventOnFilter: false,
filter: '.column-resizer, .data-table-dropdown',
animation: 150
});
};
$.on(document.body, 'mousemove', initialize);
}
bindSortColumn() {
$.on(this.header, 'click', '.data-table-col .column-title', (e, span) => {
const $cell = span.closest('.data-table-col');
let { colIndex, sortOrder } = $.data($cell);
sortOrder = getDefault(sortOrder, 'none');
const col = this.getColumn(colIndex);
if (col && col.sortable === false) {
return;
}
// reset sort indicator
$('.sort-indicator', this.header).textContent = '';
$.each('.data-table-col', this.header).map($cell => {
$.data($cell, {
sortOrder: 'none'
if (!$dropdown.classList.contains('is-active')) {
deactivateDropdown();
$dropdown.classList.add('is-active');
$activeDropdown = $dropdown;
} else {
deactivateDropdown();
}
});
});
let nextSortOrder, textContent;
if (sortOrder === 'none') {
nextSortOrder = 'asc';
textContent = '▲';
} else if (sortOrder === 'asc') {
nextSortOrder = 'desc';
textContent = '▼';
} else if (sortOrder === 'desc') {
nextSortOrder = 'none';
textContent = '';
}
$.data($cell, {
sortOrder: nextSortOrder
});
$('.sort-indicator', $cell).textContent = textContent;
this.sortColumn(colIndex, nextSortOrder);
});
}
sortColumn(colIndex, nextSortOrder) {
this.instance.freeze();
this.sortRows(colIndex, nextSortOrder)
.then(() => {
this.refreshHeader();
return this.rowmanager.refreshRows();
})
.then(() => this.instance.unfreeze())
.then(() => {
this.fireEvent('onSortColumn', this.getColumn(colIndex));
});
}
removeColumn(colIndex) {
const removedCol = this.getColumn(colIndex);
this.instance.freeze();
this.datamanager.removeColumn(colIndex)
.then(() => {
this.refreshHeader();
return this.rowmanager.refreshRows();
})
.then(() => this.instance.unfreeze())
.then(() => {
this.fireEvent('onRemoveColumn', removedCol);
});
}
switchColumn(oldIndex, newIndex) {
this.instance.freeze();
this.datamanager.switchColumn(oldIndex, newIndex)
.then(() => {
this.refreshHeader();
return this.rowmanager.refreshRows();
})
.then(() => {
this.setColumnWidth(oldIndex);
this.setColumnWidth(newIndex);
this.instance.unfreeze();
})
.then(() => {
this.fireEvent('onSwitchColumn',
this.getColumn(oldIndex), this.getColumn(newIndex)
);
});
}
toggleFilter() {
this.isFilterShown = this.isFilterShown || false;
if (this.isFilterShown) {
$.style(this.$filterRow, {
display: 'none'
});
} else {
$.style(this.$filterRow, {
display: ''
});
}
this.isFilterShown = !this.isFilterShown;
this.style.setBodyStyle();
}
focusFilter(colIndex) {
if (!this.isFilterShown) return;
const $filterInput = $(`[data-col-index="${colIndex}"] .data-table-filter`, this.$filterRow);
$filterInput.focus();
}
bindFilter() {
if (!this.options.enableInlineFilters) return;
const handler = e => {
const $filterCell = $.closest('.data-table-col', e.target);
const { colIndex } = $.data($filterCell);
const keyword = e.target.value;
this.datamanager.filterRows(keyword, colIndex)
.then(({ rowsToHide, rowsToShow }) => {
rowsToHide.map(rowIndex => {
const $tr = $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable);
$tr.classList.add('hide');
});
rowsToShow.map(rowIndex => {
const $tr = $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable);
$tr.classList.remove('hide');
});
$.on(document.body, 'click', (e) => {
if (e.target.matches('.data-table-dropdown-toggle')) return;
deactivateDropdown();
});
};
$.on(this.header, 'keydown', '.data-table-filter', debounce(handler, 300));
}
sortRows(colIndex, sortOrder) {
return this.datamanager.sortRows(colIndex, sortOrder);
}
const dropdownItems = this.options.headerDropdown;
getColumn(colIndex) {
return this.datamanager.getColumn(colIndex);
}
$.on(this.header, 'click', '.data-table-dropdown-list > div', (e, $item) => {
const $col = $.closest('.data-table-col', $item);
const {
index
} = $.data($item);
const {
colIndex
} = $.data($col);
let callback = dropdownItems[index].action;
getColumns() {
return this.datamanager.getColumns();
}
callback && callback.call(this.instance, this.getColumn(colIndex));
});
setColumnWidth(colIndex) {
colIndex = +colIndex;
this._columnWidthMap = this._columnWidthMap || [];
const { width } = this.getColumn(colIndex);
let index = this._columnWidthMap[colIndex];
const selector = `[data-col-index="${colIndex}"] .content, [data-col-index="${colIndex}"] .edit-cell`;
const styles = {
width: width + 'px'
};
index = this.style.setStyle(selector, styles, index);
this._columnWidthMap[colIndex] = index;
}
setColumnHeaderWidth(colIndex) {
colIndex = +colIndex;
this.$columnMap = this.$columnMap || [];
const selector = `.data-table-header [data-col-index="${colIndex}"] .content`;
const { width } = this.getColumn(colIndex);
let $column = this.$columnMap[colIndex];
if (!$column) {
$column = this.header.querySelector(selector);
this.$columnMap[colIndex] = $column;
function deactivateDropdown(e) {
$activeDropdown && $activeDropdown.classList.remove('is-active');
$activeDropdown = null;
}
}
$column.style.width = width + 'px';
}
bindResizeColumn() {
let isDragging = false;
let $resizingCell, startWidth, startX;
getColumnMinWidth(colIndex) {
colIndex = +colIndex;
return this.getColumn(colIndex).minWidth || 24;
}
$.on(this.header, 'mousedown', '.data-table-col .column-resizer', (e, $handle) => {
document.body.classList.add('data-table-resize');
const $cell = $handle.parentNode.parentNode;
$resizingCell = $cell;
const {
colIndex
} = $.data($resizingCell);
const col = this.getColumn(colIndex);
getFirstColumnIndex() {
if (this.options.addCheckboxColumn && this.options.addSerialNoColumn) {
return 2;
if (col && col.resizable === false) {
return;
}
isDragging = true;
startWidth = $.style($('.content', $resizingCell), 'width');
startX = e.pageX;
});
$.on(document.body, 'mouseup', (e) => {
document.body.classList.remove('data-table-resize');
if (!$resizingCell) return;
isDragging = false;
const {
colIndex
} = $.data($resizingCell);
this.setColumnWidth(colIndex);
this.style.setBodyStyle();
$resizingCell = null;
});
$.on(document.body, 'mousemove', (e) => {
if (!isDragging) return;
const finalWidth = startWidth + (e.pageX - startX);
const {
colIndex
} = $.data($resizingCell);
if (this.getColumnMinWidth(colIndex) > finalWidth) {
// don't resize past minWidth
return;
}
this.datamanager.updateColumn(colIndex, {
width: finalWidth
});
this.setColumnHeaderWidth(colIndex);
});
}
if (this.options.addCheckboxColumn || this.options.addSerialNoColumn) {
return 1;
bindMoveColumn() {
let initialized;
const initialize = () => {
if (initialized) {
$.off(document.body, 'mousemove', initialize);
return;
}
const ready = $('.data-table-col', this.header);
if (!ready) return;
const $parent = $('.data-table-row', this.header);
this.sortable = Sortable.create($parent, {
onEnd: (e) => {
const {
oldIndex,
newIndex
} = e;
const $draggedCell = e.item;
const {
colIndex
} = $.data($draggedCell);
if (+colIndex === newIndex) return;
this.switchColumn(oldIndex, newIndex);
},
preventOnFilter: false,
filter: '.column-resizer, .data-table-dropdown',
animation: 150
});
};
$.on(document.body, 'mousemove', initialize);
}
return 0;
}
bindSortColumn() {
getHeaderCell$(colIndex) {
return $(`.data-table-col[data-col-index="${colIndex}"]`, this.header);
}
$.on(this.header, 'click', '.data-table-col .column-title', (e, span) => {
const $cell = span.closest('.data-table-col');
let {
colIndex,
sortOrder
} = $.data($cell);
sortOrder = getDefault(sortOrder, 'none');
const col = this.getColumn(colIndex);
getLastColumnIndex() {
return this.datamanager.getColumnCount() - 1;
}
if (col && col.sortable === false) {
return;
}
getSerialColumnIndex() {
const columns = this.datamanager.getColumns();
// reset sort indicator
$('.sort-indicator', this.header).textContent = '';
$.each('.data-table-col', this.header).map($cell => {
$.data($cell, {
sortOrder: 'none'
});
});
return columns.findIndex(column => column.content.includes('Sr. No'));
}
let nextSortOrder, textContent;
if (sortOrder === 'none') {
nextSortOrder = 'asc';
textContent = '▲';
} else if (sortOrder === 'asc') {
nextSortOrder = 'desc';
textContent = '▼';
} else if (sortOrder === 'desc') {
nextSortOrder = 'none';
textContent = '';
}
$.data($cell, {
sortOrder: nextSortOrder
});
$('.sort-indicator', $cell).textContent = textContent;
this.sortColumn(colIndex, nextSortOrder);
});
}
sortColumn(colIndex, nextSortOrder) {
this.instance.freeze();
this.sortRows(colIndex, nextSortOrder)
.then(() => {
this.refreshHeader();
return this.rowmanager.refreshRows();
})
.then(() => this.instance.unfreeze())
.then(() => {
this.fireEvent('onSortColumn', this.getColumn(colIndex));
});
}
removeColumn(colIndex) {
const removedCol = this.getColumn(colIndex);
this.instance.freeze();
this.datamanager.removeColumn(colIndex)
.then(() => {
this.refreshHeader();
return this.rowmanager.refreshRows();
})
.then(() => this.instance.unfreeze())
.then(() => {
this.fireEvent('onRemoveColumn', removedCol);
});
}
switchColumn(oldIndex, newIndex) {
this.instance.freeze();
this.datamanager.switchColumn(oldIndex, newIndex)
.then(() => {
this.refreshHeader();
return this.rowmanager.refreshRows();
})
.then(() => {
this.setColumnWidth(oldIndex);
this.setColumnWidth(newIndex);
this.instance.unfreeze();
})
.then(() => {
this.fireEvent('onSwitchColumn',
this.getColumn(oldIndex), this.getColumn(newIndex)
);
});
}
toggleFilter() {
this.isFilterShown = this.isFilterShown || false;
if (this.isFilterShown) {
$.style(this.$filterRow, {
display: 'none'
});
} else {
$.style(this.$filterRow, {
display: ''
});
}
this.isFilterShown = !this.isFilterShown;
this.style.setBodyStyle();
}
focusFilter(colIndex) {
if (!this.isFilterShown) return;
const $filterInput = $(`[data-col-index="${colIndex}"] .data-table-filter`, this.$filterRow);
$filterInput.focus();
}
bindFilter() {
if (!this.options.enableInlineFilters) return;
const handler = e => {
const $filterCell = $.closest('.data-table-col', e.target);
const {
colIndex
} = $.data($filterCell);
const keyword = e.target.value;
this.datamanager.filterRows(keyword, colIndex)
.then(({
rowsToHide,
rowsToShow
}) => {
rowsToHide.map(rowIndex => {
const $tr = $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable);
$tr.classList.add('hide');
});
rowsToShow.map(rowIndex => {
const $tr = $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable);
$tr.classList.remove('hide');
});
});
};
$.on(this.header, 'keydown', '.data-table-filter', debounce(handler, 300));
}
sortRows(colIndex, sortOrder) {
return this.datamanager.sortRows(colIndex, sortOrder);
}
getColumn(colIndex) {
return this.datamanager.getColumn(colIndex);
}
getColumns() {
return this.datamanager.getColumns();
}
setColumnWidth(colIndex) {
colIndex = +colIndex;
this._columnWidthMap = this._columnWidthMap || [];
const {
width
} = this.getColumn(colIndex);
let index = this._columnWidthMap[colIndex];
const selector = `[data-col-index="${colIndex}"] .content, [data-col-index="${colIndex}"] .edit-cell`;
const styles = {
width: width + 'px'
};
index = this.style.setStyle(selector, styles, index);
this._columnWidthMap[colIndex] = index;
}
setColumnHeaderWidth(colIndex) {
colIndex = +colIndex;
this.$columnMap = this.$columnMap || [];
const selector = `.data-table-header [data-col-index="${colIndex}"] .content`;
const {
width
} = this.getColumn(colIndex);
let $column = this.$columnMap[colIndex];
if (!$column) {
$column = this.header.querySelector(selector);
this.$columnMap[colIndex] = $column;
}
$column.style.width = width + 'px';
}
getColumnMinWidth(colIndex) {
colIndex = +colIndex;
return this.getColumn(colIndex).minWidth || 24;
}
getFirstColumnIndex() {
if (this.options.addCheckboxColumn && this.options.addSerialNoColumn) {
return 2;
}
if (this.options.addCheckboxColumn || this.options.addSerialNoColumn) {
return 1;
}
return 0;
}
getHeaderCell$(colIndex) {
return $(`.data-table-col[data-col-index="${colIndex}"]`, this.header);
}
getLastColumnIndex() {
return this.datamanager.getColumnCount() - 1;
}
getSerialColumnIndex() {
const columns = this.datamanager.getColumns();
return columns.findIndex(column => column.content.includes('Sr. No'));
}
}
// eslint-disable-next-line
var getDropdownHTML = function getDropdownHTML(dropdownButton = 'v') {
// add dropdown buttons
const dropdownItems = this.options.headerDropdown;
// add dropdown buttons
const dropdownItems = this.options.headerDropdown;
return `<div class="data-table-dropdown-toggle">${dropdownButton}</div>
return `<div class="data-table-dropdown-toggle">${dropdownButton}</div>
<div class="data-table-dropdown-list">
${dropdownItems.map((d, i) => `<div data-index="${i}">${d.label}</div>`).join('')}
</div>
@ -415,5 +454,5 @@ var getDropdownHTML = function getDropdownHTML(dropdownButton = 'v') {
};
export {
getDropdownHTML
getDropdownHTML
};

View File

@ -1,500 +1,518 @@
import { isNumeric, promisify } from './utils';
import {
isNumeric,
promisify
} from './utils';
export default class DataManager {
constructor(options) {
this.options = options;
this.sortRows = promisify(this.sortRows, this);
this.switchColumn = promisify(this.switchColumn, this);
this.removeColumn = promisify(this.removeColumn, this);
this.filterRows = promisify(this.filterRows, this);
}
init(data) {
if (!data) {
data = this.options.data;
constructor(options) {
this.options = options;
this.sortRows = promisify(this.sortRows, this);
this.switchColumn = promisify(this.switchColumn, this);
this.removeColumn = promisify(this.removeColumn, this);
this.filterRows = promisify(this.filterRows, this);
}
this.data = data;
this.rowCount = 0;
this.columns = [];
this.rows = [];
this.prepareColumns();
this.prepareRows();
this.prepareNumericColumns();
}
// computed property
get currentSort() {
const col = this.columns.find(col => col.sortOrder !== 'none');
return col || {
colIndex: -1,
sortOrder: 'none'
};
}
prepareColumns() {
this.columns = [];
this.validateColumns();
this.prepareDefaultColumns();
this.prepareHeader();
}
prepareDefaultColumns() {
if (this.options.addCheckboxColumn && !this.hasColumnById('_checkbox')) {
const cell = {
id: '_checkbox',
content: this.getCheckboxHTML(),
editable: false,
resizable: false,
sortable: false,
focusable: false,
dropdown: false,
width: 25
};
this.columns.push(cell);
}
if (this.options.addSerialNoColumn && !this.hasColumnById('_rowIndex')) {
let cell = {
id: '_rowIndex',
content: '',
align: 'center',
editable: false,
resizable: false,
focusable: false,
dropdown: false
};
this.columns.push(cell);
}
}
prepareRow(row, i) {
const baseRowCell = {
rowIndex: i
};
return row
.map((cell, i) => this.prepareCell(cell, i))
.map(cell => Object.assign({}, baseRowCell, cell));
}
prepareHeader() {
let columns = this.columns.concat(this.options.columns);
const baseCell = {
isHeader: 1,
editable: true,
sortable: true,
resizable: true,
focusable: true,
dropdown: true,
width: null,
format: (value) => {
if (value === null || value === undefined) {
return '';
}
return value + '';
}
};
this.columns = columns
.map((cell, i) => this.prepareCell(cell, i))
.map(col => Object.assign({}, baseCell, col))
.map(col => {
col.id = col.id || col.content;
return col;
});
}
prepareCell(content, i) {
const cell = {
content: '',
align: 'left',
sortOrder: 'none',
colIndex: i,
column: this.columns[i]
};
if (content !== null && typeof content === 'object') {
// passed as column/header
Object.assign(cell, content);
} else {
cell.content = content;
}
return cell;
}
prepareNumericColumns() {
const row0 = this.getRow(0);
if (!row0) return;
this.columns = this.columns.map((column, i) => {
const cellValue = row0[i].content;
if (!column.align && cellValue && isNumeric(cellValue)) {
column.align = 'right';
}
return column;
});
}
prepareRows() {
this.validateData(this.data);
this.rows = this.data.map((d, i) => {
const index = this._getNextRowCount();
let row = [];
if (Array.isArray(d)) {
// row is an array
if (this.options.addCheckboxColumn) {
row.push(this.getCheckboxHTML());
}
if (this.options.addSerialNoColumn) {
row.push((index + 1) + '');
}
row = row.concat(d);
while (row.length < this.columns.length) {
row.push('');
init(data) {
if (!data) {
data = this.options.data;
}
} else {
// row is a dict
for (let col of this.columns) {
if (col.id === '_checkbox') {
row.push(this.getCheckboxHTML());
} else if (col.id === '_rowIndex') {
row.push((index + 1) + '');
} else {
row.push(d[col.id]);
}
this.data = data;
this.rowCount = 0;
this.columns = [];
this.rows = [];
this.prepareColumns();
this.prepareRows();
this.prepareNumericColumns();
}
// computed property
get currentSort() {
const col = this.columns.find(col => col.sortOrder !== 'none');
return col || {
colIndex: -1,
sortOrder: 'none'
};
}
prepareColumns() {
this.columns = [];
this.validateColumns();
this.prepareDefaultColumns();
this.prepareHeader();
}
prepareDefaultColumns() {
if (this.options.addCheckboxColumn && !this.hasColumnById('_checkbox')) {
const cell = {
id: '_checkbox',
content: this.getCheckboxHTML(),
editable: false,
resizable: false,
sortable: false,
focusable: false,
dropdown: false,
width: 25
};
this.columns.push(cell);
}
}
return this.prepareRow(row, index);
});
}
if (this.options.addSerialNoColumn && !this.hasColumnById('_rowIndex')) {
let cell = {
id: '_rowIndex',
content: '',
align: 'center',
editable: false,
resizable: false,
focusable: false,
dropdown: false
};
validateColumns() {
const columns = this.options.columns;
if (!Array.isArray(columns)) {
throw new DataError('`columns` must be an array');
this.columns.push(cell);
}
}
columns.forEach((column, i) => {
if (typeof column !== 'string' && typeof column !== 'object') {
throw new DataError(`column "${i}" must be a string or an object`);
}
});
}
prepareHeader() {
let columns = this.columns.concat(this.options.columns);
const baseCell = {
isHeader: 1,
editable: true,
sortable: true,
resizable: true,
focusable: true,
dropdown: true,
width: null,
format: (value) => {
if (value === null || value === undefined) {
return '';
}
return value + '';
}
};
validateData(data) {
if (Array.isArray(data) &&
(data.length === 0 || Array.isArray(data[0]) || typeof data[0] === 'object')) {
return true;
this.columns = columns
.map((cell, i) => this.prepareCell(cell, i))
.map(col => Object.assign({}, baseCell, col))
.map(col => {
col.id = col.id || col.content;
return col;
});
}
throw new DataError('`data` must be an array of arrays or objects');
}
appendRows(rows) {
this.validateData(rows);
prepareCell(content, i) {
const cell = {
content: '',
align: 'left',
sortOrder: 'none',
colIndex: i,
column: this.columns[i]
};
this.rows = this.rows.concat(this.prepareRows(rows));
}
sortRows(colIndex, sortOrder = 'none') {
colIndex = +colIndex;
// reset sortOrder and update for colIndex
this.getColumns()
.map(col => {
if (col.colIndex === colIndex) {
col.sortOrder = sortOrder;
if (content !== null && typeof content === 'object') {
// passed as column/header
Object.assign(cell, content);
} else {
col.sortOrder = 'none';
cell.content = content;
}
});
this._sortRows(colIndex, sortOrder);
}
_sortRows(colIndex, sortOrder) {
if (this.currentSort.colIndex === colIndex) {
// reverse the array if only sortOrder changed
if (
(this.currentSort.sortOrder === 'asc' && sortOrder === 'desc') ||
(this.currentSort.sortOrder === 'desc' && sortOrder === 'asc')
) {
this.reverseArray(this.rows);
this.currentSort.sortOrder = sortOrder;
return;
}
return cell;
}
this.rows.sort((a, b) => {
const _aIndex = a[0].rowIndex;
const _bIndex = b[0].rowIndex;
const _a = a[colIndex].content;
const _b = b[colIndex].content;
prepareNumericColumns() {
const row0 = this.getRow(0);
if (!row0) return;
this.columns = this.columns.map((column, i) => {
if (sortOrder === 'none') {
return _aIndex - _bIndex;
} else if (sortOrder === 'asc') {
if (_a < _b) return -1;
if (_a > _b) return 1;
if (_a === _b) return 0;
} else if (sortOrder === 'desc') {
if (_a < _b) return 1;
if (_a > _b) return -1;
if (_a === _b) return 0;
}
return 0;
});
const cellValue = row0[i].content;
if (!column.align && cellValue && isNumeric(cellValue)) {
column.align = 'right';
}
if (this.hasColumnById('_rowIndex')) {
// update row index
const srNoColIndex = this.getColumnIndexById('_rowIndex');
this.rows = this.rows.map((row, index) => {
return row.map(cell => {
if (cell.colIndex === srNoColIndex) {
cell.content = (index + 1) + '';
}
return cell;
return column;
});
});
}
}
reverseArray(array) {
let left = null;
let right = null;
let length = array.length;
for (left = 0, right = length - 1; left < right; left += 1, right -= 1) {
const temporary = array[left];
array[left] = array[right];
array[right] = temporary;
}
}
switchColumn(index1, index2) {
// update columns
const temp = this.columns[index1];
this.columns[index1] = this.columns[index2];
this.columns[index2] = temp;
this.columns[index1].colIndex = index1;
this.columns[index2].colIndex = index2;
// update rows
this.rows = this.rows.map(row => {
const newCell1 = Object.assign({}, row[index1], { colIndex: index2 });
const newCell2 = Object.assign({}, row[index2], { colIndex: index1 });
let newRow = row.map(cell => {
// make object copy
return Object.assign({}, cell);
});
newRow[index2] = newCell1;
newRow[index1] = newCell2;
return newRow;
});
}
removeColumn(index) {
index = +index;
const filter = cell => cell.colIndex !== index;
const map = (cell, i) => Object.assign({}, cell, { colIndex: i });
// update columns
this.columns = this.columns
.filter(filter)
.map(map);
// update rows
this.rows = this.rows.map(row => {
const newRow = row
.filter(filter)
.map(map);
return newRow;
});
}
updateRow(row, rowIndex) {
if (row.length < this.columns.length) {
if (this.hasColumnById('_rowIndex')) {
const val = (rowIndex + 1) + '';
row = [val].concat(row);
}
if (this.hasColumnById('_checkbox')) {
const val = '<input type="checkbox" />';
row = [val].concat(row);
}
}
const _row = this.prepareRow(row, rowIndex);
const index = this.rows.findIndex(row => row[0].rowIndex === rowIndex);
this.rows[index] = _row;
prepareRows() {
this.validateData(this.data);
return _row;
}
this.rows = this.data.map((d, i) => {
const index = this._getNextRowCount();
updateCell(colIndex, rowIndex, options) {
let cell;
if (typeof colIndex === 'object') {
// cell object was passed,
// must have colIndex, rowIndex
cell = colIndex;
colIndex = cell.colIndex;
rowIndex = cell.rowIndex;
// the object passed must be merged with original cell
options = cell;
}
cell = this.getCell(colIndex, rowIndex);
let row = [];
// mutate object directly
for (let key in options) {
const newVal = options[key];
if (newVal !== undefined) {
cell[key] = newVal;
}
if (Array.isArray(d)) {
// row is an array
if (this.options.addCheckboxColumn) {
row.push(this.getCheckboxHTML());
}
if (this.options.addSerialNoColumn) {
row.push((index + 1) + '');
}
row = row.concat(d);
while (row.length < this.columns.length) {
row.push('');
}
} else {
// row is an object
for (let col of this.columns) {
if (col.id === '_checkbox') {
row.push(this.getCheckboxHTML());
} else if (col.id === '_rowIndex') {
row.push((index + 1) + '');
} else {
row.push(d[col.id]);
}
}
}
return this.prepareRow(row, {
rowIndex: index
});
});
}
return cell;
}
prepareRow(row, props) {
const baseRowCell = {
rowIndex: props.rowIndex
};
updateColumn(colIndex, keyValPairs) {
const column = this.getColumn(colIndex);
for (let key in keyValPairs) {
const newVal = keyValPairs[key];
if (newVal !== undefined) {
column[key] = newVal;
}
}
return column;
}
row = row
.map((cell, i) => this.prepareCell(cell, i))
.map(cell => Object.assign({}, baseRowCell, cell));
filterRows(keyword, colIndex) {
let rowsToHide = [];
let rowsToShow = [];
const cells = this.rows.map(row => row[colIndex]);
cells.forEach(cell => {
const hay = cell.content.toLowerCase();
const needle = (keyword || '').toLowerCase();
if (!needle || hay.includes(needle)) {
rowsToShow.push(cell.rowIndex);
} else {
rowsToHide.push(cell.rowIndex);
}
});
return {rowsToHide, rowsToShow};
}
getRowCount() {
return this.rowCount;
}
_getNextRowCount() {
const val = this.rowCount;
this.rowCount++;
return val;
}
getRows(start, end) {
return this.rows.slice(start, end);
}
getColumns(skipStandardColumns) {
let columns = this.columns;
if (skipStandardColumns) {
columns = columns.slice(this.getStandardColumnCount());
// monkey patched in array object
row.meta = props;
return row;
}
return columns;
}
validateColumns() {
const columns = this.options.columns;
if (!Array.isArray(columns)) {
throw new DataError('`columns` must be an array');
}
getStandardColumnCount() {
if (this.options.addCheckboxColumn && this.options.addSerialNoColumn) {
return 2;
columns.forEach((column, i) => {
if (typeof column !== 'string' && typeof column !== 'object') {
throw new DataError(`column "${i}" must be a string or an object`);
}
});
}
if (this.options.addCheckboxColumn || this.options.addSerialNoColumn) {
return 1;
validateData(data) {
if (Array.isArray(data) &&
(data.length === 0 || Array.isArray(data[0]) || typeof data[0] === 'object')) {
return true;
}
throw new DataError('`data` must be an array of arrays or objects');
}
return 0;
}
appendRows(rows) {
this.validateData(rows);
getColumnCount(skipStandardColumns) {
let val = this.columns.length;
if (skipStandardColumns) {
val = val - this.getStandardColumnCount();
this.rows = this.rows.concat(this.prepareRows(rows));
}
return val;
}
sortRows(colIndex, sortOrder = 'none') {
colIndex = +colIndex;
getColumn(colIndex) {
colIndex = +colIndex;
return this.columns.find(col => col.colIndex === colIndex);
}
// reset sortOrder and update for colIndex
this.getColumns()
.map(col => {
if (col.colIndex === colIndex) {
col.sortOrder = sortOrder;
} else {
col.sortOrder = 'none';
}
});
getRow(rowIndex) {
rowIndex = +rowIndex;
return this.rows.find(row => row[0].rowIndex === rowIndex);
}
this._sortRows(colIndex, sortOrder);
}
getCell(colIndex, rowIndex) {
rowIndex = +rowIndex;
colIndex = +colIndex;
return this.rows.find(row => row[0].rowIndex === rowIndex)[colIndex];
}
_sortRows(colIndex, sortOrder) {
get() {
return {
columns: this.columns,
rows: this.rows
};
}
if (this.currentSort.colIndex === colIndex) {
// reverse the array if only sortOrder changed
if (
(this.currentSort.sortOrder === 'asc' && sortOrder === 'desc') ||
(this.currentSort.sortOrder === 'desc' && sortOrder === 'asc')
) {
this.reverseArray(this.rows);
this.currentSort.sortOrder = sortOrder;
return;
}
}
hasColumn(name) {
return Boolean(this.columns.find(col => col.content === name));
}
this.rows.sort((a, b) => {
const _aIndex = a[0].rowIndex;
const _bIndex = b[0].rowIndex;
const _a = a[colIndex].content;
const _b = b[colIndex].content;
hasColumnById(id) {
return Boolean(this.columns.find(col => col.id === id));
}
if (sortOrder === 'none') {
return _aIndex - _bIndex;
} else if (sortOrder === 'asc') {
if (_a < _b) return -1;
if (_a > _b) return 1;
if (_a === _b) return 0;
} else if (sortOrder === 'desc') {
if (_a < _b) return 1;
if (_a > _b) return -1;
if (_a === _b) return 0;
}
return 0;
});
getColumnIndex(name) {
return this.columns.findIndex(col => col.content === name);
}
if (this.hasColumnById('_rowIndex')) {
// update row index
const srNoColIndex = this.getColumnIndexById('_rowIndex');
this.rows = this.rows.map((row, index) => {
return row.map(cell => {
if (cell.colIndex === srNoColIndex) {
cell.content = (index + 1) + '';
}
return cell;
});
});
}
}
getColumnIndexById(id) {
return this.columns.findIndex(col => col.id === id);
}
reverseArray(array) {
let left = null;
let right = null;
let length = array.length;
getCheckboxHTML() {
return '<input type="checkbox" />';
}
for (left = 0, right = length - 1; left < right; left += 1, right -= 1) {
const temporary = array[left];
array[left] = array[right];
array[right] = temporary;
}
}
switchColumn(index1, index2) {
// update columns
const temp = this.columns[index1];
this.columns[index1] = this.columns[index2];
this.columns[index2] = temp;
this.columns[index1].colIndex = index1;
this.columns[index2].colIndex = index2;
// update rows
this.rows = this.rows.map(row => {
const newCell1 = Object.assign({}, row[index1], {
colIndex: index2
});
const newCell2 = Object.assign({}, row[index2], {
colIndex: index1
});
let newRow = row.map(cell => {
// make object copy
return Object.assign({}, cell);
});
newRow[index2] = newCell1;
newRow[index1] = newCell2;
return newRow;
});
}
removeColumn(index) {
index = +index;
const filter = cell => cell.colIndex !== index;
const map = (cell, i) => Object.assign({}, cell, {
colIndex: i
});
// update columns
this.columns = this.columns
.filter(filter)
.map(map);
// update rows
this.rows = this.rows.map(row => {
const newRow = row
.filter(filter)
.map(map);
return newRow;
});
}
updateRow(row, rowIndex) {
if (row.length < this.columns.length) {
if (this.hasColumnById('_rowIndex')) {
const val = (rowIndex + 1) + '';
row = [val].concat(row);
}
if (this.hasColumnById('_checkbox')) {
const val = '<input type="checkbox" />';
row = [val].concat(row);
}
}
const _row = this.prepareRow(row, rowIndex);
const index = this.rows.findIndex(row => row[0].rowIndex === rowIndex);
this.rows[index] = _row;
return _row;
}
updateCell(colIndex, rowIndex, options) {
let cell;
if (typeof colIndex === 'object') {
// cell object was passed,
// must have colIndex, rowIndex
cell = colIndex;
colIndex = cell.colIndex;
rowIndex = cell.rowIndex;
// the object passed must be merged with original cell
options = cell;
}
cell = this.getCell(colIndex, rowIndex);
// mutate object directly
for (let key in options) {
const newVal = options[key];
if (newVal !== undefined) {
cell[key] = newVal;
}
}
return cell;
}
updateColumn(colIndex, keyValPairs) {
const column = this.getColumn(colIndex);
for (let key in keyValPairs) {
const newVal = keyValPairs[key];
if (newVal !== undefined) {
column[key] = newVal;
}
}
return column;
}
filterRows(keyword, colIndex) {
let rowsToHide = [];
let rowsToShow = [];
const cells = this.rows.map(row => row[colIndex]);
cells.forEach(cell => {
const hay = cell.content.toLowerCase();
const needle = (keyword || '').toLowerCase();
if (!needle || hay.includes(needle)) {
rowsToShow.push(cell.rowIndex);
} else {
rowsToHide.push(cell.rowIndex);
}
});
return {
rowsToHide,
rowsToShow
};
}
getRowCount() {
return this.rowCount;
}
_getNextRowCount() {
const val = this.rowCount;
this.rowCount++;
return val;
}
getRows(start, end) {
return this.rows.slice(start, end);
}
getColumns(skipStandardColumns) {
let columns = this.columns;
if (skipStandardColumns) {
columns = columns.slice(this.getStandardColumnCount());
}
return columns;
}
getStandardColumnCount() {
if (this.options.addCheckboxColumn && this.options.addSerialNoColumn) {
return 2;
}
if (this.options.addCheckboxColumn || this.options.addSerialNoColumn) {
return 1;
}
return 0;
}
getColumnCount(skipStandardColumns) {
let val = this.columns.length;
if (skipStandardColumns) {
val = val - this.getStandardColumnCount();
}
return val;
}
getColumn(colIndex) {
colIndex = +colIndex;
return this.columns.find(col => col.colIndex === colIndex);
}
getRow(rowIndex) {
rowIndex = +rowIndex;
return this.rows.find(row => row[0].rowIndex === rowIndex);
}
getCell(colIndex, rowIndex) {
rowIndex = +rowIndex;
colIndex = +colIndex;
return this.rows.find(row => row[0].rowIndex === rowIndex)[colIndex];
}
get() {
return {
columns: this.columns,
rows: this.rows
};
}
hasColumn(name) {
return Boolean(this.columns.find(col => col.content === name));
}
hasColumnById(id) {
return Boolean(this.columns.find(col => col.id === id));
}
getColumnIndex(name) {
return this.columns.findIndex(col => col.content === name);
}
getColumnIndexById(id) {
return this.columns.findIndex(col => col.id === id);
}
getCheckboxHTML() {
return '<input type="checkbox" />';
}
}
// Custom Errors

View File

@ -10,50 +10,48 @@ import DEFAULT_OPTIONS from './defaults';
import './style.css';
class DataTable {
constructor(wrapper, options) {
DataTable.instances++;
constructor(wrapper, options) {
DataTable.instances++;
if (typeof wrapper === 'string') {
// css selector
wrapper = document.querySelector(wrapper);
}
this.wrapper = wrapper;
if (!(this.wrapper instanceof HTMLElement)) {
throw new Error('Invalid argument given for `wrapper`');
if (typeof wrapper === 'string') {
// css selector
wrapper = document.querySelector(wrapper);
}
this.wrapper = wrapper;
if (!(this.wrapper instanceof HTMLElement)) {
throw new Error('Invalid argument given for `wrapper`');
}
this.options = Object.assign({}, DEFAULT_OPTIONS, options);
this.options.headerDropdown =
DEFAULT_OPTIONS.headerDropdown
.concat(options.headerDropdown || []);
// custom user events
this.events = Object.assign({}, DEFAULT_OPTIONS.events, options.events || {});
this.fireEvent = this.fireEvent.bind(this);
this.prepare();
this.style = new Style(this);
this.keyboard = new Keyboard(this.wrapper);
this.datamanager = new DataManager(this.options);
this.rowmanager = new RowManager(this);
this.columnmanager = new ColumnManager(this);
this.cellmanager = new CellManager(this);
this.bodyRenderer = new BodyRenderer(this);
if (this.options.data) {
this.refresh();
}
}
this.options = Object.assign({}, DEFAULT_OPTIONS, options);
this.options.headerDropdown =
DEFAULT_OPTIONS.headerDropdown
.concat(options.headerDropdown || []);
// custom user events
this.events = Object.assign(
{}, DEFAULT_OPTIONS.events, options.events || {}
);
this.fireEvent = this.fireEvent.bind(this);
this.prepare();
this.style = new Style(this);
this.keyboard = new Keyboard(this.wrapper);
this.datamanager = new DataManager(this.options);
this.rowmanager = new RowManager(this);
this.columnmanager = new ColumnManager(this);
this.cellmanager = new CellManager(this);
this.bodyRenderer = new BodyRenderer(this);
if (this.options.data) {
this.refresh();
prepare() {
this.prepareDom();
this.unfreeze();
}
}
prepare() {
this.prepareDom();
this.unfreeze();
}
prepareDom() {
this.wrapper.innerHTML = `
prepareDom() {
this.wrapper.innerHTML = `
<div class="data-table">
<table class="data-table-header">
</table>
@ -67,110 +65,110 @@ class DataTable {
</div>
`;
this.datatableWrapper = $('.data-table', this.wrapper);
this.header = $('.data-table-header', this.wrapper);
this.bodyScrollable = $('.body-scrollable', this.wrapper);
this.freezeContainer = $('.freeze-container', this.wrapper);
}
refresh(data) {
this.datamanager.init(data);
this.render();
this.setDimensions();
}
destroy() {
this.wrapper.innerHTML = '';
this.style.destroy();
}
appendRows(rows) {
this.datamanager.appendRows(rows);
this.rowmanager.refreshRows();
}
refreshRow(row, rowIndex) {
this.rowmanager.refreshRow(row, rowIndex);
}
render() {
this.renderHeader();
this.renderBody();
}
renderHeader() {
this.columnmanager.renderHeader();
}
renderBody() {
this.bodyRenderer.render();
}
setDimensions() {
this.style.setDimensions();
}
getColumn(colIndex) {
return this.datamanager.getColumn(colIndex);
}
getColumns() {
return this.datamanager.getColumns();
}
getRows() {
return this.datamanager.getRows();
}
getCell(colIndex, rowIndex) {
return this.datamanager.getCell(colIndex, rowIndex);
}
getColumnHeaderElement(colIndex) {
return this.columnmanager.getColumnHeaderElement(colIndex);
}
getViewportHeight() {
if (!this.viewportHeight) {
this.viewportHeight = $.style(this.bodyScrollable, 'height');
this.datatableWrapper = $('.data-table', this.wrapper);
this.header = $('.data-table-header', this.wrapper);
this.bodyScrollable = $('.body-scrollable', this.wrapper);
this.freezeContainer = $('.freeze-container', this.wrapper);
}
return this.viewportHeight;
}
sortColumn(colIndex, sortOrder) {
this.columnmanager.sortColumn(colIndex, sortOrder);
}
removeColumn(colIndex) {
this.columnmanager.removeColumn(colIndex);
}
scrollToLastColumn() {
this.datatableWrapper.scrollLeft = 9999;
}
freeze() {
$.style(this.freezeContainer, {
display: ''
});
}
unfreeze() {
$.style(this.freezeContainer, {
display: 'none'
});
}
fireEvent(eventName, ...args) {
this.events[eventName].apply(this, args);
}
log() {
if (this.options.enableLogs) {
console.log.apply(console, arguments);
refresh(data) {
this.datamanager.init(data);
this.render();
this.setDimensions();
}
destroy() {
this.wrapper.innerHTML = '';
this.style.destroy();
}
appendRows(rows) {
this.datamanager.appendRows(rows);
this.rowmanager.refreshRows();
}
refreshRow(row, rowIndex) {
this.rowmanager.refreshRow(row, rowIndex);
}
render() {
this.renderHeader();
this.renderBody();
}
renderHeader() {
this.columnmanager.renderHeader();
}
renderBody() {
this.bodyRenderer.render();
}
setDimensions() {
this.style.setDimensions();
}
getColumn(colIndex) {
return this.datamanager.getColumn(colIndex);
}
getColumns() {
return this.datamanager.getColumns();
}
getRows() {
return this.datamanager.getRows();
}
getCell(colIndex, rowIndex) {
return this.datamanager.getCell(colIndex, rowIndex);
}
getColumnHeaderElement(colIndex) {
return this.columnmanager.getColumnHeaderElement(colIndex);
}
getViewportHeight() {
if (!this.viewportHeight) {
this.viewportHeight = $.style(this.bodyScrollable, 'height');
}
return this.viewportHeight;
}
sortColumn(colIndex, sortOrder) {
this.columnmanager.sortColumn(colIndex, sortOrder);
}
removeColumn(colIndex) {
this.columnmanager.removeColumn(colIndex);
}
scrollToLastColumn() {
this.datatableWrapper.scrollLeft = 9999;
}
freeze() {
$.style(this.freezeContainer, {
display: ''
});
}
unfreeze() {
$.style(this.freezeContainer, {
display: 'none'
});
}
fireEvent(eventName, ...args) {
this.events[eventName].apply(this, args);
}
log() {
if (this.options.enableLogs) {
console.log.apply(console, arguments);
}
}
}
}
DataTable.instances = 0;

View File

@ -1,51 +1,51 @@
export default {
columns: [],
data: [],
dropdownButton: '▼',
headerDropdown: [
{
label: 'Sort Ascending',
action: function (column) {
this.sortColumn(column.colIndex, 'asc');
}
columns: [],
data: [],
dropdownButton: '▼',
headerDropdown: [
{
label: 'Sort Ascending',
action: function (column) {
this.sortColumn(column.colIndex, 'asc');
}
},
{
label: 'Sort Descending',
action: function (column) {
this.sortColumn(column.colIndex, 'desc');
}
},
{
label: 'Reset sorting',
action: function (column) {
this.sortColumn(column.colIndex, 'none');
}
},
{
label: 'Remove column',
action: function (column) {
this.removeColumn(column.colIndex);
}
}
],
events: {
onRemoveColumn(column) {},
onSwitchColumn(column1, column2) {},
onSortColumn(column) {}
},
{
label: 'Sort Descending',
action: function (column) {
this.sortColumn(column.colIndex, 'desc');
}
sortIndicator: {
asc: '↑',
desc: '↓',
none: ''
},
{
label: 'Reset sorting',
action: function (column) {
this.sortColumn(column.colIndex, 'none');
}
},
{
label: 'Remove column',
action: function (column) {
this.removeColumn(column.colIndex);
}
}
],
events: {
onRemoveColumn(column) {},
onSwitchColumn(column1, column2) {},
onSortColumn(column) {}
},
sortIndicator: {
asc: '↑',
desc: '↓',
none: ''
},
freezeMessage: '',
getEditor: () => {},
addSerialNoColumn: true,
addCheckboxColumn: false,
enableClusterize: true,
enableLogs: false,
layout: 'fixed', // fixed, fluid
noDataMessage: 'No Data',
cellHeight: null,
enableInlineFilters: false
freezeMessage: '',
getEditor: () => {},
addSerialNoColumn: true,
addCheckboxColumn: false,
enableClusterize: true,
enableLogs: false,
layout: 'fixed', // fixed, fluid
noDataMessage: 'No Data',
cellHeight: null,
enableInlineFilters: false
};

View File

@ -1,172 +1,181 @@
export default function $(expr, con) {
return typeof expr === 'string' ?
(con || document).querySelector(expr) :
expr || null;
return typeof expr === 'string' ?
(con || document).querySelector(expr) :
expr || null;
}
$.each = (expr, con) => {
return typeof expr === 'string' ?
Array.from((con || document).querySelectorAll(expr)) :
expr || null;
return typeof expr === 'string' ?
Array.from((con || document).querySelectorAll(expr)) :
expr || null;
};
$.create = (tag, o) => {
let element = document.createElement(tag);
let element = document.createElement(tag);
for (let i in o) {
let val = o[i];
for (let i in o) {
let val = o[i];
if (i === 'inside') {
$(val).appendChild(element);
} else
if (i === 'around') {
let ref = $(val);
ref.parentNode.insertBefore(element, ref);
element.appendChild(ref);
} else
if (i === 'styles') {
if (typeof val === 'object') {
Object.keys(val).map(prop => {
element.style[prop] = val[prop];
});
}
if (i === 'inside') {
$(val).appendChild(element);
} else
if (i in element) {
if (i === 'around') {
let ref = $(val);
ref.parentNode.insertBefore(element, ref);
element.appendChild(ref);
} else
if (i === 'styles') {
if (typeof val === 'object') {
Object.keys(val).map(prop => {
element.style[prop] = val[prop];
});
}
} else
if (i in element) {
element[i] = val;
} else {
} else {
element.setAttribute(i, val);
}
}
}
}
return element;
return element;
};
$.on = (element, event, selector, callback) => {
if (!callback) {
callback = selector;
$.bind(element, event, callback);
} else {
$.delegate(element, event, selector, callback);
}
if (!callback) {
callback = selector;
$.bind(element, event, callback);
} else {
$.delegate(element, event, selector, callback);
}
};
$.off = (element, event, handler) => {
element.removeEventListener(event, handler);
element.removeEventListener(event, handler);
};
$.bind = (element, event, callback) => {
event.split(/\s+/).forEach(function (event) {
element.addEventListener(event, callback);
});
event.split(/\s+/).forEach(function (event) {
element.addEventListener(event, callback);
});
};
$.delegate = (element, event, selector, callback) => {
element.addEventListener(event, function (e) {
const delegatedTarget = e.target.closest(selector);
if (delegatedTarget) {
e.delegatedTarget = delegatedTarget;
callback.call(this, e, delegatedTarget);
}
});
element.addEventListener(event, function (e) {
const delegatedTarget = e.target.closest(selector);
if (delegatedTarget) {
e.delegatedTarget = delegatedTarget;
callback.call(this, e, delegatedTarget);
}
});
};
$.unbind = (element, o) => {
if (element) {
for (let event in o) {
let callback = o[event];
if (element) {
for (let event in o) {
let callback = o[event];
event.split(/\s+/).forEach(function (event) {
element.removeEventListener(event, callback);
});
event.split(/\s+/).forEach(function (event) {
element.removeEventListener(event, callback);
});
}
}
}
};
$.fire = (target, type, properties) => {
let evt = document.createEvent('HTMLEvents');
let evt = document.createEvent('HTMLEvents');
evt.initEvent(type, true, true);
evt.initEvent(type, true, true);
for (let j in properties) {
evt[j] = properties[j];
}
for (let j in properties) {
evt[j] = properties[j];
}
return target.dispatchEvent(evt);
return target.dispatchEvent(evt);
};
$.data = (element, attrs) => { // eslint-disable-line
if (!attrs) {
return element.dataset;
}
if (!attrs) {
return element.dataset;
}
for (const attr in attrs) {
element.dataset[attr] = attrs[attr];
}
for (const attr in attrs) {
element.dataset[attr] = attrs[attr];
}
};
$.style = (elements, styleMap) => { // eslint-disable-line
if (typeof styleMap === 'string') {
return $.getStyle(elements, styleMap);
}
if (!Array.isArray(elements)) {
elements = [elements];
}
elements.map(element => {
for (const prop in styleMap) {
element.style[prop] = styleMap[prop];
if (typeof styleMap === 'string') {
return $.getStyle(elements, styleMap);
}
});
if (!Array.isArray(elements)) {
elements = [elements];
}
elements.map(element => {
for (const prop in styleMap) {
element.style[prop] = styleMap[prop];
}
});
};
$.removeStyle = (elements, styleProps) => {
if (!Array.isArray(elements)) {
elements = [elements];
}
if (!Array.isArray(styleProps)) {
styleProps = [styleProps];
}
elements.map(element => {
for (const prop of styleProps) {
element.style[prop] = '';
if (!Array.isArray(elements)) {
elements = [elements];
}
});
if (!Array.isArray(styleProps)) {
styleProps = [styleProps];
}
elements.map(element => {
for (const prop of styleProps) {
element.style[prop] = '';
}
});
};
$.getStyle = (element, prop) => {
let val = getComputedStyle(element)[prop];
let val = getComputedStyle(element)[prop];
if (['width', 'height'].includes(prop)) {
val = parseFloat(val);
}
if (['width', 'height'].includes(prop)) {
val = parseFloat(val);
}
return val;
return val;
};
$.closest = (selector, element) => {
if (!element) return null;
if (!element) return null;
if (element.matches(selector)) {
return element;
}
if (element.matches(selector)) {
return element;
}
return $.closest(selector, element.parentNode);
return $.closest(selector, element.parentNode);
};
$.inViewport = (el, parentEl) => {
const { top, left, bottom, right } = el.getBoundingClientRect();
const { top: pTop, left: pLeft, bottom: pBottom, right: pRight } = parentEl.getBoundingClientRect();
const {
top,
left,
bottom,
right
} = el.getBoundingClientRect();
const {
top: pTop,
left: pLeft,
bottom: pBottom,
right: pRight
} = parentEl.getBoundingClientRect();
return top >= pTop && left >= pLeft && bottom <= pBottom && right <= pRight;
return top >= pTop && left >= pLeft && bottom <= pBottom && right <= pRight;
};
$.scrollTop = function scrollTop(element, pixels) {
requestAnimationFrame(() => {
element.scrollTop = pixels;
});
requestAnimationFrame(() => {
element.scrollTop = pixels;
});
};

View File

@ -1,56 +1,56 @@
import $ from './dom';
const KEYCODES = {
13: 'enter',
91: 'meta',
16: 'shift',
17: 'ctrl',
18: 'alt',
37: 'left',
38: 'up',
39: 'right',
40: 'down',
9: 'tab',
27: 'esc',
67: 'c',
70: 'f'
13: 'enter',
91: 'meta',
16: 'shift',
17: 'ctrl',
18: 'alt',
37: 'left',
38: 'up',
39: 'right',
40: 'down',
9: 'tab',
27: 'esc',
67: 'c',
70: 'f'
};
export default class Keyboard {
constructor(element) {
this.listeners = {};
$.on(element, 'keydown', this.handler.bind(this));
}
handler(e) {
let key = KEYCODES[e.keyCode];
if (e.shiftKey && key !== 'shift') {
key = 'shift+' + key;
constructor(element) {
this.listeners = {};
$.on(element, 'keydown', this.handler.bind(this));
}
if ((e.ctrlKey && key !== 'ctrl') || (e.metaKey && key !== 'meta')) {
key = 'ctrl+' + key;
}
handler(e) {
let key = KEYCODES[e.keyCode];
const listeners = this.listeners[key];
if (listeners && listeners.length > 0) {
for (let listener of listeners) {
const preventBubbling = listener(e);
if (preventBubbling === undefined || preventBubbling === true) {
e.preventDefault();
if (e.shiftKey && key !== 'shift') {
key = 'shift+' + key;
}
if ((e.ctrlKey && key !== 'ctrl') || (e.metaKey && key !== 'meta')) {
key = 'ctrl+' + key;
}
const listeners = this.listeners[key];
if (listeners && listeners.length > 0) {
for (let listener of listeners) {
const preventBubbling = listener(e);
if (preventBubbling === undefined || preventBubbling === true) {
e.preventDefault();
}
}
}
}
}
}
on(key, listener) {
const keys = key.split(',').map(k => k.trim());
on(key, listener) {
const keys = key.split(',').map(k => k.trim());
keys.map(key => {
this.listeners[key] = this.listeners[key] || [];
this.listeners[key].push(listener);
});
}
keys.map(key => {
this.listeners[key] = this.listeners[key] || [];
this.listeners[key].push(listener);
});
}
}

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,210 +1,224 @@
import $ from './dom';
import { makeDataAttributeString, promisify } from './utils';
import {
makeDataAttributeString,
promisify
} from './utils';
export default class RowManager {
constructor(instance) {
this.instance = instance;
this.options = this.instance.options;
this.wrapper = this.instance.wrapper;
this.bodyScrollable = this.instance.bodyScrollable;
constructor(instance) {
this.instance = instance;
this.options = this.instance.options;
this.wrapper = this.instance.wrapper;
this.bodyScrollable = this.instance.bodyScrollable;
this.bindEvents();
this.refreshRows = promisify(this.refreshRows, this);
}
get datamanager() {
return this.instance.datamanager;
}
get cellmanager() {
return this.instance.cellmanager;
}
bindEvents() {
this.bindCheckbox();
}
bindCheckbox() {
if (!this.options.addCheckboxColumn) return;
// map of checked rows
this.checkMap = [];
$.on(this.wrapper, 'click', '.data-table-col[data-col-index="0"] [type="checkbox"]', (e, $checkbox) => {
const $cell = $checkbox.closest('.data-table-col');
const { rowIndex, isHeader } = $.data($cell);
const checked = $checkbox.checked;
if (isHeader) {
this.checkAll(checked);
} else {
this.checkRow(rowIndex, checked);
}
});
}
refreshRows() {
this.instance.renderBody();
this.instance.setDimensions();
}
refreshRow(row, rowIndex) {
const _row = this.datamanager.updateRow(row, rowIndex);
_row.forEach(cell => {
this.cellmanager.refreshCell(cell);
});
}
getCheckedRows() {
if (!this.checkMap) {
return [];
this.bindEvents();
this.refreshRows = promisify(this.refreshRows, this);
}
return this.checkMap
.map((c, rowIndex) => {
if (c) {
return rowIndex;
get datamanager() {
return this.instance.datamanager;
}
get cellmanager() {
return this.instance.cellmanager;
}
bindEvents() {
this.bindCheckbox();
}
bindCheckbox() {
if (!this.options.addCheckboxColumn) return;
// map of checked rows
this.checkMap = [];
$.on(this.wrapper, 'click', '.data-table-col[data-col-index="0"] [type="checkbox"]', (e, $checkbox) => {
const $cell = $checkbox.closest('.data-table-col');
const {
rowIndex,
isHeader
} = $.data($cell);
const checked = $checkbox.checked;
if (isHeader) {
this.checkAll(checked);
} else {
this.checkRow(rowIndex, checked);
}
});
}
refreshRows() {
this.instance.renderBody();
this.instance.setDimensions();
}
refreshRow(row, rowIndex) {
const _row = this.datamanager.updateRow(row, rowIndex);
_row.forEach(cell => {
this.cellmanager.refreshCell(cell);
});
}
getCheckedRows() {
if (!this.checkMap) {
return [];
}
return null;
})
.filter(c => {
return c !== null || c !== undefined;
});
}
highlightCheckedRows() {
this.getCheckedRows()
.map(rowIndex => this.checkRow(rowIndex, true));
}
checkRow(rowIndex, toggle) {
const value = toggle ? 1 : 0;
// update internal map
this.checkMap[rowIndex] = value;
// set checkbox value explicitly
$.each(`.data-table-col[data-row-index="${rowIndex}"][data-col-index="0"] [type="checkbox"]`, this.bodyScrollable)
.map(input => {
input.checked = toggle;
});
// highlight row
this.highlightRow(rowIndex, toggle);
}
checkAll(toggle) {
const value = toggle ? 1 : 0;
// update internal map
if (toggle) {
this.checkMap = Array.from(Array(this.getTotalRows())).map(c => value);
} else {
this.checkMap = [];
}
// set checkbox value
$.each('.data-table-col[data-col-index="0"] [type="checkbox"]', this.bodyScrollable)
.map(input => {
input.checked = toggle;
});
// highlight all
this.highlightAll(toggle);
}
highlightRow(rowIndex, toggle = true) {
const $row = this.getRow$(rowIndex);
if (!$row) return;
if (!toggle && this.bodyScrollable.classList.contains('row-highlight-all')) {
$row.classList.add('row-unhighlight');
return;
return this.checkMap
.map((c, rowIndex) => {
if (c) {
return rowIndex;
}
return null;
})
.filter(c => {
return c !== null || c !== undefined;
});
}
if (toggle && $row.classList.contains('row-unhighlight')) {
$row.classList.remove('row-unhighlight');
highlightCheckedRows() {
this.getCheckedRows()
.map(rowIndex => this.checkRow(rowIndex, true));
}
this._highlightedRows = this._highlightedRows || {};
if (toggle) {
$row.classList.add('row-highlight');
this._highlightedRows[rowIndex] = $row;
} else {
$row.classList.remove('row-highlight');
delete this._highlightedRows[rowIndex];
}
}
highlightAll(toggle = true) {
if (toggle) {
this.bodyScrollable.classList.add('row-highlight-all');
} else {
this.bodyScrollable.classList.remove('row-highlight-all');
for (const rowIndex in this._highlightedRows) {
const $row = this._highlightedRows[rowIndex];
$row.classList.remove('row-highlight');
}
this._highlightedRows = {};
}
}
getRow$(rowIndex) {
return $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable);
}
getTotalRows() {
return this.datamanager.getRowCount();
}
getFirstRowIndex() {
return 0;
}
getLastRowIndex() {
return this.datamanager.getRowCount() - 1;
}
scrollToRow(rowIndex) {
rowIndex = +rowIndex;
this._lastScrollTo = this._lastScrollTo || 0;
const $row = this.getRow$(rowIndex);
if ($.inViewport($row, this.bodyScrollable)) return;
const { height } = $row.getBoundingClientRect();
const { top, bottom } = this.bodyScrollable.getBoundingClientRect();
const rowsInView = Math.floor((bottom - top) / height);
let offset = 0;
if (rowIndex > this._lastScrollTo) {
offset = height * ((rowIndex + 1) - rowsInView);
} else {
offset = height * ((rowIndex + 1) - 1);
checkRow(rowIndex, toggle) {
const value = toggle ? 1 : 0;
const selector = rowIndex =>
`.data-table-col[data-row-index="${rowIndex}"][data-col-index="0"] [type="checkbox"]`;
// update internal map
this.checkMap[rowIndex] = value;
// set checkbox value explicitly
$.each(selector(rowIndex), this.bodyScrollable)
.map(input => {
input.checked = toggle;
});
// highlight row
this.highlightRow(rowIndex, toggle);
}
this._lastScrollTo = rowIndex;
$.scrollTop(this.bodyScrollable, offset);
}
checkAll(toggle) {
const value = toggle ? 1 : 0;
getRowHTML(row, props) {
const dataAttr = makeDataAttributeString(props);
if (props.isFilter) {
row = row.map(cell => (Object.assign(cell, {
content: this.getFilterInput({ colIndex: cell.colIndex }),
isFilter: 1,
isHeader: undefined,
editable: false
})));
// update internal map
if (toggle) {
this.checkMap = Array.from(Array(this.getTotalRows())).map(c => value);
} else {
this.checkMap = [];
}
// set checkbox value
$.each('.data-table-col[data-col-index="0"] [type="checkbox"]', this.bodyScrollable)
.map(input => {
input.checked = toggle;
});
// highlight all
this.highlightAll(toggle);
}
return `
<tr class="data-table-row" ${dataAttr}>
${row.map(cell => this.cellmanager.getCellHTML(cell)).join('')}
</tr>
`;
}
highlightRow(rowIndex, toggle = true) {
const $row = this.getRow$(rowIndex);
if (!$row) return;
getFilterInput(props) {
const dataAttr = makeDataAttributeString(props);
return `<input class="data-table-filter input-style" type="text" ${dataAttr} />`;
}
if (!toggle && this.bodyScrollable.classList.contains('row-highlight-all')) {
$row.classList.add('row-unhighlight');
return;
}
if (toggle && $row.classList.contains('row-unhighlight')) {
$row.classList.remove('row-unhighlight');
}
this._highlightedRows = this._highlightedRows || {};
if (toggle) {
$row.classList.add('row-highlight');
this._highlightedRows[rowIndex] = $row;
} else {
$row.classList.remove('row-highlight');
delete this._highlightedRows[rowIndex];
}
}
highlightAll(toggle = true) {
if (toggle) {
this.bodyScrollable.classList.add('row-highlight-all');
} else {
this.bodyScrollable.classList.remove('row-highlight-all');
for (const rowIndex in this._highlightedRows) {
const $row = this._highlightedRows[rowIndex];
$row.classList.remove('row-highlight');
}
this._highlightedRows = {};
}
}
getRow$(rowIndex) {
return $(`.data-table-row[data-row-index="${rowIndex}"]`, this.bodyScrollable);
}
getTotalRows() {
return this.datamanager.getRowCount();
}
getFirstRowIndex() {
return 0;
}
getLastRowIndex() {
return this.datamanager.getRowCount() - 1;
}
scrollToRow(rowIndex) {
rowIndex = +rowIndex;
this._lastScrollTo = this._lastScrollTo || 0;
const $row = this.getRow$(rowIndex);
if ($.inViewport($row, this.bodyScrollable)) return;
const {
height
} = $row.getBoundingClientRect();
const {
top,
bottom
} = this.bodyScrollable.getBoundingClientRect();
const rowsInView = Math.floor((bottom - top) / height);
let offset = 0;
if (rowIndex > this._lastScrollTo) {
offset = height * ((rowIndex + 1) - rowsInView);
} else {
offset = height * ((rowIndex + 1) - 1);
}
this._lastScrollTo = rowIndex;
$.scrollTop(this.bodyScrollable, offset);
}
getRowHTML(row, props) {
const dataAttr = makeDataAttributeString(props);
if (props.isFilter) {
row = row.map(cell => (Object.assign(cell, {
content: this.getFilterInput({
colIndex: cell.colIndex
}),
isFilter: 1,
isHeader: undefined,
editable: false
})));
}
return `
<tr class="data-table-row" ${dataAttr}>
${row.map(cell => this.cellmanager.getCellHTML(cell)).join('')}
</tr>
`;
}
getFilterInput(props) {
const dataAttr = makeDataAttributeString(props);
return `<input class="data-table-filter input-style" type="text" ${dataAttr} />`;
}
}

View File

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

View File

@ -39,7 +39,7 @@ export default class Style {
this.styleEl.remove();
}
setStyle(rule, styleMap, index = -1) {
setStyle(selector, styleMap, index = -1) {
const styles = Object.keys(styleMap)
.map(prop => {
if (!prop.includes('-')) {
@ -48,7 +48,12 @@ export default class Style {
return `${prop}:${styleMap[prop]};`;
})
.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;
if (index !== -1) {

View File

@ -2,153 +2,153 @@ import _throttle from 'lodash/throttle';
import _debounce from 'lodash/debounce';
export function camelCaseToDash(str) {
return str.replace(/([A-Z])/g, (g) => `-${g[0].toLowerCase()}`);
return str.replace(/([A-Z])/g, (g) => `-${g[0].toLowerCase()}`);
}
export function makeDataAttributeString(props) {
const keys = Object.keys(props);
const keys = Object.keys(props);
return keys
.map((key) => {
const _key = camelCaseToDash(key);
const val = props[key];
return keys
.map((key) => {
const _key = camelCaseToDash(key);
const val = props[key];
if (val === undefined) return '';
return `data-${_key}="${val}" `;
})
.join('')
.trim();
if (val === undefined) return '';
return `data-${_key}="${val}" `;
})
.join('')
.trim();
}
export function getDefault(a, b) {
return a !== undefined ? a : b;
return a !== undefined ? a : b;
}
export function escapeRegExp(str) {
// https://stackoverflow.com/a/6969486
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
// https://stackoverflow.com/a/6969486
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
}
export function getCSSString(styleMap) {
let style = '';
let style = '';
for (const prop in styleMap) {
if (styleMap.hasOwnProperty(prop)) {
style += `${prop}: ${styleMap[prop]}; `;
for (const prop in styleMap) {
if (styleMap.hasOwnProperty(prop)) {
style += `${prop}: ${styleMap[prop]}; `;
}
}
}
return style.trim();
return style.trim();
}
export function getCSSRuleBlock(rule, styleMap) {
return `${rule} { ${getCSSString(styleMap)} }`;
return `${rule} { ${getCSSString(styleMap)} }`;
}
export function buildCSSRule(rule, styleMap, cssRulesString = '') {
// build css rules efficiently,
// append new rule if doesnt exist,
// update existing ones
// build css rules efficiently,
// append new rule if doesnt exist,
// update existing ones
const rulePatternStr = `${escapeRegExp(rule)} {([^}]*)}`;
const rulePattern = new RegExp(rulePatternStr, 'g');
const rulePatternStr = `${escapeRegExp(rule)} {([^}]*)}`;
const rulePattern = new RegExp(rulePatternStr, 'g');
if (cssRulesString && cssRulesString.match(rulePattern)) {
for (const property in styleMap) {
const value = styleMap[property];
const propPattern = new RegExp(`${escapeRegExp(property)}:([^;]*);`);
if (cssRulesString && cssRulesString.match(rulePattern)) {
for (const property in styleMap) {
const value = styleMap[property];
const propPattern = new RegExp(`${escapeRegExp(property)}:([^;]*);`);
cssRulesString = cssRulesString.replace(rulePattern, function (match, propertyStr) {
if (propertyStr.match(propPattern)) {
// property exists, replace value with new value
propertyStr = propertyStr.replace(propPattern, (match, valueStr) => {
return `${property}: ${value};`;
});
cssRulesString = cssRulesString.replace(rulePattern, function (match, propertyStr) {
if (propertyStr.match(propPattern)) {
// property exists, replace value with new value
propertyStr = propertyStr.replace(propPattern, (match, valueStr) => {
return `${property}: ${value};`;
});
}
propertyStr = propertyStr.trim();
const replacer =
`${rule} { ${propertyStr} }`;
return replacer;
});
}
propertyStr = propertyStr.trim();
const replacer =
`${rule} { ${propertyStr} }`;
return replacer;
});
return cssRulesString;
}
return cssRulesString;
}
// no match, append new rule block
return `${cssRulesString}${getCSSRuleBlock(rule, styleMap)}`;
// no match, append new rule block
return `${cssRulesString}${getCSSRuleBlock(rule, styleMap)}`;
}
export function removeCSSRule(rule, cssRulesString = '') {
const rulePatternStr = `${escapeRegExp(rule)} {([^}]*)}`;
const rulePattern = new RegExp(rulePatternStr, 'g');
let output = cssRulesString;
const rulePatternStr = `${escapeRegExp(rule)} {([^}]*)}`;
const rulePattern = new RegExp(rulePatternStr, 'g');
let output = cssRulesString;
if (cssRulesString && cssRulesString.match(rulePattern)) {
output = cssRulesString.replace(rulePattern, '');
}
if (cssRulesString && cssRulesString.match(rulePattern)) {
output = cssRulesString.replace(rulePattern, '');
}
return output.trim();
return output.trim();
}
export function copyTextToClipboard(text) {
// https://stackoverflow.com/a/30810322/5353542
var textArea = document.createElement('textarea');
// https://stackoverflow.com/a/30810322/5353542
var textArea = document.createElement('textarea');
//
// *** This styling is an extra step which is likely not required. ***
//
// Why is it here? To ensure:
// 1. the element is able to have focus and selection.
// 2. if element was to flash render it has minimal visual impact.
// 3. less flakyness with selection and copying which **might** occur if
// the textarea element is not visible.
//
// The likelihood is the element won't even render, not even a flash,
// so some of these are just precautions. However in IE the element
// is visible whilst the popup box asking the user for permission for
// the web page to copy to the clipboard.
//
//
// *** This styling is an extra step which is likely not required. ***
//
// Why is it here? To ensure:
// 1. the element is able to have focus and selection.
// 2. if element was to flash render it has minimal visual impact.
// 3. less flakyness with selection and copying which **might** occur if
// the textarea element is not visible.
//
// The likelihood is the element won't even render, not even a flash,
// so some of these are just precautions. However in IE the element
// is visible whilst the popup box asking the user for permission for
// the web page to copy to the clipboard.
//
// Place in top-left corner of screen regardless of scroll position.
textArea.style.position = 'fixed';
textArea.style.top = 0;
textArea.style.left = 0;
// Place in top-left corner of screen regardless of scroll position.
textArea.style.position = 'fixed';
textArea.style.top = 0;
textArea.style.left = 0;
// Ensure it has a small width and height. Setting to 1px / 1em
// doesn't work as this gives a negative w/h on some browsers.
textArea.style.width = '2em';
textArea.style.height = '2em';
// Ensure it has a small width and height. Setting to 1px / 1em
// doesn't work as this gives a negative w/h on some browsers.
textArea.style.width = '2em';
textArea.style.height = '2em';
// We don't need padding, reducing the size if it does flash render.
textArea.style.padding = 0;
// We don't need padding, reducing the size if it does flash render.
textArea.style.padding = 0;
// Clean up any borders.
textArea.style.border = 'none';
textArea.style.outline = 'none';
textArea.style.boxShadow = 'none';
// Clean up any borders.
textArea.style.border = 'none';
textArea.style.outline = 'none';
textArea.style.boxShadow = 'none';
// Avoid flash of white box if rendered for any reason.
textArea.style.background = 'transparent';
// Avoid flash of white box if rendered for any reason.
textArea.style.background = 'transparent';
textArea.value = text;
textArea.value = text;
document.body.appendChild(textArea);
document.body.appendChild(textArea);
textArea.select();
textArea.select();
try {
document.execCommand('copy');
} catch (err) {
console.log('Oops, unable to copy');
}
try {
document.execCommand('copy');
} catch (err) {
console.log('Oops, unable to copy');
}
document.body.removeChild(textArea);
document.body.removeChild(textArea);
}
export function isNumeric(val) {
return !isNaN(val);
return !isNaN(val);
}
export let throttle = _throttle;
@ -156,30 +156,30 @@ export let throttle = _throttle;
export let debounce = _debounce;
export function promisify(fn, context = null) {
return (...args) => {
return new Promise(resolve => {
setTimeout(() => {
const out = fn.apply(context, args);
resolve(out);
}, 0);
});
};
return (...args) => {
return new Promise(resolve => {
setTimeout(() => {
const out = fn.apply(context, args);
resolve(out);
}, 0);
});
};
};
export function chainPromises(promises) {
return promises.reduce(
(prev, cur) => prev.then(cur), Promise.resolve()
);
return promises.reduce(
(prev, cur) => prev.then(cur), Promise.resolve()
);
};
export function linkProperties(target, source, properties) {
const props = properties.reduce((acc, prop) => {
acc[prop] = {
get() {
return source[prop];
}
};
return acc;
}, {});
Object.defineProperties(target, props);
const props = properties.reduce((acc, prop) => {
acc[prop] = {
get() {
return source[prop];
}
};
return acc;
}, {});
Object.defineProperties(target, props);
};