@revolist/revogrid
Version:
Virtual reactive data grid spreadsheet component - RevoGrid.
680 lines (679 loc) • 27.4 kB
JavaScript
/*!
* 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);
}