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:
mARTin-B78 2026-05-11 11:43:40 +02:00
parent 6ccf61f09a
commit 55ef3614d1
2 changed files with 45 additions and 20 deletions

View File

@ -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": {

View File

@ -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",