React/Templates
Loading live demo…from dhtmlx.github.io
React
Templates
Customize task text, controls, headers, and delete confirmation UI with React template components.
- Demo.tsx
- demoData.ts
- TaskText.tsx
- StatusButton.tsx
- HeaderMenu.tsx
- TaskDeleteDialog.tsx
- styles.css
src/examples/templates/Demo.tsx View on GitHub
import { useEffect, useRef, useState, useMemo } from 'react';
import ReactGantt, {GanttStatic, Task, Link, GanttConfig, ReactGanttRef, OnBeforeTaskDeleteConfirmArgs, GanttTemplates, ReactGanttProps, useWorkTime} from "@dhtmlx/trial-react-gantt";
import "@dhtmlx/trial-react-gantt/dist/react-gantt.css";
import Button from '@mui/material/Button';
import { initialData } from "./demoData";
import TaskTextComponent from "./components/TaskText";
import StatusButtonComponent from "./components/StatusButton";
import ColumnMenu from "./components/HeaderMenu";
import BeforeTaskDeleteDialog from "./components/TaskDeleteDialog";
import "./styles.css";
export default function GanttTemplatesDemo() {
const ganttRef = useRef<ReactGanttRef>(null);
const { isWorkTime }= useWorkTime(ganttRef);
useEffect(() => {
document.title = "DHTMLX React Gantt | Configs & Templates";
}, []);
const [theme, setTheme] = useState<string>("terrace");
const [locale, setLocale] = useState<string>("en");
const [showTaskDeleteDialog, setShowTaskDeleteDialog] = useState(false);
const [pendingTaskDelete, setPendingTaskDelete] = useState<null | {
message: string;
callback: () => void;
}>(null);
const handleDeleteTaskConfirm: (args: OnBeforeTaskDeleteConfirmArgs) => void = ({
task,
message,
callback
}) => {
setPendingTaskDelete({ message: `Are you sure want to delete ${task.text}?`, callback });
setShowTaskDeleteDialog(true);
} ;
function onDialogOption(result: boolean) {
if (result && pendingTaskDelete?.callback) {
pendingTaskDelete.callback();
}
closeDialog();
}
function closeDialog() {
setShowTaskDeleteDialog(false);
setPendingTaskDelete(null);
}
const [filterLabel, setFilterLabel] = useState("All");
const [filter, setFilter] = useState<((task: Task) => boolean) | null>(null);
function handleFilterSelected(filterType: string) {
if (filterType === "done") {
setFilterLabel("Done");
setFilter(() => (task: Task) => !!task.completed);
} else if (filterType === "notDone") {
setFilterLabel("Not Done");
setFilter(() => (task: Task) => !task.completed);
} else {
setFilterLabel("All");
setFilter(null);
}
}
const switchTheme = () => {
setTheme((prevTheme) => (prevTheme === "terrace" ? "dark" : "terrace"));
};
const switchLocale = () => {
setLocale((prevLocale) => (prevLocale === "en" ? "es" : "en"));
};
const collapseAll = () => {
const gantt = ganttRef.current?.instance;
if(!gantt) return;
gantt.eachTask((task: Task) => {
task.$open = false;
});
gantt.render();
}
const expandAll = () => {
const gantt = ganttRef.current?.instance;
if(!gantt) return;
gantt.eachTask((task: Task) => {
task.$open = true;
});
gantt.render();
}
const handlerTaskTextClick = () => {
console.log("Button clicked!");
};
const toggleCompleted = (task: Task) => {
task.completed = !task.completed;
};
const templates: GanttTemplates = useMemo(() => ({
task_text: (start: Date, end: Date, task: Task) => {
return <TaskTextComponent task={task} onClick={handlerTaskTextClick} />;
},
scale_cell_class: (date: Date) => {
return isWorkTime({date}) ? "" : "weekend";
},
timeline_cell_class: (task: Task, date: Date) => {
return isWorkTime({date, task}) ? "" : "weekend";
}
}), []);
const config: GanttConfig = useMemo(() => ({
scales: [
{ unit: "year", step: 1, format: "%Y" },
{ unit: "month", step: 1, format: "%F, %Y" },
{ unit: "day", step: 1, format: "%d %M" },
],
columns: [
{
name: "text",
tree: true,
width: 180,
resize: true
},
{ name: "start_date", width: 150, align: "center", resize: true },
{ name: "duration", width: 80, align: "center", resize: true },
{
name: "custom",
align: "center",
label: <ColumnMenu
key={filterLabel}
onFilterSelected={handleFilterSelected}
currentFilterLabel={filterLabel}
/>
,
width: 160,
template: (task: Task) => (
<StatusButtonComponent
task={task}
onClick={() => {
toggleCompleted(task);
ganttRef.current?.instance?.updateTask(task.id);
}}
/>
),
resize: true,
},
{ name: "add", width: 44 },
],
work_time: true,
row_height: 50,
scale_height: 90
}), [filterLabel]);
const dataCallback: ReactGanttProps["data"] = {
save: (action, entity, id, data) => {
console.log(action, entity, id, data);
}
};
return (
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
<div style={{ display: 'flex', justifyContent: 'center', padding: '10px 10px 20px', gap: '10px' }}>
<Button variant="contained" onClick={() => switchTheme()}>Switch Theme</Button>
<Button variant="contained" onClick={() => switchLocale()}>Switch Locale</Button>
<Button variant="contained" onClick={() => collapseAll()}>Collapse All</Button>
<Button variant="contained" onClick={() => expandAll()}>Expand All</Button>
</div>
<BeforeTaskDeleteDialog
open={showTaskDeleteDialog}
text={pendingTaskDelete?.message || ""}
onConfirm={() => onDialogOption(true)}
onCancel={() => onDialogOption(false)}
/>
<ReactGantt
tasks={initialData.tasks}
links={initialData.links}
templates={templates}
config={config}
theme={theme}
locale={locale}
ref={ganttRef}
filter={filter}
modals={{
onBeforeTaskDelete: handleDeleteTaskConfirm,
}}
data={dataCallback}
/>
</div>
);
};
src/examples/templates/demoData.ts View on GitHub
import { Task, Link } from "@dhtmlx/trial-react-gantt";
export const initialData: { tasks: Task[], links: Link[] } = {
tasks: [
{ id: 1, text: "Office itinerancy", type: "project", start_date: new Date(2025, 3, 2), duration: 17, progress: 0.4, parent: 0, open: true },
{ id: 2, text: "Office facing", type: "project", start_date: new Date(2025, 3, 2), duration: 8, progress: 0.6, parent: 1, open: true },
{ id: 3, text: "Furniture installation", type: "project", start_date: new Date(2025, 3, 11), duration: 8, progress: 0.6, parent: 1, open: true },
{ id: 4, text: "The employee relocation", type: "project", start_date: new Date(2025, 3, 13), duration: 5, progress: 0.5, parent: 1, priority: 3, open: true },
{ id: 5, text: "Interior office", type: "task", start_date: new Date(2025, 3, 3), duration: 7, progress: 0.6, completed: true, parent: 2, priority: 1 },
{ id: 6, text: "Air conditioners check", type: "task", start_date: new Date(2025, 3, 3), duration: 7, progress: 0.6, completed: true, parent: 2, priority: 2 },
{ id: 7, text: "Workplaces preparation", type: "task", start_date: new Date(2025, 3, 12), duration: 8, progress: 0.6, completed: true, parent: 3 },
{ id: 8, text: "Preparing workplaces", type: "task", start_date: new Date(2025, 3, 14), duration: 5, progress: 0.5, parent: 4, priority: 1 },
{ id: 9, text: "Workplaces importation", type: "task", start_date: new Date(2025, 3, 21), duration: 4, progress: 0.5, parent: 4 },
{ id: 10, text: "Workplaces exportation", type: "task", start_date: new Date(2025, 3, 27), duration: 3, progress: 0.5, parent: 4, priority: 2 },
{ id: 11, text: "Product launch", type: "project", start_date: new Date(2025, 3, 2), duration: 13, progress: 0.6, parent: 0, open: true },
{ id: 12, text: "Perform Initial testing", type: "task", start_date: new Date(2025, 3, 3), duration: 5, progress: 1, parent: 11 },
{ id: 13, text: "Development", type: "project", start_date: new Date(2025, 3, 3), duration: 11, progress: 0.5, parent: 11, open: true },
{ id: 14, text: "Analysis", type: "task", start_date: new Date(2025, 3, 3), duration: 6, progress: 0.8, parent: 11 },
{ id: 15, text: "Design", type: "project", start_date: new Date(2025, 3, 3), duration: 5, progress: 0.2, parent: 11, open: true },
{ id: 16, text: "Documentation creation", type: "task", start_date: new Date(2025, 3, 3), duration: 7, progress: 0, parent: 11, priority: 1 },
{ id: 17, text: "Develop System", type: "task", start_date: new Date(2025, 3, 3), duration: 3, progress: 1, parent: 13, priority: 2 },
{ id: 25, text: "Beta Release", type: "milestone", start_date: new Date(2025, 3, 8), duration: 0, progress: 0, parent: 13 },
{ id: 18, text: "Integrate System", type: "task", start_date: new Date(2025, 3, 8), duration: 4, progress: 0.8, parent: 13, priority: 3 },
{ id: 19, text: "Test", type: "task", start_date: new Date(2025, 3, 13), duration: 4, progress: 0.2, parent: 13 },
{ id: 20, text: "Marketing", type: "task", start_date: new Date(2025, 3, 13), duration: 4, progress: 0, parent: 13, priority: 1 },
{ id: 21, text: "Design database", type: "task", start_date: new Date(2025, 3, 3), duration: 4, progress: 0.5, parent: 15 },
{ id: 22, text: "Software design", type: "task", start_date: new Date(2025, 3, 3), duration: 4, progress: 0.1, parent: 15, priority: 1 },
{ id: 23, text: "Interface setup", type: "task", start_date: new Date(2025, 3, 3), duration: 5, progress: 0, parent: 15, priority: 1 },
{ id: 24, text: "Release v1.0", type: "milestone", start_date: new Date(2025, 3, 18), duration: 0, progress: 0, parent: 11 }
],
links: [
{ id: 2, source: 2, target: 3, type: "0" },
{ id: 3, source: 3, target: 4, type: "0" },
{ id: 7, source: 8, target: 9, type: "0" },
{ id: 8, source: 9, target: 10, type: "0" },
{ id: 16, source: 17, target: 25, type: "0" },
{ id: 17, source: 18, target: 19, type: "0" },
{ id: 18, source: 19, target: 20, type: "0" },
{ id: 22, source: 13, target: 24, type: "0" },
{ id: 23, source: 25, target: 18, type: "0" }
]
};
src/examples/templates/components/TaskText.tsx View on GitHub
import { Box, Tooltip, Typography } from "@mui/material";
import DoneIcon from "@mui/icons-material/Done";
import PendingIcon from "@mui/icons-material/Pending";
import { Task } from "@dhtmlx/trial-react-gantt";
interface TaskTextProps {
task: Task;
onClick: () => void;
}
export default function TaskTextComponent({ task, onClick }: TaskTextProps) {
return (
<Box sx={{
display: "flex",
border: "1px solid",
padding: "2px",
alignItems: "center",
borderColor: "divider",
gap: "5px",
borderRadius: 2,
}}>
<Tooltip title={task.completed ? "Status: Completed" : "Status: Pending"}>
<Box
sx={{
display: "flex",
alignItems: "center",
borderRadius: "50%",
padding: "2px",
"&:hover": {
backgroundColor: "action.hover",
cursor: "pointer",
},
}}
onClick={onClick}
>
{task.completed ? <DoneIcon fontSize="small" /> : <PendingIcon fontSize="small" />}
</Box>
</Tooltip>
<Typography variant="button" component="div" sx={{ padding: "5px" }}>
{task.text}
</Typography>
</Box>
);
};
src/examples/templates/components/StatusButton.tsx View on GitHub
import { Button } from "@mui/material";
import { Task } from "@dhtmlx/trial-react-gantt";
interface StatusButtonProps {
task: Task;
onClick: () => void;
}
export default function StatusButton({ task, onClick }: StatusButtonProps) {
return (
<Button
color={task.completed ? "success" : "info"}
variant="contained"
onClick={onClick}
>
{task.completed ? "done" : "not done"}
</Button>
);
}
src/examples/templates/components/HeaderMenu.tsx View on GitHub
import * as React from 'react';
import Button from '@mui/material/Button';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
interface ColumnMenuProps {
onFilterSelected: (filterType: string) => void;
currentFilterLabel?: string;
}
export default function PositionedMenu(props: ColumnMenuProps) {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const handleMenuItemClick = (filterType: string) => {
props.onFilterSelected(filterType);
handleClose();
};
return (
<div>
<Button
id="demo-positioned-button"
aria-controls={open ? 'demo-positioned-menu' : undefined}
aria-haspopup="true"
aria-expanded={open ? 'true' : undefined}
onClick={handleClick}
>
Show: {props.currentFilterLabel || 'All'}
</Button>
<Menu
id="demo-positioned-menu"
aria-labelledby="demo-positioned-button"
anchorEl={anchorEl}
open={open}
onClose={handleClose}
anchorOrigin={{
vertical: 'top',
horizontal: 'left',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
>
<MenuItem onClick={() => handleMenuItemClick("done")}>Show Done</MenuItem>
<MenuItem onClick={() => handleMenuItemClick("notDone")}>Show Not Done</MenuItem>
<MenuItem onClick={() => handleMenuItemClick("all")}>Show All</MenuItem>
</Menu>
</div>
);
}
src/examples/templates/components/TaskDeleteDialog.tsx View on GitHub
import React from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
DialogContentText,
DialogActions,
Button
} from '@mui/material';
interface MyConfirmDialogProps {
open: boolean;
text: string;
title?: string;
onConfirm: () => void;
onCancel: () => void;
}
const MyConfirmDialog: React.FC<MyConfirmDialogProps> = ({
open,
text,
//title = "Confirm Deletion",
onConfirm,
onCancel
}) => {
return (
<Dialog open={open} onClose={onCancel} aria-labelledby="confirm-dialog-title">
{/* <DialogTitle id="confirm-dialog-title">{title}</DialogTitle> */}
<DialogContent>
<DialogContentText>{text}</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={onCancel}>No</Button>
<Button color="error" onClick={onConfirm} autoFocus>
DELETE
</Button>
</DialogActions>
</Dialog>
);
};
export default MyConfirmDialog;
src/examples/templates/styles.css View on GitHub
.project-end{
background-color: #0ca30a;
}