@syncfusion/ej2-spreadsheet
Version:
Feature-rich JavaScript Spreadsheet (Excel) control with built-in support for selection, editing, formatting, importing and exporting to Excel
512 lines (511 loc) • 26 kB
JavaScript
import { SheetRender, RowRenderer, CellRenderer } from './index';
import { extend, remove } from '@syncfusion/ej2-base';
import { getSheetName, getRowsHeight, getColumnsWidth, getData, getSheetIndexFromId } from '../../workbook/index';
import { getCellAddress, getCellIndexes, workbookFormulaOperation, moveOrDuplicateSheet, skipHiddenIdx } from '../../workbook/index';
import { sheetTabs, onContentScroll, deInitProperties, beforeDataBound, updateTranslate } from '../common/index';
import { spreadsheetDestroyed, isFormulaBarEdit, editOperation } from '../common/index';
import { getSiblingsHeight, refreshSheetTabs, focus, getUpdatedScrollPosition } from '../common/index';
import { ribbon, formulaBar, beforeVirtualContentLoaded, setAriaOptions } from '../common/index';
/**
* Render module is used to render the spreadsheet
*
* @hidden
*/
var Render = /** @class */ (function () {
function Render(parent) {
this.parent = parent;
this.addEventListener();
this.instantiateRenderer();
}
Render.prototype.render = function () {
this.parent.setProperties({ activeSheetIndex: this.parent.skipHiddenSheets(this.parent.activeSheetIndex) }, true);
if (!this.parent.isMobileView()) {
this.parent.notify(ribbon, null);
this.parent.notify(formulaBar, null);
}
var sheetPanel = this.parent.createElement('div', {
id: this.parent.element.id + '_sheet_panel', className: 'e-sheet-panel'
});
if (this.parent.enableRtl) {
sheetPanel.classList.add('e-rtl');
}
this.parent.element.appendChild(sheetPanel);
if (this.parent.showSheetTabs) {
this.parent.notify(sheetTabs, null);
}
else { // for formula calculation
this.parent.notify(workbookFormulaOperation, { action: 'initSheetInfo' });
this.parent.notify(workbookFormulaOperation, { action: 'initiateDefinedNames' });
}
if (this.parent.isMobileView()) {
this.parent.notify(formulaBar, null);
this.parent.notify(ribbon, null);
}
if (this.parent.password && (this.parent.password.length > 0 || this.parent.isProtected)) {
this.parent.isProtected = true;
if (this.parent.showSheetTabs) {
this.parent.element.querySelector('.e-add-sheet-tab').setAttribute('disabled', 'true');
this.parent.element.querySelector('.e-add-sheet-tab').classList.add('e-disabled');
}
}
if (this.parent.selectionSettings.mode === 'None') {
this.parent.allowAutoFill = false;
}
this.setSheetPanelSize();
this.renderSheet(sheetPanel);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.checkTopLeftCell(!this.parent.refreshing);
};
Render.prototype.checkTopLeftCell = function (initLoad, isRefreshing, scrollTop, scrollLeft, preventModelCheck, openOptions) {
var sheet = this.parent.getActiveSheet();
this.parent.showSpinner();
var isTopLeftCell = sheet.topLeftCell === 'A1';
var indexes = getCellIndexes(sheet.topLeftCell);
var isFreezeScrolled;
if (sheet.topLeftCell !== sheet.paneTopLeftCell && (sheet.frozenRows || sheet.frozenColumns)) {
var paneIndexes = getCellIndexes(sheet.paneTopLeftCell);
isFreezeScrolled = this.parent.scrollSettings.enableVirtualization;
isTopLeftCell = sheet.frozenRows && sheet.frozenColumns ? indexes[0] + sheet.frozenRows === paneIndexes[0] &&
indexes[1] + sheet.frozenColumns === paneIndexes[1] : (sheet.frozenRows ? indexes[0] + sheet.frozenRows === paneIndexes[0]
&& indexes[1] === 0 : indexes[1] + sheet.frozenColumns === paneIndexes[1] && indexes[0] === 0);
if (indexes[0] && paneIndexes[0] > indexes[0]) {
this.parent.viewport.beforeFreezeHeight = getRowsHeight(sheet, 0, indexes[0] - 1, true);
}
else {
this.parent.viewport.beforeFreezeHeight = 0;
}
if (indexes[1] && paneIndexes[1] > indexes[1]) {
this.parent.viewport.beforeFreezeWidth = getColumnsWidth(sheet, 0, indexes[1] - 1, true);
}
else {
this.parent.viewport.beforeFreezeWidth = 0;
}
}
else {
this.parent.viewport.beforeFreezeHeight = this.parent.viewport.beforeFreezeWidth = 0;
}
var frozenRow = this.parent.frozenRowCount(sheet);
var frozenCol = this.parent.frozenColCount(sheet);
if (!this.parent.scrollSettings.enableVirtualization || isTopLeftCell) {
this.refreshUI({ rowIndex: indexes[0], colIndex: indexes[1], refresh: 'All' }, null, initLoad, isRefreshing, preventModelCheck, openOptions);
if (isFreezeScrolled) {
this.parent.viewport.topIndex = skipHiddenIdx(sheet, frozenRow, true) - frozenRow;
this.parent.viewport.leftIndex = skipHiddenIdx(sheet, frozenCol, true, 'columns') - frozenCol;
}
}
else {
var pIndexes = sheet.paneTopLeftCell === sheet.topLeftCell ? indexes : getCellIndexes(sheet.paneTopLeftCell);
var eventArgs = { preventScroll: true };
eventArgs.scrollTop = scrollTop || (pIndexes[0] > frozenRow ? getRowsHeight(sheet, frozenRow, pIndexes[0] - 1, true) : 0);
eventArgs.scrollLeft = scrollLeft || (pIndexes[1] > frozenCol ? getColumnsWidth(sheet, frozenCol, pIndexes[1] - 1, true) : 0);
this.parent.notify(onContentScroll, eventArgs);
var threshold = this.parent.getThreshold('row');
var rowIndex = sheet.frozenRows ? indexes[0] : (indexes[0] > threshold ?
skipHiddenIdx(sheet, indexes[0] - threshold, true) : 0);
var frozenIndexes = [];
if (sheet.frozenRows) {
frozenIndexes.push(pIndexes[0] - threshold > frozenRow ? pIndexes[0] - threshold : frozenRow);
}
threshold = this.parent.getThreshold('col');
var colIndex = sheet.frozenColumns ? indexes[1] :
(indexes[1] > threshold ? skipHiddenIdx(sheet, indexes[1] - threshold, true, 'columns') : 0);
if (sheet.frozenColumns) {
if (!frozenIndexes.length) {
frozenIndexes.push(frozenRow);
}
frozenIndexes.push(pIndexes[1] - threshold > frozenCol ? pIndexes[1] - threshold : frozenCol);
}
else if (frozenIndexes.length) {
frozenIndexes.push(frozenCol);
}
this.refreshUI({ rowIndex: rowIndex, colIndex: colIndex, refresh: 'All', top: eventArgs.scrollTop, left: eventArgs.scrollLeft,
frozenIndexes: frozenIndexes }, null, initLoad, isRefreshing, preventModelCheck, openOptions);
if (isFreezeScrolled) {
if (frozenRow && frozenIndexes[0] >= frozenRow) {
this.parent.viewport.topIndex = skipHiddenIdx(sheet, frozenIndexes[0], true) - frozenRow;
}
if (frozenCol && frozenIndexes[1] >= frozenCol) {
this.parent.viewport.leftIndex = skipHiddenIdx(sheet, frozenIndexes[1], true, 'columns') - frozenCol;
}
}
}
};
Render.prototype.renderSheet = function (panel) {
if (panel === void 0) { panel = document.getElementById(this.parent.element.id + '_sheet_panel'); }
panel.appendChild(this.parent.createElement('div', { className: 'e-sheet', id: this.parent.element.id + '_sheet', styles: 'background-color: #fff' }));
this.parent.serviceLocator.getService('sheet').renderPanel();
};
/**
* @hidden
* @param {RefreshArgs} args - Specifies the RefreshArgs.
* @param {string} address - Specifies the address.
* @param {boolean} initLoad - Specifies the initLoad.
* @param {boolean} isRefreshing - Specifies the isRefreshing.
* @param {boolean} preventModelCheck - Specifies the preventModelCheck.
* @param {boolean} openOptions - Specifies the open response options.
* @returns {void}
*/
// tslint:disable-next-line:max-func-body-length
Render.prototype.refreshUI = function (args, address, initLoad, isRefreshing, preventModelCheck, openOptions) {
var _this = this;
if (args.refresh !== 'All') {
this.parent.showSpinner();
}
var sheetModule = this.parent.serviceLocator.getService('sheet');
var sheet = this.parent.getActiveSheet();
var sheetName = getSheetName(this.parent);
var prevRowColCnt = { rowCount: sheet.rowCount, colCount: sheet.colCount };
args.frozenIndexes = args.frozenIndexes ? args.frozenIndexes : [];
if (!address) {
if (this.parent.scrollSettings.enableVirtualization) {
var lastRow = args.rowIndex + this.parent.viewport.rowCount + (this.parent.getThreshold('row') * 2);
var lastCol = args.colIndex + this.parent.viewport.colCount + (this.parent.getThreshold('col') * 2);
var frozenRow = this.parent.frozenRowCount(sheet);
var frozenCol = this.parent.frozenColCount(sheet);
if (args.frozenIndexes.length) {
lastRow += (args.frozenIndexes[0] - frozenRow);
lastCol += (args.frozenIndexes[1] - frozenCol);
}
if (args.refresh === 'Row') {
lastRow += frozenRow;
}
else {
lastRow += sheet.frozenRows;
}
if (args.refresh === 'Column') {
lastCol += frozenCol;
}
else {
lastCol += sheet.frozenColumns;
}
var rowIdx = args.frozenIndexes[0] > frozenRow ? args.frozenIndexes[0] : args.rowIndex + (args.refresh === 'Row' ?
frozenRow : sheet.frozenRows);
var indexes = this.parent.skipHidden(rowIdx, lastRow, 'rows', false);
lastRow = indexes[1];
if (rowIdx !== indexes[0]) {
var topLeftCell = getCellIndexes(sheet.paneTopLeftCell);
if (topLeftCell[0] === rowIdx) {
this.parent.updateTopLeftCell(indexes[0] - frozenRow, topLeftCell[1], 'col');
}
}
indexes[0] -= frozenRow;
var count = sheet.rowCount - 1;
var diff = 0;
var startRow = args.rowIndex;
if (this.parent.scrollSettings.isFinite && lastRow > count) {
diff = lastRow - count;
lastRow = skipHiddenIdx(sheet, count, false);
if (indexes[0] + frozenRow > skipHiddenIdx(sheet, frozenRow, true)) {
var startIdx = args.rowIndex - diff;
startIdx = startIdx < 0 ? 0 : startIdx;
startIdx = this.decreaseHidden(startIdx, args.rowIndex - 1, frozenRow);
if (args.top && startIdx < args.rowIndex) {
this.parent.notify(updateTranslate, { height: getRowsHeight(sheet, startIdx + frozenRow, args.rowIndex - 1 + frozenRow, true),
isRender: true });
}
this.parent.viewport.topIndex = indexes[0] = startIdx;
startRow = args.refresh === 'Row' ? startIdx : startRow;
}
}
if (args.refresh === 'Row') {
args.rowIndex = skipHiddenIdx(sheet, startRow + frozenRow, true) - frozenRow;
}
else {
startRow = args.rowIndex = frozenRow ? skipHiddenIdx(sheet, startRow, true) : indexes[0];
}
var colIdx = args.frozenIndexes[1] > frozenCol ? args.frozenIndexes[1] : args.colIndex + (args.refresh ===
'Column' ? frozenCol : sheet.frozenColumns);
indexes = this.parent.skipHidden(colIdx, lastCol, 'columns', false);
lastCol = indexes[1];
if (colIdx !== indexes[0]) {
var topLeftCell = getCellIndexes(sheet.paneTopLeftCell);
if (topLeftCell[1] === colIdx) {
this.parent.updateTopLeftCell(topLeftCell[0], indexes[0] - frozenCol, 'row');
}
}
indexes[0] -= frozenCol;
count = sheet.colCount - 1;
diff = 0;
var startCol = args.colIndex;
if (this.parent.scrollSettings.isFinite && lastCol > count) {
diff = lastCol - count;
lastCol = skipHiddenIdx(sheet, count, false, 'columns');
if (indexes[0] + frozenCol > skipHiddenIdx(sheet, frozenCol, true, 'columns')) {
var startIdx = args.colIndex - diff;
startIdx = startIdx > -1 ? startIdx : 0;
startIdx = this.decreaseHidden(startIdx, args.colIndex - 1, frozenCol, 'columns');
if (args.left && startIdx < args.colIndex) {
this.parent.notify(updateTranslate, { width: getColumnsWidth(sheet, startIdx + frozenCol, args.colIndex - 1 + frozenCol, true),
isRender: true });
}
this.parent.viewport.leftIndex = indexes[0] = startIdx;
startCol = args.refresh === 'Column' ? startIdx : startCol;
}
}
if (args.refresh === 'Column') {
args.colIndex = skipHiddenIdx(sheet, startCol + frozenCol, true, 'columns') - frozenCol;
}
else {
startCol = args.colIndex = frozenCol ? skipHiddenIdx(sheet, startCol, true, 'columns') : indexes[0];
}
if (args.refresh === 'Row') {
startRow += frozenRow;
if (frozenRow) {
lastRow += getCellIndexes(sheet.topLeftCell)[0];
}
lastCol = this.parent.viewport.rightIndex;
}
if (args.refresh === 'Column') {
startCol += frozenCol;
if (frozenCol) {
lastCol += getCellIndexes(sheet.topLeftCell)[1];
}
lastRow = this.parent.viewport.bottomIndex;
}
this.parent.viewport.topIndex = args.rowIndex;
this.parent.viewport.bottomIndex = lastRow;
this.parent.viewport.leftIndex = args.colIndex;
this.parent.viewport.rightIndex = lastCol;
address = getCellAddress(startRow, startCol) + ":" + getCellAddress(lastRow, lastCol);
}
else {
if (args.refresh === 'All') {
this.updateTopLeftScrollPosition(extend(args, { sheet: sheet }));
}
this.parent.viewport.bottomIndex = sheet.rowCount - 1;
this.parent.viewport.rightIndex = sheet.colCount - 1;
address = getCellAddress(args.rowIndex, args.colIndex) + ":" + getCellAddress(this.parent.viewport.bottomIndex, this.parent.viewport.rightIndex);
}
}
if (args.refresh === 'All') {
this.parent.trigger(beforeDataBound, {});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
var isOpen = this.parent.isOpen || this.parent.refreshing;
setAriaOptions(this.parent.getMainContent(), { busy: true });
var sheetsLen = this.parent.sheets.length;
getData(this.parent, sheetName + "!" + address, null, null, args.frozenIndexes).then(function (values) {
if (!_this.parent || sheetsLen < _this.parent.sheets.length) {
return;
}
var sheetIdx = getSheetIndexFromId(_this.parent, sheet.id);
if (!preventModelCheck && (sheetIdx === undefined || sheetIdx !== _this.parent.activeSheetIndex)) {
if (sheetIdx > -1) {
_this.checkTopLeftCell();
}
return;
}
var indexes = [args.rowIndex, args.colIndex].concat(getCellIndexes(address.split(':')[1]));
var isEdit;
var arg;
switch (args.refresh) {
case 'All':
sheetModule.renderTable({ cells: values, indexes: indexes, top: args.top, left: args.left, initLoad: initLoad, isRefreshing: isRefreshing, isOpen: isOpen, openOptions: openOptions });
break;
case 'Row':
sheetModule.refreshRowContent({ cells: values, indexes: indexes, skipUpdateOnFirst: args.skipUpdateOnFirst, prevRowColCnt: prevRowColCnt });
isEdit = false;
arg = { isEdit: isEdit };
_this.parent.notify(isFormulaBarEdit, arg);
if (arg.isEdit) {
_this.parent.notify(editOperation, { action: 'startEdit', refreshCurPos: false });
}
break;
case 'Column':
sheetModule.refreshColumnContent({
cells: values, indexes: indexes, skipUpdateOnFirst: args.skipUpdateOnFirst,
prevRowColCnt: prevRowColCnt, insertDelete: args.insertDelete
});
break;
case 'RowPart':
sheetModule.updateRowContent({
cells: values, indexes: indexes, direction: args.direction, skipUpdateOnFirst: args.skipUpdateOnFirst,
prevRowColCnt: prevRowColCnt
});
break;
case 'ColumnPart':
sheetModule.updateColContent({
cells: values, indexes: indexes, direction: args.direction, skipUpdateOnFirst: args.skipUpdateOnFirst,
prevRowColCnt: prevRowColCnt
});
break;
}
if (_this.parent && _this.parent.isReact) {
_this.parent['renderReactTemplates']();
}
});
this.parent.notify(beforeVirtualContentLoaded, { refresh: args.refresh, skipTranslate: args.skipTranslate });
};
Render.prototype.updateTopLeftScrollPosition = function (args) {
var topLeftCell = getCellIndexes(args.sheet.topLeftCell);
var paneTopLeftCell = getCellIndexes(args.sheet.paneTopLeftCell);
if (args.sheet.frozenRows) {
var frozenRow = this.parent.frozenRowCount(args.sheet);
if (paneTopLeftCell[0] > frozenRow) {
args.top = getRowsHeight(args.sheet, frozenRow, paneTopLeftCell[0] - 1, true);
}
}
else {
if (args.rowIndex) {
args.rowIndex = 0;
}
if (topLeftCell[0] !== 0) {
args.top = getRowsHeight(args.sheet, 0, topLeftCell[0] - 1, true);
}
}
if (args.sheet.frozenColumns) {
var frozenCol = this.parent.frozenColCount(args.sheet);
if (paneTopLeftCell[1] > frozenCol) {
args.left = getColumnsWidth(args.sheet, frozenCol, paneTopLeftCell[1] - 1, true);
}
}
else {
if (args.colIndex) {
args.colIndex = 0;
}
if (topLeftCell[1] !== 0) {
args.left = getColumnsWidth(args.sheet, 0, topLeftCell[1] - 1, true);
}
}
};
Render.prototype.removeSheet = function () {
if (document.getElementById(this.parent.element.id + '_sheet')) {
remove(document.getElementById(this.parent.element.id + '_sheet'));
}
};
/**
* Refresh the active sheet.
*
* @param {boolean} isOpen - Specifies the isOpen.
* @param {boolean} resize - Set `true` to refresh the sheet with exiting scroll top and left.
* @param {boolean} focusEle - Specify the focusEle.
* @param {boolean} preventModelCheck - Specifies the preventModelCheck.
* @param {boolean} openOptions - Specifies the open response options.
* @returns {void}
*/
Render.prototype.refreshSheet = function (isOpen, resize, focusEle, preventModelCheck, openOptions) {
var scrollTop = 0;
var scrollLeft = 0;
if (resize) {
var mainPanel = this.parent.element.getElementsByClassName('e-main-panel')[0];
if (mainPanel) {
scrollTop = mainPanel.scrollTop;
}
var sheetContent = this.parent.getMainContent();
if (sheetContent) {
scrollLeft = sheetContent.scrollLeft;
}
}
this.removeSheet();
this.renderSheet();
this.parent.notify(deInitProperties, {});
this.checkTopLeftCell(false, isOpen, scrollTop, scrollLeft, preventModelCheck, openOptions);
if (focusEle) {
focus(this.parent.element);
}
};
/**
* Used to set sheet panel size.
*
* @param {number} colMinWidth - Specifies column minimum width value.
* @returns {void}
*/
Render.prototype.setSheetPanelSize = function (colMinWidth) {
var panel = document.getElementById(this.parent.element.id + '_sheet_panel');
var offset = this.parent.element.getBoundingClientRect();
var height;
this.parent.viewport.scaleY = this.parent.viewport.scaleX = 1;
if (this.parent.enableScaling) {
var offsetHeight = this.parent.element.offsetHeight;
var scaleY = offsetHeight / offset.height;
if (scaleY !== 1 && Math.abs(offsetHeight - offset.height) >= offsetHeight * 0.1) {
this.parent.viewport.scaleY = scaleY;
}
var offsetWidth = this.parent.element.offsetWidth;
var scaleX = offsetWidth / offset.width;
if (scaleX !== 1 && Math.abs(offsetWidth - offset.width) >= offsetWidth * 0.1) {
this.parent.viewport.scaleX = scaleX;
}
}
if (this.parent.height === 'auto') {
panel.style.height = '260px';
height = 230;
}
else {
height = (offset.height * this.parent.viewport.scaleY) - getSiblingsHeight(panel, null, this.parent.viewport.scaleY);
panel.style.height = height + "px";
height -= (32 / this.parent.viewport.scaleY);
}
if (colMinWidth !== undefined) {
this.colMinWidth = colMinWidth;
}
this.parent.viewport.height = height;
var width = offset.width * this.parent.viewport.scaleX;
this.parent.viewport.width = width - (32 / this.parent.viewport.scaleX);
this.parent.viewport.rowCount = this.roundValue(height, 20);
this.parent.viewport.colCount = this.roundValue(width, this.colMinWidth || 64);
};
Render.prototype.roundValue = function (size, threshold) {
var value = size / threshold;
var roundedValue = Math.round(value);
return Math.abs(value - roundedValue) < 0.5 ? roundedValue : roundedValue - 1;
};
Render.prototype.moveOrDuplicateSheetHandler = function (args) {
this.parent.notify(refreshSheetTabs, null);
if (args.refresh) {
this.refreshSheet(args.isDuplicate);
}
};
Render.prototype.decreaseHidden = function (startIdx, endIdx, freezeCount, layout) {
if (layout === void 0) { layout = 'rows'; }
startIdx += freezeCount;
endIdx += freezeCount;
var sheet = this.parent.getActiveSheet();
for (var i = endIdx; i >= startIdx; i--) {
if ((sheet["" + layout])[i] && (sheet["" + layout])[i].hidden) {
startIdx--;
if (startIdx < freezeCount) {
startIdx = skipHiddenIdx(sheet, freezeCount, true, layout);
break;
}
}
}
return startIdx - freezeCount;
};
/**
* Registing the renderer related services.
*
* @returns {void}
*/
Render.prototype.instantiateRenderer = function () {
this.parent.serviceLocator.register('cell', new CellRenderer(this.parent));
this.parent.serviceLocator.register('row', new RowRenderer(this.parent));
this.parent.serviceLocator.register('sheet', new SheetRender(this.parent));
};
/**
* Destroy the Render module.
*
* @returns {void}
*/
Render.prototype.destroy = function () {
this.removeEventListener();
this.parent.serviceLocator.getService('row').destroy();
this.parent.serviceLocator.getService('cell').destroy();
if (this.colMinWidth) {
this.colMinWidth = null;
}
this.parent = null;
};
Render.prototype.addEventListener = function () {
this.parent.on(spreadsheetDestroyed, this.destroy, this);
this.parent.on(moveOrDuplicateSheet, this.moveOrDuplicateSheetHandler, this);
this.parent.on(getUpdatedScrollPosition, this.updateTopLeftScrollPosition, this);
};
Render.prototype.removeEventListener = function () {
this.parent.off(spreadsheetDestroyed, this.destroy);
this.parent.off(moveOrDuplicateSheet, this.moveOrDuplicateSheetHandler);
this.parent.off(getUpdatedScrollPosition, this.updateTopLeftScrollPosition);
};
return Render;
}());
export { Render };