@hellobetterdigitalnz/betterui 0.0.3-310 → 0.0.3-312

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,11 @@
1
- import React, { useCallback, useMemo, useRef, useState, useEffect } from "react";
1
+ import React, {
2
+ useCallback,
3
+ useMemo,
4
+ useRef,
5
+ useState,
6
+ useEffect,
7
+ ReactNode,
8
+ } from "react";
2
9
  import {
3
10
  addDays,
4
11
  addMinutes,
@@ -7,27 +14,33 @@ import {
7
14
  formatISO,
8
15
  startOfDay,
9
16
  } from "date-fns";
10
- import "./GanttChart.scss";
17
+ import styles from "./GanttChart.module.scss";
11
18
  import { DotsSixVertical } from "../../Icons";
12
-
13
- // -------------------- Types --------------------
19
+ import CaretLeft from "../../Icons/Arrows/CaretLeft/CaretLeft.tsx";
20
+ import CaretRight from "../../Icons/Arrows/CaretRight/CaretRight.tsx";
14
21
 
15
22
  type GanttTask = {
16
23
  id: string;
17
24
  name: string;
18
- start: string; // ISO
19
- end: string; // ISO
25
+ start: string;
26
+ end: string;
20
27
  color?: string;
21
28
  user?: string;
29
+ userId?: string;
30
+ };
31
+
32
+ type GanttTaskChange = {
33
+ task: GanttTask;
34
+ previousTask: GanttTask;
35
+ changeType: "move" | "resize-start" | "resize-end";
22
36
  };
23
37
 
24
38
  type GanttChartProps = {
25
39
  tasks?: GanttTask[];
26
- onChange?: (tasks: GanttTask[]) => void;
40
+ onChange?: (tasks: GanttTask[], change?: GanttTaskChange) => void;
41
+ dropdown?:ReactNode
27
42
  };
28
43
 
29
- // -------------------- Utils --------------------
30
-
31
44
  function dateToMinuteIndex(base: Date, d: Date) {
32
45
  return differenceInMinutes(d, base);
33
46
  }
@@ -40,60 +53,18 @@ function clamp(n: number, min: number, max: number) {
40
53
  return Math.min(max, Math.max(min, n));
41
54
  }
42
55
 
43
- // -------------------- Component --------------------
44
56
 
45
- export default function GanttChart({ tasks: initialTasks, onChange }: GanttChartProps) {
57
+ const GanttChart = ({ tasks: initialTasks, onChange, dropdown }: GanttChartProps)=> {
46
58
  const [currentDay, setCurrentDay] = useState<Date>(startOfDay(new Date()));
47
59
  const startDate = startOfDay(currentDay);
48
- const totalDays = 1;
60
+ const totalDays = 7;
61
+
49
62
 
50
- // Use header hours for sizing (stable across rows). Avoid a shared ref across many rows.
51
63
  const headerHoursRef = useRef<HTMLDivElement>(null);
52
- const [cellSize, setCellSize] = useState<number>(60); // default aligns with CSS min-width
64
+ const [cellSize, setCellSize] = useState<number>(60);
53
65
 
54
66
  const [tasks, setTasks] = useState<GanttTask[]>(
55
- initialTasks ?? [
56
- {
57
- id: "1",
58
- name: "Design",
59
- start: "2025-08-27T05:30:00",
60
- end: "2025-08-27T10:30:00",
61
- color: "blue",
62
- user: "Alice",
63
- },
64
- {
65
- id: "2",
66
- name: "Development",
67
- start: "2025-08-27T09:45:00",
68
- end: "2025-08-27T17:30:00", // FIX: valid 24h time and correct date
69
- color: "green",
70
- user: "Bob",
71
- },
72
- {
73
- id: "3",
74
- name: "QA",
75
- start: "2025-08-27T08:10:00",
76
- end: "2025-08-27T09:40:00",
77
- color: "orange",
78
- user: "Charlie",
79
- },
80
- {
81
- id: "4",
82
- name: "QA",
83
- start: "2025-08-27T08:10:00",
84
- end: "2025-08-27T09:40:00",
85
- color: "red",
86
- user: "Mamudu",
87
- },
88
- {
89
- id: "5",
90
- name: "QA",
91
- start: "2025-08-27T08:10:00",
92
- end: "2025-08-27T09:40:00",
93
- color: "black",
94
- user: "",
95
- },
96
- ]
67
+ initialTasks ?? []
97
68
  );
98
69
 
99
70
  const [dragGhost, setDragGhost] = useState<null | {
@@ -101,21 +72,62 @@ export default function GanttChart({ tasks: initialTasks, onChange }: GanttChart
101
72
  x: number;
102
73
  y: number;
103
74
  rowUser: string;
75
+ rowUserId: string;
104
76
  }>(null);
105
77
 
106
78
  const minuteWidth = useMemo(() => (cellSize > 0 ? cellSize / 60 : 1), [cellSize]);
107
79
  const pxToMinutes = useCallback((px: number) => px / minuteWidth, [minuteWidth]);
108
80
  const minutesToPx = useCallback((minutes: number) => minutes * minuteWidth, [minuteWidth]);
109
81
 
110
- const timeUnits = useMemo(() => {
111
- return Array.from({ length: 24 }).map((_, h) => addMinutes(startDate, h * 60));
112
- }, [startDate]);
82
+ const daysArray = useMemo(() => {
83
+ return Array.from({ length: totalDays }).map((_, i) => addDays(startDate, i));
84
+ }, [startDate, totalDays]);
85
+
86
+ // const timeUnits = useMemo(() => {
87
+ // return Array.from({ length: 24 }).map((_, h) => addMinutes(startDate, h * 60));
88
+ // }, [startDate]);
89
+
90
+ // Group tasks by user and maintain all unique users
91
+ const groupedTasks = useMemo(() => {
92
+ // Collect all unique users from tasks with their userIds
93
+ const allUsers = new Map<string, string>();
94
+ tasks.forEach((task) => {
95
+ const userName = task.user?.trim() || "";
96
+ allUsers.set(userName, task.userId || "");
97
+ });
98
+
99
+ const groups = new Map<string, GanttTask[]>();
100
+
101
+ // Initialize groups for all users
102
+ allUsers.forEach((_, userName) => {
103
+ groups.set(userName, []);
104
+ });
105
+
106
+
107
+ // Populate groups with tasks
108
+ tasks.forEach((task) => {
109
+ const key = task.user?.trim() || "";
110
+ groups.get(key)!.push(task);
111
+ });
112
+
113
+ // Sort so unassigned ("") comes first, and store userId alongside
114
+ const sorted = Array.from(groups.entries()).map(([userName, tasks]) => {
115
+ const userId = allUsers.get(userName) || "";
116
+ return { userName, userId, tasks };
117
+ }).sort((a, b) => {
118
+ if (!a.userName) return -1;
119
+ if (!b.userName) return 1;
120
+ return a.userName.localeCompare(b.userName);
121
+ });
122
+
123
+ return sorted;
124
+ }, [tasks]);
113
125
 
114
126
  useEffect(() => {
115
127
  const updateSize = () => {
116
- const firstHour = headerHoursRef.current?.querySelector<HTMLDivElement>(".hour");
128
+ const firstHour = headerHoursRef.current?.querySelector(`.${styles.hour}`);
117
129
  if (firstHour) {
118
- setCellSize(firstHour.offsetWidth);
130
+ setCellSize((firstHour as HTMLElement).offsetWidth);
119
131
  }
120
132
  };
121
133
 
@@ -124,12 +136,11 @@ export default function GanttChart({ tasks: initialTasks, onChange }: GanttChart
124
136
  return () => window.removeEventListener("resize", updateSize);
125
137
  }, []);
126
138
 
127
- // Keep consumer in sync without stale closures
128
- useEffect(() => {
129
- if (onChange) onChange(tasks);
130
- }, [tasks, onChange]);
139
+ // useEffect(() => {
140
+ // // Only call onChange for initial load, not for drag operations
141
+ // // Drag operations handle onChange in onPointerUp
142
+ // }, []);
131
143
 
132
- // Drag state
133
144
  const dragState = useRef<null | {
134
145
  mode: "move" | "resize-start" | "resize-end";
135
146
  taskId: string;
@@ -137,17 +148,19 @@ export default function GanttChart({ tasks: initialTasks, onChange }: GanttChart
137
148
  originLeftMin: number;
138
149
  originRightMin: number;
139
150
  originUser?: string;
151
+ originalTask?: GanttTask;
140
152
  }>(null);
141
153
 
142
- function getRowInfoFromPoint(x: number, y: number): { user: string; daysEl: HTMLElement | null } {
154
+ function getRowInfoFromPoint(x: number, y: number): { user: string; userId: string; daysEl: HTMLElement | null } {
143
155
  const el = document.elementFromPoint(x, y) as HTMLElement | null;
144
- const row = el?.closest(".gantt-row") as HTMLElement | null;
145
- if (!row) return { user: "", daysEl: null };
146
-
147
- const label = row.querySelector(".task-column-label")?.textContent?.trim();
148
- const user = label && label !== "Unassigned" ? label : ""; // empty string is our Unassigned key
149
- const daysEl = row.querySelector(".days") as HTMLElement | null;
150
- return { user, daysEl };
156
+ const row = el?.closest(`.${styles.ganttRow}`) as HTMLElement | null;
157
+ if (!row) return { user: "", userId: "", daysEl: null };
158
+
159
+ const label = row.querySelector(`.${styles.taskColLabel}`)?.textContent?.trim();
160
+ const user = label && label !== "Unassigned" ? label : "";
161
+ const userId = row.getAttribute("data-user-id") || "";
162
+ const daysEl = row.querySelector(`.${styles.days}`) as HTMLElement | null;
163
+ return { user, userId, daysEl };
151
164
  }
152
165
 
153
166
  const onPointerDownBar = (
@@ -156,6 +169,7 @@ export default function GanttChart({ tasks: initialTasks, onChange }: GanttChart
156
169
  mode: "move" | "resize-start" | "resize-end"
157
170
  ) => {
158
171
  e.preventDefault();
172
+ e.stopPropagation();
159
173
  const task = tasks.find((t) => t.id === taskId);
160
174
  if (!task) return;
161
175
 
@@ -172,53 +186,44 @@ export default function GanttChart({ tasks: initialTasks, onChange }: GanttChart
172
186
  originLeftMin: dateToMinuteIndex(startDate, taskStart),
173
187
  originRightMin: dateToMinuteIndex(startDate, taskEnd),
174
188
  originUser: task.user ?? "",
189
+ originalTask: { ...task },
175
190
  };
176
191
 
177
192
  if (mode === "move") {
178
- setDragGhost({ taskId, x: e.clientX, y: e.clientY, rowUser: task.user ?? "" });
193
+ setDragGhost({ taskId, x: e.clientX, y: e.clientY, rowUser: task.user ?? "", rowUserId: task.userId ?? "" });
179
194
  }
180
195
 
181
196
  const onPointerMove = (ev: PointerEvent) => {
182
197
  if (!dragState.current) return;
183
198
  if (minuteWidth <= 0) return;
184
199
  const ds = dragState.current;
185
- const dx = ev.clientX - ds.originX;
186
- const deltaMinutes = Math.round(pxToMinutes(dx));
187
200
 
188
201
  if (ds.mode === "move") {
189
- const { user: hoverUser } = getRowInfoFromPoint(ev.clientX, ev.clientY);
202
+ const { user: hoverUser, userId: hoverUserId } = getRowInfoFromPoint(ev.clientX, ev.clientY);
190
203
  setDragGhost((ghost) =>
191
- ghost ? { ...ghost, x: ev.clientX, y: ev.clientY, rowUser: hoverUser } : null
204
+ ghost ? { ...ghost, x: ev.clientX, y: ev.clientY, rowUser: hoverUser, rowUserId: hoverUserId } : null
192
205
  );
193
- }
206
+ } else {
207
+ // Only update task position for resize operations
208
+ const dx = ev.clientX - ds.originX;
209
+ const deltaMinutes = Math.round(pxToMinutes(dx));
194
210
 
195
- setTasks((prev) =>
196
- prev.map((t) => {
197
- if (t.id !== ds.taskId) return t;
198
-
199
- if (ds.mode === "move") {
200
- const widthMinutes = minutesBetween(new Date(t.start), new Date(t.end));
201
- const newLeft = clamp(ds.originLeftMin + deltaMinutes, 0, totalDays * 24 * 60 - widthMinutes);
202
- const newStart = addMinutes(startDate, newLeft);
203
- const newEnd = addMinutes(startDate, newLeft + widthMinutes);
204
- return {
205
- ...t,
206
- start: formatISO(newStart, { representation: "complete" }),
207
- end: formatISO(newEnd, { representation: "complete" }),
208
- };
209
- }
211
+ setTasks((prev) =>
212
+ prev.map((t) => {
213
+ if (t.id !== ds.taskId) return t;
210
214
 
211
- if (ds.mode === "resize-start") {
212
- const newLeft = clamp(ds.originLeftMin + deltaMinutes, 0, ds.originRightMin - 1);
213
- const newStart = addMinutes(startDate, newLeft);
214
- return { ...t, start: formatISO(newStart, { representation: "complete" }) };
215
- }
215
+ if (ds.mode === "resize-start") {
216
+ const newLeft = clamp(ds.originLeftMin + deltaMinutes, 0, ds.originRightMin - 1);
217
+ const newStart = addMinutes(startDate, newLeft);
218
+ return { ...t, start: formatISO(newStart, { representation: "complete" }) };
219
+ }
216
220
 
217
- const newRight = clamp(ds.originRightMin + deltaMinutes, ds.originLeftMin + 1, totalDays * 24 * 60);
218
- const newEnd = addMinutes(startDate, newRight);
219
- return { ...t, end: formatISO(newEnd, { representation: "complete" }) };
220
- })
221
- );
221
+ const newRight = clamp(ds.originRightMin + deltaMinutes, ds.originLeftMin + 1, totalDays * 24 * 60);
222
+ const newEnd = addMinutes(startDate, newRight);
223
+ return { ...t, end: formatISO(newEnd, { representation: "complete" }) };
224
+ })
225
+ );
226
+ }
222
227
  };
223
228
 
224
229
  const onPointerUp = (ev: PointerEvent) => {
@@ -226,30 +231,62 @@ export default function GanttChart({ tasks: initialTasks, onChange }: GanttChart
226
231
  target.releasePointerCapture(e.pointerId);
227
232
  } catch {}
228
233
 
229
- const { user: dropUser, daysEl } = dragGhost
230
- ? getRowInfoFromPoint(dragGhost.x, dragGhost.y)
231
- : getRowInfoFromPoint(ev.clientX, ev.clientY);
232
-
233
234
  const ds = dragState.current;
234
- if (ds && daysEl) {
235
- const rect = daysEl.getBoundingClientRect();
236
- const dropX = (dragGhost?.x ?? ev.clientX) - rect.left;
237
- const dropMinutes = Math.round(pxToMinutes(dropX));
238
235
 
239
- setTasks((prev) => {
240
- return prev.map((t) => {
241
- if (t.id !== ds.taskId) return t;
242
- const duration = minutesBetween(new Date(t.start), new Date(t.end));
243
- const newStart = addMinutes(startDate, clamp(dropMinutes, 0, 24 * 60 - duration));
244
- const newEnd = addMinutes(newStart, duration);
245
- return {
246
- ...t,
247
- start: formatISO(newStart, { representation: "complete" }),
248
- end: formatISO(newEnd, { representation: "complete" }),
249
- user: dropUser ?? t.user,
250
- };
236
+ if (ds && ds.mode === "move") {
237
+ const { user: dropUser, userId: dropUserId, daysEl } = dragGhost
238
+ ? getRowInfoFromPoint(dragGhost.x, dragGhost.y)
239
+ : getRowInfoFromPoint(ev.clientX, ev.clientY);
240
+
241
+ if (daysEl) {
242
+ const rect = daysEl.getBoundingClientRect();
243
+ const dropX = (dragGhost?.x ?? ev.clientX) - rect.left;
244
+ const dropMinutes = Math.round(pxToMinutes(dropX));
245
+
246
+ setTasks((prev) => {
247
+ const updatedTasks = prev.map((t) => {
248
+ if (t.id !== ds.taskId) return t;
249
+ const duration = minutesBetween(new Date(t.start), new Date(t.end));
250
+ const maxMinutes = totalDays * 24 * 60 - duration;
251
+ const newLeft = clamp(dropMinutes, 0, maxMinutes);
252
+ const newStart = addMinutes(startDate, newLeft);
253
+ const newEnd = addMinutes(newStart, duration);
254
+ return {
255
+ ...t,
256
+ start: formatISO(newStart, { representation: "complete" }),
257
+ end: formatISO(newEnd, { representation: "complete" }),
258
+ user: dropUser !== undefined ? dropUser : t.user,
259
+ userId: dropUserId !== undefined ? dropUserId : t.userId,
260
+ };
261
+ });
262
+
263
+ // Call onChange with change details
264
+ if (onChange && ds.originalTask) {
265
+ const updatedTask = updatedTasks.find(t => t.id === ds.taskId);
266
+ if (updatedTask) {
267
+ onChange(updatedTasks, {
268
+ task: updatedTask,
269
+ previousTask: ds.originalTask,
270
+ changeType: ds.mode,
271
+ });
272
+ }
273
+ }
274
+
275
+ return updatedTasks;
251
276
  });
252
- });
277
+ }
278
+ } else if (ds && (ds.mode === "resize-start" || ds.mode === "resize-end")) {
279
+ // For resize operations, call onChange
280
+ if (onChange && ds.originalTask) {
281
+ const currentTask = tasks.find(t => t.id === ds.taskId);
282
+ if (currentTask) {
283
+ onChange(tasks, {
284
+ task: currentTask,
285
+ previousTask: ds.originalTask,
286
+ changeType: ds.mode,
287
+ });
288
+ }
289
+ }
253
290
  }
254
291
 
255
292
  setDragGhost(null);
@@ -266,15 +303,15 @@ export default function GanttChart({ tasks: initialTasks, onChange }: GanttChart
266
303
  const taskStart = new Date(task.start);
267
304
  const taskEnd = new Date(task.end);
268
305
 
269
- const dayStart = startOfDay(currentDay);
270
- const dayEnd = addMinutes(dayStart, 24 * 60);
306
+ const rangeStart = startDate;
307
+ const rangeEnd = addDays(startDate, totalDays);
271
308
 
272
- const displayStart = taskStart < dayStart ? dayStart : taskStart;
273
- const displayEnd = taskEnd > dayEnd ? dayEnd : taskEnd;
309
+ const displayStart = taskStart < rangeStart ? rangeStart : taskStart;
310
+ const displayEnd = taskEnd > rangeEnd ? rangeEnd : taskEnd;
274
311
 
275
312
  if (displayStart >= displayEnd) return null;
276
313
 
277
- const minutesFromStart = dateToMinuteIndex(dayStart, displayStart);
314
+ const minutesFromStart = dateToMinuteIndex(rangeStart, displayStart);
278
315
  const minutesWidth = minutesBetween(displayStart, displayEnd);
279
316
 
280
317
  const leftPx = minutesToPx(minutesFromStart);
@@ -282,7 +319,8 @@ export default function GanttChart({ tasks: initialTasks, onChange }: GanttChart
282
319
 
283
320
  return (
284
321
  <div
285
- className="task-bar"
322
+ key={task.id}
323
+ className={styles.taskBar}
286
324
  style={{
287
325
  left: leftPx,
288
326
  width: widthPx,
@@ -290,61 +328,71 @@ export default function GanttChart({ tasks: initialTasks, onChange }: GanttChart
290
328
  opacity: dragGhost?.taskId === task.id ? 0.4 : 1,
291
329
  }}
292
330
  >
293
- {/* <div className="resize-handle left" onPointerDown={(e) => onPointerDownBar(e, task.id, "resize-start")} /> */}
294
- <div className="bar">
295
- <div onPointerDown={(e) => onPointerDownBar(e, task.id, "move")} className={"bar-drag"}>
331
+ <div className={styles.bar}>
332
+ <div onPointerDown={(e) => onPointerDownBar(e, task.id, "move")} className={styles.barDrag}>
296
333
  <DotsSixVertical />
297
334
  </div>
298
335
  <span>{task.name}</span>
299
336
  </div>
300
- {/* <div className="resize-handle right" onPointerDown={(e) => onPointerDownBar(e, task.id, "resize-end")} /> */}
301
337
  </div>
302
338
  );
303
339
  };
304
340
 
305
341
  return (
306
342
  <>
307
- <div className="month-controls">
308
- <button onClick={() => setCurrentDay((d) => addDays(d, -1))}>Prev</button>
309
- <span>{format(currentDay, "MMMM dd, yyyy")}</span>
310
- <button onClick={() => setCurrentDay((d) => addDays(d, 1))}>Next</button>
343
+ <div className={styles.monthControls}>
344
+ <div className={styles.monthControlLeft}>
345
+ <div className={styles.prevBtn} onClick={() => setCurrentDay((d) => addDays(d, -1))}><CaretLeft/></div>
346
+ <div className={styles.nxtBtn} onClick={() => setCurrentDay((d) => addDays(d, 1))}><CaretRight/></div>
347
+ <span className={styles.period}>{format(currentDay, "MMMM dd, yyyy")}</span>
348
+ </div>
349
+ <div className={styles.monthControlRight}>
350
+ {dropdown}
351
+ </div>
311
352
  </div>
312
353
 
313
- <div className="gantt-chart">
314
- <div className="gantt-header">
315
- <div className="task-column"></div>
316
- <div className="days">
317
- <div className="date-day-view current-day">
318
- <div className="single-date">{format(currentDay, "MM/dd")}</div>
319
- <div className="hours" ref={headerHoursRef}>
320
- {Array.from({ length: 24 }).map((_, h) => (
321
- <div className="hour" key={h}>
322
- {`${String(h).padStart(2, "0")}:00`}
323
- </div>
324
- ))}
354
+ <div className={styles.ganttChart}>
355
+ <div className={styles.ganttHeader}>
356
+ <div className={styles.taskCol}></div>
357
+ <div className={styles.days}>
358
+ {daysArray.map((day, idx) => (
359
+ <div
360
+ key={day.toISOString()}
361
+ className={`${styles.dateDayView} ${idx === 0 ? styles.currentDay : ''}`}
362
+ >
363
+ <div className={styles.singleDate}>{format(day, "MM/dd")}</div>
364
+ <div className={styles.hours} ref={idx === 0 ? headerHoursRef : null}>
365
+ {Array.from({ length: 24 }).map((_, h) => (
366
+ <div className={styles.hour} key={h}>
367
+ {`${String(h).padStart(2, "0")}:00`}
368
+ </div>
369
+ ))}
370
+ </div>
325
371
  </div>
326
- </div>
372
+ ))}
327
373
  </div>
328
374
  </div>
329
375
 
330
- <div className="gantt-body">
331
- {[...tasks]
332
- .sort((a, b) => {
333
- if (!a.user) return -1;
334
- if (!b.user) return 1;
335
- return 0;
336
- })
337
- .map((task) => (
338
- <div key={task.id} className="gantt-row">
339
- <div className="task-column-label">{task.user?.trim() || "Unassigned"}</div>
340
- <div className="days">
341
- {timeUnits.map((d) => (
342
- <div date-cell={d.toISOString()} key={d.toISOString()} />
343
- ))}
344
- {renderBar(task)}
345
- </div>
376
+ <div className={styles.ganttBody}>
377
+ {groupedTasks.map(({ userName, userId, tasks: userTasks }) => (
378
+ <div
379
+ key={userName || "unassigned"}
380
+ className={styles.ganttRow}
381
+ data-user-id={userId}
382
+ >
383
+ <div className={styles.taskColLabel}>{userName || "Unassigned"}</div>
384
+ <div className={styles.days}>
385
+ {daysArray.map((day) => (
386
+ <div key={day.toISOString()} className={styles.dayColumn}>
387
+ {Array.from({ length: 24 }).map((_, h) => (
388
+ <div className={styles.dayHrs} key={h} />
389
+ ))}
390
+ </div>
391
+ ))}
392
+ {userTasks.map((task) => renderBar(task))}
346
393
  </div>
347
- ))}
394
+ </div>
395
+ ))}
348
396
  </div>
349
397
  </div>
350
398
 
@@ -354,15 +402,21 @@ export default function GanttChart({ tasks: initialTasks, onChange }: GanttChart
354
402
  style={{
355
403
  position: "fixed",
356
404
  left: dragGhost.x,
357
- top: dragGhost.y,
405
+ top: dragGhost.y - 12,
358
406
  pointerEvents: "none",
359
- opacity: 0.8,
407
+ opacity: 0.9,
360
408
  background: tasks.find((t) => t.id === dragGhost.taskId)?.color ?? "gray",
361
- padding: "0px 6px",
409
+ padding: "4px 8px",
362
410
  height: "24px",
363
- width: "100px",
411
+ minWidth: "100px",
364
412
  borderRadius: 4,
365
413
  zIndex: 9999,
414
+ display: "flex",
415
+ alignItems: "center",
416
+ fontSize: "12px",
417
+ color: "white",
418
+ fontWeight: 500,
419
+ boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
366
420
  }}
367
421
  >
368
422
  <span>{tasks.find((t) => t.id === dragGhost.taskId)?.name}</span>
@@ -371,3 +425,5 @@ export default function GanttChart({ tasks: initialTasks, onChange }: GanttChart
371
425
  </>
372
426
  );
373
427
  }
428
+
429
+ export default GanttChart
@@ -85,3 +85,5 @@ export type { default as TableRowProps} from './Table/TableRowProps';
85
85
  export {default as CalendarView} from './Calendar/Calendar.tsx'
86
86
  export type {default as CalendarProps} from './Calendar/CalendarProps.tsx'
87
87
 
88
+ export {default as GanttChart} from './GanttChart/GanttChart.tsx'
89
+