feat: fix iPhone Today button, add all settings to sidebar, fix layout

- Fix goToToday landing on yesterday in single-day view (skip startDayOffset)
- Add quick actions panel to settings sidebar for all screen sizes (not just mobile)
- Add view style switcher, visible hours, search, and new project to sidebar
- Change weekly-container to height:100vh for proper flex layout
- Someday/all-day sections use flex-shrink:0 for stable resize behavior
- Collapse uses height:30px!important to override inline styles

v1.40.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
mARTin 2026-03-17 10:41:12 +01:00
parent afa2695e0c
commit 85982f06a5
3 changed files with 112 additions and 13 deletions

View File

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

@ -478,11 +478,12 @@ h3 {
/* Weekly Main Container */ /* Weekly Main Container */
.weekly-container { .weekly-container {
min-height: 100vh; height: 100vh;
background: var(--weekly-bg); background: var(--weekly-bg);
font-family: var(--weekly-font); font-family: var(--weekly-font);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden;
} }
/* Weekly Header */ /* Weekly Header */
@ -497,6 +498,7 @@ h3 {
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
position: relative; position: relative;
flex-shrink: 0;
} }
.weekly-logo { .weekly-logo {
flex-shrink: 0; flex-shrink: 0;
@ -1179,11 +1181,11 @@ h3 {
/* Someday Section */ /* Someday Section */
.weekly-someday { .weekly-someday {
background-color: #f9f9f9; background-color: #f9f9f9;
width: 100%; width: 100%;
padding: 0 1rem; padding: 0 1rem;
transition: max-height 0.3s ease;
position: relative; position: relative;
flex-shrink: 0;
} }
.weekly-container.dark-mode .weekly-someday { .weekly-container.dark-mode .weekly-someday {
@ -1191,6 +1193,8 @@ h3 {
} }
.weekly-someday.collapsed { .weekly-someday.collapsed {
height: 30px !important;
min-height: 30px;
max-height: 30px; max-height: 30px;
overflow: hidden; overflow: hidden;
} }
@ -2652,6 +2656,7 @@ h3 {
border-top: 1px solid var(--weekly-border); border-top: 1px solid var(--weekly-border);
padding: 2px 0; padding: 2px 0;
position: relative; position: relative;
flex-shrink: 0;
} }
/* Resize handle for draggable section borders */ /* Resize handle for draggable section borders */
@ -2665,6 +2670,7 @@ h3 {
touch-action: none; touch-action: none;
position: relative; position: relative;
z-index: 10; z-index: 10;
flex-shrink: 0;
border-top: 1px solid var(--weekly-border, #e5e7eb); border-top: 1px solid var(--weekly-border, #e5e7eb);
border-bottom: 1px solid var(--weekly-border, #e5e7eb); border-bottom: 1px solid var(--weekly-border, #e5e7eb);
} }

View File

@ -1641,6 +1641,7 @@ export default function WeeklyView() {
const startResize = useCallback((e: React.MouseEvent | React.TouchEvent, target: 'someday' | 'allday') => { const startResize = useCallback((e: React.MouseEvent | React.TouchEvent, target: 'someday' | 'allday') => {
e.preventDefault(); e.preventDefault();
e.stopPropagation();
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY; const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
const section = target === 'someday' ? somedaySectionRef.current : (document.querySelector('.all-day-events-section') as HTMLElement); const section = target === 'someday' ? somedaySectionRef.current : (document.querySelector('.all-day-events-section') as HTMLElement);
if (!section) return; if (!section) return;
@ -3589,7 +3590,10 @@ export default function WeeklyView() {
const goToToday = () => { const goToToday = () => {
const d = new Date(); const d = new Date();
d.setHours(0, 0, 0, 0); d.setHours(0, 0, 0, 0);
d.setDate(d.getDate() + (profile?.startDayOffset || 0)); // Only apply startDayOffset for multi-day views; on single-day view, go directly to today
if (viewDays > 1) {
d.setDate(d.getDate() + (profile?.startDayOffset || 0));
}
setCurrentWeekStart(d); setCurrentWeekStart(d);
}; };
@ -6395,9 +6399,9 @@ export default function WeeklyView() {
})()} })()}
{/* Main Grid with Time Column */} {/* Main Grid with Time Column */}
{viewStyle !== "kanban" && <div style={{ position: "relative" }}> {viewStyle !== "kanban" && <div style={{ position: "relative", flex: 1, minHeight: 0, overflow: 'hidden' }}>
<div className="time-grid-wrapper" ref={timeGridWrapperRef} style={showTimeGrid && !isMobile ? { <div className="time-grid-wrapper" ref={timeGridWrapperRef} style={showTimeGrid && !isMobile ? {
maxHeight: `${(endHour - startHour) * (60 / cellDuration) * getSlotHeight(cellDuration) + measuredHeaderHeight}px`, maxHeight: '100%',
} : undefined}> } : undefined}>
{/* Time Column */} {/* Time Column */}
{showTimeGrid && ( {showTimeGrid && (
@ -8279,7 +8283,7 @@ export default function WeeklyView() {
profile={profile} profile={profile}
setProfile={setProfile} setProfile={setProfile}
isMobile={isMobile} isMobile={isMobile}
mobileActions={isMobile ? { mobileActions={{
goToPrevWeek, goToPrevWeek,
goToPrevDay, goToPrevDay,
goToToday, goToToday,
@ -8290,10 +8294,12 @@ export default function WeeklyView() {
const now = new Date(); const now = new Date();
setCalendarEventModal({ isOpen: true, event: undefined, initialDate: now, initialStartTime: `${String(now.getHours()).padStart(2, "0")}:00` }); setCalendarEventModal({ isOpen: true, event: undefined, initialDate: now, initialStartTime: `${String(now.getHours()).padStart(2, "0")}:00` });
}, },
onAddProject: () => { setShowSettings(true); setActiveTab("general"); },
onRecurringTasks: () => setIsRecurringTasksOpen(true), onRecurringTasks: () => setIsRecurringTasksOpen(true),
onToggleNextTask: () => { const newVal = !showNextTask; setShowNextTask(newVal); saveSetting("showNextTask", newVal); }, onToggleNextTask: () => { const newVal = !showNextTask; setShowNextTask(newVal); saveSetting("showNextTask", newVal); },
onFocusMode: () => setShowFocusMode(true), onFocusMode: () => setShowFocusMode(true),
onToggleDarkMode: () => setDarkMode(!darkMode), onToggleDarkMode: () => setDarkMode(!darkMode),
onSearch: () => setIsSearchOpen(true),
onUndo: handleUndo, onUndo: handleUndo,
onRedo: handleRedo, onRedo: handleRedo,
onRefresh: () => { fetchCalendarEvents(true); fetchTasks(); }, onRefresh: () => { fetchCalendarEvents(true); fetchTasks(); },
@ -8306,7 +8312,13 @@ export default function WeeklyView() {
showTimeGrid, showTimeGrid,
cellDuration, cellDuration,
onCellDurationChange: (d: CellDuration) => { setCellDuration(d); saveSetting("cellDuration", d); }, onCellDurationChange: (d: CellDuration) => { setCellDuration(d); saveSetting("cellDuration", d); },
} : undefined} viewStyle,
onViewStyleChange: (s: string) => { setViewStyle(s as any); saveSetting("viewStyle", s); },
startHour,
endHour,
onStartHourChange: (h: number) => { setStartHour(h); saveSetting("startHour", h); },
onEndHourChange: (h: number) => { setEndHour(h); saveSetting("endHour", h); },
}}
/> />
) )
} }
@ -9583,7 +9595,7 @@ interface SettingsSidebarProps {
onProjectsChanged: () => void; onProjectsChanged: () => void;
kanbanStages: KanbanStage[]; kanbanStages: KanbanStage[];
saveKanbanStages: (stages: KanbanStage[]) => Promise<void>; saveKanbanStages: (stages: KanbanStage[]) => Promise<void>;
// Mobile quick actions // Quick actions
isMobile?: boolean; isMobile?: boolean;
mobileActions?: { mobileActions?: {
goToPrevWeek: () => void; goToPrevWeek: () => void;
@ -9593,10 +9605,12 @@ interface SettingsSidebarProps {
goToNextWeek: () => void; goToNextWeek: () => void;
onJumpToDate: () => void; onJumpToDate: () => void;
onAddCalendarEvent: () => void; onAddCalendarEvent: () => void;
onAddProject: () => void;
onRecurringTasks: () => void; onRecurringTasks: () => void;
onToggleNextTask: () => void; onToggleNextTask: () => void;
onFocusMode: () => void; onFocusMode: () => void;
onToggleDarkMode: () => void; onToggleDarkMode: () => void;
onSearch: () => void;
onUndo: () => void; onUndo: () => void;
onRedo: () => void; onRedo: () => void;
onRefresh: () => void; onRefresh: () => void;
@ -9609,6 +9623,12 @@ interface SettingsSidebarProps {
showTimeGrid: boolean; showTimeGrid: boolean;
cellDuration: CellDuration; cellDuration: CellDuration;
onCellDurationChange: (d: CellDuration) => void; onCellDurationChange: (d: CellDuration) => void;
viewStyle: string;
onViewStyleChange: (style: string) => void;
startHour: number;
endHour: number;
onStartHourChange: (h: number) => void;
onEndHourChange: (h: number) => void;
}; };
} }
// Notes Sidebar Component // Notes Sidebar Component
@ -10294,8 +10314,8 @@ function SettingsSidebar({
</button> </button>
</header> </header>
{/* Mobile Quick Actions Panel */} {/* Quick Actions Panel */}
{isMobileSidebar && mobileActions && ( {mobileActions && (
<div className="mobile-quick-actions" style={{ <div className="mobile-quick-actions" style={{
padding: "12px 16px", padding: "12px 16px",
borderBottom: "1px solid var(--weekly-border, #eee)", borderBottom: "1px solid var(--weekly-border, #eee)",
@ -10325,12 +10345,18 @@ function SettingsSidebar({
{/* Quick Action Buttons */} {/* Quick Action Buttons */}
<div style={{ display: "flex", flexDirection: "column", gap: "2px" }}> <div style={{ display: "flex", flexDirection: "column", gap: "2px" }}>
<button className="mobile-quick-action-btn" onClick={() => { mobileActions.onSearch(); handleClose(); }}>
<Search size={18} /> <span>{t.search || "Search"}</span>
</button>
<button className="mobile-quick-action-btn" onClick={() => { mobileActions.onJumpToDate(); handleClose(); }}> <button className="mobile-quick-action-btn" onClick={() => { mobileActions.onJumpToDate(); handleClose(); }}>
<Calendar size={18} /> <span>{t.jumpToDate || "Jump to date"}</span> <Calendar size={18} /> <span>{t.jumpToDate || "Jump to date"}</span>
</button> </button>
<button className="mobile-quick-action-btn" onClick={() => { mobileActions.onAddCalendarEvent(); handleClose(); }}> <button className="mobile-quick-action-btn" onClick={() => { mobileActions.onAddCalendarEvent(); handleClose(); }}>
<Plus size={18} /> <span>{t.addCalendarEvent || "Add Calendar Event"}</span> <Plus size={18} /> <span>{t.addCalendarEvent || "Add Calendar Event"}</span>
</button> </button>
<button className="mobile-quick-action-btn" onClick={() => { mobileActions.onAddProject(); }}>
<FolderPlus size={18} /> <span>{t.addProject || "New Project"}</span>
</button>
<button className="mobile-quick-action-btn" onClick={() => { mobileActions.onRecurringTasks(); handleClose(); }}> <button className="mobile-quick-action-btn" onClick={() => { mobileActions.onRecurringTasks(); handleClose(); }}>
<Repeat size={18} /> <span>{t.recurringTasks || "Recurring Tasks"}</span> <Repeat size={18} /> <span>{t.recurringTasks || "Recurring Tasks"}</span>
</button> </button>
@ -10347,8 +10373,36 @@ function SettingsSidebar({
</button> </button>
</div> </div>
{/* View Controls */} {/* View Style */}
<div style={{ display: "flex", flexDirection: "column", gap: "8px", padding: "4px 0" }}> <div style={{ display: "flex", flexDirection: "column", gap: "8px", padding: "4px 0" }}>
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<span style={{ fontSize: "0.75rem", color: "var(--weekly-text-light, #9ca3af)", minWidth: "40px" }}>{t.view || "View"}</span>
<div style={{ display: "flex", gap: "4px", flex: 1 }}>
{[
{ key: "simple", icon: <CalendarDays size={14} />, label: "Simple" },
{ key: "calendar", icon: <Calendar size={14} />, label: "Calendar" },
{ key: "list", icon: <ListTodo size={14} />, label: "List" },
{ key: "kanban", icon: <Kanban size={14} />, label: "Kanban" },
].map((v) => (
<button
key={v.key}
onClick={() => { mobileActions.onViewStyleChange(v.key); }}
title={v.label}
style={{
flex: 1, padding: "6px 0", borderRadius: "6px", border: "none", cursor: "pointer",
display: "flex", alignItems: "center", justifyContent: "center",
fontWeight: mobileActions.viewStyle === v.key ? 700 : 400,
background: mobileActions.viewStyle === v.key ? "#0ea5e9" : "var(--weekly-settings-toggle-bg, #f3f4f6)",
color: mobileActions.viewStyle === v.key ? "#fff" : "var(--weekly-text, #333)",
}}
>
{v.icon}
</button>
))}
</div>
</div>
{/* Days */}
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}> <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<span style={{ fontSize: "0.75rem", color: "var(--weekly-text-light, #9ca3af)", minWidth: "40px" }}>{t.days || "Days"}</span> <span style={{ fontSize: "0.75rem", color: "var(--weekly-text-light, #9ca3af)", minWidth: "40px" }}>{t.days || "Days"}</span>
<div style={{ display: "flex", gap: "4px", flex: 1 }}> <div style={{ display: "flex", gap: "4px", flex: 1 }}>
@ -10368,6 +10422,8 @@ function SettingsSidebar({
))} ))}
</div> </div>
</div> </div>
{/* Slot Duration */}
{mobileActions.showTimeGrid && ( {mobileActions.showTimeGrid && (
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}> <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<span style={{ fontSize: "0.75rem", color: "var(--weekly-text-light, #9ca3af)", minWidth: "40px" }}>{t.slot || "Slot"}</span> <span style={{ fontSize: "0.75rem", color: "var(--weekly-text-light, #9ca3af)", minWidth: "40px" }}>{t.slot || "Slot"}</span>
@ -10389,6 +10445,43 @@ function SettingsSidebar({
</div> </div>
</div> </div>
)} )}
{/* Visible Hours */}
{mobileActions.showTimeGrid && (
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<span style={{ fontSize: "0.75rem", color: "var(--weekly-text-light, #9ca3af)", minWidth: "40px" }}>
<Clock size={14} />
</span>
<div style={{ display: "flex", alignItems: "center", gap: "4px", flex: 1 }}>
<input
type="number"
min={0}
max={23}
value={mobileActions.startHour}
onChange={(e) => mobileActions.onStartHourChange(Math.max(0, Math.min(23, parseInt(e.target.value) || 0)))}
style={{
width: "48px", padding: "4px 6px", borderRadius: "6px", border: "1px solid var(--weekly-border, #e5e7eb)",
background: "var(--weekly-settings-toggle-bg, #f3f4f6)", color: "var(--weekly-text, #333)",
fontSize: "0.8rem", textAlign: "center", outline: "none",
}}
/>
<span style={{ fontSize: "0.75rem", color: "var(--weekly-text-light, #9ca3af)" }}></span>
<input
type="number"
min={1}
max={24}
value={mobileActions.endHour}
onChange={(e) => mobileActions.onEndHourChange(Math.max(1, Math.min(24, parseInt(e.target.value) || 24)))}
style={{
width: "48px", padding: "4px 6px", borderRadius: "6px", border: "1px solid var(--weekly-border, #e5e7eb)",
background: "var(--weekly-settings-toggle-bg, #f3f4f6)", color: "var(--weekly-text, #333)",
fontSize: "0.8rem", textAlign: "center", outline: "none",
}}
/>
<span style={{ fontSize: "0.7rem", color: "var(--weekly-text-light, #9ca3af)" }}>h</span>
</div>
</div>
)}
</div> </div>
{/* Utility Row */} {/* Utility Row */}