feat: move someday controls to horizontal tab bar, remove label column

Replace the someday-label-column with a unified horizontal tab bar
containing: collapse chevron, "any day" label, new list icon (ListPlus),
new tab icon (FolderPlus), divider, then tab buttons. All icons have
tooltips. Inline input for creating new tabs directly in the bar.

v1.27.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
mARTin 2026-03-11 08:53:30 +01:00
parent f05a7c9035
commit 8959aa7f5a
3 changed files with 157 additions and 190 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "my-weekly-todo-list", "name": "my-weekly-todo-list",
"version": "1.26.2", "version": "1.27.0",
"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

@ -1509,6 +1509,33 @@ h3 {
display: none; display: none;
} }
.someday-bar-icon-btn {
background: none;
border: none;
cursor: pointer;
color: var(--weekly-text-light, #999);
padding: 3px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.15s, color 0.15s;
flex-shrink: 0;
}
.someday-bar-icon-btn:hover {
background: var(--weekly-hover-bg, rgba(0, 0, 0, 0.06));
color: var(--weekly-text, #333);
}
.someday-tabs-bar-divider {
width: 1px;
height: 16px;
background: var(--weekly-border, #e5e7eb);
flex-shrink: 0;
margin: 0 2px;
}
/* Someday Tab Buttons (horizontal) */ /* Someday Tab Buttons (horizontal) */
.someday-tab-btn-h { .someday-tab-btn-h {
background: none; background: none;

View File

@ -54,6 +54,8 @@ import {
PanelLeftOpen, PanelLeftOpen,
Type, Type,
FolderOpen, FolderOpen,
FolderPlus,
ListPlus,
Circle, Circle,
X, X,
Cable, Cable,
@ -256,6 +258,7 @@ const translations: Record<string, any> = {
confirmPassword: "Confirm Password", confirmPassword: "Confirm Password",
someday: "SOMEDAY", someday: "SOMEDAY",
lists: "Lists", lists: "Lists",
newList: "New list",
allTabs: "All", allTabs: "All",
newTab: "New tab", newTab: "New tab",
newTabName: "New tab name:", newTabName: "New tab name:",
@ -449,6 +452,7 @@ const translations: Record<string, any> = {
confirmPassword: "Passwort bestätigen", confirmPassword: "Passwort bestätigen",
someday: "IRGENDWANN", someday: "IRGENDWANN",
lists: "Listen", lists: "Listen",
newList: "Neue Liste",
allTabs: "Alle", allTabs: "Alle",
newTab: "Neuer Tab", newTab: "Neuer Tab",
newTabName: "Neuer Tab-Name:", newTabName: "Neuer Tab-Name:",
@ -641,6 +645,7 @@ const translations: Record<string, any> = {
confirmPassword: "Confirmer le mot de passe", confirmPassword: "Confirmer le mot de passe",
someday: "UN JOUR", someday: "UN JOUR",
lists: "Listes", lists: "Listes",
newList: "Nouvelle liste",
allTabs: "Tous", allTabs: "Tous",
newTab: "Nouvel onglet", newTab: "Nouvel onglet",
newTabName: "Nom du nouvel onglet :", newTabName: "Nom du nouvel onglet :",
@ -833,6 +838,7 @@ const translations: Record<string, any> = {
confirmPassword: "Confirmar contraseña", confirmPassword: "Confirmar contraseña",
someday: "ALGÚN DÍA", someday: "ALGÚN DÍA",
lists: "Listas", lists: "Listas",
newList: "Nueva lista",
allTabs: "Todas", allTabs: "Todas",
newTab: "Nueva pestaña", newTab: "Nueva pestaña",
newTabName: "Nombre de nueva pestaña:", newTabName: "Nombre de nueva pestaña:",
@ -1025,6 +1031,7 @@ const translations: Record<string, any> = {
confirmPassword: "Conferma password", confirmPassword: "Conferma password",
someday: "UN GIORNO", someday: "UN GIORNO",
lists: "Liste", lists: "Liste",
newList: "Nuova lista",
allTabs: "Tutte", allTabs: "Tutte",
newTab: "Nuova scheda", newTab: "Nuova scheda",
newTabName: "Nome nuova scheda:", newTabName: "Nome nuova scheda:",
@ -1461,6 +1468,8 @@ export default function WeeklyView() {
const [renamingTabValue, setRenamingTabValue] = useState(""); const [renamingTabValue, setRenamingTabValue] = useState("");
const [newTabForListId, setNewTabForListId] = useState<string | null>(null); const [newTabForListId, setNewTabForListId] = useState<string | null>(null);
const [newTabNameValue, setNewTabNameValue] = useState(""); const [newTabNameValue, setNewTabNameValue] = useState("");
const [creatingNewTab, setCreatingNewTab] = useState(false);
const [creatingNewTabName, setCreatingNewTabName] = useState("");
useEffect(() => { useEffect(() => {
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
@ -6362,203 +6371,134 @@ export default function WeeklyView() {
ref={somedaySectionRefCb} ref={somedaySectionRefCb}
className={`weekly-someday ${somedayExpanded ? "expanded" : "collapsed"} transition-colors duration-200`} className={`weekly-someday ${somedayExpanded ? "expanded" : "collapsed"} transition-colors duration-200`}
> >
<div style={{ display: "flex", flexDirection: "row", maxWidth: "100%", width: "100%" }}> {/* Someday tabs bar */}
{showTimeGrid && ( <div className="someday-tabs-bar">
<div <div
className="someday-label-column" onClick={() => setSomedayExpanded(!somedayExpanded)}
style={{
cursor: "pointer",
display: "flex",
alignItems: "center",
gap: "4px",
marginRight: "4px",
}}
title={somedayExpanded ? "Collapse" : "Expand"}
>
<ChevronRight
size={14}
style={{ style={{
width: "50px", transform: somedayExpanded ? "rotate(90deg)" : "rotate(0deg)",
transition: "transform 0.15s",
color: "var(--weekly-text-light)",
flexShrink: 0, flexShrink: 0,
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "flex-start",
borderRight: "1px solid var(--weekly-border)",
padding: "4px 4px",
gap: "4px",
position: "relative",
}} }}
> />
<div <span style={{
onClick={() => setSomedayExpanded(!somedayExpanded)} fontSize: "0.7rem",
style={{ fontWeight: 600,
cursor: "pointer", color: "var(--weekly-text-light)",
display: "flex", textTransform: "uppercase",
flexDirection: "column", letterSpacing: "0.05em",
alignItems: "center", whiteSpace: "nowrap",
gap: "0px", }}>
}} any day
title={somedayExpanded ? "Collapse" : "Expand"} </span>
> </div>
<span <button
style={{ className="someday-bar-icon-btn"
fontSize: "0.6rem", onClick={() => handleStartAddSomedayList()}
fontWeight: 600, title={t.newList}
color: "var(--weekly-text-light)", >
textTransform: "uppercase", <ListPlus size={14} />
letterSpacing: "0.05em", </button>
lineHeight: 1.1, <button
textAlign: "center", className="someday-bar-icon-btn"
}} onClick={() => { setCreatingNewTab(true); setCreatingNewTabName(""); }}
> title={t.newTab}
any day >
</span> <FolderPlus size={14} />
<span </button>
style={{ {creatingNewTab && (
fontSize: "0.55rem", <input
color: "#888", className="someday-tab-rename-input"
marginTop: "1px", value={creatingNewTabName}
}} onChange={(e) => setCreatingNewTabName(e.target.value)}
> onBlur={() => {
{somedayLists.length}{" "} if (creatingNewTabName.trim()) {
{translations[language]?.lists || translations["en"].lists} setSomedayTab(creatingNewTabName.trim());
</span> }
</div> setCreatingNewTab(false);
<button }}
className="someday-add-btn dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:border-white transition-colors" onKeyDown={(e) => {
onClick={(e) => { if (e.key === "Enter") e.currentTarget.blur();
e.stopPropagation(); if (e.key === "Escape") setCreatingNewTab(false);
handleStartAddSomedayList(); }}
}} placeholder={t.newTab}
title="Add new list" autoFocus
style={{
background: "none",
border: "1px dashed #ccc",
borderRadius: "50%",
width: "18px",
height: "18px",
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
color: "#888",
fontSize: "0.8rem",
lineHeight: 1,
padding: 0,
}}
>
+
</button>
</div>
)}
{!showTimeGrid && (
<div
className="someday-label-column"
style={{ style={{
display: "flex", fontSize: "0.75rem",
alignItems: "center", border: "1px solid var(--weekly-border)",
padding: "4px 8px", borderRadius: "3px",
gap: "6px", padding: "2px 6px",
background: "var(--weekly-bg)",
color: "var(--weekly-text)",
width: "80px",
}} }}
> />
<div
onClick={() => setSomedayExpanded(!somedayExpanded)}
style={{
cursor: "pointer",
display: "flex",
alignItems: "center",
gap: "6px",
}}
title={somedayExpanded ? "Collapse" : "Expand"}
>
<span
style={{
fontSize: "0.6rem",
fontWeight: 600,
color: "var(--weekly-text-light)",
textTransform: "uppercase",
letterSpacing: "0.05em",
}}
>
any day
</span>
<span style={{ fontSize: "0.55rem", color: "#888" }}>
{somedayLists.length}{" "}
{translations[language]?.lists || translations["en"].lists}
</span>
</div>
<button
className="someday-add-btn dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:border-white transition-colors"
onClick={(e) => {
e.stopPropagation();
handleStartAddSomedayList();
}}
title="Add new list"
style={{
background: "none",
border: "1px dashed #ccc",
borderRadius: "50%",
width: "18px",
height: "18px",
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
color: "#888",
fontSize: "0.8rem",
lineHeight: 1,
padding: 0,
}}
>
+
</button>
</div>
)} )}
<div ref={somedayGridRef} style={{ flex: 1, minWidth: 0, overflowX: "auto" }}> <div className="someday-tabs-bar-divider" />
{/* Horizontal tab bar above grid */} <button
{somedayExpanded && somedayTabs.length > 0 && ( className={`someday-tab-btn-h ${activeSomedayTab === null ? "active" : ""}`}
<div className="someday-tabs-bar"> onClick={() => setSomedayTab(null)}
>{t.allTabs} <span className="someday-tab-count">{somedayLists.length}</span></button>
{somedayTabs.map(tab => (
editingTabName === tab ? (
<input
key={tab}
className="someday-tab-rename-input"
value={renamingTabValue}
onChange={(e) => setRenamingTabValue(e.target.value)}
onBlur={() => {
renameTab(tab, renamingTabValue);
setEditingTabName(null);
}}
onKeyDown={(e) => {
if (e.key === "Enter") e.currentTarget.blur();
if (e.key === "Escape") setEditingTabName(null);
}}
autoFocus
style={{
fontSize: "0.75rem",
border: "1px solid var(--weekly-border)",
borderRadius: "3px",
padding: "2px 6px",
background: "var(--weekly-bg)",
color: "var(--weekly-text)",
width: "80px",
}}
/>
) : (
<div key={tab} className="someday-tab-wrapper-h">
<button <button
className={`someday-tab-btn-h ${activeSomedayTab === null ? "active" : ""}`} className={`someday-tab-btn-h ${activeSomedayTab === tab ? "active" : ""}`}
onClick={() => setSomedayTab(null)} onClick={() => setSomedayTab(tab)}
>{t.allTabs} <span className="someday-tab-count">{somedayLists.length}</span></button> onDoubleClick={() => {
{somedayTabs.map(tab => ( setEditingTabName(tab);
editingTabName === tab ? ( setRenamingTabValue(tab);
<input }}
key={tab} title={t.renameTab}
className="someday-tab-rename-input" >{tab} <span className="someday-tab-count">{somedayLists.filter(l => l.tab === tab).length}</span></button>
value={renamingTabValue} <button
onChange={(e) => setRenamingTabValue(e.target.value)} className="someday-tab-dissolve-h"
onBlur={() => { onClick={(e) => { e.stopPropagation(); dissolveTab(tab); }}
renameTab(tab, renamingTabValue); title={t.dissolveTab}
setEditingTabName(null); ><X size={10} /></button>
}}
onKeyDown={(e) => {
if (e.key === "Enter") e.currentTarget.blur();
if (e.key === "Escape") setEditingTabName(null);
}}
autoFocus
style={{
fontSize: "0.75rem",
border: "1px solid var(--weekly-border)",
borderRadius: "3px",
padding: "2px 6px",
background: "var(--weekly-bg)",
color: "var(--weekly-text)",
width: "80px",
}}
/>
) : (
<div key={tab} className="someday-tab-wrapper-h">
<button
className={`someday-tab-btn-h ${activeSomedayTab === tab ? "active" : ""}`}
onClick={() => setSomedayTab(tab)}
onDoubleClick={() => {
setEditingTabName(tab);
setRenamingTabValue(tab);
}}
title={t.renameTab}
>{tab} <span className="someday-tab-count">{somedayLists.filter(l => l.tab === tab).length}</span></button>
<button
className="someday-tab-dissolve-h"
onClick={(e) => { e.stopPropagation(); dissolveTab(tab); }}
title={t.dissolveTab}
><X size={10} /></button>
</div>
)
))}
</div> </div>
)} )
))}
</div>
<div style={{ display: "flex", flexDirection: "row", maxWidth: "100%", width: "100%" }}>
<div ref={somedayGridRef} style={{ flex: 1, minWidth: 0, overflowX: "auto" }}>
{somedayExpanded && ( {somedayExpanded && (
<div <div
className={`weekly-someday-lists-grid cols-${Math.min(7, Math.max(1, viewDays))}`} className={`weekly-someday-lists-grid cols-${Math.min(7, Math.max(1, viewDays))}`}