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