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:
parent
c3099d37c2
commit
aa322dfac3
@ -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": {
|
||||||
|
|||||||
@ -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); }}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user