<commit_before>import { GridOptionsWrapper } from "../../gridOptionsWrapper";
import { ExpressionService } from "../../valueService/expressionService";
import { Constants } from "../../constants/constants";
import { Autowired } from "../../context/context";
import { Component } from "../../widgets/component";
import { ICellRendererComp, ICellRendererParams } from "./iCellRenderer";
import { RowNode } from "../../entities/rowNode";
import { ValueFormatterService } from "../valueFormatterService";
import { CheckboxSelectionComponent } from "../checkboxSelectionComponent";
import { ColumnController } from "../../columnController/columnController";
import { Column } from "../../entities/column";
import { RefSelector } from "../../widgets/componentAnnotations";
import { ColDef } from "../../entities/colDef";
import {
    ComponentClassDef,
    ComponentSource,
    UserComponentFactory
} from "../../components/framework/userComponentFactory";
import { Promise } from "../../utils";
import { doOnce } from "../../utils/function";
import { get, cloneObject } from "../../utils/object";
import { bindCellRendererToHtmlElement } from "../../utils/general";
import { addOrRemoveCssClass, setDisplayed } from "../../utils/dom";
import { createIconNoSpan } from "../../utils/icon";
import { isKeyPressed } from "../../utils/keyboard";
import { missing, exists } from "../../utils/generic";
import { isStopPropagationForAgGrid, stopPropagationForAgGrid, isElementInEventPath } from "../../utils/event";
import { setAriaExpanded, removeAriaExpanded } from "../../utils/aria";
import { KeyCode } from '../../constants/keyCode';

export interface GroupCellRendererParams extends ICellRendererParams {
    pinned: string;
    suppressPadding: boolean;
    suppressDoubleClickExpand: boolean;
    suppressEnterExpand: boolean;
    footerValueGetter: any;
    suppressCount: boolean;
    fullWidth: boolean;
    checkbox: any;
    scope: any;

    /** @deprecated */
    padding: number;
}

export class GroupCellRenderer extends Component implements ICellRendererComp {

    private static TEMPLATE = /* html */
        `<span class="ag-cell-wrapper">
            <span class="ag-group-expanded" ref="eExpanded"></span>
            <span class="ag-group-contracted" ref="eContracted"></span>
            <span class="ag-group-checkbox ag-invisible" ref="eCheckbox"></span>
            <span class="ag-group-value" ref="eValue"></span>
            <span class="ag-group-child-count" ref="eChildCount"></span>
        </span>`;

    @Autowired('gridOptionsWrapper') private gridOptionsWrapper: GridOptionsWrapper;
    @Autowired('expressionService') private expressionService: ExpressionService;
    @Autowired('valueFormatterService') private valueFormatterService: ValueFormatterService;
    @Autowired('columnController') private columnController: ColumnController;
    @Autowired('userComponentFactory') private userComponentFactory: UserComponentFactory;

    @RefSelector('eExpanded') private eExpanded: HTMLElement;
    @RefSelector('eContracted') private eContracted: HTMLElement;
    @RefSelector('eCheckbox') private eCheckbox: HTMLElement;
    @RefSelector('eValue') private eValue: HTMLElement;
    @RefSelector('eChildCount') private eChildCount: HTMLElement;

    private params: GroupCellRendererParams;

    // will be true if the node was pulled down
    private draggedFromHideOpenParents: boolean;

    // this is normally the rowNode of this row, however when doing hideOpenParents, it will
    // be the parent who's details we are actually showing if the data was pulled down.
    private displayedGroup: RowNode;

    private cellIsBlank: boolean;

    // keep reference to this, so we can remove again when indent changes
    private indentClass: string;

    // this cell renderer
    private innerCellRenderer: ICellRendererComp;

    constructor() {
        super(GroupCellRenderer.TEMPLATE);
    }

    public init(params: GroupCellRendererParams): void {
        this.params = params;

        if (this.gridOptionsWrapper.isGroupIncludeTotalFooter()) {
            this.assignBlankValueToGroupFooterCell(params);
        }

        const embeddedRowMismatch = this.isEmbeddedRowMismatch();
        // This allows for empty strings to appear as groups since
        // it will only return for null or undefined.
        const nullValue = params.value == null;
        let skipCell = false;

        // if the groupCellRenderer is inside of a footer and groupHideOpenParents is true
        // we should only display the groupCellRenderer if the current column is the rowGroupedColumn
        if (this.gridOptionsWrapper.isGroupIncludeFooter() && this.gridOptionsWrapper.isGroupHideOpenParents()) {
            const node = params.node;

            if (node.footer) {
                const showRowGroup = params.colDef && params.colDef.showRowGroup;
                const rowGroupColumnId = node.rowGroupColumn && node.rowGroupColumn.getColId();

                skipCell = showRowGroup !== rowGroupColumnId;
            }
        }

        this.cellIsBlank = embeddedRowMismatch || nullValue || skipCell;

        if (this.cellIsBlank) { return; }

        this.setupDragOpenParents();
        this.addExpandAndContract();
        this.addCheckboxIfNeeded();
        this.addValueElement();
        this.setupIndent();
    }

    private assignBlankValueToGroupFooterCell(params: GroupCellRendererParams) {
        // this is not ideal, but it was the only way we could get footer working for the root node
        if (!params.value && params.node.level == -1) {
            params.value = '';
        }
    }

    // if we are doing embedded full width rows, we only show the renderer when
    // in the body, or if pinning in the pinned section, or if pinning and RTL,
    // in the right section. otherwise we would have the cell repeated in each section.
    private isEmbeddedRowMismatch(): boolean {
        if (!this.params.fullWidth || !this.gridOptionsWrapper.isEmbedFullWidthRows()) { return false; }

        const pinnedLeftCell = this.params.pinned === Constants.PINNED_LEFT;
        const pinnedRightCell = this.params.pinned === Constants.PINNED_RIGHT;
        const bodyCell = !pinnedLeftCell && !pinnedRightCell;

        if (this.gridOptionsWrapper.isEnableRtl()) {
            if (this.columnController.isPinningLeft()) {
                return !pinnedRightCell;
            }
            return !bodyCell;
        }

        if (this.columnController.isPinningLeft()) {
            return !pinnedLeftCell;
        }

        return !bodyCell;
    }

    private setIndent(): void {
        if (this.gridOptionsWrapper.isGroupHideOpenParents()) { return; }

        const params = this.params;
        const rowNode: RowNode = params.node;
        // if we are only showing one group column, we don't want to be indenting based on level
        const manyDimensionThisColumn = params.colDef.showRowGroup === true;
        const paddingCount = manyDimensionThisColumn ? rowNode.uiLevel : 0;
        const userProvidedPaddingPixelsTheDeprecatedWay = params.padding >= 0;

        if (userProvidedPaddingPixelsTheDeprecatedWay) {
            this.setPaddingDeprecatedWay(paddingCount, params.padding);
            return;<commit_msg>AG-3975 Custom grouping example has wrong indentation<commit_after>import { GridOptionsWrapper } from "../../gridOptionsWrapper";
import { ExpressionService } from "../../valueService/expressionService";
import { Constants } from "../../constants/constants";
import { Autowired } from "../../context/context";
import { Component } from "../../widgets/component";
import { ICellRendererComp, ICellRendererParams } from "./iCellRenderer";
import { RowNode } from "../../entities/rowNode";
import { ValueFormatterService } from "../valueFormatterService";
import { CheckboxSelectionComponent } from "../checkboxSelectionComponent";
import { ColumnController } from "../../columnController/columnController";
import { Column } from "../../entities/column";
import { RefSelector } from "../../widgets/componentAnnotations";
import { ColDef } from "../../entities/colDef";
import {
    ComponentClassDef,
    ComponentSource,
    UserComponentFactory
} from "../../components/framework/userComponentFactory";
import { Promise } from "../../utils";
import { doOnce } from "../../utils/function";
import { get, cloneObject } from "../../utils/object";
import { bindCellRendererToHtmlElement } from "../../utils/general";
import { addOrRemoveCssClass, setDisplayed } from "../../utils/dom";
import { createIconNoSpan } from "../../utils/icon";
import { isKeyPressed } from "../../utils/keyboard";
import { missing, exists } from "../../utils/generic";
import { isStopPropagationForAgGrid, stopPropagationForAgGrid, isElementInEventPath } from "../../utils/event";
import { setAriaExpanded, removeAriaExpanded } from "../../utils/aria";
import { KeyCode } from '../../constants/keyCode';

export interface GroupCellRendererParams extends ICellRendererParams {
    pinned: string;
    suppressPadding: boolean;
    suppressDoubleClickExpand: boolean;
    suppressEnterExpand: boolean;
    footerValueGetter: any;
    suppressCount: boolean;
    fullWidth: boolean;
    checkbox: any;
    scope: any;

    /** @deprecated */
    padding: number;
}

export class GroupCellRenderer extends Component implements ICellRendererComp {

    private static TEMPLATE = /* html */
        `<span class="ag-cell-wrapper">
            <span class="ag-group-expanded" ref="eExpanded"></span>
            <span class="ag-group-contracted" ref="eContracted"></span>
            <span class="ag-group-checkbox ag-invisible" ref="eCheckbox"></span>
            <span class="ag-group-value" ref="eValue"></span>
            <span class="ag-group-child-count" ref="eChildCount"></span>
        </span>`;

    @Autowired('gridOptionsWrapper') private gridOptionsWrapper: GridOptionsWrapper;
    @Autowired('expressionService') private expressionService: ExpressionService;
    @Autowired('valueFormatterService') private valueFormatterService: ValueFormatterService;
    @Autowired('columnController') private columnController: ColumnController;
    @Autowired('userComponentFactory') private userComponentFactory: UserComponentFactory;

    @RefSelector('eExpanded') private eExpanded: HTMLElement;
    @RefSelector('eContracted') private eContracted: HTMLElement;
    @RefSelector('eCheckbox') private eCheckbox: HTMLElement;
    @RefSelector('eValue') private eValue: HTMLElement;
    @RefSelector('eChildCount') private eChildCount: HTMLElement;

    private params: GroupCellRendererParams;

    // will be true if the node was pulled down
    private draggedFromHideOpenParents: boolean;

    // this is normally the rowNode of this row, however when doing hideOpenParents, it will
    // be the parent who's details we are actually showing if the data was pulled down.
    private displayedGroup: RowNode;

    private cellIsBlank: boolean;

    // keep reference to this, so we can remove again when indent changes
    private indentClass: string;

    // this cell renderer
    private innerCellRenderer: ICellRendererComp;

    constructor() {
        super(GroupCellRenderer.TEMPLATE);
    }

    public init(params: GroupCellRendererParams): void {
        this.params = params;

        if (this.gridOptionsWrapper.isGroupIncludeTotalFooter()) {
            this.assignBlankValueToGroupFooterCell(params);
        }

        const embeddedRowMismatch = this.isEmbeddedRowMismatch();
        // This allows for empty strings to appear as groups since
        // it will only return for null or undefined.
        const nullValue = params.value == null;
        let skipCell = false;

        // if the groupCellRenderer is inside of a footer and groupHideOpenParents is true
        // we should only display the groupCellRenderer if the current column is the rowGroupedColumn
        if (this.gridOptionsWrapper.isGroupIncludeFooter() && this.gridOptionsWrapper.isGroupHideOpenParents()) {
            const node = params.node;

            if (node.footer) {
                const showRowGroup = params.colDef && params.colDef.showRowGroup;
                const rowGroupColumnId = node.rowGroupColumn && node.rowGroupColumn.getColId();

                skipCell = showRowGroup !== rowGroupColumnId;
            }
        }

        this.cellIsBlank = embeddedRowMismatch || nullValue || skipCell;

        if (this.cellIsBlank) { return; }

        this.setupDragOpenParents();
        this.addExpandAndContract();
        this.addCheckboxIfNeeded();
        this.addValueElement();
        this.setupIndent();
    }

    private assignBlankValueToGroupFooterCell(params: GroupCellRendererParams) {
        // this is not ideal, but it was the only way we could get footer working for the root node
        if (!params.value && params.node.level == -1) {
            params.value = '';
        }
    }

    // if we are doing embedded full width rows, we only show the renderer when
    // in the body, or if pinning in the pinned section, or if pinning and RTL,
    // in the right section. otherwise we would have the cell repeated in each section.
    private isEmbeddedRowMismatch(): boolean {
        if (!this.params.fullWidth || !this.gridOptionsWrapper.isEmbedFullWidthRows()) { return false; }

        const pinnedLeftCell = this.params.pinned === Constants.PINNED_LEFT;
        const pinnedRightCell = this.params.pinned === Constants.PINNED_RIGHT;
        const bodyCell = !pinnedLeftCell && !pinnedRightCell;

        if (this.gridOptionsWrapper.isEnableRtl()) {
            if (this.columnController.isPinningLeft()) {
                return !pinnedRightCell;
            }
            return !bodyCell;
        }

        if (this.columnController.isPinningLeft()) {
            return !pinnedLeftCell;
        }

        return !bodyCell;
    }

    private setIndent(): void {
        if (this.gridOptionsWrapper.isGroupHideOpenParents()) { return; }

        const params = this.params;
        const rowNode: RowNode = params.node;
        // if we are only showing one group column, we don't want to be indenting based on level
        const fullWithRow = !!params.colDef;
        const manyDimensionThisColumn = !fullWithRow || params.colDef.showRowGroup === true;
        const paddingCount = manyDimensionThisColumn ? rowNode.uiLevel : 0;
        const userProvidedPaddingPixelsTheDeprecatedWay = params.padding >= 0;

        if (userProvidedPaddingPixelsTheDeprecatedWay) {
            this.setPaddingDeprecatedWay(paddingCount, params.padding);
            return;
{'fraction_non_alphanumeric': 0.1095937179924889, 'fraction_numerical': 0.0006828269033799932, 'mean_word_length': 3.1596137460948595, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2563548', 'n_tokens_mistral': 3820, 'n_tokens_neox': 3801, 'n_words': 1055}