SUPPORT-8975: add custom grid

This commit is contained in:
adel.ka 2025-03-05 18:06:13 +03:00
parent e5c7e003a0
commit 1697d8302b
21 changed files with 1013 additions and 4 deletions

View file

@ -0,0 +1,54 @@
<div class="grid"
[ngbTooltip]="tooltip | emptyIfNull">
<ag-grid-angular [ngClass]="theme"
[ngStyle]="style"
[columnDefs]="columnDefs"
[defaultColDef]="defaultColDef"
[headerHeight]="headerHeight"
[rowHeight]="rowHeight"
[rowSelection]="rowSelection"
[rowMultiSelectWithClick]="isRowMultiSelectWithClick()"
[suppressRowClickSelection]="isSuppressRowClickSelection()"
[suppressLoadingOverlay]="isSuppressLoadingOverlay()"
[suppressNoRowsOverlay]="isSuppressNoRowsOverlay()"
[rowClassRules]="rowClassRules"
[rowModelType]="getRowModelType()"
[datasource]="datasource"
[maxConcurrentDatasourceRequests]="maxConcurrentDatasourceRequests"
[blockLoadDebounceMillis]="blockLoadDebounceMillis"
[cacheBlockSize]="getBlockSize()"
[pagination]="pagination"
[paginationPageSize]="fetchSize"
[maxBlocksInCache]="0"
[getRowId]="getRowIdFunc()"
[isRowSelectable]="isRowSelectableFunc()"
[pinnedBottomRowData]="pinnedBottomRowData"
[suppressDragLeaveHidesColumns]="true"
[suppressCopyRowsToClipboard]="true"
[processCellForClipboard]="processCellForClipboard"
[allowContextMenuWithControlKey]="allowContextMenuWithControlKey"
[localeText]="localeText"
[enableCellTextSelection]="enableCellTextSelection"
[overlayLoadingTemplate]="getLoadingOverlayTemplate()"
[overlayNoRowsTemplate]="getNoRowsOverlayTemplate()"
[tooltipShowDelay]="tooltipDelay"
[accentedSort]="true"
(gridReady)="onGridReady($event)"
(cellClicked)="onCellClicked($event)"
(rowClicked)="onRowClicked($event)"
(rowDoubleClicked)="onRowDoubleClicked($event)"
(selectionChanged)="onSelectionChanged($event)"
(sortChanged)="onSortChanged($event)"
(bodyScroll)="onBodyScroll($event)"
(columnResized)="onColumnResized($event)"
(columnMoved)="onColumnMoved($event)"
(columnVisible)="onColumnVisibilityChanged($event)"
(filterChanged)="columnFilterChanged($event)"
(componentStateChanged)="componentStateChanged($event)">
</ag-grid-angular>
<div [hidden]="true">
<ng-content></ng-content>
</div>
</div>

View file

@ -0,0 +1,26 @@
import {
FilterType
} from "../../../generated/ru/micord/ervu/account_applications/component/property/grid/FilterType";
import {DateFilter, NumberFilter, TextFilter} from "ag-grid-community";
import {SetFilter} from "./SetFilter";
export class CustomGridColumnFilterUtils {
public static columnFilter(type: FilterType) {
if (!type) {
return null;
}
switch (type) {
case FilterType.NUMBER:
return NumberFilter;
case FilterType.DATE:
return DateFilter;
case FilterType.SET:
return SetFilter;
case FilterType.TEXT:
default:
return TextFilter;
}
}
}

View file

@ -0,0 +1,95 @@
import {ChangeDetectionStrategy, Component} from "@angular/core";
import {Event, GridRpcService, GridV2, GridV2Column, Visible} from "@webbpm/base-package";
import {
StaticGridColumn
} from "../../../generated/ru/micord/ervu/account_applications/component/property/grid/StaticGridColumn";
import {
ColDef,
FilterChangedEvent,
ICellRendererParams,
ITooltipParams,
ValueFormatterParams,
ValueGetterParams
} from "ag-grid-community";
import {StaticColumnInitializer} from "./StaticColumnInitializer";
@Component({
moduleId: module.id,
selector: 'ervu-static-grid',
templateUrl: './../../../../../src/resources/template/account_applications/component/grid/ErvuStaticGrid.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ErvuStaticGrid extends GridV2 {
// todo: remove on updating platform version up to 3.188
@Visible("false")
public columnFiltersChanged: Event<any> = new Event<any>();
private rpcService: GridRpcService;
protected initGrid() {
super.initGrid();
this.rpcService = this.getScript(GridRpcService);
}
getColumns(): any[] {
return this.getScriptsInChildren(GridV2Column)
.map(columnV2 => columnV2.getScript(StaticGridColumn));
}
protected columnV2ToColumnDef(column: GridV2Column): ColDef {
let gridColumn = column.getScript(StaticGridColumn);
let colDef = this.columnToColumnDef(gridColumn);
if (column.renderer) {
colDef.cellRenderer = (params: ICellRendererParams) => column.renderer.render(params);
}
if (column.valueGetter) {
colDef.valueGetter = (params: ValueGetterParams) => column.valueGetter.get(params);
}
if (column.tooltipValueGetter) {
if (gridColumn.columnTooltip) {
throw new Error(
"Only one type of tooltip should be specified: tooltipValueGetter or columnTooltip");
}
colDef.tooltipValueGetter = (params: ITooltipParams) => column.tooltipValueGetter.get(params);
}
column.setGridRef(this);
column.setColDef(colDef);
return colDef;
}
protected columnToColumnDef(column: any): ColDef {
let colDef = StaticColumnInitializer.columnToColumnDef(this, column);
let columnComp = column.context;
colDef['columnUid'] = columnComp.getObjectId();
if (columnComp.cellCssClassRulesProvider) {
colDef.cellClassRules = columnComp.cellCssClassRulesProvider.getRules();
}
if (columnComp.valueFormatter) {
colDef.valueFormatter = (params?: ValueFormatterParams) => {
return columnComp.valueFormatter.format(params);
}
}
console.log("Создан ColDef:", colDef);
return colDef;
}
@Visible()
public getRowDataSize(): number {
return this.rowData ? this.rowData.length : 0;
}
// todo: remove on updating platform version up to 3.188
@Visible()
public hasColumnFilters(): boolean {
const filterModel: { [key: string]: any; } = this.gridApi.getFilterModel();
return !!filterModel && Object.keys(filterModel).length > 0;
}
// todo: remove on updating platform version up to 3.188
public columnFilterChanged(event: FilterChangedEvent) {
this.columnFiltersChanged.trigger(event);
super.columnFilterChanged(event);
}
}

View file

@ -0,0 +1,116 @@
import {AgPromise, IDoesFilterPassParams, IFilterComp, IFilterParams} from "ag-grid-community";
export class SetFilter implements IFilterComp {
private OPTION_TEMPLATE = `<label class="ag-set-filter-item">
<input type="checkbox" class="ag-filter-checkbox" checked/>
<span class="ag-filter-value"></span>
</label>`;
private eGui!: HTMLDivElement;
private selectAll: HTMLInputElement;
private checkboxes: HTMLInputElement[] = [];
private values: any[] = [];
private initialValues: any[] = [];
private filterActive: boolean;
private filterChangedCallback!: (additionalEventAttributes?: any) => void;
private filterParams!: IFilterParams;
private valueType: string;
init(params: IFilterParams): void {
this.eGui = document.createElement('div');
this.eGui.className = 'ag-set-filter';
let index = 0;
this.selectAll = this.initCheckBox('selectAll', 'Все', index);
this.checkboxes.push(this.selectAll);
params.api.getRenderedNodes()
.map(node => node.data[params.colDef.field])
.sort((n1, n2) => n1 > n2 ? 1 : n1 < n2 ? -1 : 0)
.forEach(value => {
if (this.values.includes(value)) {
return;
}
index++;
let id = `option-${index}`;
let checkbox = this.initCheckBox(id, value, index);
this.checkboxes.push(checkbox);
this.values.push(value);
});
this.initialValues = this.values.slice();
this.filterParams = params;
this.filterActive = false;
this.filterChangedCallback = params.filterChangedCallback;
if (this.values.length > 0) {
this.valueType = typeof this.values[0];
}
};
private initCheckBox(id: string, value: string, index: number): HTMLInputElement {
this.eGui.insertAdjacentHTML('beforeend', this.OPTION_TEMPLATE);
this.eGui.querySelectorAll('.ag-filter-value')[index].innerHTML = value;
let checkbox = this.eGui.querySelectorAll('.ag-filter-checkbox')[index] as HTMLInputElement;
checkbox.setAttribute('id', id);
checkbox.addEventListener('change', this.onCheckBoxChanged.bind(this));
return checkbox;
}
getGui(): HTMLDivElement {
return this.eGui;
};
onCheckBoxChanged(event: any) {
let checked = event.target.checked;
if (event.target === this.selectAll) {
this.checkboxes.forEach(checkbox => checkbox.checked = checked);
this.values = checked ? this.initialValues.slice() : [];
}
else {
let value = event.target.nextElementSibling.textContent;
value = this.valueType === 'number' ? +value : value;
if (checked) {
this.values.push(value);
if (this.values.length == this.initialValues.length) {
this.selectAll.checked = true;
}
}
else {
let index = this.values.indexOf(value);
this.values.splice(index, 1);
this.selectAll.checked = false;
}
}
this.filterActive = !this.selectAll.checked;
this.filterChangedCallback();
}
doesFilterPass(params: IDoesFilterPassParams): boolean {
let { field } = this.filterParams.colDef;
return this.values.includes(params.data[field]);
}
getModel(): any {
return this.isFilterActive() ? { value: this.values } : null;
}
isFilterActive(): boolean {
return this.filterActive;
}
setModel(model: any): void | AgPromise<void> {
this.values = model == null ? this.initialValues.slice() : model.value;
let predicate = checkbox => model == null ? true : model.value.includes(checkbox.value);
this.checkboxes.filter(predicate)
.forEach(checkbox => checkbox.checked = true);
this.filterActive = !this.selectAll.checked;
this.filterChangedCallback();
}
destroy(): void {
this.checkboxes.forEach(checkBox => checkBox.removeEventListener('change', this.onCheckBoxChanged.bind(this)));
}
}

View file

@ -0,0 +1,120 @@
import {
DateTimeUtil,
DefaultTooltip,
GridCellTooltipUtils,
GridColumnComparatorUtils,
GridColumnKeyboardUtils,
GridValueFormatterUtils,
GridValueRendererUtils,
PinnedType,
} from "@webbpm/base-package";
import {
ColDef,
DateFilter,
ICellRendererFunc,
SuppressKeyboardEventParams
} from "ag-grid-community";
import {
StaticGridColumn
} from "../../../generated/ru/micord/ervu/account_applications/component/property/grid/StaticGridColumn";
import {CustomGridColumnFilterUtils} from "./CustomGridColumnFilterUtils";
import {Moment} from "moment";
import * as moment from "moment-timezone";
export class StaticColumnInitializer {
public static columnToColumnDef(gridRef: any, column: StaticGridColumn) {
const columnDef: ColDef = {};
columnDef.headerName = column.displayName ? column.displayName : '';
columnDef.headerClass = "custom-header";
columnDef.width = column.width;
columnDef.suppressSizeToFit = column.widthFixed;
columnDef.hide = column.hidden;
columnDef.resizable = !column.widthFixed;
columnDef.headerComponentParams = {"disable_hiding": column.disableHiding || false};
columnDef.lockVisible = column.disableHiding;
columnDef.headerTooltip = column.headerTooltip ? column.headerTooltip : column.displayName;
columnDef.suppressMenu = column.suppressHeaderMenu;
if (column.pinned) {
columnDef.pinned = column.pinned == PinnedType.LEFT ? 'left' : 'right';
}
columnDef['gridComp'] = this;
columnDef.sortable = column.sortable;
if (column.sortable == null) {
columnDef.sortable = true;
}
if (column.autoHeight) {
columnDef.autoHeight = column.autoHeight;
columnDef.cellClass = 'ag-grid-cell-wrap-text';
}
if (column.field) {
columnDef.field = column.field.column;
let type = column.field.type;
if (type != null) {
if (gridRef.getRowModelType() == "clientSide") {
columnDef.comparator = GridColumnComparatorUtils.columnComparator(type);
}
columnDef.valueFormatter = GridValueFormatterUtils.columnFormatter(type);
columnDef.cellRenderer = gridRef.createRenderer(column);
}
if (column.filter !== false) {
columnDef.floatingFilter = gridRef.floatingFilter;
columnDef.filter = CustomGridColumnFilterUtils.columnFilter(column.field.filterType);
if (columnDef.filter === DateFilter) {
columnDef.filterParams = {
comparator: function (filterLocalDateAtMidnight, cellValue) {
if (!cellValue) {
return -1;
}
let filterMoment: Moment = moment.utc(filterLocalDateAtMidnight)
.add(-filterLocalDateAtMidnight.getTimezoneOffset(), 'm');
let cellMoment: Moment = DateTimeUtil.parseToMidnightUTC(cellValue);
if (filterMoment.isSame(cellMoment)) {
return 0;
}
if (cellMoment.isBefore(filterMoment)) {
return -1;
}
if (cellMoment.isAfter(filterMoment)) {
return 1;
}
},
browserDatePicker: true,
};
}
}
columnDef.suppressKeyboardEvent = (params: SuppressKeyboardEventParams) => {
return GridColumnKeyboardUtils.suppressHomeAndEndKeyboardEvent(params);
}
}
if (column.displayPopup) {
const renderer: ICellRendererFunc = columnDef.cellRenderer as ICellRendererFunc;
columnDef.cellRenderer = GridValueRendererUtils.tooltipValueRenderer(renderer);
}
else if (column.columnTooltip) {
columnDef.tooltipComponent = DefaultTooltip;
columnDef.tooltipValueGetter = GridCellTooltipUtils.fixedValueTooltip(column.columnTooltip);
}
else if (!gridRef.suppressColumnTooltip) {
columnDef.tooltipComponent = DefaultTooltip;
columnDef.tooltipValueGetter = GridCellTooltipUtils.defaultFormattedTooltip(
column.field ? column.field.type : null);
}
return columnDef;
}
}

View file

@ -24,6 +24,7 @@ import {TokenInterceptor} from "./interceptor/token.interceptor.service";
import {DropdownTreeViewComponent}from "../../account_applications/component/field/DropdownTreeViewComponent";
import {DropdownTreeviewSelectComponent} from "../../account_applications/component/external/ngx-treeview/dropdown-treeview-select/dropdown-treeview-select.component";
import {TreeviewModule} from "ngx-treeview";
import {ErvuStaticGrid} from "../../account_applications/component/grid/ErvuStaticGrid";
registerLocaleData(localeRu);
export const DIRECTIVES = [
@ -34,7 +35,8 @@ export const DIRECTIVES = [
forwardRef(() => VBoxLoadValues),
forwardRef(() => DropdownTreeViewComponent),
forwardRef(() => DropdownTreeviewSelectComponent),
forwardRef(() => ErvuAccountTextFieldGridEditor)
forwardRef(() => ErvuAccountTextFieldGridEditor),
forwardRef(() => ErvuStaticGrid)
];
@NgModule({