feat: fix tab edit popover + align someday task items
- Fix tab double-click popover not showing: tab bar overflow-x:auto was clipping the position:absolute popover. Now uses position:fixed anchored to the button's bounding rect, captured on dblclick. Also stabilizes the outside-click listener (onCloseRef pattern, empty deps). - Restructure someday task item order to: [project icon column] [checkbox] [task name]. Fixed-width 18px icon column (placeholder when no project) keeps all checkboxes and task names vertically aligned. v1.108.0
This commit is contained in:
parent
6ccf61f09a
commit
55ef3614d1
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "my-weekly-todo-list",
|
||||
"version": "1.107.3",
|
||||
"version": "1.108.0",
|
||||
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@ -724,6 +724,7 @@ export default function WeeklyView() {
|
||||
// Punkt 7+8: per-list color/icon edit popover, and per-tab settings popover
|
||||
const [editingListVisualsId, setEditingListVisualsId] = useState<string | null>(null);
|
||||
const [editingTabVisualsName, setEditingTabVisualsName] = useState<string | null>(null);
|
||||
const [editingTabVisualsRect, setEditingTabVisualsRect] = useState<DOMRect | null>(null);
|
||||
const [activeSomedayTab, setActiveSomedayTab] = useState<string | null>(null);
|
||||
const [editingTabName, setEditingTabName] = useState<string | null>(null);
|
||||
const [renamingTabValue, setRenamingTabValue] = useState("");
|
||||
@ -8069,7 +8070,7 @@ export default function WeeklyView() {
|
||||
<button
|
||||
className={`someday-tab-btn-h ${activeSomedayTab === tab ? "active" : ""} ${dragOverTab === tab ? "drag-over" : ""}`}
|
||||
onClick={() => setSomedayTab(tab)}
|
||||
onDoubleClick={(e) => { e.stopPropagation(); setEditingTabVisualsName(editingTabVisualsName === tab ? null : tab); }}
|
||||
onDoubleClick={(e) => { e.stopPropagation(); const newTab = editingTabVisualsName === tab ? null : tab; if (newTab) setEditingTabVisualsRect((e.currentTarget as HTMLElement).getBoundingClientRect()); setEditingTabVisualsName(newTab); }}
|
||||
style={tv.color ? {
|
||||
background: activeSomedayTab === tab ? tv.color : `${tv.color}22`,
|
||||
color: activeSomedayTab === tab ? "#fff" : tv.color,
|
||||
@ -8097,6 +8098,7 @@ export default function WeeklyView() {
|
||||
onRename={(newName) => { renameTab(tab, newName); setEditingTabVisualsName(null); }}
|
||||
onDissolve={() => dissolveTab(tab)}
|
||||
onClose={() => setEditingTabVisualsName(null)}
|
||||
anchorRect={editingTabVisualsRect}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@ -10188,8 +10190,8 @@ function TaskItem({
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
gap: "0.5rem",
|
||||
alignItems: isSomeday ? "center" : "flex-start",
|
||||
gap: isSomeday ? "4px" : "0.5rem",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
@ -10238,6 +10240,23 @@ function TaskItem({
|
||||
</form>
|
||||
) : (
|
||||
<>
|
||||
{/* Someday: fixed-width project icon column for alignment */}
|
||||
{isSomeday && showProjectIcons && (
|
||||
<span style={{ width: "18px", minWidth: "18px", flexShrink: 0, display: "inline-flex", alignItems: "center", justifyContent: "center" }}>
|
||||
{task.project && <ProjectIcon icon={task.project.icon} size={13} color={task.project.color || "#888"} />}
|
||||
</span>
|
||||
)}
|
||||
{/* Someday: checkbox outside text span so it aligns in its own column */}
|
||||
{isSomeday && showTaskCheckboxes && (
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={task.completed}
|
||||
onChange={(e) => { e.stopPropagation(); onToggle(); }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="task-checkbox flex-shrink-0"
|
||||
style={{ width: "16px", height: "16px", margin: 0, cursor: "pointer", flexShrink: 0, accentColor: "var(--weekly-teal, #009a9a)", WebkitAppearance: "checkbox" }}
|
||||
/>
|
||||
)}
|
||||
<span
|
||||
className={`weekly-task-text flex-1 ${task.completed ? "completed" : ""}`}
|
||||
onClick={(e) => {
|
||||
@ -10251,21 +10270,22 @@ function TaskItem({
|
||||
: { display: "flex", alignItems: "center", gap: "6px", ...(task.completed && showTaskCheckboxes ? { opacity: 0.5 } : {}) }
|
||||
}
|
||||
>
|
||||
{showTaskCheckboxes && (
|
||||
{/* Non-someday: checkbox stays inside text span */}
|
||||
{!isSomeday && showTaskCheckboxes && (
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={task.completed}
|
||||
onChange={(e) => { e.stopPropagation(); onToggle(); }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="task-checkbox flex-shrink-0"
|
||||
style={{
|
||||
width: "16px",
|
||||
height: "16px",
|
||||
margin: 0,
|
||||
cursor: "pointer",
|
||||
position: "relative",
|
||||
top: "3px",
|
||||
left: "-2px",
|
||||
style={{
|
||||
width: "16px",
|
||||
height: "16px",
|
||||
margin: 0,
|
||||
cursor: "pointer",
|
||||
position: "relative",
|
||||
top: "3px",
|
||||
left: "-2px",
|
||||
accentColor: "var(--weekly-teal, #009a9a)",
|
||||
WebkitAppearance: "checkbox"
|
||||
}}
|
||||
@ -10297,7 +10317,8 @@ function TaskItem({
|
||||
: <User size={11} color="#8b5cf6" />}
|
||||
</span>
|
||||
)}
|
||||
{showProjectIcons && task.project && (
|
||||
{/* Non-someday: inline project icon; someday uses the column above */}
|
||||
{showProjectIcons && task.project && !isSomeday && (
|
||||
<span style={{ marginRight: "4px", verticalAlign: "middle" }}>
|
||||
<ProjectIcon icon={task.project.icon} size={13} color={task.project.color || "#888"} />
|
||||
</span>
|
||||
@ -10992,11 +11013,13 @@ function TabVisualsPopover({
|
||||
onRename,
|
||||
onDissolve,
|
||||
onClose,
|
||||
anchorRect,
|
||||
}: {
|
||||
tabName: string;
|
||||
visuals: { color?: string; icon?: string };
|
||||
darkMode: boolean;
|
||||
language: string;
|
||||
anchorRect?: DOMRect | null;
|
||||
onChange: (updates: { color?: string | null; icon?: string | null }) => void;
|
||||
onRename: (newName: string) => void;
|
||||
onDissolve: () => void;
|
||||
@ -11005,23 +11028,25 @@ function TabVisualsPopover({
|
||||
const [iconPickerOpen, setIconPickerOpen] = useState(false);
|
||||
const [renameValue, setRenameValue] = useState(tabName);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const onCloseRef = useRef(onClose);
|
||||
useEffect(() => { onCloseRef.current = onClose; }, [onClose]);
|
||||
useEffect(() => {
|
||||
const onDoc = (e: MouseEvent) => {
|
||||
if (containerRef.current && !containerRef.current.contains(e.target as Node)) onClose();
|
||||
if (containerRef.current && !containerRef.current.contains(e.target as Node)) onCloseRef.current();
|
||||
};
|
||||
document.addEventListener("mousedown", onDoc);
|
||||
return () => document.removeEventListener("mousedown", onDoc);
|
||||
}, [onClose]);
|
||||
}, []);
|
||||
const de = language === "de";
|
||||
const labelStyle = { fontSize: "0.75rem", color: darkMode ? "#9ca3af" : "#666", flex: 1 } as const;
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "calc(100% + 6px)",
|
||||
left: 0,
|
||||
zIndex: 100,
|
||||
position: "fixed",
|
||||
top: anchorRect ? anchorRect.bottom + 6 : -9999,
|
||||
left: anchorRect ? anchorRect.left : -9999,
|
||||
zIndex: 10000,
|
||||
background: darkMode ? "#1e1e2e" : "#fff",
|
||||
border: `1px solid ${darkMode ? "#444" : "#e5e7eb"}`,
|
||||
borderRadius: "10px",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user