Hi there !
I am working on a website which is also distributed as an app which have both the classical "brower desktop" and "electron / webapp" version.
I did some tweaks on the "browser" version and I'm now pretty satisfied on how it's looking. What I needed was mainly 3 functionalities:
- Should center on a task when double click on the grid cell (to allow easy long project navigation)
- Should open a custom popup of mine when double clicking on a task bar in the gantt (to show details about the task with my custom logic).
- Should be able to open/collapse the “grid” part easily.
Using this code, with the React Wrapper:
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import "@bryntum/gantt/gantt.material.css";
import type { GanttBase, GanttConfig } from "@bryntum/gantt";
import { BryntumGantt } from "@bryntum/gantt-react";
import * as React from "react";
const serverData = [
{
children: [
{
children: false,
draggable: false,
endDate: "2021-10-30T00:00:00.000Z",
id: "5a33440c-c1b5-4945-a999-edb80c774ab4",
index: 0,
manuallyScheduled: true,
name: "test",
parentId: "357fb463-97aa-40b9-8ef3-f8ec1dc88f88",
percentDone: 0,
resizable: false,
startDate: "2021-10-01T00:00:00.000Z",
taskNumber: "1.1",
type: "subtask",
},
],
draggable: true,
endDate: "2021-10-30T00:00:00.000Z",
expanded: true,
id: "357fb463-97aa-40b9-8ef3-f8ec1dc88f88",
manuallyScheduled: true,
name: "build a wall (Link to Tour Eiffel project)",
percentDone: 100,
resizable: true,
startDate: "2021-10-01T00:00:00.000Z",
taskNumber: "1",
type: "task",
},
{
children: [],
draggable: true,
endDate: "2021-10-26T00:00:00.000Z",
expanded: false,
id: "af098e9c-0ae4-4821-9d29-bf12ce30e823",
manuallyScheduled: true,
name: "talk to contractor",
resizable: true,
startDate: "2021-10-24T00:00:00.000Z",
taskNumber: "2",
type: "task",
},
{
children: [],
draggable: true,
endDate: undefined,
expanded: false,
id: "38b491a4-012d-4201-bcdd-4539a8cf5bad",
manuallyScheduled: true,
name: "Task without dates",
resizable: true,
startDate: undefined,
taskNumber: "3",
type: "task",
},
];
type Config = Partial<GanttConfig> | any;
function Gantt() {
const ganttRef = React.useRef<{ instance: GanttBase }>();
const [tasks, setTasks] = React.useState(serverData);
const [taskSelected, setTaskSelected] = React.useState(null);
const config: Config = {
barMargin: 10,
columns: [
{ field: "taskNumber", text: "N°", type: "number", width: 10 },
{ field: "name", type: "name", width: 50 },
],
features: {
// We don't want to use the gantt default edit form
cellEdit: false,
columnAutoWidth: true,
columnLines: false,
// We don't need the task/link dependencies
dependencies: false,
dependencyEdit: false,
nonWorkingTime: false,
// Used to have the percent of progress of a task (validated / subtasks progress)
percentBar: {
allowResize: false,
showPercentage: true,
},
// Used to get the "red line" to show what's the progress on the planning
progressLine: true,
// Should show projectStart projectEnd markers
// TODO: Find why it doesn't work as expected
projectLines: {
// TODO: find out why this doesn't show the "Today" marker
showCurrentTimeLine: {
name: "Today",
},
showHeaderElements: true,
},
taskCopyPaste: false,
// Should be able to change date of a task with drag/drop
// TODO: we should handle all thoses events in offline mode
taskDrag: true,
// It's not really "drag to create" but more "assigne" start/end date to tasks
// who doesn't already have one
// TODO: actually use this to open the "create task" dialog with prefill start/end date
taskDragCreate: true,
// We don't want to use the default taskEdit form, instead we open our own dialog
taskEdit: false,
taskMenu: false,
taskResize: {
allowResizeToZero: false,
},
taskTooltip: true,
timeRanges: {
showHeaderElements: true,
},
tree: true,
},
// Keep the scroll within project range
infiniteScroll: true,
maxHeight: "100vh",
maxWidth: "100vw",
// set max zoom level to day
maxZoomLevel: 11,
// set minimal zoom level to acceptable values (multiples years with quarters)
minZoomLevel: 5,
tbar: [
{
checked: true,
label: "Show Project Line",
listeners: {
change: ({ checked }: { checked: boolean }) => {
if (ganttRef.current?.instance.features.progressLine) {
ganttRef.current.instance.features.progressLine.disabled =
!checked;
}
},
},
type: "checkbox",
},
{
inputWidth: "7em",
label: "Project Status Date",
listeners: {
change: ({ value }: { value: Date }) => {
if (ganttRef.current?.instance.features.progressLine) {
ganttRef.current.instance.features.progressLine.statusDate =
value;
}
},
},
step: "1d",
type: "datefield",
value: new Date(),
},
],
viewPreset: {
// By default, show (day, week, month) on the same timebar
headers: [
{
align: "start",
renderer: (startDate: Date) => startDate.getMonth(),
unit: "month",
},
{
renderer: (startDate: Date) => startDate.getDate(),
unit: "week",
},
{
dateFormat: "DD",
unit: "day",
},
],
timeResolution: {
increment: 1,
unit: "day",
},
},
};
return (
<div style={{ height: "90vh", width: "90vw" }}>
<div
style={{
backgroundColor: "#ecf0f1",
display: taskSelected === null ? "none" : "block",
height: "100vh",
inset: 0,
position: "absolute",
width: "100vw",
zIndex: 999,
}}
>
Popup for {JSON.stringify(taskSelected)}
<button
style={{
backgroundColor: "red",
borderRadius: "10px",
height: "50px",
width: "150px",
}}
onClick={() => setTaskSelected(null)}
>
Close popup
</button>
</div>
<BryntumGantt
{...config}
ref={ganttRef}
tasks={tasks}
onTaskDblClick={(e: any) => {
const { taskRecord } = e;
setTaskSelected(taskRecord.originalData);
}}
onTaskDrop={({ taskRecords }: { taskRecords: any[] }) => {
const taskRecord = taskRecords[0];
const taskData = taskRecord.originalData;
const filteredTasks = tasks.filter((t) => t.id !== taskData.id);
// Save change into the store
setTasks([
...filteredTasks,
{
...taskData,
endDate: taskRecord.endDate,
startDate: taskRecord.startDate,
},
]);
}}
onCellDblClick={(e: any) => {
// extract the record from the event
const { record } = e;
ganttRef.current?.instance?.scrollTaskIntoView(record, {
animate: true,
block: "center",
});
}}
/>
</div>
);
}
export { Gantt };
I've been able to achieve everything I want pretty nicely:
However, when I'm dealing with touch devices (here in the google chrome mobile device emulator), it seems that my logic isn't called anymore:
It's probably related to touch instead of click actions. Issue is but I haven't found anything related to this in the documentation.
How could I intercept the "double-touch | longpress" events on the grid and on the Gantt tasks bar ? Did I missed some properties ?
Also, I didn't found a way to "keep the grid collapse" buttons always visible to the user with the buttons for "collapse/open/fullsize" accessible on mobile ? (without the need to drag it, which can be tedious for users, and can be produce blinking).
PS: Thank's for the amazing work everyone at Bryntum do to make this library such an useful tool. From what I've seen it's the best on the market so far, both in terms of features and developer experience.