Our state of the art Gantt chart


Post by faiyaz@newscred.com »

Hi,

So i was wondering if we can render custom react component in the header cell, the reason for this is we want to add bunch of buttons for the name cell, such as expand all, create new button like below:

Image

The left most arrows controls expand and collapse all, and on the right we have the create and sort icons. There is a headerRender function that only accepts strings, i was only able to pass rendered react cells but cannot add event handler function, as i believe the functions are stripped during parsing in bryntum.

Is there any way to render react components like we can do in the column renderer function for cells?


Post by saki »

Installing listeners to the header elements that are created by returning html from the headerRenderer is not that difficult. I've modified out Gantt basic example the following way:

AppConfig.js (first column):

    columns: [
        {
            type: 'name',
            field: 'name',
            width: 250,
            headerRenderer(context) {
                return 'Name (renderer)&nbsp;&nbsp;<button class="my-button b-button b-gray">Click</button>';
            },
            renderer: ({record}) => {
                return record.isLeaf ? <span>{record.name}</span> : <b>{record.name}</b>
            }
        },

and App.js (full content):

/**
 * Application
 */
// Polyfills for Edge <= 18. Remove this line if you don't need support for it.
import 'core-js/stable';

import React, { Fragment, useCallback, useRef } from 'react';

import { BryntumDemoHeader, BryntumThemeCombo, BryntumGantt } from '@bryntum/gantt-react';
import { EventHelper } from '@bryntum/gantt/gantt.umd';

import { ganttConfig } from './AppConfig';
import './App.scss';

const App = () => {
    const ganttRef = useRef(null);

    // edit button click handler
    const handleEditClick = ({ record, grid: gantt }) => {
        gantt.editTask(record);
    };

    const handleHeaderButtonClick = useCallback(() => {
        console.log('Header button clicked')
    },[]);

    const listeners = {
        paint({ firstPaint }) {
            if (firstPaint) {
                EventHelper.on({
                    element: this.element,
                    delegate: 'button.my-button',
                    capture:true,
                    click: event => {
                        event.stopPropagation();
                        handleHeaderButtonClick();
                    }
                })
            }
        }
    }

    return (
        <Fragment>
            <BryntumDemoHeader
                title="React Basic demo"
                href="../../../../../#example-frameworks-react-javascript-basic"
                children={<BryntumThemeCombo />}
            />
            <BryntumGantt
                ref={ganttRef}
                {...ganttConfig}
                listeners={listeners}
                extraData={{ handleEditClick }}
            />
        </Fragment>
    );
};

export default App;

Post by faiyaz@newscred.com »

In the code snippet you provided, can you explain the following:

EventHelper.on({
                    element: this.element,
                    delegate: 'button.my-button',
                    capture:true,
                    click: event => {
                        event.stopPropagation();
                        handleHeaderButtonClick();
                    }
                })

What's this.element referring to?

I tried passing in the reference to the component that i'm rendering the bryntum gannt component in, in my case it's returning an error:

Cannot read property 'addEventListener' of null
    at Function.addElementListener (gantt.module.js?3374:10)
    at Function.on (gantt.module.js?3374:10)
    at Gantt.paint (TimelineCalendar.tsx?8102:179)
    at Gantt.trigger (gantt.module.js?3374:10)
    at Gantt.triggerPaint (gantt.module.js?3374:10)
    at Gantt.render (gantt.module.js?3374:10)
    at Gantt.render (gantt.module.js?3374:10)
    at Gantt.render (gantt.module.js?3374:67)
    at Gantt.render (gantt.module.js?3374:82)
    at functionChainRunner (gantt.module.js?3374:10)

Post by saki »

Sure here are added comments:

/**
 * Application
 */
// Polyfills for Edge <= 18. Remove this line if you don't need support for it.
import 'core-js/stable';

import React, { Fragment, useCallback, useRef } from 'react';

import { BryntumDemoHeader, BryntumThemeCombo, BryntumGantt } from '@bryntum/gantt-react';
import { EventHelper } from '@bryntum/gantt/gantt.umd';

import { ganttConfig } from './AppConfig';
import './App.scss';

const App = () => {
    const ganttRef = useRef(null); // ganttRef holds the reference to the React component (Gantt wrapper)

    // edit button click handler
    const handleEditClick = ({ record, grid: gantt }) => {
        gantt.editTask(record);
    };

    const handleHeaderButtonClick = useCallback(() => {
        console.log('Header button clicked')
    },[]);
   
    // listeners object is passed directly to the underlying native Bryntum Gantt component
    const listeners = { 
        // here we install the listener that runs on paint of the Gantt 
        paint({ firstPaint }) { 
            if (firstPaint) {
                EventHelper.on({
                    // we are inside of the paint listener that by default run in the scope of Bryntum Gantt so this is the Gantt
                    // we install listener on Gantt element
                    element: this.element, 

                    // the listener will only listen to events that match this selector
                    delegate: 'button.my-button', 

                    capture:true,

                    // we install click listener
                    click: event => {
                        event.stopPropagation();
                        handleHeaderButtonClick();
                    }
                })
            }
        }
    }

    return (
        <Fragment>
            <BryntumDemoHeader
                title="React Basic demo"
                href="../../../../../#example-frameworks-react-javascript-basic"
                children={<BryntumThemeCombo />}
            />
            <BryntumGantt
                ref={ganttRef}
                {...ganttConfig}
                listeners={listeners}
                extraData={{ handleEditClick }}
            />
        </Fragment>
    );
};

export default App;

Should you still have problems, post please a runnable showcase, we will help you to sort it out.


Post by faiyaz@newscred.com »

I was able to make this work, though i think your initial reference created the confusion. In your snippet this.element does not exist here:

paint({ firstPaint }) { 
            if (firstPaint) {
                EventHelper.on({
                    // we are inside of the paint listener that by default run in the scope of Bryntum Gantt so this is the Gantt
                    // we install listener on Gantt element
                    element: this.element,

Hence i was getting the error i mentioned above, instead paint returns an event object with params {firstPaint, source}

I used source.currentElement to attach the listener which is the Gantt element. Now it's working!


Post Reply