Our state of the art Gantt chart


Post by jhill@achieveit.com »

I'm trying to return DOMConfig from column renderer as a workaround for the autoHeight bug https://github.com/bryntum/support/issues/8246. I'm trying to add a click event handler to the element html string, but the function is not being called. Below is what I am doing:

  const [columns] = useState([
    {
      field: 'custom',
      text: 'Custom column',
      width: 200,
      renderer: (args) => {
        // onclick or onClick? neither work
        const html = `<div onclick="function handleClick() {
          console.log('clicked');
        }">${args.value}</div>`;
        return { html };
      } 
    }
  ]);

With the above, you can see in the DOM the div has onclick="...myFunctionCode" but the console.log() is never called. Is this not possible or am I not using DomConfig correctly? Here's a public repo that reproduces the issue: https://github.com/jamesonhill/-bryntum-issue


Post by tasnim »

Hi,

You should just provide the code inside the function 🙂

Please check the example below

renderer({ value }) {
    const html = `<div onclick="console.log('clicked')">${value}</div>`;
    return { html };
}

This should work


Post by jhill@achieveit.com »

That doesn't work for multi-line functions. Your code does work, but the below does not.

const html = `<div onclick="
        console.log('clicked1');
        console.log('clicked2);
        ">${args.value}</div>`;

Post by tasnim »

Please try this approach.

Add a identifier in the HTML string by assigning a custom data attribute

            renderer({ value }) {
                const html = `<div data-cell-click>${value}</div>`;
                return { html };
            }

Now add a listener to the grid's element

grid.element.addEventListener('click', (e) => {
    if (e.target.matches('[data-cell-click]')) {
        console.log('clicked cell');
    }
});

Hope this helps


Post by Animal »

Use a listener on the grid element and use the delegate option to filter the events which have bubbled from the elements in your DOM structure:

https://bryntum.com/products/grid/docs/#Core/helper/EventHelper#typedef-ElementListenerConfig


Post by Animal »


Post by jhill@achieveit.com »

Thanks. I'll give that a try.


Post by jhill@achieveit.com »

Is there any documentation on how to use EventHelper in react component wrapper? The code below throws an error when it runs, so I assume I'm doing something wrong.

  useEffect(() => {
    if (ganttRef.current && data) { // add data check otherwise this never runs since gantt not initialized on first render cycle
      const instance = ganttRef.current.instance;
      EventHelper.on({
        element: instance,
        delegate: 'div.name',
        click: function(e: any) {
          console.log('clicky click', e);
        }
      });
    }
    
// when the above .on block runs, the below error is thrown gantt.module.js:13323 Uncaught TypeError: element.addEventListener is not a function at Function.addElementListener (gantt.module.js:13323:1) at Function.on (gantt.module.js:13284:1) at PlanTimelineViewGrid.tsx:199:1 at invokePassiveEffectCreate (react-dom.development.js:20253:1) at HTMLUnknownElement.callCallback (react-dom.development.js:3551:1) at Object.invokeGuardedCallbackDev (react-dom.development.js:3595:1) at invokeGuardedCallback (react-dom.development.js:3650:1) at flushPassiveEffectsImpl (react-dom.development.js:20323:1) at unstable_runWithPriority (scheduler.development.js:400:1) at runWithPriority$1 (react-dom.development.js:9885:1) }, [data]);

Post by tasnim »

Hi,

Please try putting a debugger there, the element might not be available at that moment.
You can also upload a runnable test case here, we can help you debug this issue!

Best regards,
Tasnim


Post by jhill@achieveit.com »

Here's a runnable example. It completely blows up and crashes the page.

import './App.css';
import { useState, useRef, useMemo, useEffect } from 'react';
import '@bryntum/gantt/gantt.stockholm.css';
import { BryntumGantt } from '@bryntum/gantt-react';
import { EventHelper } from '@bryntum/gantt';

function BryntumComponent({ data }) {
  const ganttRef = useRef(null);

  const tasks = useMemo(() => {
    return data.map((task) => {
      return {
        ...task,
        startDate: task.start,
        endDate: task.due,
        manuallyScheduled: true
      };
    });
  }, [data]);

  const [config] = useState({
    autoAdjustTimeAxis: false,
    fixedRowHeight: false,
    cellEditFeature: false,
    cellMenuFeature: false,
    columnReorderFeature: false,
    taskMenuFeature: false,
    taskEditFeature: false,
    rowReorderFeature: false,
    columnLines: false,
    projectLinesFeature: false,
    headerMenuFeature: false,
    zoomOnTimeAxisDoubleClick: false,
    zoomOnMouseWheel: false,
    dependenciesFeature: { allowCreate: false },
    sortFeature: false
  })

  const [columns] = useState([
    {
      type: 'name',
      field: 'name',
      id: 'name',
      autoHeight: true,
      width: 100,
      renderer: (args) => {
        return `<h1>${args.value}</h1>`
      },
      sortable: false,
      leafIconCls: null,
      htmlEncode: false
    }
  ]);

  useEffect(() => {
    if (ganttRef.current) {
      const instance = ganttRef.current.instance;
      EventHelper.on({
        element: instance,
        delegate: 'h1',
        click: function(e) {
          console.log('clicky click', e);
        }
      });
    }
  }, []);

  return (
      <div style={{ height: '100%', flex: 1, border: '1px solid black' }}>
        <BryntumGantt 
          ref={ganttRef}
          columns={columns}
          project={{ tasks }}
          {...config}
        />
      </div>
  );
}

function App() {
  const [data] = useState([{ id: 1, alternateId: 5, name: 'task 1', start: new Date(2024, 0, 1), due: new Date(2024,1, 27)}, { id: 2, alternateId: 2, name: 'task 2', start: new Date(2024,1, 28), due: new Date(2024,2, 28)}]);

  return (
    <div style={{ height: '100%', display: 'flex', padding: 10}}>
      <BryntumComponent data={data} />
    </div>
  );
}

export default App;

Post Reply