@revolist/revogrid
Version:
Virtual reactive data grid spreadsheet component - RevoGrid.
234 lines (233 loc) • 8.91 kB
JavaScript
/*!
* Built by Revolist OU ❤️
*/
/**
* Plugin module for revo-grid grid system
* Add support for automatic column resize
*/
import each from "lodash/each";
import reduce from "lodash/reduce";
import { BasePlugin } from "./base.plugin";
import { getSourceItem, columnTypes } from "../store/index";
import { getColumnType } from "../utils/column.utils";
const LETTER_BLOCK_SIZE = 7;
export var ColumnAutoSizeMode;
(function (ColumnAutoSizeMode) {
// increases column width on header click according the largest text value
ColumnAutoSizeMode["headerClickAutosize"] = "headerClickAutoSize";
// increases column width on data set and text edit, decreases performance
ColumnAutoSizeMode["autoSizeOnTextOverlap"] = "autoSizeOnTextOverlap";
// increases and decreases column width based on all items sizes, worst for performance
ColumnAutoSizeMode["autoSizeAll"] = "autoSizeAll";
})(ColumnAutoSizeMode || (ColumnAutoSizeMode = {}));
export class AutoSizeColumnPlugin extends BasePlugin {
constructor(revogrid, providers, config) {
super(revogrid, providers);
this.providers = providers;
this.config = config;
this.autoSizeColumns = null;
/** for edge case when no columns defined before data */
this.dataResolve = null;
this.dataReject = null;
this.letterBlockSize = (config === null || config === void 0 ? void 0 : config.letterBlockSize) || LETTER_BLOCK_SIZE;
// create test container to check text width
if (config === null || config === void 0 ? void 0 : config.preciseSize) {
this.precsizeCalculationArea = this.initiatePresizeElement();
revogrid.appendChild(this.precsizeCalculationArea);
}
const aftersourceset = ({ detail: { source }, }) => {
this.setSource(source);
};
const beforecolumnsset = ({ detail: { columns }, }) => {
this.columnSet(columns);
};
this.addEventListener('beforecolumnsset', beforecolumnsset);
switch (config === null || config === void 0 ? void 0 : config.mode) {
case ColumnAutoSizeMode.autoSizeOnTextOverlap:
this.addEventListener('aftersourceset', aftersourceset);
this.addEventListener('afteredit', ({ detail }) => {
this.afteredit(detail);
});
break;
case ColumnAutoSizeMode.autoSizeAll:
this.addEventListener('aftersourceset', aftersourceset);
this.addEventListener('afteredit', ({ detail }) => {
this.afterEditAll(detail);
});
break;
default:
this.addEventListener('headerdblclick', ({ detail }) => {
const type = getColumnType(detail.column);
const size = this.getColumnSize(detail.index, type);
if (size) {
this.providers.dimension.setCustomSizes(type, {
[detail.index]: size,
}, true);
}
});
break;
}
}
async setSource(source) {
let autoSize = this.autoSizeColumns;
if (this.dataReject) {
this.dataReject();
this.clearPromise();
}
/** If data set first and no column provided await until get one */
if (!autoSize) {
const request = new Promise((resolve, reject) => {
this.dataResolve = resolve;
this.dataReject = reject;
});
try {
autoSize = await request;
}
catch (e) {
return;
}
}
// calculate sizes
each(autoSize, (_v, type) => {
const sizes = {};
each(autoSize[type], rgCol => {
// calculate size
rgCol.size = sizes[rgCol.index] = source.reduce((prev, rgRow) => Math.max(prev, this.getLength(rgRow[rgCol.prop])), this.getLength(rgCol.name || ''));
});
this.providers.dimension.setCustomSizes(type, sizes, true);
});
}
getLength(len) {
var _a;
const padding = 15;
if (!len) {
return 0;
}
try {
const str = len.toString();
/**if exact calculation required proxy with html element, slow operation */
if ((_a = this.config) === null || _a === void 0 ? void 0 : _a.preciseSize) {
this.precsizeCalculationArea.innerText = str;
return this.precsizeCalculationArea.scrollWidth + padding * 2;
}
return str.length * this.letterBlockSize + padding * 2;
}
catch (e) {
return 0;
}
}
afteredit(e) {
let data;
if (this.isRangeEdit(e)) {
data = e.data;
}
else {
data = { 0: { [e.prop]: e.val } };
}
each(this.autoSizeColumns, (columns, type) => {
const sizes = {};
each(columns, rgCol => {
var _a;
// calculate size
const size = reduce(data, (prev, rgRow) => {
if (typeof rgRow[rgCol.prop] === 'undefined') {
return prev;
}
return Math.max(prev || 0, this.getLength(rgRow[rgCol.prop]));
}, undefined);
if (size && ((_a = rgCol.size) !== null && _a !== void 0 ? _a : 0) < size) {
rgCol.size = sizes[rgCol.index] = size;
}
});
this.providers.dimension.setCustomSizes(type, sizes, true);
});
}
afterEditAll(e) {
const props = {};
if (this.isRangeEdit(e)) {
each(e.data, r => each(r, (_v, p) => (props[p] = true)));
}
else {
props[e.prop] = true;
}
each(this.autoSizeColumns, (columns, type) => {
const sizes = {};
each(columns, rgCol => {
if (props[rgCol.prop]) {
const size = this.getColumnSize(rgCol.index, type);
if (size) {
sizes[rgCol.index] = size;
}
}
});
this.providers.dimension.setCustomSizes(type, sizes, true);
});
}
getColumnSize(index, type) {
var _a, _b;
const rgCol = (_b = (_a = this.autoSizeColumns) === null || _a === void 0 ? void 0 : _a[type]) === null || _b === void 0 ? void 0 : _b[index];
if (!rgCol) {
return 0;
}
return reduce(this.providers.data.stores, (r, s) => {
const perStore = reduce(s.store.get('items'), (prev, _row, i) => {
const item = getSourceItem(s.store, i);
return Math.max(prev || 0, this.getLength(item === null || item === void 0 ? void 0 : item[rgCol.prop]));
}, 0);
return Math.max(r, perStore);
}, rgCol.size || 0);
}
columnSet(columns) {
var _a;
for (let t of columnTypes) {
const type = t;
const cols = columns[type];
for (let i in cols) {
if (cols[i].autoSize || ((_a = this.config) === null || _a === void 0 ? void 0 : _a.allColumns)) {
if (!this.autoSizeColumns) {
this.autoSizeColumns = {};
}
if (!this.autoSizeColumns[type]) {
this.autoSizeColumns[type] = {};
}
this.autoSizeColumns[type][i] = Object.assign(Object.assign({}, cols[i]), { index: parseInt(i, 10) });
}
}
}
if (this.dataResolve) {
this.dataResolve(this.autoSizeColumns || {});
this.clearPromise();
}
}
clearPromise() {
this.dataResolve = null;
this.dataReject = null;
}
isRangeEdit(e) {
return !!e.data;
}
initiatePresizeElement() {
var _a;
const styleForFontTest = {
position: 'absolute',
fontSize: '14px',
height: '0',
width: '0',
whiteSpace: 'nowrap',
top: '0',
overflowX: 'scroll',
display: 'block',
};
const el = document.createElement('div');
for (let s in styleForFontTest) {
el.style[s] = (_a = styleForFontTest[s]) !== null && _a !== void 0 ? _a : '';
}
el.classList.add('revo-test-container');
return el;
}
destroy() {
var _a;
super.destroy();
(_a = this.precsizeCalculationArea) === null || _a === void 0 ? void 0 : _a.remove();
}
}