UNPKG

@revolist/revogrid

Version:

Virtual reactive data grid spreadsheet component - RevoGrid.

680 lines (679 loc) 27.4 kB
/*! * Built by Revolist OU ❤️ */ import { h, } from "@stencil/core"; import { getItemByIndex } from "../../store/index"; import { HEADER_ACTUAL_ROW_CLASS, HEADER_ROW_CLASS } from "../../utils/consts"; import HeaderRenderer from "./header-renderer"; import GroupHeaderRenderer from "./header-group-renderer"; export class RevogrHeaderComponent { constructor() { /** * Grouping depth, how many levels of grouping */ this.groupingDepth = 0; /** * Extra properties to pass into header renderer, such as vue or react components to handle parent */ this.additionalData = {}; } onResize({ width }, index) { const col = this.colData[index]; const event = this.beforeResize.emit([ Object.assign(Object.assign({}, col), { size: width || undefined }), ]); if (event.defaultPrevented) { return; } this.headerresize.emit({ [index]: width || 0 }); } onResizeGroup(changedX, startIndex, endIndex) { const sizes = {}; const change = changedX / (endIndex - startIndex + 1); for (let i = startIndex; i <= endIndex; i++) { const item = getItemByIndex(this.dimensionCol.state, i); sizes[i] = item.end - item.start + change; } this.headerresize.emit(sizes); } componentDidRender() { this.afterHeaderRender.emit(this.providers); } render() { var _a; const cols = this.viewportCol.get('items'); const range = (_a = this.selectionStore) === null || _a === void 0 ? void 0 : _a.get('range'); const { cells } = this.renderHeaderColumns(cols, range); const groupRow = this.renderGroupingColumns(); return [ h("div", { key: '3cc466db6bc4df0cd61c47a22c3a0473318e5dd8', class: "group-rgRow" }, groupRow), h("div", { key: '9742a3fa4d4b75073aef5544806f42386ebffdea', class: `${HEADER_ROW_CLASS} ${HEADER_ACTUAL_ROW_CLASS}` }, cells), ]; } renderHeaderColumns(cols, range) { const columnsToRender = []; const renderOffset = this.viewportCol.get('renderOffset') || 0; for (let rgCol of cols) { const colData = this.colData[rgCol.itemIndex]; const props = { range, column: rgCol, data: Object.assign(Object.assign({}, colData), { index: rgCol.itemIndex, providers: this.providers }), canFilter: !!this.columnFilter, canResize: this.canResize, renderOffset, active: this.resizeHandler, additionalData: this.additionalData, onResize: e => this.onResize(e, rgCol.itemIndex), onDblClick: e => this.headerdblClick.emit(e), onClick: e => this.initialHeaderClick.emit(e), }; const event = this.beforeHeaderRender.emit(props); if (!event.defaultPrevented) { columnsToRender.push(event.detail); } } const duplicateProps = this.getDuplicateHeaderProps(columnsToRender); const cells = columnsToRender.map(detail => h(HeaderRenderer, Object.assign({ key: this.getHeaderCellKey(detail.data, this.type, duplicateProps) }, detail))); return { cells }; } renderGroupingColumns() { const visibleGroupRange = this.getVisibleGroupRange(); return Array.from({ length: this.groupingDepth }, (_, level) => this.renderGroupRow(level, visibleGroupRange)).flat(); } renderGroupRow(level, visibleGroupRange) { const groupCells = (this.groups[level] || []) .map(group => this.renderGroupColumn(group, level, visibleGroupRange)) .filter((cell) => !!cell); return [ ...groupCells, h('div', { key: `group-row-${level}`, class: { [HEADER_ROW_CLASS]: true, group: true, }, }), ]; } renderGroupColumn(group, level, visibleGroupRange) { const groupRange = this.getGroupIndexRange(group); const groupBounds = this.getGroupBounds(groupRange); const props = { level, providers: this.providers, start: groupBounds.start, end: groupBounds.end, group, renderOffset: this.viewportCol.get('renderOffset') || 0, active: this.resizeHandler, canResize: this.canResize, additionalData: this.additionalData, onResize: e => { var _a; return groupRange ? this.onResizeGroup((_a = e.changedX) !== null && _a !== void 0 ? _a : 0, groupRange.startIndex, groupRange.endIndex) : undefined; }, }; const event = this.beforeGroupHeaderRender.emit(props); if (event.defaultPrevented) { return; } const renderRange = this.getGroupIndexRange(event.detail.group); if (!renderRange || !visibleGroupRange || !isGroupInVisibleRange(renderRange.startIndex, renderRange.endIndex, visibleGroupRange)) { return; } if (event.detail.onResize === props.onResize) { event.detail.onResize = e => { var _a; return this.onResizeGroup((_a = e.changedX) !== null && _a !== void 0 ? _a : 0, renderRange.startIndex, renderRange.endIndex); }; } const renderBounds = this.getGroupBounds(renderRange); if (event.detail.start === props.start) { event.detail.start = renderBounds.start; } if (event.detail.end === props.end) { event.detail.end = renderBounds.end; } return h(GroupHeaderRenderer, Object.assign({ key: this.getGroupHeaderCellKey(event.detail.group, level) }, event.detail)); } getGroupIndexRange(group) { var _a; const startIndex = (_a = group.indexes[0]) !== null && _a !== void 0 ? _a : -1; if (startIndex < 0) { return; } const endIndex = group.indexes[group.indexes.length - 1]; return { startIndex, endIndex, }; } getGroupBounds(range) { if (!range) { return { start: 0, end: 0 }; } return { start: getItemByIndex(this.dimensionCol.state, range.startIndex).start, end: getItemByIndex(this.dimensionCol.state, range.endIndex).end, }; } getVisibleGroupRange() { const visibleColumns = this.viewportCol.get('items'); if (!visibleColumns.length) { return; } return visibleColumns.reduce((range, column) => ({ start: Math.min(range.start, column.itemIndex), end: Math.max(range.end, column.itemIndex), }), { start: visibleColumns[0].itemIndex, end: visibleColumns[0].itemIndex, }); } getHeaderCellKey(column, type, duplicateProps) { if ((column === null || column === void 0 ? void 0 : column.prop) === undefined) { return `${type}-${String(column === null || column === void 0 ? void 0 : column.index)}`; } const propKey = String(column.prop); if (duplicateProps.has(propKey)) { return `${type}-${propKey}-${String(column.index)}`; } return `${type}-${propKey}`; } getDuplicateHeaderProps(columns) { const seenProps = new Set(); const duplicateProps = new Set(); columns.forEach(({ data }) => { if ((data === null || data === void 0 ? void 0 : data.prop) !== undefined) { const propKey = String(data.prop); if (seenProps.has(propKey)) { duplicateProps.add(propKey); } else { seenProps.add(propKey); } } }); return duplicateProps; } getGroupHeaderCellKey(group, level) { return `group-${level}-${group.name}-${group.indexes.join('-')}`; } get providers() { return { type: this.type, readonly: this.readonly, data: this.colData, viewport: this.viewportCol, dimension: this.dimensionCol, selection: this.selectionStore, }; } static get is() { return "revogr-header"; } static get originalStyleUrls() { return { "$": ["revogr-header-style.scss"] }; } static get styleUrls() { return { "$": ["revogr-header-style.css"] }; } static get properties() { return { "viewportCol": { "type": "unknown", "mutable": false, "complexType": { "original": "Observable<ViewportState>", "resolved": "ViewportState", "references": { "Observable": { "location": "import", "path": "../../utils", "id": "src/utils/index.ts::Observable", "referenceLocation": "Observable" }, "ViewportState": { "location": "import", "path": "@type", "id": "src/types/index.ts::ViewportState", "referenceLocation": "ViewportState" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Viewport X" }, "getter": false, "setter": false }, "dimensionCol": { "type": "unknown", "mutable": false, "complexType": { "original": "Observable<DimensionSettingsState>", "resolved": "DimensionSettingsState", "references": { "Observable": { "location": "import", "path": "../../utils", "id": "src/utils/index.ts::Observable", "referenceLocation": "Observable" }, "DimensionSettingsState": { "location": "import", "path": "@type", "id": "src/types/index.ts::DimensionSettingsState", "referenceLocation": "DimensionSettingsState" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Dimension settings X" }, "getter": false, "setter": false }, "selectionStore": { "type": "unknown", "mutable": false, "complexType": { "original": "Observable<SelectionStoreState>", "resolved": "{ range: RangeArea | null; tempRange: RangeArea | null; tempRangeType: string | null; focus: Cell | null; edit: EditCellStore | null; lastCell: Cell | null; nextFocus: Cell | null; }", "references": { "Observable": { "location": "import", "path": "../../utils", "id": "src/utils/index.ts::Observable", "referenceLocation": "Observable" }, "SelectionStoreState": { "location": "import", "path": "@type", "id": "src/types/index.ts::SelectionStoreState", "referenceLocation": "SelectionStoreState" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Selection, range, focus" }, "getter": false, "setter": false }, "groups": { "type": "unknown", "mutable": false, "complexType": { "original": "Groups", "resolved": "{ [x: number]: Group[]; }", "references": { "Groups": { "location": "import", "path": "@store", "id": "src/store/index.ts::Groups", "referenceLocation": "Groups" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Column groups" }, "getter": false, "setter": false }, "groupingDepth": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Grouping depth, how many levels of grouping" }, "getter": false, "setter": false, "reflect": false, "attribute": "grouping-depth", "defaultValue": "0" }, "readonly": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Readonly mode" }, "getter": false, "setter": false, "reflect": false, "attribute": "readonly" }, "canResize": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "If columns can be resized" }, "getter": false, "setter": false, "reflect": false, "attribute": "can-resize" }, "resizeHandler": { "type": "unknown", "mutable": false, "complexType": { "original": "ResizeProps['active']", "resolved": "(\"r\" | \"b\" | \"rt\" | \"lt\" | \"rb\" | \"lb\" | \"l\" | \"t\")[]", "references": { "ResizeProps": { "location": "import", "path": "./resizable.directive", "id": "src/components/header/resizable.directive.tsx::ResizeProps", "referenceLocation": "ResizeProps" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Defines resize position" }, "getter": false, "setter": false }, "colData": { "type": "unknown", "mutable": false, "complexType": { "original": "ColumnRegular[]", "resolved": "ColumnRegular<ColumnProp, DataType<any, ColumnProp>>[]", "references": { "ColumnRegular": { "location": "import", "path": "@type", "id": "src/types/index.ts::ColumnRegular", "referenceLocation": "ColumnRegular" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Columns - defines an array of grid columns." }, "getter": false, "setter": false }, "columnFilter": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Column filter" }, "getter": false, "setter": false, "reflect": false, "attribute": "column-filter" }, "type": { "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", "referenceLocation": "DimensionCols" } } }, "required": true, "optional": false, "docs": { "tags": [], "text": "Column type" }, "getter": false, "setter": false, "reflect": false, "attribute": "type" }, "additionalData": { "type": "any", "mutable": false, "complexType": { "original": "any", "resolved": "any", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Extra properties to pass into header renderer, such as vue or react components to handle parent" }, "getter": false, "setter": false, "reflect": false, "attribute": "additional-data", "defaultValue": "{}" } }; } static get events() { return [{ "method": "initialHeaderClick", "name": "beforeheaderclick", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "On initial header click" }, "complexType": { "original": "InitialHeaderClick", "resolved": "{ index: number; originalEvent: MouseEvent; column: ColumnRegular<ColumnProp, DataType<any, ColumnProp>>; providers: ProvidersColumns<DimensionCols | \"rowHeaders\">; }", "references": { "InitialHeaderClick": { "location": "import", "path": "@type", "id": "src/types/index.ts::InitialHeaderClick", "referenceLocation": "InitialHeaderClick" } } } }, { "method": "headerresize", "name": "headerresize", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "On header resize" }, "complexType": { "original": "ViewSettingSizeProp", "resolved": "{ [x: string]: number; }", "references": { "ViewSettingSizeProp": { "location": "import", "path": "@type", "id": "src/types/index.ts::ViewSettingSizeProp", "referenceLocation": "ViewSettingSizeProp" } } } }, { "method": "beforeResize", "name": "beforeheaderresize", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "On before header resize" }, "complexType": { "original": "ColumnRegular[]", "resolved": "ColumnRegular<ColumnProp, DataType<any, ColumnProp>>[]", "references": { "ColumnRegular": { "location": "import", "path": "@type", "id": "src/types/index.ts::ColumnRegular", "referenceLocation": "ColumnRegular" } } } }, { "method": "headerdblClick", "name": "headerdblclick", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "On header double click" }, "complexType": { "original": "InitialHeaderClick", "resolved": "{ index: number; originalEvent: MouseEvent; column: ColumnRegular<ColumnProp, DataType<any, ColumnProp>>; providers: ProvidersColumns<DimensionCols | \"rowHeaders\">; }", "references": { "InitialHeaderClick": { "location": "import", "path": "@type", "id": "src/types/index.ts::InitialHeaderClick", "referenceLocation": "InitialHeaderClick" } } } }, { "method": "beforeHeaderRender", "name": "beforeheaderrender", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Before each header cell render function. Allows to override cell properties" }, "complexType": { "original": "HeaderRenderProps", "resolved": "{ column: VirtualPositionItem; additionalData: any; data: ColumnTemplateProp<ColumnProp>; range?: RangeArea | null | undefined; canResize?: boolean | undefined; canFilter?: boolean | undefined; renderOffset?: number | undefined; onResize?(e: ResizeEvent): void; onClick?(data: InitialHeaderClick): void; onDblClick?(data: InitialHeaderClick): void; } & Partial<Pick<ResizeProps, \"active\">>", "references": { "HeaderRenderProps": { "location": "import", "path": "./header-renderer", "id": "src/components/header/header-renderer.tsx::HeaderRenderProps", "referenceLocation": "HeaderRenderProps" } } } }, { "method": "beforeGroupHeaderRender", "name": "beforegroupheaderrender", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Before each group header cell render function. Allows to override group header cell properties" }, "complexType": { "original": "HeaderGroupRendererProps", "resolved": "{ level: number; start: number; end: number; group: Group; providers: ProvidersColumns<DimensionCols | \"rowHeaders\">; additionalData: any; canResize?: boolean | undefined; renderOffset?: number | undefined; onResize?(e: ResizeEvent): void; } & Partial<Pick<ResizeProps, \"active\">>", "references": { "HeaderGroupRendererProps": { "location": "import", "path": "./header-group-renderer", "id": "src/components/header/header-group-renderer.tsx::HeaderGroupRendererProps", "referenceLocation": "HeaderGroupRendererProps" } } } }, { "method": "afterHeaderRender", "name": "afterheaderrender", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "After all header cells rendered. Finalizes cell rendering." }, "complexType": { "original": "ProvidersColumns", "resolved": "ProvidersColumns<DimensionCols | \"rowHeaders\">", "references": { "ProvidersColumns": { "location": "import", "path": "@type", "id": "src/types/index.ts::ProvidersColumns", "referenceLocation": "ProvidersColumns" } } } }]; } static get elementRef() { return "element"; } } function isGroupInVisibleRange(groupStartIndex, groupEndIndex, visibleRange) { return (groupStartIndex <= visibleRange.end && groupEndIndex >= visibleRange.start); }