refactor(task-list): simplify subtask handling in AddSubtaskRow and TaskRowWithSubtasks
- Updated AddSubtaskRow to remove rowId dependency from onSubtaskAdded and onActivate callbacks, streamlining the subtask addition process. - Enhanced input handling to maintain focus and visibility after adding a subtask. - Refactored TaskRowWithSubtasks to consolidate add subtask row management, ensuring a single add subtask row is displayed when not loading.
This commit is contained in:
@@ -32,11 +32,11 @@ interface AddSubtaskRowProps {
|
|||||||
width: string;
|
width: string;
|
||||||
isSticky?: boolean;
|
isSticky?: boolean;
|
||||||
}>;
|
}>;
|
||||||
onSubtaskAdded: (rowId: string) => void;
|
onSubtaskAdded: () => void; // Simplified - no rowId needed
|
||||||
rowId: string; // Unique identifier for this add subtask row
|
rowId: string; // Unique identifier for this add subtask row
|
||||||
autoFocus?: boolean; // Whether this row should auto-focus on mount
|
autoFocus?: boolean; // Whether this row should auto-focus on mount
|
||||||
isActive?: boolean; // Whether this row should show the input/button
|
isActive?: boolean; // Whether this row should show the input/button
|
||||||
onActivate?: (rowId: string) => void; // Callback when row becomes active
|
onActivate?: () => void; // Simplified - no rowId needed
|
||||||
}
|
}
|
||||||
|
|
||||||
const AddSubtaskRow: React.FC<AddSubtaskRowProps> = memo(({
|
const AddSubtaskRow: React.FC<AddSubtaskRowProps> = memo(({
|
||||||
@@ -93,17 +93,31 @@ const AddSubtaskRow: React.FC<AddSubtaskRowProps> = memo(({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear the input but keep it focused for the next subtask
|
||||||
setSubtaskName('');
|
setSubtaskName('');
|
||||||
// Keep the input active and notify parent to create new row
|
// Keep isAdding as true so the input stays visible
|
||||||
onSubtaskAdded(rowId);
|
// Focus the input again after a short delay to ensure it's ready
|
||||||
}, [subtaskName, dispatch, parentTaskId, projectId, connected, socket, currentSession, onSubtaskAdded, rowId]);
|
setTimeout(() => {
|
||||||
|
if (inputRef.current) {
|
||||||
|
inputRef.current.focus();
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
|
||||||
|
// Notify parent that subtask was added
|
||||||
|
onSubtaskAdded();
|
||||||
|
}, [subtaskName, dispatch, parentTaskId, projectId, connected, socket, currentSession, onSubtaskAdded]);
|
||||||
|
|
||||||
const handleCancel = useCallback(() => {
|
const handleCancel = useCallback(() => {
|
||||||
|
setSubtaskName('');
|
||||||
|
setIsAdding(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleBlur = useCallback(() => {
|
||||||
|
// Only cancel if the input is empty, otherwise keep it active
|
||||||
if (subtaskName.trim() === '') {
|
if (subtaskName.trim() === '') {
|
||||||
setSubtaskName('');
|
handleCancel();
|
||||||
setIsAdding(false);
|
|
||||||
}
|
}
|
||||||
}, [subtaskName]);
|
}, [subtaskName, handleCancel]);
|
||||||
|
|
||||||
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
|
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
|
||||||
if (e.key === 'Escape') {
|
if (e.key === 'Escape') {
|
||||||
@@ -135,7 +149,9 @@ const AddSubtaskRow: React.FC<AddSubtaskRowProps> = memo(({
|
|||||||
!isAdding ? (
|
!isAdding ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onActivate?.(rowId);
|
if (onActivate) {
|
||||||
|
onActivate();
|
||||||
|
}
|
||||||
setIsAdding(true);
|
setIsAdding(true);
|
||||||
}}
|
}}
|
||||||
className="flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-colors h-full"
|
className="flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-colors h-full"
|
||||||
@@ -149,14 +165,14 @@ const AddSubtaskRow: React.FC<AddSubtaskRowProps> = memo(({
|
|||||||
value={subtaskName}
|
value={subtaskName}
|
||||||
onChange={(e) => setSubtaskName(e.target.value)}
|
onChange={(e) => setSubtaskName(e.target.value)}
|
||||||
onPressEnter={handleAddSubtask}
|
onPressEnter={handleAddSubtask}
|
||||||
onBlur={handleCancel}
|
onBlur={handleBlur}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
placeholder="Type subtask name and press Enter to save"
|
placeholder="Type subtask name and press Enter to save"
|
||||||
className="w-full h-full border-none shadow-none bg-transparent"
|
className="w-full h-full border-none shadow-none bg-transparent"
|
||||||
style={{
|
style={{
|
||||||
height: '100%',
|
height: '100%',
|
||||||
minHeight: '32px',
|
minHeight: '32px',
|
||||||
padding: '0',
|
padding: '4px 8px',
|
||||||
fontSize: '14px'
|
fontSize: '14px'
|
||||||
}}
|
}}
|
||||||
autoFocus
|
autoFocus
|
||||||
@@ -172,7 +188,7 @@ const AddSubtaskRow: React.FC<AddSubtaskRowProps> = memo(({
|
|||||||
default:
|
default:
|
||||||
return <div style={baseStyle} />;
|
return <div style={baseStyle} />;
|
||||||
}
|
}
|
||||||
}, [isAdding, subtaskName, handleAddSubtask, handleCancel, handleKeyDown, t]);
|
}, [isAdding, subtaskName, handleAddSubtask, handleCancel, handleBlur, handleKeyDown, t, isActive, onActivate]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center min-w-max px-1 py-0.5 hover:bg-gray-50 dark:hover:bg-gray-800 min-h-[36px] border-b border-gray-200 dark:border-gray-700">
|
<div className="flex items-center min-w-max px-1 py-0.5 hover:bg-gray-50 dark:hover:bg-gray-800 min-h-[36px] border-b border-gray-200 dark:border-gray-700">
|
||||||
@@ -197,31 +213,10 @@ const TaskRowWithSubtasks: React.FC<TaskRowWithSubtasksProps> = memo(({
|
|||||||
const task = useAppSelector(state => selectTaskById(state, taskId));
|
const task = useAppSelector(state => selectTaskById(state, taskId));
|
||||||
const isLoadingSubtasks = useAppSelector(state => selectSubtaskLoading(state, taskId));
|
const isLoadingSubtasks = useAppSelector(state => selectSubtaskLoading(state, taskId));
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const [addSubtaskRows, setAddSubtaskRows] = useState<string[]>([`add-subtask-${taskId}-0`]);
|
|
||||||
const [activeRowId, setActiveRowId] = useState<string | null>(null);
|
|
||||||
|
|
||||||
const handleSubtaskAdded = useCallback((rowId: string) => {
|
const handleSubtaskAdded = useCallback(() => {
|
||||||
// Refresh subtasks after adding a new one
|
// After adding a subtask, the AddSubtaskRow will handle its own state reset
|
||||||
// The socket event will handle the real-time update
|
// We don't need to do anything here
|
||||||
|
|
||||||
// Only add a new row if this is the last (most recent) row
|
|
||||||
setAddSubtaskRows(prev => {
|
|
||||||
const currentIndex = prev.indexOf(rowId);
|
|
||||||
const isLastRow = currentIndex === prev.length - 1;
|
|
||||||
|
|
||||||
if (isLastRow) {
|
|
||||||
const newRowId = `add-subtask-${taskId}-${prev.length}`;
|
|
||||||
// Set the new row as active
|
|
||||||
setActiveRowId(newRowId);
|
|
||||||
return [...prev, newRowId];
|
|
||||||
}
|
|
||||||
|
|
||||||
return prev; // Don't add new row if this isn't the last row
|
|
||||||
});
|
|
||||||
}, [taskId]);
|
|
||||||
|
|
||||||
const handleRowActivate = useCallback((rowId: string) => {
|
|
||||||
setActiveRowId(rowId);
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!task) {
|
if (!task) {
|
||||||
@@ -262,30 +257,20 @@ const TaskRowWithSubtasks: React.FC<TaskRowWithSubtasksProps> = memo(({
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Add subtask rows - only show when not loading */}
|
{/* Add subtask row - only show when not loading */}
|
||||||
{!isLoadingSubtasks && (
|
{!isLoadingSubtasks && (
|
||||||
<>
|
<div className="bg-gray-50 dark:bg-gray-800/50 border-l-2 border-blue-200 dark:border-blue-700">
|
||||||
{/* Render all add subtask rows */}
|
<AddSubtaskRow
|
||||||
{addSubtaskRows.map((rowId, index) => {
|
parentTaskId={taskId}
|
||||||
const isLastRow = index === addSubtaskRows.length - 1;
|
projectId={projectId}
|
||||||
const isRowActive = activeRowId === null ? isLastRow : activeRowId === rowId;
|
visibleColumns={visibleColumns}
|
||||||
|
onSubtaskAdded={handleSubtaskAdded}
|
||||||
return (
|
rowId={`add-subtask-${taskId}`}
|
||||||
<div key={rowId} className="bg-gray-50 dark:bg-gray-800/50 border-l-2 border-blue-200 dark:border-blue-700">
|
autoFocus={false}
|
||||||
<AddSubtaskRow
|
isActive={true} // Always show the add subtask row
|
||||||
parentTaskId={taskId}
|
onActivate={undefined} // Not needed anymore
|
||||||
projectId={projectId}
|
/>
|
||||||
visibleColumns={visibleColumns}
|
</div>
|
||||||
onSubtaskAdded={handleSubtaskAdded}
|
|
||||||
rowId={rowId}
|
|
||||||
autoFocus={isLastRow && activeRowId === rowId} // Auto-focus the latest row when it becomes active
|
|
||||||
isActive={isRowActive}
|
|
||||||
onActivate={handleRowActivate}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ const AddTaskRow: React.FC<AddTaskRowProps> = memo(({
|
|||||||
style={{
|
style={{
|
||||||
height: '100%',
|
height: '100%',
|
||||||
minHeight: '32px',
|
minHeight: '32px',
|
||||||
padding: '0',
|
padding: '4px 8px',
|
||||||
fontSize: '14px'
|
fontSize: '14px'
|
||||||
}}
|
}}
|
||||||
autoFocus
|
autoFocus
|
||||||
|
|||||||
Reference in New Issue
Block a user