feat: AnyDay tab edit via double-click; remove pencil icons; fix active tab style
CSS: - Active tab no longer shows accent underline; instead border-bottom matches the someday area bg (#f9f9f9 / #1a1a1b dark), creating a raised browser-tab look where the active tab visually merges with the content below - margin-bottom: -1px to cover the tab bar border-bottom on active tab JSX: - Double-click on any tab now opens TabVisualsPopover (edit tab) - Pencil icon and dissolve-X button removed from tab wrappers - TabVisualsPopover extended with: rename input, dissolve button (dissolve removes the tab grouping; lists are kept) - onRename and onDissolve props added to TabVisualsPopover v1.107.3
This commit is contained in:
parent
85912beb4b
commit
6ccf61f09a
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "my-weekly-todo-list",
|
"name": "my-weekly-todo-list",
|
||||||
"version": "1.107.2",
|
"version": "1.107.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": {
|
||||||
|
|||||||
@ -1677,9 +1677,14 @@ h3 {
|
|||||||
color: var(--weekly-text, #222);
|
color: var(--weekly-text, #222);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
border-color: var(--weekly-border, #e5e7eb);
|
border-color: var(--weekly-border, #e5e7eb);
|
||||||
border-bottom-color: var(--weekly-accent, #6366f1);
|
border-bottom-color: #f9f9f9;
|
||||||
background: var(--weekly-bg, #fff);
|
background: var(--weekly-bg, #fff);
|
||||||
box-shadow: 0 -1px 0 0 var(--weekly-bg, #fff);
|
box-shadow: none;
|
||||||
|
margin-bottom: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weekly-container.dark-mode .someday-tab-btn-h.active {
|
||||||
|
border-bottom-color: #1a1a1b;
|
||||||
}
|
}
|
||||||
|
|
||||||
.someday-tab-btn-h.drag-over {
|
.someday-tab-btn-h.drag-over {
|
||||||
|
|||||||
@ -8069,16 +8069,14 @@ export default function WeeklyView() {
|
|||||||
<button
|
<button
|
||||||
className={`someday-tab-btn-h ${activeSomedayTab === tab ? "active" : ""} ${dragOverTab === tab ? "drag-over" : ""}`}
|
className={`someday-tab-btn-h ${activeSomedayTab === tab ? "active" : ""} ${dragOverTab === tab ? "drag-over" : ""}`}
|
||||||
onClick={() => setSomedayTab(tab)}
|
onClick={() => setSomedayTab(tab)}
|
||||||
onDoubleClick={() => {
|
onDoubleClick={(e) => { e.stopPropagation(); setEditingTabVisualsName(editingTabVisualsName === tab ? null : tab); }}
|
||||||
setEditingTabName(tab);
|
|
||||||
setRenamingTabValue(tab);
|
|
||||||
}}
|
|
||||||
style={tv.color ? {
|
style={tv.color ? {
|
||||||
background: activeSomedayTab === tab ? tv.color : `${tv.color}22`,
|
background: activeSomedayTab === tab ? tv.color : `${tv.color}22`,
|
||||||
color: activeSomedayTab === tab ? "#fff" : tv.color,
|
color: activeSomedayTab === tab ? "#fff" : tv.color,
|
||||||
borderColor: tv.color,
|
borderColor: tv.color,
|
||||||
|
borderBottomColor: activeSomedayTab === tab ? "transparent" : tv.color,
|
||||||
} : undefined}
|
} : undefined}
|
||||||
title={t.renameTab}
|
title={profile.language === "de" ? "Doppelklick zum Bearbeiten" : "Double-click to edit"}
|
||||||
>
|
>
|
||||||
{tv.icon && (
|
{tv.icon && (
|
||||||
<span style={{ display: "inline-flex", alignItems: "center", marginRight: "4px" }}>
|
<span style={{ display: "inline-flex", alignItems: "center", marginRight: "4px" }}>
|
||||||
@ -8089,16 +8087,6 @@ export default function WeeklyView() {
|
|||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
<button
|
|
||||||
onClick={(e) => { e.stopPropagation(); setEditingTabVisualsName(editingTabVisualsName === tab ? null : tab); }}
|
|
||||||
title={profile.language === "de" ? "Tab-Stil bearbeiten" : "Edit tab style"}
|
|
||||||
style={{ background: "none", border: "none", cursor: "pointer", padding: "2px", color: "#bbb", display: "inline-flex", alignItems: "center" }}
|
|
||||||
><Pencil size={10} /></button>
|
|
||||||
<button
|
|
||||||
className="someday-tab-dissolve-h"
|
|
||||||
onClick={(e) => { e.stopPropagation(); dissolveTab(tab); }}
|
|
||||||
title={t.dissolveTab}
|
|
||||||
><X size={10} /></button>
|
|
||||||
{editingTabVisualsName === tab && (
|
{editingTabVisualsName === tab && (
|
||||||
<TabVisualsPopover
|
<TabVisualsPopover
|
||||||
tabName={tab}
|
tabName={tab}
|
||||||
@ -8106,6 +8094,8 @@ export default function WeeklyView() {
|
|||||||
darkMode={darkMode}
|
darkMode={darkMode}
|
||||||
language={profile.language}
|
language={profile.language}
|
||||||
onChange={(updates) => updateTabVisuals(tab, updates)}
|
onChange={(updates) => updateTabVisuals(tab, updates)}
|
||||||
|
onRename={(newName) => { renameTab(tab, newName); setEditingTabVisualsName(null); }}
|
||||||
|
onDissolve={() => dissolveTab(tab)}
|
||||||
onClose={() => setEditingTabVisualsName(null)}
|
onClose={() => setEditingTabVisualsName(null)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -10999,6 +10989,8 @@ function TabVisualsPopover({
|
|||||||
darkMode,
|
darkMode,
|
||||||
language,
|
language,
|
||||||
onChange,
|
onChange,
|
||||||
|
onRename,
|
||||||
|
onDissolve,
|
||||||
onClose,
|
onClose,
|
||||||
}: {
|
}: {
|
||||||
tabName: string;
|
tabName: string;
|
||||||
@ -11006,9 +10998,12 @@ function TabVisualsPopover({
|
|||||||
darkMode: boolean;
|
darkMode: boolean;
|
||||||
language: string;
|
language: string;
|
||||||
onChange: (updates: { color?: string | null; icon?: string | null }) => void;
|
onChange: (updates: { color?: string | null; icon?: string | null }) => void;
|
||||||
|
onRename: (newName: string) => void;
|
||||||
|
onDissolve: () => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}) {
|
}) {
|
||||||
const [iconPickerOpen, setIconPickerOpen] = useState(false);
|
const [iconPickerOpen, setIconPickerOpen] = useState(false);
|
||||||
|
const [renameValue, setRenameValue] = useState(tabName);
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onDoc = (e: MouseEvent) => {
|
const onDoc = (e: MouseEvent) => {
|
||||||
@ -11018,36 +11013,50 @@ function TabVisualsPopover({
|
|||||||
return () => document.removeEventListener("mousedown", onDoc);
|
return () => document.removeEventListener("mousedown", onDoc);
|
||||||
}, [onClose]);
|
}, [onClose]);
|
||||||
const de = language === "de";
|
const de = language === "de";
|
||||||
|
const labelStyle = { fontSize: "0.75rem", color: darkMode ? "#9ca3af" : "#666", flex: 1 } as const;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: "calc(100% + 4px)",
|
top: "calc(100% + 6px)",
|
||||||
left: 0,
|
left: 0,
|
||||||
zIndex: 100,
|
zIndex: 100,
|
||||||
background: darkMode ? "#1e1e2e" : "#fff",
|
background: darkMode ? "#1e1e2e" : "#fff",
|
||||||
border: `1px solid ${darkMode ? "#444" : "#e5e7eb"}`,
|
border: `1px solid ${darkMode ? "#444" : "#e5e7eb"}`,
|
||||||
borderRadius: "10px",
|
borderRadius: "10px",
|
||||||
padding: "10px",
|
padding: "12px",
|
||||||
boxShadow: "0 8px 24px rgba(0,0,0,0.18)",
|
boxShadow: "0 8px 24px rgba(0,0,0,0.18)",
|
||||||
minWidth: "220px",
|
minWidth: "230px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: "8px",
|
gap: "10px",
|
||||||
}}
|
}}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
|
{/* Header */}
|
||||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
||||||
<span style={{ fontSize: "0.78rem", fontWeight: 600, color: darkMode ? "#d1d5db" : "#444" }}>
|
<span style={{ fontSize: "0.78rem", fontWeight: 700, color: darkMode ? "#d1d5db" : "#333" }}>
|
||||||
{de ? `Tab-Stil: ${tabName}` : `Tab style: ${tabName}`}
|
{de ? "Tab bearbeiten" : "Edit tab"}
|
||||||
</span>
|
</span>
|
||||||
<button onClick={onClose} style={{ background: "none", border: "none", cursor: "pointer", color: darkMode ? "#9ca3af" : "#888", padding: "2px" }}>
|
<button onClick={onClose} style={{ background: "none", border: "none", cursor: "pointer", color: darkMode ? "#9ca3af" : "#888", padding: "2px" }}>
|
||||||
<X size={14} />
|
<X size={14} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{/* Rename */}
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||||
<span style={{ fontSize: "0.75rem", color: darkMode ? "#9ca3af" : "#666", flex: 1 }}>{de ? "Icon" : "Icon"}</span>
|
<span style={labelStyle}>{de ? "Name" : "Name"}</span>
|
||||||
|
<input
|
||||||
|
value={renameValue}
|
||||||
|
onChange={(e) => setRenameValue(e.target.value)}
|
||||||
|
onBlur={() => { if (renameValue.trim() && renameValue.trim() !== tabName) onRename(renameValue.trim()); }}
|
||||||
|
onKeyDown={(e) => { if (e.key === "Enter") { if (renameValue.trim() && renameValue.trim() !== tabName) onRename(renameValue.trim()); (e.target as HTMLInputElement).blur(); } }}
|
||||||
|
style={{ flex: 1, fontSize: "0.8rem", border: `1px solid ${darkMode ? "#444" : "#e5e7eb"}`, borderRadius: "6px", padding: "4px 8px", background: darkMode ? "#2a2a3a" : "#f9f9f9", color: darkMode ? "#e5e7eb" : "#222", outline: "none" }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* Icon */}
|
||||||
|
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||||
|
<span style={labelStyle}>{de ? "Icon" : "Icon"}</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => setIconPickerOpen((v) => !v)}
|
onClick={() => setIconPickerOpen((v) => !v)}
|
||||||
style={{ width: "32px", height: "32px", borderRadius: "8px", border: `1px solid ${darkMode ? "#444" : "#e5e7eb"}`, background: darkMode ? "#2a2a3a" : "#fff", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center" }}
|
style={{ width: "32px", height: "32px", borderRadius: "8px", border: `1px solid ${darkMode ? "#444" : "#e5e7eb"}`, background: darkMode ? "#2a2a3a" : "#fff", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center" }}
|
||||||
@ -11061,7 +11070,7 @@ function TabVisualsPopover({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{iconPickerOpen && (
|
{iconPickerOpen && (
|
||||||
<div style={{ position: "absolute", top: "70px", left: "10px", zIndex: 110 }}>
|
<div style={{ position: "absolute", top: "110px", left: "10px", zIndex: 110 }}>
|
||||||
<IconPicker
|
<IconPicker
|
||||||
selectedIcon={visuals.icon || ""}
|
selectedIcon={visuals.icon || ""}
|
||||||
onSelect={(name) => { onChange({ icon: name }); setIconPickerOpen(false); }}
|
onSelect={(name) => { onChange({ icon: name }); setIconPickerOpen(false); }}
|
||||||
@ -11069,8 +11078,9 @@ function TabVisualsPopover({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{/* Color */}
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||||
<span style={{ fontSize: "0.75rem", color: darkMode ? "#9ca3af" : "#666", flex: 1 }}>{de ? "Farbe" : "Color"}</span>
|
<span style={labelStyle}>{de ? "Farbe" : "Color"}</span>
|
||||||
<input
|
<input
|
||||||
type="color"
|
type="color"
|
||||||
value={visuals.color || "#6366f1"}
|
value={visuals.color || "#6366f1"}
|
||||||
@ -11083,6 +11093,18 @@ function TabVisualsPopover({
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{/* Dissolve / delete */}
|
||||||
|
<div style={{ borderTop: `1px solid ${darkMode ? "#374151" : "#e5e7eb"}`, paddingTop: "8px" }}>
|
||||||
|
<button
|
||||||
|
onClick={() => { onDissolve(); onClose(); }}
|
||||||
|
style={{ width: "100%", display: "flex", alignItems: "center", gap: "8px", padding: "6px 8px", fontSize: "0.8rem", color: "#ef4444", background: "none", border: "none", cursor: "pointer", borderRadius: "6px", textAlign: "left" }}
|
||||||
|
onMouseEnter={(e) => (e.currentTarget.style.background = "rgba(239,68,68,0.08)")}
|
||||||
|
onMouseLeave={(e) => (e.currentTarget.style.background = "none")}
|
||||||
|
>
|
||||||
|
<Trash2 size={13} />
|
||||||
|
{de ? "Tab auflösen (Listen bleiben)" : "Dissolve tab (lists stay)"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user