Detail renderer

`GanttDetailRenderer` and `GanttDetailRenderProps`, the types behind `renderDetail`

renderDetail replaces the body of the detail panel. Its types are exported from @jaeungkim/gantt-chart. Detail panel covers the behaviour and worked examples.

import type {
  GanttDetailRenderer,
  GanttDetailRenderProps,
  Task,
} from '@jaeungkim/gantt-chart';

Task and TaskTransformed are documented in Task and task types and GanttScaleKey in Scale and theme types. ReactNode is a type from react.

GanttDetailRenderer

src/shared/types.ts
/** Props handed to a `renderDetail` override */
export interface GanttDetailRenderProps {
  task: TaskTransformed;
  /** Closes the panel - wire it to your own close control */
  close: () => void;
  scale: GanttScaleKey;
  /**
   * Patches the open task through the chart's normal commit path (the store, then
   * `onTasksChange`), so a custom body can commit edits in uncontrolled mode. Applies NO
   * permission checks - a custom body enforces its own rules.
   */
  update: (patch: Partial<Omit<Task, 'id'>>) => void;
}

export type GanttDetailRenderer = (props: GanttDetailRenderProps) => ReactNode;

Fields

FieldTypeValue
taskTaskTransformedThe open task, as the chart transformed it
close() => voidCloses the panel. The built-in close button calls the same function
scaleGanttScaleKeyThe scale currently selected
update(patch: Partial<Omit<Task, 'id'>>) => voidPatches the open task through the chart's commit path

Example

const renderDetail: GanttDetailRenderer = ({ task, close, scale, update }) => (
  <>
    <button type="button" onClick={close} aria-label="Close task details">
      Close
    </button>
    <h2>{task.name}</h2>
    <p>
      {task.startDate.slice(0, 10)} to {task.endDate.slice(0, 10)} ({scale})
    </p>
    <button type="button" onClick={() => update({ progress: 100 })}>
      Mark done
    </button>
  </>
);

Constraints

The renderer supplies the panel's children, so there is no props bag to spread. No other part of the chart accepts a custom renderer.

  • The chart renders the <aside class="gantt-detail" role="complementary" aria-label="Task details"> element itself. The --gantt-detail-width custom property sets its width (320px). The renderer does not change it.
  • The built-in close button is not carried over into a custom body. close calls the same function that button calls. Escape closes the panel from anywhere on the page, even when the body renders no close control.
  • Returning null or undefined renders an empty panel, not the default body.
  • Passing the renderer turns the panel on when showDetail is omitted. An explicit showDetail={false} keeps it off.
  • The renderer always receives a task. Closing the panel does not unmount it immediately. The panel keeps the last task through the 200ms close transition, and a 400ms timer releases it if transitionend never fires. A body with mount side effects gets extra render passes while the panel slides shut. The <aside> leaves the DOM after that transition.
  • update uses the same commit path as the built-in body's inputs. It writes the task to the store and calls onTasksChange. An edit applies in uncontrolled mode, so your app does not have to pass tasks back.
  • update applies no checks of its own. The interaction flags, the date clamping and the progress normalization gate the built-in body only. A patch passed here reaches the store and onTasksChange as given, so a custom body enforces its own rules.
  • The patch cannot change the task's id. The type omits it, and the patch applies to the open task.
  • The default body shows the name, the dates and the duration. Progress and predecessors appear only when the task has them. No prop adds a row or translates the captions, so other fields reach the panel only through the renderer.

Every prop named here is indexed in GanttProps.