UNPKG

@revolist/revogrid

Version:

Virtual reactive data grid spreadsheet component - RevoGrid.

699 lines (698 loc) 27.2 kB
/*! * Built by Revolist OU ❤️ */ import { Host, h, } from "@stencil/core"; import ColumnService from "./column.service"; import { DATA_COL, DATA_ROW, ROW_FOCUSED_CLASS } from "../../utils/consts"; import { getSourceItem } from "../../store/index"; import RowRenderer, { PADDING_DEPTH } from "./row-renderer"; import GroupingRowRenderer from "../../plugins/groupingRow/grouping.row.renderer"; import { isGrouping } from "../../plugins/groupingRow/grouping.service"; import { RowHighlightPlugin } from "./row-highlight.plugin"; import { convertVNodeToHTML } from "../vnode/vnode.utils"; import { CellRenderer } from "./cell-renderer"; /** * This component is responsible for rendering data * Rows, columns, groups and cells */ export class RevogrData { constructor() { /** * Rendered rows - virtual index vs vnode */ this.renderedRows = new Map(); this.readonly = undefined; this.range = undefined; this.rowClass = undefined; this.additionalData = undefined; this.rowSelectionStore = undefined; this.viewportRow = undefined; this.viewportCol = undefined; this.dimensionRow = undefined; this.colData = undefined; this.dataStore = undefined; this.type = undefined; this.colType = undefined; this.jobsBeforeRender = []; this.providers = undefined; } /** * Pointed cell update. */ async updateCell(e) { var _a, _b, _c; // Stencil tweak to update cell content const cell = (_b = (_a = this.renderedRows.get(e.row)) === null || _a === void 0 ? void 0 : _a.$children$) === null || _b === void 0 ? void 0 : _b[e.col]; if ((_c = cell === null || cell === void 0 ? void 0 : cell.$attrs$) === null || _c === void 0 ? void 0 : _c.redraw) { const children = await convertVNodeToHTML(this.element, cell.$attrs$.redraw); cell.$elm$.innerHTML = children.html; cell.$key$ = Math.random(); } } onDataStoreChange() { this.onStoreChange(); } onColDataChange() { this.onStoreChange(); } onStoreChange() { var _a, _b; (_a = this.columnService) === null || _a === void 0 ? void 0 : _a.destroy(); this.columnService = new ColumnService(this.dataStore, this.colData); // make sure we have correct data, before render this.providers = { type: this.type, colType: this.colType, readonly: this.readonly, data: this.dataStore, columns: this.colData, viewport: this.viewportCol, dimension: this.dimensionRow, selection: this.rowSelectionStore, }; (_b = this.rangeUnsubscribe) === null || _b === void 0 ? void 0 : _b.call(this); this.rangeUnsubscribe = this.rowSelectionStore.onChange('range', (e) => this.rowHighlightPlugin.selectionChange(e, this.renderedRows)); } connectedCallback() { this.rowHighlightPlugin = new RowHighlightPlugin(); this.onStoreChange(); } disconnectedCallback() { var _a, _b; (_a = this.columnService) === null || _a === void 0 ? void 0 : _a.destroy(); (_b = this.rangeUnsubscribe) === null || _b === void 0 ? void 0 : _b.call(this); } async componentWillRender() { this.beforeDataRender.emit({ rowType: this.type, colType: this.colType, }); return Promise.all(this.jobsBeforeRender.map(p => typeof p === 'function' ? p() : p)); } componentDidRender() { this.afterrender.emit({ type: this.type }); } render() { this.renderedRows = new Map(); const columnsData = this.columnService.columns; if (!columnsData.length) { return; } const rows = this.viewportRow.get('items'); if (!rows.length) { return; } const cols = this.viewportCol.get('items'); if (!cols.length) { return; } const rowsEls = []; const depth = this.dataStore.get('groupingDepth'); const groupingCustomRenderer = this.dataStore.get('groupingCustomRenderer'); const groupDepth = this.columnService.hasGrouping ? depth : 0; for (let rgRow of rows) { const dataItem = getSourceItem(this.dataStore, rgRow.itemIndex); // #region Grouping if (isGrouping(dataItem)) { const gmodel = Object.assign(Object.assign({}, rgRow), { index: rgRow.itemIndex, model: dataItem, groupingCustomRenderer, // Only show expand button if grouping is enabled and not in row headers hasExpand: this.columnService.hasGrouping && this.colType !== 'rowHeaders', columnItems: cols, providers: this.providers }); rowsEls.push(h(GroupingRowRenderer, Object.assign({}, gmodel))); continue; } // #endregion const cells = []; // #region Cells for (let rgCol of cols) { const smodel = Object.assign(Object.assign({}, this.columnService.rowDataModel(rgRow.itemIndex, rgCol.itemIndex)), { providers: this.providers }); // call before cell render const cellEvent = this.triggerBeforeCellRender(smodel, rgRow, rgCol); // if event was prevented if (cellEvent.defaultPrevented) { continue; } const { detail: { column: columnProps, row: rowProps, model: schemaModel }, } = cellEvent; const defaultProps = { [DATA_COL]: columnProps.itemIndex, [DATA_ROW]: rowProps.itemIndex, style: { width: `${columnProps.size}px`, transform: `translateX(${columnProps.start}px)`, height: rowProps.size ? `${rowProps.size}px` : undefined, }, }; /** * For grouping, can be removed in the future and replaced with event */ if (groupDepth && !columnProps.itemIndex && defaultProps.style) { defaultProps.style.paddingLeft = `${PADDING_DEPTH * groupDepth}px`; } const props = this.columnService.mergeProperties(rowProps.itemIndex, columnProps.itemIndex, defaultProps, schemaModel); // Never use webcomponent for cell render // It's very slow because of webcomponent initialization takes time const cellNode = h(CellRenderer, { renderProps: { schemaModel, additionalData: this.additionalData, dragStartCell: this.dragStartCell, }, cellProps: props }); cells.push(cellNode); } // #endregion // #region Rows let rowClass = this.rowClass ? this.columnService.getRowClass(rgRow.itemIndex, this.rowClass) : ''; if (this.rowHighlightPlugin.isRowFocused(rgRow.itemIndex)) { rowClass += ` ${ROW_FOCUSED_CLASS}`; } const row = (h(RowRenderer, { index: rgRow.itemIndex, rowClass: rowClass, size: rgRow.size, start: rgRow.start }, cells)); this.beforerowrender.emit({ node: row, item: rgRow, model: dataItem, colType: this.columnService.type, rowType: this.type, }); rowsEls.push(row); this.renderedRows.set(rgRow.itemIndex, row); // #endregion } return (h(Host, null, h("slot", null), rowsEls)); } triggerBeforeCellRender(model, row, column) { const detail = { column, row, model, rowType: model.type, colType: model.colType, }; return this.beforeCellRender.emit(detail); } static get is() { return "revogr-data"; } static get originalStyleUrls() { return { "$": ["revogr-data-style.scss"] }; } static get styleUrls() { return { "$": ["revogr-data-style.css"] }; } static get properties() { return { "readonly": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Readonly mode" }, "getter": false, "setter": false, "attribute": "readonly", "reflect": false }, "range": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Range allowed" }, "getter": false, "setter": false, "attribute": "range", "reflect": false }, "rowClass": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Defines property from which to read row class" }, "getter": false, "setter": false, "attribute": "row-class", "reflect": false }, "additionalData": { "type": "any", "mutable": false, "complexType": { "original": "any", "resolved": "any", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Additional data to pass to renderer\nUsed in plugins such as vue or react to pass root app entity to cells" }, "getter": false, "setter": false, "attribute": "additional-data", "reflect": false }, "rowSelectionStore": { "type": "unknown", "mutable": false, "complexType": { "original": "Observable<SelectionStoreState>", "resolved": "ObservableMap<SelectionStoreState>", "references": { "Observable": { "location": "import", "path": "../../utils", "id": "src/utils/index.ts::Observable" }, "SelectionStoreState": { "location": "import", "path": "@type", "id": "src/types/index.ts::SelectionStoreState" } } }, "required": true, "optional": false, "docs": { "tags": [], "text": "Selection, range, focus for row selection" }, "getter": false, "setter": false }, "viewportRow": { "type": "unknown", "mutable": false, "complexType": { "original": "Observable<ViewportState>", "resolved": "ObservableMap<ViewportState>", "references": { "Observable": { "location": "import", "path": "../../utils", "id": "src/utils/index.ts::Observable" }, "ViewportState": { "location": "import", "path": "@type", "id": "src/types/index.ts::ViewportState" } } }, "required": true, "optional": false, "docs": { "tags": [], "text": "Viewport Y" }, "getter": false, "setter": false }, "viewportCol": { "type": "unknown", "mutable": false, "complexType": { "original": "Observable<ViewportState>", "resolved": "ObservableMap<ViewportState>", "references": { "Observable": { "location": "import", "path": "../../utils", "id": "src/utils/index.ts::Observable" }, "ViewportState": { "location": "import", "path": "@type", "id": "src/types/index.ts::ViewportState" } } }, "required": true, "optional": false, "docs": { "tags": [], "text": "Viewport X" }, "getter": false, "setter": false }, "dimensionRow": { "type": "unknown", "mutable": false, "complexType": { "original": "Observable<DimensionSettingsState>", "resolved": "ObservableMap<DimensionSettingsState>", "references": { "Observable": { "location": "import", "path": "../../utils", "id": "src/utils/index.ts::Observable" }, "DimensionSettingsState": { "location": "import", "path": "@type", "id": "src/types/index.ts::DimensionSettingsState" } } }, "required": true, "optional": false, "docs": { "tags": [], "text": "Dimension settings Y" }, "getter": false, "setter": false }, "colData": { "type": "unknown", "mutable": false, "complexType": { "original": "Observable<DSourceState<ColumnRegular, DimensionCols>>", "resolved": "ObservableMap<DSourceState<ColumnRegular, DimensionCols>>", "references": { "Observable": { "location": "import", "path": "../../utils", "id": "src/utils/index.ts::Observable" }, "DSourceState": { "location": "import", "path": "@store", "id": "src/store/index.ts::DSourceState" }, "ColumnRegular": { "location": "import", "path": "@type", "id": "src/types/index.ts::ColumnRegular" }, "DimensionCols": { "location": "import", "path": "@type", "id": "src/types/index.ts::DimensionCols" } } }, "required": true, "optional": false, "docs": { "tags": [], "text": "Column source" }, "getter": false, "setter": false }, "dataStore": { "type": "unknown", "mutable": false, "complexType": { "original": "Observable<DSourceState<DataType, DimensionRows>>", "resolved": "ObservableMap<DSourceState<DataType, DimensionRows>>", "references": { "Observable": { "location": "import", "path": "../../utils", "id": "src/utils/index.ts::Observable" }, "DSourceState": { "location": "import", "path": "@store", "id": "src/store/index.ts::DSourceState" }, "DataType": { "location": "import", "path": "@type", "id": "src/types/index.ts::DataType" }, "DimensionRows": { "location": "import", "path": "@type", "id": "src/types/index.ts::DimensionRows" } } }, "required": true, "optional": false, "docs": { "tags": [], "text": "Data rows source" }, "getter": false, "setter": false }, "type": { "type": "string", "mutable": false, "complexType": { "original": "DimensionRows", "resolved": "\"rgRow\" | \"rowPinEnd\" | \"rowPinStart\"", "references": { "DimensionRows": { "location": "import", "path": "@type", "id": "src/types/index.ts::DimensionRows" } } }, "required": true, "optional": false, "docs": { "tags": [], "text": "Row data type" }, "getter": false, "setter": false, "attribute": "type", "reflect": true }, "colType": { "type": "string", "mutable": false, "complexType": { "original": "DimensionCols | 'rowHeaders'", "resolved": "\"colPinEnd\" | \"colPinStart\" | \"rgCol\" | \"rowHeaders\"", "references": { "DimensionCols": { "location": "import", "path": "@type", "id": "src/types/index.ts::DimensionCols" } } }, "required": true, "optional": false, "docs": { "tags": [], "text": "Column data type" }, "getter": false, "setter": false, "attribute": "col-type", "reflect": true }, "jobsBeforeRender": { "type": "unknown", "mutable": false, "complexType": { "original": "(Promise<any> | (() => Promise<any>))[]", "resolved": "(Promise<any> | (() => Promise<any>))[]", "references": { "Promise": { "location": "global", "id": "global::Promise" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Prevent rendering until job is done.\nCan be used for initial rendering performance improvement.\nWhen several plugins require initial rendering this will prevent double initial rendering." }, "getter": false, "setter": false, "defaultValue": "[]" } }; } static get states() { return { "providers": {} }; } static get events() { return [{ "method": "beforerowrender", "name": "beforerowrender", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Before each row render" }, "complexType": { "original": "BeforeRowRenderEvent", "resolved": "BeforeRowRenderEvent<any>", "references": { "BeforeRowRenderEvent": { "location": "import", "path": "@type", "id": "src/types/index.ts::BeforeRowRenderEvent" } } } }, { "method": "afterrender", "name": "afterrender", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "When data render finished for the designated type" }, "complexType": { "original": "{ type: DimensionRows }", "resolved": "{ type: DimensionRows; }", "references": { "DimensionRows": { "location": "import", "path": "@type", "id": "src/types/index.ts::DimensionRows" } } } }, { "method": "beforeCellRender", "name": "beforecellrender", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Before each cell render function. Allows to override cell properties" }, "complexType": { "original": "BeforeCellRenderEvent<CellTemplateProp>", "resolved": "BeforeCellRenderEvent<CellTemplateProp>", "references": { "BeforeCellRenderEvent": { "location": "import", "path": "@type", "id": "src/types/index.ts::BeforeCellRenderEvent" }, "CellTemplateProp": { "location": "import", "path": "@type", "id": "src/types/index.ts::CellTemplateProp" } } } }, { "method": "beforeDataRender", "name": "beforedatarender", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Before data render" }, "complexType": { "original": "AllDimensionType", "resolved": "AllDimensionType", "references": { "AllDimensionType": { "location": "import", "path": "@type", "id": "src/types/index.ts::AllDimensionType" } } } }, { "method": "dragStartCell", "name": "dragstartcell", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Event emitted on cell drag start" }, "complexType": { "original": "DragStartEvent", "resolved": "DragStartEvent", "references": { "DragStartEvent": { "location": "import", "path": "@type", "id": "src/types/index.ts::DragStartEvent" } } } }]; } static get methods() { return { "updateCell": { "complexType": { "signature": "(e: { row: number; col: number; }) => Promise<void>", "parameters": [{ "name": "e", "type": "{ row: number; col: number; }", "docs": "" }], "references": { "Promise": { "location": "global", "id": "global::Promise" } }, "return": "Promise<void>" }, "docs": { "text": "Pointed cell update.", "tags": [] } } }; } static get elementRef() { return "element"; } static get watchers() { return [{ "propName": "dataStore", "methodName": "onDataStoreChange" }, { "propName": "colData", "methodName": "onColDataChange" }]; } } //# sourceMappingURL=revogr-data.js.map