Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrong row height when updating React component #237

Open
mike-lischke opened this issue Jun 4, 2021 · 0 comments
Open

Wrong row height when updating React component #237

mike-lischke opened this issue Jun 4, 2021 · 0 comments

Comments

@mike-lischke
Copy link

mike-lischke commented Jun 4, 2021

Wrong row height when updating React component

Environment Details

  • react-tabulator version: 0.15.0
  • OS: macOS 11.4
  • Node.js version: 16.1.0

Long Description
I use ReactTabulator in one of my apps as part of another compontent (a DB tree), which works pretty well:

shot1

However, when I update the component (by calling setState on the parent component, which re-renders the tree) the row heights are suddenly wrong:

shot2

I do not set row heights in any way! When I expand an entry it will be drawn correctly again:

shot3

From reading the Tabulator documentation it might have to do with setting values on the Tabulator element, before it was shown. So I tried to fix this issue by calling tree.redraw(true); This indeed works, but causes ugly flicker (first wrong display, then correct one).

Not sure if that helps at all, but here's my TreeGrid component, which is just a thin layer above ReactTabulator.

import "./TreeGrid.css";

import { ReactTabulator } from "react-tabulator";

import React from "react";

import { IComponentProperties, Component, SelectionType } from "..";
import { isNil } from "../../../utilities/helpers";

// eslint-disable-next-line @typescript-eslint/no-var-requires
const Tabulator = require("tabulator-tables");

export interface ITreeGridMenuEntry {
    label?: string | ((component: any) => string);
    disabled?: boolean;
    separator?: boolean;

    menu?: ITreeGridMenuEntry[]; // For sub menus.

    action?: (e: Event, column: any) => void;
}

// Options to fine tune the behavior/look of the tree beyond the component properties.
export interface ITreeGridOptions {
    // The field name in the tree data, which contains child node data (default: children).
    childKey?: string;

    // The field name of the column to use for the outline.
    // If specified it enables the display of a tree in that column.
    treeColumn?: string;

    // Determines how columns are initially layed out (default: none).
    layout?: "fitData" | "fitDataFill" | "fitDataStretch" | "fitDataTable" | "fitColumns";

    // If true then columns are laid out again when new data arrives (default: false).
    layoutColumnsOnNewData?: boolean;

    // If false, no header is shown (default: true).
    showHeader?: boolean;

    // If true horizontal and/or vertical grid lines are shown.
    verticalGridLines?: boolean;
    horizontalGridLines?: boolean;

    // If true, odd rows get a slightly lighter background.
    alternatingRowBackgrounds?: boolean;

    selectionType?: SelectionType;
}

export interface ITreeGridProperties extends IComponentProperties {
    height?: number;
    columns: Tabulator.ColumnDefinition[];

    // A list of objects each with a member for each column.
    tableData: any[];
    options?: ITreeGridOptions;

    // Menu entries for Tabulator provided menu. Do not mix with the `onRowContext` member.
    rowContextMenu?: ITreeGridMenuEntry[];

    onRowExpanded?: (row: Tabulator.RowComponent, level: number) => void;
    onRowCollapsed?: (row: Tabulator.RowComponent, level: number) => void;

    // Return the initial expansion state of the given row.
    isRowExpanded?: (row: Tabulator.RowComponent, level: number) => boolean;

    onFormatRow?: (row: Tabulator.RowComponent) => void;

    // Triggered when a row context is required. It allows to show an own menu implementation.
    onRowContext?: (event: Event, row: Tabulator.RowComponent) => void;
}

export class TreeGrid extends Component<ITreeGridProperties> {

    private tableRef = React.createRef<ReactTabulator>();

    public constructor(props: ITreeGridProperties) {
        super(props);

        this.addHandledProperties(
            "height", "columns", "tableData", "options", "rowContextMenu", "onRowExpanded", "onRowCollapsed",
            "isRowExpanded", "onFormatRow", "onRowContext",
        );
    }

    public render(): React.ReactNode {
        const {
            height, columns, tableData, options, rowContextMenu, onRowExpanded, onRowCollapsed, isRowExpanded,
            onFormatRow, onRowContext,
        } = this.mergedProps;

        const className = this.getEffectiveClassNames([
            "treeGrid",
            this.classFromProperty(options?.horizontalGridLines, "horizontalGrid"),
            this.classFromProperty(options?.verticalGridLines, "verticalGrid"),
            this.classFromProperty(options?.alternatingRowBackgrounds, "alternatingRows"),
        ]);

        let selectable: number | boolean | "highlight";
        switch (options?.selectionType) {
            case SelectionType.Highlight: {
                selectable = "highlight";
                break;
            }

            case SelectionType.Single: {
                selectable = 1;
                break;
            }

            case SelectionType.Multi: {
                selectable = true;
                break;
            }

            default: {
                selectable = false;
                break;
            }
        }

        const effectiveOptions: Tabulator.Options = {
            dataTree: !isNil(options?.treeColumn),
            dataTreeChildField: options?.childKey ?? "children",
            dataTreeElementColumn: options?.treeColumn,
            dataTreeExpandElement: "<span class='treeToggle'>▸</span>",
            dataTreeCollapseElement: "<span class='treeToggle expanded'>▸</span>",
            dataTreeBranchElement: false, // Disable tree lines.
            dataTreeChildIndent: 18,
            dataTreeRowExpanded: onRowExpanded,
            dataTreeRowCollapsed: onRowCollapsed,
            dataTreeStartExpanded: isRowExpanded,
            rowFormatter: onFormatRow,

            layout: options?.layout,
            headerVisible: options?.showHeader ?? true,
            selectable,
            selectableRangeMode: "click",
            reactiveData: false, // Very slow when enabled.

            rowContextMenu,

            height: "100%",
        };

        return (
            <ReactTabulator
                ref={this.tableRef}
                className={className}
                data={tableData}

                columns={columns}
                autoResize={true}
                height={height}
                layoutColumnsOnNewData={options?.layoutColumnsOnNewData}
                options={effectiveOptions}
                rowContext={onRowContext}

                {...this.unhandledProperties}
            />
        );
    }

    /**
     * Provides access to the underlying Tabulator table object.
     *
     * @returns The tabulator instance or undefined if the TreeGrid is not mounted yet.
     */
    public get table(): Tabulator | undefined {
        return this.tableRef.current?.table;
    }

}

The TreeGrid is then rendered like that:

    public render(): React.ReactNode {

        const schemaTreeColumns: Tabulator.ColumnDefinition[] = [{
            title: "",
            field: "caption",
            resizable: false,
            hozAlign: "left",
            formatter: this.schemaTreeCellFormatter,
            cellDblClick: this.handleSchemaTreeDoubleClick,
        }];

        const schemaTreeOptions: ITreeGridOptions = {
            treeColumn: "caption",
            selectionType: SelectionType.Single,
            showHeader: false,
            layout: "fitColumns",
        };

        const schemaSectionContent = (schemaList?.length || 0) === 0
            ? <Accordion.Item caption="<no schemas>" />
            : <TreeGrid
                ref={this.tableRef}
                options={schemaTreeOptions}
                columns={schemaTreeColumns}
                tableData={schemaList!}

                onRowExpanded={this.handleRowExpanded}
                onRowCollapsed={this.handleRowCollapsed}
                isRowExpanded={this.isRowExpanded}
                onFormatRow={this.handleFormatRow}
                onRowContext={this.handleRowContext}
            />;

        return (
            <>
                {schemaSectionContent}
           </>
        );
    }

(rendering is simplified, actually it is an accordion control having the schema tree as one section).

Workaround

As mentioned tree.redraw(true); helps a bit, but it's a really ugly workaround.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant