UNPKG

@revolist/revogrid

Version:

Virtual reactive data grid spreadsheet component - RevoGrid.

234 lines (233 loc) 8.91 kB
/*! * 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(); } }