fix: 4 mobile issues — slot indicator, view days, list header, scroll sync

1. cellDuration indicator: compare against local `cellDuration` state
   (not effectiveCellDuration which reads stale profile.cellDuration
   since saveSetting bails early for DEVICE_SETTINGS_KEYS)

2. viewDays on mobile now uses orientation instead of fixed width:
   portrait (h > w) → 1 day, landscape → 3 days, for ≤768px.
   Applied consistently in useEffect, early profile load, and cookie
   override blocks so the responsive value is never overridden by
   the saved DB preference on mobile.

3. list-view day header on mobile: changed from sticky to
   position:relative so it flows with content and never covers tasks

4. time/day column scroll sync: weekly-days-grid was overflow-y:auto
   on mobile, creating an independent scroll container that drifted
   from the time column. Changed to overflow-y:visible so only
   time-grid-wrapper scrolls. Added touch-action:pan-y to day columns
   so iOS handles pan immediately without waiting for JS.

v1.81.7
This commit is contained in:
mARTin 2026-04-02 09:29:48 +02:00
parent 5ee99d6d2f
commit 676c5612ed
3 changed files with 28 additions and 20 deletions

View File

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

@ -3479,10 +3479,11 @@ h3 {
border-bottom-color: var(--weekly-border, #333); border-bottom-color: var(--weekly-border, #333);
} }
/* On mobile, list-view day header must clear the sticky mobile header bar (~48px) */ /* On mobile, list-view day header is not sticky — it just flows with content */
@media (max-width: 768px) { @media (max-width: 768px) {
.weekly-container.list-view .weekly-day-header { .weekly-container.list-view .weekly-day-header {
top: calc(48px + env(safe-area-inset-top, 0px)); position: relative !important;
top: auto !important;
} }
} }
@ -4943,16 +4944,17 @@ h3 {
min-height: 100% !important; min-height: 100% !important;
} }
} }
/* Mobile: allow scroll on grid but keep day columns visible for sticky headers */ /* Mobile: all inner containers stay non-scrolling — time-grid-wrapper is the sole scroll container */
@media (max-width: 768px) { @media (max-width: 768px) {
.time-grid-on .weekly-days-grid { .time-grid-on .weekly-days-grid {
overflow-y: auto !important; overflow-y: visible !important;
overflow-x: clip !important; overflow-x: clip !important;
} }
.time-grid-on .weekly-day-column { .time-grid-on .weekly-day-column {
overflow-y: visible !important; overflow-y: visible !important;
overflow-x: visible !important; overflow-x: visible !important;
max-height: none !important; max-height: none !important;
touch-action: pan-y;
} }
.time-grid-on .time-slots-container, .time-grid-on .time-slots-container,
.time-grid-on .time-column-slots, .time-grid-on .time-column-slots,

View File

@ -569,17 +569,19 @@ export default function WeeklyView() {
const savedViewDaysRef = useRef(7); // Track user's saved preference for restoring on resize const savedViewDaysRef = useRef(7); // Track user's saved preference for restoring on resize
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
// Responsive: auto-adjust viewDays based on screen width // Responsive: auto-adjust viewDays based on screen orientation / width
useEffect(() => { useEffect(() => {
const getResponsiveViewDays = (width: number): number => { const getResponsiveViewDays = (width: number, height: number): number => {
if (width <= 480) return 1; if (width <= 768) {
if (width <= 768) return 3; // Mobile: portrait → 1 day, landscape → 3 days
return height > width ? 1 : 3;
}
if (width <= 1024) return Math.min(savedViewDaysRef.current, 5); if (width <= 1024) return Math.min(savedViewDaysRef.current, 5);
return savedViewDaysRef.current; return savedViewDaysRef.current;
}; };
const handleResize = () => { const handleResize = () => {
const responsiveDays = getResponsiveViewDays(window.innerWidth); const responsiveDays = getResponsiveViewDays(window.innerWidth, window.innerHeight);
setViewDays(responsiveDays); setViewDays(responsiveDays);
}; };
@ -1249,7 +1251,13 @@ export default function WeeklyView() {
// Sync individual states to profile data // Sync individual states to profile data
if (profileData.viewStyle) setViewStyle(profileData.viewStyle); if (profileData.viewStyle) setViewStyle(profileData.viewStyle);
if (profileData.viewDays) setViewDays(profileData.viewDays); if (profileData.viewDays) {
savedViewDaysRef.current = profileData.viewDays;
const w = window.innerWidth, h = window.innerHeight;
if (w <= 768) setViewDays(h > w ? 1 : 3);
else if (w <= 1024) setViewDays(Math.min(profileData.viewDays, 5));
else setViewDays(profileData.viewDays);
}
if (profileData.showTimeGrid !== undefined) setShowTimeGrid(profileData.showTimeGrid); if (profileData.showTimeGrid !== undefined) setShowTimeGrid(profileData.showTimeGrid);
if (profileData.showSomeday !== undefined) setShowSomeday(profileData.showSomeday); if (profileData.showSomeday !== undefined) setShowSomeday(profileData.showSomeday);
if (profileData.showAllDayEvents !== undefined) setShowAllDay(profileData.showAllDayEvents); if (profileData.showAllDayEvents !== undefined) setShowAllDay(profileData.showAllDayEvents);
@ -2244,10 +2252,9 @@ export default function WeeklyView() {
} }
if (data.user.viewDays !== undefined) { if (data.user.viewDays !== undefined) {
savedViewDaysRef.current = data.user.viewDays; savedViewDaysRef.current = data.user.viewDays;
const width = window.innerWidth; const w = window.innerWidth, h = window.innerHeight;
if (width <= 480) setViewDays(1); if (w <= 768) setViewDays(h > w ? 1 : 3);
else if (width <= 768) setViewDays(3); else if (w <= 1024) setViewDays(Math.min(data.user.viewDays, 5));
else if (width <= 1024) setViewDays(Math.min(data.user.viewDays, 5));
else setViewDays(data.user.viewDays); else setViewDays(data.user.viewDays);
} }
if (data.user.cellDuration !== undefined) if (data.user.cellDuration !== undefined)
@ -2258,10 +2265,9 @@ export default function WeeklyView() {
if (cookieViewDays) { if (cookieViewDays) {
const v = Number(cookieViewDays); const v = Number(cookieViewDays);
savedViewDaysRef.current = v; savedViewDaysRef.current = v;
const width = window.innerWidth; const w = window.innerWidth, h = window.innerHeight;
if (width <= 480) setViewDays(1); if (w <= 768) setViewDays(h > w ? 1 : 3);
else if (width <= 768) setViewDays(3); else if (w <= 1024) setViewDays(Math.min(v, 5));
else if (width <= 1024) setViewDays(Math.min(v, 5));
else setViewDays(v); else setViewDays(v);
} }
if (data.user.weekdayFormat) { if (data.user.weekdayFormat) {
@ -5442,7 +5448,7 @@ export default function WeeklyView() {
<div style={{ display: "flex", gap: "4px" }}> <div style={{ display: "flex", gap: "4px" }}>
{[15, 30, 60].map((d) => ( {[15, 30, 60].map((d) => (
<button key={d} onClick={() => { setCellDuration(d as CellDuration); saveSetting("cellDuration", d); }} <button key={d} onClick={() => { setCellDuration(d as CellDuration); saveSetting("cellDuration", d); }}
style={{ flex: 1, padding: "5px 0", fontSize: "0.8rem", borderRadius: "6px", border: "none", cursor: "pointer", fontWeight: effectiveCellDuration === d ? 700 : 400, background: effectiveCellDuration === d ? (darkMode ? "#374151" : "#333") : (darkMode ? "#1f2937" : "#e5e7eb"), color: effectiveCellDuration === d ? "#fff" : (darkMode ? "#9ca3af" : "#6b7280") }}> style={{ flex: 1, padding: "5px 0", fontSize: "0.8rem", borderRadius: "6px", border: "none", cursor: "pointer", fontWeight: cellDuration === d ? 700 : 400, background: cellDuration === d ? (darkMode ? "#374151" : "#333") : (darkMode ? "#1f2937" : "#e5e7eb"), color: cellDuration === d ? "#fff" : (darkMode ? "#9ca3af" : "#6b7280") }}>
{d}m {d}m
</button> </button>
))} ))}