@@ -58,9 +58,12 @@ export interface GanttViewProps {
5858 startDate ?: Date
5959 endDate ?: Date
6060 onTaskClick ?: ( task : GanttTask ) => void
61+ onTaskUpdate ?: ( task : GanttTask , changes : Partial < Pick < GanttTask , 'title' | 'start' | 'end' | 'progress' > > ) => void
6162 onViewChange ?: ( view : GanttViewMode ) => void
6263 onAddClick ?: ( ) => void
6364 className ?: string
65+ /** Enable inline editing of task fields */
66+ inlineEdit ?: boolean
6467}
6568
6669export function GanttView ( {
@@ -69,15 +72,19 @@ export function GanttView({
6972 startDate,
7073 endDate,
7174 onTaskClick,
75+ onTaskUpdate,
7276 onViewChange,
7377 onAddClick,
74- className
78+ className,
79+ inlineEdit = false ,
7580} : GanttViewProps ) {
7681 const [ currentDate , setCurrentDate ] = React . useState ( new Date ( ) ) ;
7782 const [ rowHeight , setRowHeight ] = React . useState (
7883 typeof window !== 'undefined' && window . innerWidth < 640 ? 32 : 40
7984 ) ;
8085 const [ columnWidth , setColumnWidth ] = React . useState ( getResponsiveColumnWidth ( ) ) ;
86+ const [ editingTask , setEditingTask ] = React . useState < string | number | null > ( null ) ;
87+ const [ editValues , setEditValues ] = React . useState < Record < string , string > > ( { } ) ;
8188
8289 React . useEffect ( ( ) => {
8390 const handleResize = ( ) => {
@@ -251,28 +258,85 @@ export function GanttView({
251258 ref = { listRef }
252259 style = { { width : taskListWidth , minWidth : taskListWidth } }
253260 >
254- { tasks . map ( ( task ) => (
261+ { tasks . map ( ( task ) => {
262+ const isEditing = inlineEdit && editingTask === task . id ;
263+ return (
255264 < div
256265 key = { task . id }
257266 className = "flex items-center border-b px-2 sm:px-4 hover:bg-accent/50 cursor-pointer transition-colors touch-manipulation"
258267 style = { { height : rowHeight } }
259- onClick = { ( ) => onTaskClick ?.( task ) }
268+ onClick = { ( ) => ! isEditing && onTaskClick ?.( task ) }
269+ onDoubleClick = { ( ) => {
270+ if ( inlineEdit && onTaskUpdate ) {
271+ setEditingTask ( task . id ) ;
272+ setEditValues ( {
273+ title : task . title ,
274+ start : task . start . toISOString ( ) . split ( 'T' ) [ 0 ] ,
275+ end : task . end . toISOString ( ) . split ( 'T' ) [ 0 ] ,
276+ progress : String ( task . progress ) ,
277+ } ) ;
278+ }
279+ } }
260280 >
261281 < div className = "flex-1 truncate font-medium text-xs sm:text-sm flex items-center gap-2" >
262282 < div
263283 className = "w-2 h-2 rounded-full shrink-0"
264284 style = { { backgroundColor : task . color || '#3b82f6' } }
265285 />
266- { task . title }
286+ { isEditing ? (
287+ < input
288+ className = "border rounded px-1 py-0.5 text-xs w-full bg-background"
289+ value = { editValues . title || '' }
290+ onChange = { ( e ) => setEditValues ( prev => ( { ...prev , title : e . target . value } ) ) }
291+ onKeyDown = { ( e ) => {
292+ if ( e . key === 'Enter' ) {
293+ onTaskUpdate ?.( task , {
294+ title : editValues . title ,
295+ start : new Date ( editValues . start ) ,
296+ end : new Date ( editValues . end ) ,
297+ progress : Number ( editValues . progress ) || 0 ,
298+ } ) ;
299+ setEditingTask ( null ) ;
300+ } else if ( e . key === 'Escape' ) {
301+ setEditingTask ( null ) ;
302+ }
303+ } }
304+ onClick = { ( e ) => e . stopPropagation ( ) }
305+ autoFocus
306+ />
307+ ) : (
308+ task . title
309+ ) }
267310 </ div >
268311 < div className = "w-16 sm:w-20 text-right text-xs text-muted-foreground hidden sm:block" >
269- { task . start . toLocaleDateString ( undefined , { month : 'numeric' , day : 'numeric' } ) }
312+ { isEditing ? (
313+ < input
314+ type = "date"
315+ className = "border rounded px-1 py-0.5 text-xs w-full bg-background"
316+ value = { editValues . start || '' }
317+ onChange = { ( e ) => setEditValues ( prev => ( { ...prev , start : e . target . value } ) ) }
318+ onClick = { ( e ) => e . stopPropagation ( ) }
319+ />
320+ ) : (
321+ task . start . toLocaleDateString ( undefined , { month : 'numeric' , day : 'numeric' } )
322+ ) }
270323 </ div >
271324 < div className = "w-16 sm:w-20 text-right text-xs text-muted-foreground hidden sm:block" >
272- { task . end . toLocaleDateString ( undefined , { month : 'numeric' , day : 'numeric' } ) }
325+ { isEditing ? (
326+ < input
327+ type = "date"
328+ className = "border rounded px-1 py-0.5 text-xs w-full bg-background"
329+ value = { editValues . end || '' }
330+ onChange = { ( e ) => setEditValues ( prev => ( { ...prev , end : e . target . value } ) ) }
331+ onClick = { ( e ) => e . stopPropagation ( ) }
332+ />
333+ ) : (
334+ task . end . toLocaleDateString ( undefined , { month : 'numeric' , day : 'numeric' } )
335+ ) }
273336 </ div >
274337 </ div >
275- ) ) }
338+ ) ;
339+ } ) }
276340 </ div >
277341
278342 { /* Right Side: Timeline */ }
0 commit comments