fix: list style popover no longer clipped by card overflow:hidden

ListVisualsPopover used position:absolute which was clipped by the
weekly-someday-list card's overflow:hidden. Now uses position:fixed
anchored to the card's bounding rect. IconPicker sub-panel also
updated to position:fixed. Stabilized mousedown listener (onCloseRef
pattern, empty deps).

v1.108.3
This commit is contained in:
mARTin-B78 2026-05-11 12:19:55 +02:00
parent c3099d37c2
commit aa322dfac3
2 changed files with 17 additions and 9 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "my-weekly-todo-list", "name": "my-weekly-todo-list",
"version": "1.108.2", "version": "1.108.3",
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view", "description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -723,6 +723,7 @@ export default function WeeklyView() {
const [menuOpenListId, setMenuOpenListId] = useState<string | null>(null); const [menuOpenListId, setMenuOpenListId] = useState<string | null>(null);
// Punkt 7+8: per-list color/icon edit popover, and per-tab settings popover // Punkt 7+8: per-list color/icon edit popover, and per-tab settings popover
const [editingListVisualsId, setEditingListVisualsId] = useState<string | null>(null); const [editingListVisualsId, setEditingListVisualsId] = useState<string | null>(null);
const [editingListVisualsRect, setEditingListVisualsRect] = useState<DOMRect | null>(null);
const [editingTabVisualsName, setEditingTabVisualsName] = useState<string | null>(null); const [editingTabVisualsName, setEditingTabVisualsName] = useState<string | null>(null);
const [editingTabVisualsRect, setEditingTabVisualsRect] = useState<DOMRect | null>(null); const [editingTabVisualsRect, setEditingTabVisualsRect] = useState<DOMRect | null>(null);
const [activeSomedayTab, setActiveSomedayTab] = useState<string | null>(null); const [activeSomedayTab, setActiveSomedayTab] = useState<string | null>(null);
@ -8468,7 +8469,7 @@ export default function WeeklyView() {
</select> </select>
</div> </div>
{/* Edit style */} {/* Edit style */}
<button onClick={(e) => { e.stopPropagation(); setEditingListVisualsId(list.id); setMenuOpenListId(null); }} style={{ width: "100%", display: "flex", alignItems: "center", gap: "8px", padding: "8px 12px", fontSize: "0.8rem", color: darkMode ? "#e5e7eb" : "#374151", background: "none", border: "none", cursor: "pointer", textAlign: "left" }}> <button onClick={(e) => { e.stopPropagation(); setEditingListVisualsRect((e.currentTarget as HTMLElement).closest(".weekly-someday-list")?.getBoundingClientRect() ?? (e.currentTarget as HTMLElement).getBoundingClientRect()); setEditingListVisualsId(list.id); setMenuOpenListId(null); }} style={{ width: "100%", display: "flex", alignItems: "center", gap: "8px", padding: "8px 12px", fontSize: "0.8rem", color: darkMode ? "#e5e7eb" : "#374151", background: "none", border: "none", cursor: "pointer", textAlign: "left" }}>
<Pencil size={13} style={{ opacity: 0.6, flexShrink: 0 }} /> <Pencil size={13} style={{ opacity: 0.6, flexShrink: 0 }} />
{profile.language === "de" ? "Stil bearbeiten" : "Edit style"} {profile.language === "de" ? "Stil bearbeiten" : "Edit style"}
</button> </button>
@ -8513,6 +8514,7 @@ export default function WeeklyView() {
language={profile.language} language={profile.language}
onChange={(updates) => updateListVisuals(list.id, updates)} onChange={(updates) => updateListVisuals(list.id, updates)}
onClose={() => setEditingListVisualsId(null)} onClose={() => setEditingListVisualsId(null)}
anchorRect={editingListVisualsRect}
/> />
)} )}
@ -10917,31 +10919,37 @@ function ListVisualsPopover({
language, language,
onChange, onChange,
onClose, onClose,
anchorRect,
}: { }: {
list: { id: string; title: string; color?: string | null; icon?: string | null }; list: { id: string; title: string; color?: string | null; icon?: string | null };
darkMode: boolean; darkMode: boolean;
language: string; language: string;
anchorRect?: DOMRect | null;
onChange: (updates: { color?: string | null; icon?: string | null }) => void; onChange: (updates: { color?: string | null; icon?: string | null }) => void;
onClose: () => void; onClose: () => void;
}) { }) {
const [iconPickerOpen, setIconPickerOpen] = useState(false); const [iconPickerOpen, setIconPickerOpen] = useState(false);
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const onCloseRef = useRef(onClose);
useEffect(() => { onCloseRef.current = onClose; }, [onClose]);
useEffect(() => { useEffect(() => {
const onDoc = (e: MouseEvent) => { 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); document.addEventListener("mousedown", onDoc);
return () => document.removeEventListener("mousedown", onDoc); return () => document.removeEventListener("mousedown", onDoc);
}, [onClose]); }, []);
const de = language === "de"; const de = language === "de";
const top = anchorRect ? anchorRect.top + 32 : -9999;
const left = anchorRect ? anchorRect.left + 4 : -9999;
return ( return (
<div <div
ref={containerRef} ref={containerRef}
style={{ style={{
position: "absolute", position: "fixed",
top: "32px", top,
right: "4px", left,
zIndex: 50, zIndex: 10000,
background: darkMode ? "#1e1e2e" : "#fff", background: darkMode ? "#1e1e2e" : "#fff",
border: `1px solid ${darkMode ? "#444" : "#e5e7eb"}`, border: `1px solid ${darkMode ? "#444" : "#e5e7eb"}`,
borderRadius: "10px", borderRadius: "10px",
@ -10981,7 +10989,7 @@ function ListVisualsPopover({
)} )}
</div> </div>
{iconPickerOpen && ( {iconPickerOpen && (
<div style={{ position: "absolute", top: "78px", right: "12px", zIndex: 60 }}> <div style={{ position: "fixed", top: top + 78, left, zIndex: 10001 }}>
<IconPicker <IconPicker
selectedIcon={list.icon || ""} selectedIcon={list.icon || ""}
onSelect={(name) => { onChange({ icon: name }); setIconPickerOpen(false); }} onSelect={(name) => { onChange({ icon: name }); setIconPickerOpen(false); }}