fix(hooks): definitively resolve React Hook violations in GridTaskBlock
This commit is contained in:
parent
9652bd374e
commit
b1ad9ec546
@ -1,5 +1,5 @@
|
|||||||
import React, { useState, useRef, useEffect } from "react";
|
import React, { useState, useRef, useEffect } from "react";
|
||||||
import { Repeat, ChevronDown, ChevronRight, FileText, CheckCircle, Circle, Copy } from "lucide-react";
|
import { Repeat } from "lucide-react";
|
||||||
import { Task } from "./WeeklyView";
|
import { Task } from "./WeeklyView";
|
||||||
|
|
||||||
interface GridTaskBlockProps {
|
interface GridTaskBlockProps {
|
||||||
@ -107,7 +107,6 @@ export function GridTaskBlock({
|
|||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate dimensions (needed by resize useEffect, must be before early return)
|
|
||||||
const pixelsPerMinute = getSlotHeight(cellDuration) / cellDuration;
|
const pixelsPerMinute = getSlotHeight(cellDuration) / cellDuration;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -144,20 +143,16 @@ export function GridTaskBlock({
|
|||||||
};
|
};
|
||||||
}, [isResizing, resizeHeight, pixelsPerMinute, task.id, updateTaskDuration, task.startTime]);
|
}, [isResizing, resizeHeight, pixelsPerMinute, task.id, updateTaskDuration, task.startTime]);
|
||||||
|
|
||||||
|
// Conditional rendering should only happen after hooks
|
||||||
if (!task.startTime) return null;
|
if (!task.startTime) return null;
|
||||||
|
|
||||||
const [startHour, startMinute] = task.startTime.split(":").map(Number);
|
const [startHour, startMinute] = task.startTime.split(":").map(Number);
|
||||||
const startMinutes = (startHour - workingHoursStart) * 60 + startMinute;
|
const startMinutes = (startHour - workingHoursStart) * 60 + startMinute;
|
||||||
|
|
||||||
// Calculate top offset
|
|
||||||
const topOffset = startMinutes * pixelsPerMinute;
|
const topOffset = startMinutes * pixelsPerMinute;
|
||||||
|
const duration = task.duration || 15;
|
||||||
// Calculate height
|
|
||||||
const duration = task.duration || 15; // default 15m if not set
|
|
||||||
const baseHeight = duration * pixelsPerMinute;
|
const baseHeight = duration * pixelsPerMinute;
|
||||||
const currentHeight = isResizing && resizeHeight !== null ? resizeHeight : baseHeight;
|
const currentHeight = isResizing && resizeHeight !== null ? resizeHeight : baseHeight;
|
||||||
|
|
||||||
// Handlers for resizing
|
|
||||||
const onResizeStart = (e: React.MouseEvent) => {
|
const onResizeStart = (e: React.MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -167,7 +162,6 @@ export function GridTaskBlock({
|
|||||||
document.body.style.cursor = "ns-resize";
|
document.body.style.cursor = "ns-resize";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`time-slot-task ${task.completed && !showTaskCheckboxes ? "completed" : ""} ${draggedTask?.id === task.id ? "dragging" : ""}`}
|
className={`time-slot-task ${task.completed && !showTaskCheckboxes ? "completed" : ""} ${draggedTask?.id === task.id ? "dragging" : ""}`}
|
||||||
@ -202,7 +196,7 @@ export function GridTaskBlock({
|
|||||||
onSubmit={(e) => {
|
onSubmit={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const input = e.currentTarget.elements.namedItem("title") as HTMLInputElement;
|
const input = e.currentTarget.elements.namedItem("title") as HTMLInputElement;
|
||||||
updateTask(task.id, input.value);
|
updateTask(task.id, input.value || \"\");
|
||||||
}}
|
}}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
style={{ width: "100%", paddingRight: "20px" }}
|
style={{ width: "100%", paddingRight: "20px" }}
|
||||||
@ -246,48 +240,11 @@ export function GridTaskBlock({
|
|||||||
style={{ width: "12px", height: "12px", margin: 0, cursor: "pointer", position: "relative", top: "3px", left: "-2px", accentColor: "#FFF" }}
|
style={{ width: "12px", height: "12px", margin: 0, cursor: "pointer", position: "relative", top: "3px", left: "-2px", accentColor: "#FFF" }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{task.externalProvider && (
|
|
||||||
!task.externalId ||
|
|
||||||
!task.lastSyncedAt ||
|
|
||||||
new Date(task.updatedAt) > new Date(task.lastSyncedAt)
|
|
||||||
) && (
|
|
||||||
<span title="Needs to be synced" className="text-yellow-500 flex-shrink-0 mt-[2px]">
|
|
||||||
<svg viewBox="0 0 24 24" width="10" height="10" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
|
||||||
<path d="M21.5 2v6h-6M21.34 15.57a10 10 0 1 1-.57-8.38" />
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{task.subTasks && task.subTasks.length > 0 && (
|
|
||||||
<span
|
|
||||||
className="task-subtask-icon cursor-pointer flex-shrink-0 mt-[2px]"
|
|
||||||
onClick={(e) => { e.stopPropagation(); setIsSubTasksOpen(!isSubTasksOpen); }}
|
|
||||||
>
|
|
||||||
<svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ transform: isSubTasksOpen ? "rotate(90deg)" : "rotate(0deg)", transition: "transform 0.2s" }}>
|
|
||||||
<polygon points="5 3 19 12 5 21 5 3" />
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<span style={task.completed && showTaskCheckboxes ? { opacity: 0.5, flex: 1 } : { flex: 1 }}>{task.title}</span>
|
<span style={task.completed && showTaskCheckboxes ? { opacity: 0.5, flex: 1 } : { flex: 1 }}>{task.title}</span>
|
||||||
{task.markdownContent && (
|
|
||||||
<span
|
|
||||||
className="task-note-icon cursor-pointer flex-shrink-0 mt-[2px]"
|
|
||||||
onClick={(e) => { e.stopPropagation(); setIsNotesOpen(!isNotesOpen); }}
|
|
||||||
style={{ marginLeft: "auto" }}
|
|
||||||
>
|
|
||||||
<svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
||||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
|
||||||
<polyline points="14 2 14 8 20 8" />
|
|
||||||
<line x1="16" y1="13" x2="8" y2="13" />
|
|
||||||
<line x1="16" y1="17" x2="8" y2="17" />
|
|
||||||
</svg>
|
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Task Actions - shown below text on hover */}
|
|
||||||
<div
|
<div
|
||||||
className="task-actions"
|
className="task-actions"
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
@ -311,13 +268,13 @@ export function GridTaskBlock({
|
|||||||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
|
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button className={`task-action-btn ${isSubTaskInputOpen ? "active" : ""}`} onClick={(e) => { e.stopPropagation(); setIsSubTaskInputOpen(!isSubTaskInputOpen); if (!isSubTaskInputOpen) { setIsSubTasksOpen(true); setTimeout(() => subTaskInputRef.current?.focus(), 50); } }} title="Add sub-task">
|
<button className={`task-action-btn ${isSubTaskInputOpen ? \"active\" : \"\"}`} onClick={(e) => { e.stopPropagation(); setIsSubTaskInputOpen(!isSubTaskInputOpen); if (!isSubTaskInputOpen) { setIsSubTasksOpen(true); setTimeout(() => subTaskInputRef.current?.focus(), 50); } }} title="Add sub-task">
|
||||||
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round">
|
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round">
|
||||||
<line x1="12" y1="5" x2="12" y2="19" />
|
<line x1="12" y1="5" x2="12" y2="19" />
|
||||||
<line x1="5" y1="12" x2="19" y2="12" />
|
<line x1="5" y1="12" x2="19" y2="12" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button className={`task-action-btn ${isNotesOpen ? "active" : ""}`} onClick={(e) => { e.stopPropagation(); setIsNotesOpen(!isNotesOpen); }} title="Notes">
|
<button className={`task-action-btn ${isNotesOpen ? \"active\" : \"\"}`} onClick={(e) => { e.stopPropagation(); setIsNotesOpen(!isNotesOpen); }} title="Notes">
|
||||||
< svg viewBox = "0 0 24 24" width = "12" height = "12" stroke = "currentColor" strokeWidth = "2.5" fill = "none" strokeLinecap = "round" strokeLinejoin = "round" >
|
< svg viewBox = "0 0 24 24" width = "12" height = "12" stroke = "currentColor" strokeWidth = "2.5" fill = "none" strokeLinecap = "round" strokeLinejoin = "round" >
|
||||||
<line x1="3" y1="12" x2="21" y2="12" />
|
<line x1="3" y1="12" x2="21" y2="12" />
|
||||||
<line x1="3" y1="6" x2="21" y2="6" />
|
<line x1="3" y1="6" x2="21" y2="6" />
|
||||||
@ -326,26 +283,31 @@ export function GridTaskBlock({
|
|||||||
</button >
|
</button >
|
||||||
{!task.completed && (
|
{!task.completed && (
|
||||||
<button
|
<button
|
||||||
className={`task-action-btn ${task.isRolling ? "active" : ""}`}
|
className={`task-action-btn ${task.isRolling ?\"active\" : \"\"}`}
|
||||||
onClick={(e) => { e.stopPropagation(); toggleTaskRolling(task.id); }}
|
onClick = {(e) => { e.stopPropagation(); toggleTaskRolling(task.id); }
|
||||||
title={task.isRolling ? "Disable rolling" : "Enable rolling"}
|
}
|
||||||
|
title = {
|
||||||
|
task.isRolling ?\"Disable rolling\" : \"Enable rolling\"}
|
||||||
>
|
>
|
||||||
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round">
|
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round">
|
||||||
<polyline points="23 4 23 10 17 10" />
|
<polyline points="23 4 23 10 17 10" />
|
||||||
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10" />
|
<path d=\"M20.49 15a9 9 0 1 1-2.12-9.36L23 10\" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{!task.completed && (
|
{
|
||||||
|
!task.completed && (
|
||||||
<button
|
<button
|
||||||
className={`task-action-btn ${task.isRecurring ? "active" : ""}`}
|
className={`task-action-btn ${task.isRecurring ?\"active\" : \"\"}`}
|
||||||
onClick={(e) => { e.stopPropagation(); setSelectedTaskForRecurrence(task); }}
|
onClick = {(e) => { e.stopPropagation(); setSelectedTaskForRecurrence(task); }
|
||||||
title={task.isRecurring ? "Edit recurrence" : "Make recurring"}
|
}
|
||||||
|
title = {
|
||||||
|
task.isRecurring ?\"Edit recurrence\" : \"Make recurring\"}
|
||||||
>
|
>
|
||||||
<Repeat size={12} />
|
<Repeat size={12} />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button className="task-action-btn delete text-red-500 hover:text-red-700 hover:bg-red-100/50 dark:hover:bg-red-900/30 rounded" onClick={(e) => { e.stopPropagation(); deleteTask(task.id); }} title="Delete">
|
<button className="task-action-btn delete" onClick={(e) => { e.stopPropagation(); deleteTask(task.id); }} title="Delete">
|
||||||
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round">
|
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round">
|
||||||
<polyline points="3 6 5 6 21 6"></polyline>
|
<polyline points="3 6 5 6 21 6"></polyline>
|
||||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
||||||
@ -353,32 +315,31 @@ export function GridTaskBlock({
|
|||||||
</button>
|
</button>
|
||||||
</div >
|
</div >
|
||||||
|
|
||||||
{/* Inline Expanders Container */}
|
|
||||||
<div style={{ paddingLeft: "4px", paddingRight: "4px", paddingBottom: "10px", marginTop: "4px" }}>
|
<div style={{ paddingLeft: "4px", paddingRight: "4px", paddingBottom: "10px", marginTop: "4px" }}>
|
||||||
{isNotesOpen && (
|
{isNotesOpen && (
|
||||||
<div
|
<div
|
||||||
className={`weekly-notes-popup ${topOffset > 180 ? "on-top" : ""}`}
|
className={`weekly-notes-popup ${topOffset > 180 ? \"on-top\" : \"\"}`}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
style={{
|
style={{
|
||||||
top: topOffset > 180 ? "auto" : "100%",
|
top: topOffset > 180 ? \"auto\" : \"100%\",
|
||||||
bottom: topOffset > 180 ? "100%" : "auto",
|
bottom: topOffset > 180 ? \"100%\" : \"auto\",
|
||||||
marginTop: topOffset > 180 ? "0" : "10px",
|
marginTop: topOffset > 180 ? \"0\" : \"10px\",
|
||||||
marginBottom: topOffset > 180 ? "10px" : "0",
|
marginBottom: topOffset > 180 ? \"10px\" : \"0\",
|
||||||
left: "-10px",
|
left: \"-10px\",
|
||||||
right: "-10px",
|
right: \"-10px\",
|
||||||
width: "auto",
|
width: \"auto\",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="notes-toolbar">
|
<div className="notes-toolbar">
|
||||||
<button className="notes-toolbar-btn" onClick={() => insertMarkdown("**", "**")} title="Bold">B</button>
|
<button className="notes-toolbar-btn" onClick={() => insertMarkdown(\"**\", \"**\")} title=\"Bold\">B</button>
|
||||||
<button className="notes-toolbar-btn" onClick={() => insertMarkdown("*", "*")} title="Italic">i</button>
|
<button className="notes-toolbar-btn" onClick={() => insertMarkdown(\"*\", \"*\")} title=\"Italic\">i</button>
|
||||||
<button className="notes-toolbar-btn" onClick={() => insertMarkdown("- ")} title="List">☑</button>
|
<button className="notes-toolbar-btn" onClick={() => insertMarkdown(\"- \")} title=\"List\">☑</button>
|
||||||
<span style={{ marginLeft: "auto", fontSize: "0.75rem", color: "#999" }}>Markdown</span>
|
<span style={{ marginLeft: \"auto\", fontSize: \"0.75rem\", color: \"#999\" }}>Markdown</span>
|
||||||
<button
|
<button
|
||||||
className="notes-toolbar-btn"
|
className="notes-toolbar-btn"
|
||||||
onClick={() => setIsNotesOpen(false)}
|
onClick={() => setIsNotesOpen(false)}
|
||||||
title="Close"
|
title=\"Close\"
|
||||||
style={{ marginLeft: "8px", fontSize: "1rem", lineHeight: 1 }}
|
style={{ marginLeft: \"8px\", fontSize: \"1rem\", lineHeight: 1 }}
|
||||||
>×</button>
|
>×</button>
|
||||||
</div>
|
</div>
|
||||||
<textarea
|
<textarea
|
||||||
@ -388,42 +349,45 @@ export function GridTaskBlock({
|
|||||||
onChange={(e) => setNotesValue(e.target.value)}
|
onChange={(e) => setNotesValue(e.target.value)}
|
||||||
onBlur={handleNotesBlur}
|
onBlur={handleNotesBlur}
|
||||||
placeholder="Add notes..."
|
placeholder="Add notes..."
|
||||||
style={{ minHeight: "120px", padding: "4px" }}
|
style={{ minHeight: \"120px\", padding: \"4px\" }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isSubTasksOpen && task.subTasks && task.subTasks.length > 0 && (
|
{isSubTasksOpen && task.subTasks && task.subTasks.length > 0 && (
|
||||||
<ul className="subtask-list mt-1" onClick={(e) => e.stopPropagation()}>
|
<ul className="subtask-list mt-1" onClick={(e) => e.stopPropagation()}>
|
||||||
{task.subTasks.map((subTask: Task) => (
|
{task.subTasks.map((subTask: Task) => (
|
||||||
<li key={subTask.id} className={`subtask-item ${subTask.completed ? "completed" : ""}`}>
|
<li key={subTask.id} className={`subtask-item ${subTask.completed ? \"completed\" : \"\"}`}>
|
||||||
<button className="subtask-checkbox" onClick={() => toggleSubTask(subTask.id)}>
|
<button className="subtask-checkbox" onClick={() => toggleSubTask(subTask.id)}>
|
||||||
{subTask.completed ? (
|
{subTask.completed ? (
|
||||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12" /></svg>
|
<svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><polyline points=\"20 6 9 17 4 12\" /></svg>
|
||||||
) : (
|
) : (
|
||||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10" /></svg>
|
<svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\" /></svg>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
{onSetEditingTaskId && editingTaskId === subTask.id ? (
|
{onSetEditingTaskId && editingTaskId === subTask.id ? (
|
||||||
<form onSubmit={(e) => { e.preventDefault(); const input = e.currentTarget.querySelector("input"); if (input) { updateSubTask(subTask.id, input.value); onSetEditingTaskId(null); } }} style={{ flex: 1 }}>
|
<form onSubmit={(e) => { e.preventDefault(); const input = e.currentTarget.querySelector(\"input\"); if (input) { updateSubTask(subTask.id, input.value); onSetEditingTaskId(null); } }} style={{ flex: 1 }}>
|
||||||
<input type="text" defaultValue={subTask.title} autoFocus className="subtask-edit-input" onBlur={(e) => { updateSubTask(subTask.id, e.target.value); onSetEditingTaskId(null); }} onKeyDown={(e) => { if (e.key === "Escape") onSetEditingTaskId(null); }} />
|
<input type=\"text\" defaultValue={subTask.title} autoFocus className=\"subtask-edit-input\" onBlur={(e) => { updateSubTask(subTask.id, e.target.value); onSetEditingTaskId(null); }} onKeyDown={(e) => { if (e.key === \"Escape\") onSetEditingTaskId(null); }} />
|
||||||
</form>
|
</form>
|
||||||
) : (
|
) : (
|
||||||
<span className={`subtask-title ${subTask.completed ? "completed" : ""}`} onClick={() => onSetEditingTaskId && onSetEditingTaskId(subTask.id)}>{subTask.title}</span>
|
<span className={`subtask-title ${subTask.completed ?\"completed\" : \"\"}`} onClick={() => onSetEditingTaskId && onSetEditingTaskId(subTask.id)}>{subTask.title}</span>
|
||||||
)}
|
)}
|
||||||
<button className="subtask-delete-btn" onClick={() => deleteSubTask(subTask.id)} title="Remove"><svg viewBox="0 0 24 24" width="10" height="10" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round"><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></svg></button>
|
<button className=\"subtask-delete-btn\" onClick={() => deleteSubTask(subTask.id)} title=\"Remove\"><svg viewBox=\"0 0 24 24\" width=\"10\" height=\"10\" stroke=\"currentColor\" strokeWidth=\"2.5\" fill=\"none\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" /><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" /></svg></button>
|
||||||
</li >
|
</li >
|
||||||
))}
|
))}
|
||||||
</ul >
|
</ul >
|
||||||
)}
|
)}
|
||||||
{isSubTaskInputOpen && (
|
{
|
||||||
|
isSubTaskInputOpen && (
|
||||||
<div className="subtask-add-row mt-1" onClick={(e) => e.stopPropagation()}>
|
<div className="subtask-add-row mt-1" onClick={(e) => e.stopPropagation()}>
|
||||||
<form onSubmit={(e) => { e.preventDefault(); if (newSubTaskTitle.trim()) { addSubTask(task.id, newSubTaskTitle.trim()); setNewSubTaskTitle(""); } }} style={{ display: "flex", alignItems: "center", gap: "0.25rem", flex: 1 }}>
|
<form onSubmit={(e) => { e.preventDefault(); if (newSubTaskTitle.trim()) { addSubTask(task.id, newSubTaskTitle.trim()); setNewSubTaskTitle(\"\"); } }} style={{ display: \"flex\", alignItems: \"center\", gap: \"0.25rem\", flex: 1 }}>
|
||||||
<svg viewBox="0 0 24 24" width="12" height="12" stroke="var(--weekly-text-muted, #999)" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}><circle cx="12" cy="12" r="10" /></svg>
|
<svg viewBox=\"0 0 24 24\" width=\"12\" height=\"12\" stroke=\"var(--weekly-text-muted, #999)\" strokeWidth=\"2\" fill=\"none\" strokeLinecap=\"round\" strokeLinejoin=\"round\" style={{ flexShrink: 0 }}><circle cx=\"12\" cy=\"12\" r=\"10\" /></svg>
|
||||||
<input ref={subTaskInputRef} type="text" value={newSubTaskTitle} onChange={(e) => setNewSubTaskTitle(e.target.value)} onBlur={() => { if (!newSubTaskTitle.trim()) setIsSubTaskInputOpen(false); }} onKeyDown={(e) => { if (e.key === "Escape") { setNewSubTaskTitle(""); setIsSubTaskInputOpen(false); } }} placeholder="Add sub-task..." className="subtask-add-input" autoFocus />
|
<input ref={subTaskInputRef} type=\"text\" value={newSubTaskTitle} onChange={(e) => setNewSubTaskTitle(e.target.value)} onBlur={() => { if (!newSubTaskTitle.trim()) setIsSubTaskInputOpen(false); }} onKeyDown={(e) => { if (e.key === \"Escape\") { setNewSubTaskTitle(\"\"); setIsSubTaskInputOpen(false); } }} placeholder=\"Add sub-task...\" className=\"subtask-add-input\" autoFocus />
|
||||||
</form >
|
</form >
|
||||||
</div >
|
</div >
|
||||||
)}
|
)
|
||||||
|
}
|
||||||
</div >
|
</div >
|
||||||
|
<div className="resize-handle" onMouseDown={onResizeStart} />
|
||||||
</div >
|
</div >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user