x-data-spreadsheet
Version:
a javascript xpreadsheet
982 lines (921 loc) • 26.9 kB
JavaScript
/* global window */
import { h } from './element';
import {
bind,
mouseMoveUp,
bindTouch,
createEventEmitter,
} from './event';
import Resizer from './resizer';
import Scrollbar from './scrollbar';
import Selector from './selector';
import Editor from './editor';
import Print from './print';
import ContextMenu from './contextmenu';
import Table from './table';
import Toolbar from './toolbar/index';
import ModalValidation from './modal_validation';
import SortFilter from './sort_filter';
import { xtoast } from './message';
import { cssPrefix } from '../config';
import { formulas } from '../core/formula';
/**
* @desc throttle fn
* @param func function
* @param wait Delay in milliseconds
*/
function throttle(func, wait) {
let timeout;
return (...arg) => {
const that = this;
const args = arg;
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(that, args);
}, wait);
}
};
}
function scrollbarMove() {
const {
data, verticalScrollbar, horizontalScrollbar,
} = this;
const {
l, t, left, top, width, height,
} = data.getSelectedRect();
const tableOffset = this.getTableOffset();
// console.log(',l:', l, ', left:', left, ', tOffset.left:', tableOffset.width);
if (Math.abs(left) + width > tableOffset.width) {
horizontalScrollbar.move({ left: l + width - tableOffset.width });
} else {
const fsw = data.freezeTotalWidth();
if (left < fsw) {
horizontalScrollbar.move({ left: l - 1 - fsw });
}
}
// console.log('top:', top, ', height:', height, ', tof.height:', tableOffset.height);
if (Math.abs(top) + height > tableOffset.height) {
verticalScrollbar.move({ top: t + height - tableOffset.height - 1 });
} else {
const fsh = data.freezeTotalHeight();
if (top < fsh) {
verticalScrollbar.move({ top: t - 1 - fsh });
}
}
}
function selectorSet(multiple, ri, ci, indexesUpdated = true, moving = false) {
if (ri === -1 && ci === -1) return;
const {
table, selector, toolbar, data,
contextMenu,
} = this;
contextMenu.setMode((ri === -1 || ci === -1) ? 'row-col' : 'range');
const cell = data.getCell(ri, ci);
if (multiple) {
selector.setEnd(ri, ci, moving);
this.trigger('cells-selected', cell, selector.range);
} else {
// trigger click event
selector.set(ri, ci, indexesUpdated);
this.trigger('cell-selected', cell, ri, ci);
}
toolbar.reset();
table.render();
}
// multiple: boolean
// direction: left | right | up | down | row-first | row-last | col-first | col-last
function selectorMove(multiple, direction) {
const {
selector, data,
} = this;
const { rows, cols } = data;
let [ri, ci] = selector.indexes;
const { eri, eci } = selector.range;
if (multiple) {
[ri, ci] = selector.moveIndexes;
}
// console.log('selector.move:', ri, ci);
if (direction === 'left') {
if (ci > 0) ci -= 1;
} else if (direction === 'right') {
if (eci !== ci) ci = eci;
if (ci < cols.len - 1) ci += 1;
} else if (direction === 'up') {
if (ri > 0) ri -= 1;
} else if (direction === 'down') {
if (eri !== ri) ri = eri;
if (ri < rows.len - 1) ri += 1;
} else if (direction === 'row-first') {
ci = 0;
} else if (direction === 'row-last') {
ci = cols.len - 1;
} else if (direction === 'col-first') {
ri = 0;
} else if (direction === 'col-last') {
ri = rows.len - 1;
}
if (multiple) {
selector.moveIndexes = [ri, ci];
}
selectorSet.call(this, multiple, ri, ci);
scrollbarMove.call(this);
}
// private methods
function overlayerMousemove(evt) {
// console.log('x:', evt.offsetX, ', y:', evt.offsetY);
if (evt.buttons !== 0) return;
if (evt.target.className === `${cssPrefix}-resizer-hover`) return;
const { offsetX, offsetY } = evt;
const {
rowResizer, colResizer, tableEl, data,
} = this;
const { rows, cols } = data;
if (offsetX > cols.indexWidth && offsetY > rows.height) {
rowResizer.hide();
colResizer.hide();
return;
}
const tRect = tableEl.box();
const cRect = data.getCellRectByXY(evt.offsetX, evt.offsetY);
if (cRect.ri >= 0 && cRect.ci === -1) {
cRect.width = cols.indexWidth;
rowResizer.show(cRect, {
width: tRect.width,
});
if (rows.isHide(cRect.ri - 1)) {
rowResizer.showUnhide(cRect.ri);
} else {
rowResizer.hideUnhide();
}
} else {
rowResizer.hide();
}
if (cRect.ri === -1 && cRect.ci >= 0) {
cRect.height = rows.height;
colResizer.show(cRect, {
height: tRect.height,
});
if (cols.isHide(cRect.ci - 1)) {
colResizer.showUnhide(cRect.ci);
} else {
colResizer.hideUnhide();
}
} else {
colResizer.hide();
}
}
// let scrollThreshold = 15;
function overlayerMousescroll(evt) {
// scrollThreshold -= 1;
// if (scrollThreshold > 0) return;
// scrollThreshold = 15;
const { verticalScrollbar, horizontalScrollbar, data } = this;
const { top } = verticalScrollbar.scroll();
const { left } = horizontalScrollbar.scroll();
// console.log('evt:::', evt.wheelDelta, evt.detail * 40);
const { rows, cols } = data;
// deltaY for vertical delta
const { deltaY, deltaX } = evt;
const loopValue = (ii, vFunc) => {
let i = ii;
let v = 0;
do {
v = vFunc(i);
i += 1;
} while (v <= 0);
return v;
};
// console.log('deltaX', deltaX, 'evt.detail', evt.detail);
// if (evt.detail) deltaY = evt.detail * 40;
const moveY = (vertical) => {
if (vertical > 0) {
// up
const ri = data.scroll.ri + 1;
if (ri < rows.len) {
const rh = loopValue(ri, i => rows.getHeight(i));
verticalScrollbar.move({ top: top + rh - 1 });
}
} else {
// down
const ri = data.scroll.ri - 1;
if (ri >= 0) {
const rh = loopValue(ri, i => rows.getHeight(i));
verticalScrollbar.move({ top: ri === 0 ? 0 : top - rh });
}
}
};
// deltaX for Mac horizontal scroll
const moveX = (horizontal) => {
if (horizontal > 0) {
// left
const ci = data.scroll.ci + 1;
if (ci < cols.len) {
const cw = loopValue(ci, i => cols.getWidth(i));
horizontalScrollbar.move({ left: left + cw - 1 });
}
} else {
// right
const ci = data.scroll.ci - 1;
if (ci >= 0) {
const cw = loopValue(ci, i => cols.getWidth(i));
horizontalScrollbar.move({ left: ci === 0 ? 0 : left - cw });
}
}
};
const tempY = Math.abs(deltaY);
const tempX = Math.abs(deltaX);
const temp = Math.max(tempY, tempX);
// console.log('event:', evt);
// detail for windows/mac firefox vertical scroll
if (/Firefox/i.test(window.navigator.userAgent)) throttle(moveY(evt.detail), 50);
if (temp === tempX) throttle(moveX(deltaX), 50);
if (temp === tempY) throttle(moveY(deltaY), 50);
}
function overlayerTouch(direction, distance) {
const { verticalScrollbar, horizontalScrollbar } = this;
const { top } = verticalScrollbar.scroll();
const { left } = horizontalScrollbar.scroll();
if (direction === 'left' || direction === 'right') {
horizontalScrollbar.move({ left: left - distance });
} else if (direction === 'up' || direction === 'down') {
verticalScrollbar.move({ top: top - distance });
}
}
function verticalScrollbarSet() {
const { data, verticalScrollbar } = this;
const { height } = this.getTableOffset();
const erth = data.exceptRowTotalHeight(0, -1);
// console.log('erth:', erth);
verticalScrollbar.set(height, data.rows.totalHeight() - erth);
}
function horizontalScrollbarSet() {
const { data, horizontalScrollbar } = this;
const { width } = this.getTableOffset();
if (data) {
horizontalScrollbar.set(width, data.cols.totalWidth());
}
}
function sheetFreeze() {
const {
selector, data, editor,
} = this;
const [ri, ci] = data.freeze;
if (ri > 0 || ci > 0) {
const fwidth = data.freezeTotalWidth();
const fheight = data.freezeTotalHeight();
editor.setFreezeLengths(fwidth, fheight);
}
selector.resetAreaOffset();
}
function sheetReset() {
const {
tableEl,
overlayerEl,
overlayerCEl,
table,
toolbar,
selector,
el,
} = this;
const tOffset = this.getTableOffset();
const vRect = this.getRect();
tableEl.attr(vRect);
overlayerEl.offset(vRect);
overlayerCEl.offset(tOffset);
el.css('width', `${vRect.width}px`);
verticalScrollbarSet.call(this);
horizontalScrollbarSet.call(this);
sheetFreeze.call(this);
table.render();
toolbar.reset();
selector.reset();
}
function clearClipboard() {
const { data, selector } = this;
data.clearClipboard();
selector.hideClipboard();
}
function copy() {
const { data, selector } = this;
data.copy();
data.copyToSystemClipboard();
selector.showClipboard();
}
function cut() {
const { data, selector } = this;
data.cut();
selector.showClipboard();
}
function paste(what, evt) {
const { data } = this;
if (data.settings.mode === 'read') return;
if (data.paste(what, msg => xtoast('Tip', msg))) {
sheetReset.call(this);
} else if (evt) {
const cdata = evt.clipboardData.getData('text/plain');
this.data.pasteFromText(cdata);
sheetReset.call(this);
}
}
function hideRowsOrCols() {
this.data.hideRowsOrCols();
sheetReset.call(this);
}
function unhideRowsOrCols(type, index) {
this.data.unhideRowsOrCols(type, index);
sheetReset.call(this);
}
function autofilter() {
const { data } = this;
data.autofilter();
sheetReset.call(this);
}
function toolbarChangePaintformatPaste() {
const { toolbar } = this;
if (toolbar.paintformatActive()) {
paste.call(this, 'format');
clearClipboard.call(this);
toolbar.paintformatToggle();
}
}
function overlayerMousedown(evt) {
// console.log(':::::overlayer.mousedown:', evt.detail, evt.button, evt.buttons, evt.shiftKey);
// console.log('evt.target.className:', evt.target.className);
const {
selector, data, table, sortFilter,
} = this;
const { offsetX, offsetY } = evt;
const isAutofillEl = evt.target.className === `${cssPrefix}-selector-corner`;
const cellRect = data.getCellRectByXY(offsetX, offsetY);
const {
left, top, width, height,
} = cellRect;
let { ri, ci } = cellRect;
// sort or filter
const { autoFilter } = data;
if (autoFilter.includes(ri, ci)) {
if (left + width - 20 < offsetX && top + height - 20 < offsetY) {
const items = autoFilter.items(ci, (r, c) => data.rows.getCell(r, c));
sortFilter.hide();
sortFilter.set(ci, items, autoFilter.getFilter(ci), autoFilter.getSort(ci));
sortFilter.setOffset({ left, top: top + height + 2 });
return;
}
}
// console.log('ri:', ri, ', ci:', ci);
if (!evt.shiftKey) {
// console.log('selectorSetStart:::');
if (isAutofillEl) {
selector.showAutofill(ri, ci);
} else {
selectorSet.call(this, false, ri, ci);
}
// mouse move up
mouseMoveUp(window, (e) => {
// console.log('mouseMoveUp::::');
({ ri, ci } = data.getCellRectByXY(e.offsetX, e.offsetY));
if (isAutofillEl) {
selector.showAutofill(ri, ci);
} else if (e.buttons === 1 && !e.shiftKey) {
selectorSet.call(this, true, ri, ci, true, true);
}
}, () => {
if (isAutofillEl && selector.arange && data.settings.mode !== 'read') {
if (data.autofill(selector.arange, 'all', msg => xtoast('Tip', msg))) {
table.render();
}
}
selector.hideAutofill();
toolbarChangePaintformatPaste.call(this);
});
}
if (!isAutofillEl && evt.buttons === 1) {
if (evt.shiftKey) {
// console.log('shiftKey::::');
selectorSet.call(this, true, ri, ci);
}
}
}
function editorSetOffset() {
const { editor, data } = this;
const sOffset = data.getSelectedRect();
const tOffset = this.getTableOffset();
let sPosition = 'top';
// console.log('sOffset:', sOffset, ':', tOffset);
if (sOffset.top > tOffset.height / 2) {
sPosition = 'bottom';
}
editor.setOffset(sOffset, sPosition);
}
function editorSet() {
const { editor, data } = this;
if (data.settings.mode === 'read') return;
editorSetOffset.call(this);
editor.setCell(data.getSelectedCell(), data.getSelectedValidator());
clearClipboard.call(this);
}
function verticalScrollbarMove(distance) {
const { data, table, selector } = this;
data.scrolly(distance, () => {
selector.resetBRLAreaOffset();
editorSetOffset.call(this);
table.render();
});
}
function horizontalScrollbarMove(distance) {
const { data, table, selector } = this;
data.scrollx(distance, () => {
selector.resetBRTAreaOffset();
editorSetOffset.call(this);
table.render();
});
}
function rowResizerFinished(cRect, distance) {
const { ri } = cRect;
const { table, selector, data } = this;
data.rows.setHeight(ri, distance);
table.render();
selector.resetAreaOffset();
verticalScrollbarSet.call(this);
editorSetOffset.call(this);
}
function colResizerFinished(cRect, distance) {
const { ci } = cRect;
const { table, selector, data } = this;
data.cols.setWidth(ci, distance);
// console.log('data:', data);
table.render();
selector.resetAreaOffset();
horizontalScrollbarSet.call(this);
editorSetOffset.call(this);
}
function dataSetCellText(text, state = 'finished') {
const { data, table } = this;
// const [ri, ci] = selector.indexes;
if (data.settings.mode === 'read') return;
data.setSelectedCellText(text, state);
const { ri, ci } = data.selector;
if (state === 'finished') {
table.render();
} else {
this.trigger('cell-edited', text, ri, ci);
}
}
function insertDeleteRowColumn(type) {
const { data } = this;
if (data.settings.mode === 'read') return;
if (type === 'insert-row') {
data.insert('row');
} else if (type === 'delete-row') {
data.delete('row');
} else if (type === 'insert-column') {
data.insert('column');
} else if (type === 'delete-column') {
data.delete('column');
} else if (type === 'delete-cell') {
data.deleteCell();
} else if (type === 'delete-cell-format') {
data.deleteCell('format');
} else if (type === 'delete-cell-text') {
data.deleteCell('text');
} else if (type === 'cell-printable') {
data.setSelectedCellAttr('printable', true);
} else if (type === 'cell-non-printable') {
data.setSelectedCellAttr('printable', false);
} else if (type === 'cell-editable') {
data.setSelectedCellAttr('editable', true);
} else if (type === 'cell-non-editable') {
data.setSelectedCellAttr('editable', false);
}
clearClipboard.call(this);
sheetReset.call(this);
}
function toolbarChange(type, value) {
const { data } = this;
if (type === 'undo') {
this.undo();
} else if (type === 'redo') {
this.redo();
} else if (type === 'print') {
this.print.preview();
} else if (type === 'paintformat') {
if (value === true) copy.call(this);
else clearClipboard.call(this);
} else if (type === 'clearformat') {
insertDeleteRowColumn.call(this, 'delete-cell-format');
} else if (type === 'link') {
// link
} else if (type === 'chart') {
// chart
} else if (type === 'autofilter') {
// filter
autofilter.call(this);
} else if (type === 'freeze') {
if (value) {
const { ri, ci } = data.selector;
this.freeze(ri, ci);
} else {
this.freeze(0, 0);
}
} else {
data.setSelectedCellAttr(type, value);
if (type === 'formula' && !data.selector.multiple()) {
editorSet.call(this);
}
sheetReset.call(this);
}
}
function sortFilterChange(ci, order, operator, value) {
// console.log('sort:', sortDesc, operator, value);
this.data.setAutoFilter(ci, order, operator, value);
sheetReset.call(this);
}
function sheetInitEvents() {
const {
selector,
overlayerEl,
rowResizer,
colResizer,
verticalScrollbar,
horizontalScrollbar,
editor,
contextMenu,
toolbar,
modalValidation,
sortFilter,
} = this;
// overlayer
overlayerEl
.on('mousemove', (evt) => {
overlayerMousemove.call(this, evt);
})
.on('mousedown', (evt) => {
editor.clear();
contextMenu.hide();
// the left mouse button: mousedown → mouseup → click
// the right mouse button: mousedown → contenxtmenu → mouseup
if (evt.buttons === 2) {
if (this.data.xyInSelectedRect(evt.offsetX, evt.offsetY)) {
contextMenu.setPosition(evt.offsetX, evt.offsetY);
} else {
overlayerMousedown.call(this, evt);
contextMenu.setPosition(evt.offsetX, evt.offsetY);
}
evt.stopPropagation();
} else if (evt.detail === 2) {
editorSet.call(this);
} else {
overlayerMousedown.call(this, evt);
}
})
.on('mousewheel.stop', (evt) => {
overlayerMousescroll.call(this, evt);
})
.on('mouseout', (evt) => {
const { offsetX, offsetY } = evt;
if (offsetY <= 0) colResizer.hide();
if (offsetX <= 0) rowResizer.hide();
});
selector.inputChange = (v) => {
dataSetCellText.call(this, v, 'input');
editorSet.call(this);
};
// slide on mobile
bindTouch(overlayerEl.el, {
move: (direction, d) => {
overlayerTouch.call(this, direction, d);
},
});
// toolbar change
toolbar.change = (type, value) => toolbarChange.call(this, type, value);
// sort filter ok
sortFilter.ok = (ci, order, o, v) => sortFilterChange.call(this, ci, order, o, v);
// resizer finished callback
rowResizer.finishedFn = (cRect, distance) => {
rowResizerFinished.call(this, cRect, distance);
};
colResizer.finishedFn = (cRect, distance) => {
colResizerFinished.call(this, cRect, distance);
};
// resizer unhide callback
rowResizer.unhideFn = (index) => {
unhideRowsOrCols.call(this, 'row', index);
};
colResizer.unhideFn = (index) => {
unhideRowsOrCols.call(this, 'col', index);
};
// scrollbar move callback
verticalScrollbar.moveFn = (distance, evt) => {
verticalScrollbarMove.call(this, distance, evt);
};
horizontalScrollbar.moveFn = (distance, evt) => {
horizontalScrollbarMove.call(this, distance, evt);
};
// editor
editor.change = (state, itext) => {
dataSetCellText.call(this, itext, state);
};
// modal validation
modalValidation.change = (action, ...args) => {
if (action === 'save') {
this.data.addValidation(...args);
} else {
this.data.removeValidation();
}
};
// contextmenu
contextMenu.itemClick = (type) => {
// console.log('type:', type);
if (type === 'validation') {
modalValidation.setValue(this.data.getSelectedValidation());
} else if (type === 'copy') {
copy.call(this);
} else if (type === 'cut') {
cut.call(this);
} else if (type === 'paste') {
paste.call(this, 'all');
} else if (type === 'paste-value') {
paste.call(this, 'text');
} else if (type === 'paste-format') {
paste.call(this, 'format');
} else if (type === 'hide') {
hideRowsOrCols.call(this);
} else {
insertDeleteRowColumn.call(this, type);
}
};
bind(window, 'resize', () => {
this.reload();
});
bind(window, 'click', (evt) => {
this.focusing = overlayerEl.contains(evt.target);
});
bind(window, 'paste', (evt) => {
if (!this.focusing) return;
paste.call(this, 'all', evt);
evt.preventDefault();
});
// for selector
bind(window, 'keydown', (evt) => {
if (!this.focusing) return;
const keyCode = evt.keyCode || evt.which;
const {
key, ctrlKey, shiftKey, metaKey,
} = evt;
// console.log('keydown.evt: ', keyCode);
if (ctrlKey || metaKey) {
// const { sIndexes, eIndexes } = selector;
// let what = 'all';
// if (shiftKey) what = 'text';
// if (altKey) what = 'format';
switch (keyCode) {
case 90:
// undo: ctrl + z
this.undo();
evt.preventDefault();
break;
case 89:
// redo: ctrl + y
this.redo();
evt.preventDefault();
break;
case 67:
// ctrl + c
copy.call(this);
evt.preventDefault();
break;
case 88:
// ctrl + x
cut.call(this);
evt.preventDefault();
break;
case 85:
// ctrl + u
toolbar.trigger('underline');
evt.preventDefault();
break;
case 86:
// ctrl + v
// => paste
// evt.preventDefault();
break;
case 37:
// ctrl + left
selectorMove.call(this, shiftKey, 'row-first');
evt.preventDefault();
break;
case 38:
// ctrl + up
selectorMove.call(this, shiftKey, 'col-first');
evt.preventDefault();
break;
case 39:
// ctrl + right
selectorMove.call(this, shiftKey, 'row-last');
evt.preventDefault();
break;
case 40:
// ctrl + down
selectorMove.call(this, shiftKey, 'col-last');
evt.preventDefault();
break;
case 32:
// ctrl + space, all cells in col
selectorSet.call(this, false, -1, this.data.selector.ci, false);
evt.preventDefault();
break;
case 66:
// ctrl + B
toolbar.trigger('bold');
break;
case 73:
// ctrl + I
toolbar.trigger('italic');
break;
default:
break;
}
} else {
// console.log('evt.keyCode:', evt.keyCode);
switch (keyCode) {
case 32:
if (shiftKey) {
// shift + space, all cells in row
selectorSet.call(this, false, this.data.selector.ri, -1, false);
}
break;
case 27: // esc
contextMenu.hide();
clearClipboard.call(this);
break;
case 37: // left
selectorMove.call(this, shiftKey, 'left');
evt.preventDefault();
break;
case 38: // up
selectorMove.call(this, shiftKey, 'up');
evt.preventDefault();
break;
case 39: // right
selectorMove.call(this, shiftKey, 'right');
evt.preventDefault();
break;
case 40: // down
selectorMove.call(this, shiftKey, 'down');
evt.preventDefault();
break;
case 9: // tab
editor.clear();
// shift + tab => move left
// tab => move right
selectorMove.call(this, false, shiftKey ? 'left' : 'right');
evt.preventDefault();
break;
case 13: // enter
editor.clear();
// shift + enter => move up
// enter => move down
selectorMove.call(this, false, shiftKey ? 'up' : 'down');
evt.preventDefault();
break;
case 8: // backspace
insertDeleteRowColumn.call(this, 'delete-cell-text');
evt.preventDefault();
break;
default:
break;
}
if (key === 'Delete') {
insertDeleteRowColumn.call(this, 'delete-cell-text');
evt.preventDefault();
} else if ((keyCode >= 65 && keyCode <= 90)
|| (keyCode >= 48 && keyCode <= 57)
|| (keyCode >= 96 && keyCode <= 105)
|| evt.key === '='
) {
dataSetCellText.call(this, evt.key, 'input');
editorSet.call(this);
} else if (keyCode === 113) {
// F2
editorSet.call(this);
}
}
});
}
export default class Sheet {
constructor(targetEl, data) {
this.eventMap = createEventEmitter();
const { view, showToolbar, showContextmenu } = data.settings;
this.el = h('div', `${cssPrefix}-sheet`);
this.toolbar = new Toolbar(data, view.width, !showToolbar);
this.print = new Print(data);
targetEl.children(this.toolbar.el, this.el, this.print.el);
this.data = data;
// table
this.tableEl = h('canvas', `${cssPrefix}-table`);
// resizer
this.rowResizer = new Resizer(false, data.rows.height);
this.colResizer = new Resizer(true, data.cols.minWidth);
// scrollbar
this.verticalScrollbar = new Scrollbar(true);
this.horizontalScrollbar = new Scrollbar(false);
// editor
this.editor = new Editor(
formulas,
() => this.getTableOffset(),
data.rows.height,
);
// data validation
this.modalValidation = new ModalValidation();
// contextMenu
this.contextMenu = new ContextMenu(() => this.getRect(), !showContextmenu);
// selector
this.selector = new Selector(data);
this.overlayerCEl = h('div', `${cssPrefix}-overlayer-content`)
.children(
this.editor.el,
this.selector.el,
);
this.overlayerEl = h('div', `${cssPrefix}-overlayer`)
.child(this.overlayerCEl);
// sortFilter
this.sortFilter = new SortFilter();
// root element
this.el.children(
this.tableEl,
this.overlayerEl.el,
this.rowResizer.el,
this.colResizer.el,
this.verticalScrollbar.el,
this.horizontalScrollbar.el,
this.contextMenu.el,
this.modalValidation.el,
this.sortFilter.el,
);
// table
this.table = new Table(this.tableEl.el, data);
sheetInitEvents.call(this);
sheetReset.call(this);
// init selector [0, 0]
selectorSet.call(this, false, 0, 0);
}
on(eventName, func) {
this.eventMap.on(eventName, func);
return this;
}
trigger(eventName, ...args) {
const { eventMap } = this;
eventMap.fire(eventName, args);
}
resetData(data) {
// before
this.editor.clear();
// after
this.data = data;
verticalScrollbarSet.call(this);
horizontalScrollbarSet.call(this);
this.toolbar.resetData(data);
this.print.resetData(data);
this.selector.resetData(data);
this.table.resetData(data);
}
loadData(data) {
this.data.setData(data);
sheetReset.call(this);
return this;
}
// freeze rows or cols
freeze(ri, ci) {
const { data } = this;
data.setFreeze(ri, ci);
sheetReset.call(this);
return this;
}
undo() {
this.data.undo();
sheetReset.call(this);
}
redo() {
this.data.redo();
sheetReset.call(this);
}
reload() {
sheetReset.call(this);
return this;
}
getRect() {
const { data } = this;
return { width: data.viewWidth(), height: data.viewHeight() };
}
getTableOffset() {
const { rows, cols } = this.data;
const { width, height } = this.getRect();
return {
width: width - cols.indexWidth,
height: height - rows.height,
left: cols.indexWidth,
top: rows.height,
};
}
}