fix: tab persistence and move tabs to horizontal bar above someday grid

Fix tabs vanishing on reload by adding missing `tab` field in fetchTasks()
list mapping. Replace cramped vertical tabs in someday-label-column with
a readable horizontal tab bar above the someday grid, working in both
time-grid and non-time-grid modes. Double-click to rename, X to dissolve.

v1.26.1

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
mARTin 2026-03-11 08:44:17 +01:00
parent 1334656300
commit b2a1981385
3 changed files with 57 additions and 196 deletions

View File

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

@ -1493,46 +1493,31 @@ h3 {
color: #666; color: #666;
} }
/* Someday Tab Buttons (vertical in label column) */ /* Someday Tabs Bar (horizontal above grid) */
.someday-tab-btn { .someday-tabs-bar {
background: none; display: flex;
border: none; flex-direction: row;
cursor: pointer; align-items: center;
color: var(--weekly-text-light, #999); gap: 4px;
font-size: 0.45rem; padding: 4px 8px;
font-weight: 500; border-bottom: 1px solid var(--weekly-border);
padding: 3px 4px; overflow-x: auto;
border-radius: 3px; scrollbar-width: none;
text-align: center;
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.2;
transition: background 0.15s, color 0.15s;
text-transform: uppercase;
letter-spacing: 0.03em;
} }
.someday-tab-btn:hover { .someday-tabs-bar::-webkit-scrollbar {
background: var(--weekly-hover-bg, rgba(0, 0, 0, 0.06)); display: none;
color: var(--weekly-text, #333);
} }
.someday-tab-btn.active { /* Someday Tab Buttons (horizontal) */
background: var(--weekly-accent, #6366f1);
color: #fff;
}
/* Someday Tab Buttons (horizontal in non-time-grid) */
.someday-tab-btn-h { .someday-tab-btn-h {
background: none; background: none;
border: none; border: none;
cursor: pointer; cursor: pointer;
color: var(--weekly-text-light, #999); color: var(--weekly-text-light, #999);
font-size: 0.6rem; font-size: 0.75rem;
font-weight: 500; font-weight: 500;
padding: 2px 8px; padding: 3px 10px;
border-radius: 3px; border-radius: 3px;
white-space: nowrap; white-space: nowrap;
transition: background 0.15s, color 0.15s; transition: background 0.15s, color 0.15s;
@ -1548,37 +1533,7 @@ h3 {
color: #fff; color: #fff;
} }
/* Someday Tab Wrappers with dissolve buttons */ /* Someday Tab Wrapper with dissolve button */
.someday-tab-wrapper {
position: relative;
display: flex;
align-items: center;
}
.someday-tab-dissolve {
position: absolute;
right: -2px;
top: -2px;
background: var(--weekly-bg, #fff);
border: 1px solid var(--weekly-border, #e5e7eb);
border-radius: 50%;
cursor: pointer;
color: var(--weekly-text-light, #999);
padding: 1px;
line-height: 0;
opacity: 0;
transition: opacity 0.15s;
}
.someday-tab-wrapper:hover .someday-tab-dissolve {
opacity: 1;
}
.someday-tab-dissolve:hover {
color: #ef4444;
border-color: #ef4444;
}
.someday-tab-wrapper-h { .someday-tab-wrapper-h {
position: relative; position: relative;
display: inline-flex; display: inline-flex;
@ -1620,33 +1575,6 @@ h3 {
color: var(--weekly-text, #333); color: var(--weekly-text, #333);
} }
/* Mobile Someday Tabs Bar */
.someday-tabs-mobile {
display: none;
}
@media (max-width: 768px) {
.someday-tabs-mobile {
display: flex;
flex-direction: row;
align-items: center;
gap: 4px;
padding: 6px 8px;
overflow-x: auto;
border-bottom: 1px solid var(--weekly-border);
scrollbar-width: none;
}
.someday-tabs-mobile::-webkit-scrollbar {
display: none;
}
/* Hide the vertical tabs in label column on mobile */
.someday-tabs {
display: none !important;
}
}
/* Preferences Slide-in Panel */ /* Preferences Slide-in Panel */
.preferences-panel { .preferences-panel {
position: fixed; position: fixed;

View File

@ -2706,6 +2706,7 @@ export default function WeeklyView() {
fetchedLists = listData.lists.map((l: any) => ({ fetchedLists = listData.lists.map((l: any) => ({
id: l.id, id: l.id,
title: l.title, title: l.title,
tab: l.tab || null,
tasks: [], tasks: [],
externalId: l.externalId || null, externalId: l.externalId || null,
externalProvider: l.externalProvider || null, externalProvider: l.externalProvider || null,
@ -6438,80 +6439,6 @@ export default function WeeklyView() {
> >
+ +
</button> </button>
{/* Someday Tabs */}
{somedayTabs.length > 0 && (
<div className="someday-tabs" style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "1px",
width: "100%",
marginTop: "4px",
borderTop: "1px solid var(--weekly-border)",
paddingTop: "4px",
}}>
<button
className={`someday-tab-btn ${activeSomedayTab === null ? "active" : ""}`}
onClick={() => setSomedayTab(null)}
title={t.allTabs}
>
{t.allTabs}
</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={{
width: "100%",
fontSize: "0.5rem",
textAlign: "center",
border: "1px solid var(--weekly-border)",
borderRadius: "3px",
padding: "2px",
background: "var(--weekly-bg)",
color: "var(--weekly-text)",
}}
/>
) : (
<div key={tab} className="someday-tab-wrapper">
<button
className={`someday-tab-btn ${activeSomedayTab === tab ? "active" : ""}`}
onClick={() => setSomedayTab(tab)}
onDoubleClick={() => {
setEditingTabName(tab);
setRenamingTabValue(tab);
}}
title={t.renameTab}
>
{tab}
</button>
<button
className="someday-tab-dissolve"
onClick={(e) => {
e.stopPropagation();
dissolveTab(tab);
}}
title={t.dissolveTab}
>
<X size={8} />
</button>
</div>
)
))}
</div>
)}
</div> </div>
)} )}
{!showTimeGrid && ( {!showTimeGrid && (
@ -6575,14 +6502,43 @@ export default function WeeklyView() {
> >
+ +
</button> </button>
{/* Horizontal tabs for non-time-grid */} </div>
{somedayTabs.length > 0 && ( )}
<> <div ref={somedayGridRef} style={{ flex: 1, minWidth: 0, overflowX: "auto" }}>
<button {/* Horizontal tab bar above grid */}
className={`someday-tab-btn-h ${activeSomedayTab === null ? "active" : ""}`} {somedayExpanded && somedayTabs.length > 0 && (
onClick={() => setSomedayTab(null)} <div className="someday-tabs-bar">
>{t.allTabs}</button> <button
{somedayTabs.map(tab => ( className={`someday-tab-btn-h ${activeSomedayTab === null ? "active" : ""}`}
onClick={() => setSomedayTab(null)}
>{t.allTabs}</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"> <div key={tab} className="someday-tab-wrapper-h">
<button <button
className={`someday-tab-btn-h ${activeSomedayTab === tab ? "active" : ""}`} className={`someday-tab-btn-h ${activeSomedayTab === tab ? "active" : ""}`}
@ -6591,6 +6547,7 @@ export default function WeeklyView() {
setEditingTabName(tab); setEditingTabName(tab);
setRenamingTabValue(tab); setRenamingTabValue(tab);
}} }}
title={t.renameTab}
>{tab}</button> >{tab}</button>
<button <button
className="someday-tab-dissolve-h" className="someday-tab-dissolve-h"
@ -6598,31 +6555,7 @@ export default function WeeklyView() {
title={t.dissolveTab} title={t.dissolveTab}
><X size={10} /></button> ><X size={10} /></button>
</div> </div>
))} )
</>
)}
</div>
)}
<div ref={somedayGridRef} style={{ flex: 1, minWidth: 0, overflowX: "auto" }}>
{/* Mobile tabs bar (visible only on small screens) */}
{somedayExpanded && somedayTabs.length > 0 && (
<div className="someday-tabs-mobile">
<button
className={`someday-tab-btn-h ${activeSomedayTab === null ? "active" : ""}`}
onClick={() => setSomedayTab(null)}
>{t.allTabs}</button>
{somedayTabs.map(tab => (
<div key={tab} className="someday-tab-wrapper-h">
<button
className={`someday-tab-btn-h ${activeSomedayTab === tab ? "active" : ""}`}
onClick={() => setSomedayTab(tab)}
>{tab}</button>
<button
className="someday-tab-dissolve-h"
onClick={(e) => { e.stopPropagation(); dissolveTab(tab); }}
title={t.dissolveTab}
><X size={10} /></button>
</div>
))} ))}
</div> </div>
)} )}