From 5ebdf6d751afc34babbe5c5756eb3ee3aef05801 Mon Sep 17 00:00:00 2001 From: mARTin-B78 Date: Wed, 13 May 2026 00:43:19 +0200 Subject: [PATCH] Refine sidebar header controls and resize handles --- src/app/globals.css | 63 +++++++++++++--- src/components/SettingsSidebar.tsx | 35 +++++++++ src/components/WeeklyView.tsx | 112 +++++++++++++++++++---------- 3 files changed, 161 insertions(+), 49 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index e6742e2..255a5aa 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -3270,26 +3270,29 @@ h3 { /* Resize handle for draggable section borders */ .resize-handle { - height: 7px; + height: 2px; + border: 1px solid #ccc; cursor: ns-resize; display: flex; align-items: center; justify-content: center; + -webkit-user-select: none; + -moz-user-select: none; user-select: none; touch-action: none; position: relative; - z-index: 10; + z-index: 30; flex-shrink: 0; - border-top: 1px solid var(--weekly-border, #e5e7eb); - border-bottom: 1px solid var(--weekly-border, #e5e7eb); + overflow: visible; + background-color: #ddd; } .resize-handle:hover { - background: rgba(59, 130, 246, 0.08); - border-color: var(--weekly-accent, #3b82f6); + background-color: #d7d7d7; + border-color: #b8b8b8; } .resize-handle:active { - background: rgba(59, 130, 246, 0.12); - border-color: var(--weekly-accent, #3b82f6); + background-color: #d1d1d1; + border-color: #aaa; } .resize-handle-bar { width: 32px; @@ -3299,10 +3302,50 @@ h3 { opacity: 0.6; transition: background 0.15s, opacity 0.15s; } +.resize-handle-anyday { + height: 2px; +} +.resize-handle-allday { + height: 2px; +} +.resize-handle-anyday .resize-handle-bar { + width: 23px; + height: 9px; + border-radius: 4px; + background: radial-gradient(circle, color-mix(in srgb, var(--weekly-text, #333) 48%, transparent) 1.25px, transparent 1.35px) 2px 1.5px / 6px 5px repeat; + background-color: #ddd; + border: 1px solid #ccc; + opacity: 1; + position: relative; + bottom: 0; + overflow: visible; + z-index: 50; +} +.resize-handle-allday .resize-handle-bar { + width: 23px; + height: 9px; + border-radius: 4px; + background: radial-gradient(circle, color-mix(in srgb, var(--weekly-text, #333) 48%, transparent) 1.25px, transparent 1.35px) 2px 1.5px / 6px 5px repeat; + background-color: #ddd; + border: 1px solid #ccc; + opacity: 1; + position: relative; + bottom: 0; + overflow: visible; + z-index: 50; +} .resize-handle:hover .resize-handle-bar, .resize-handle:active .resize-handle-bar { - background: var(--weekly-accent, #3b82f6); - opacity: 0.5; + opacity: 1; +} +.resize-handle-anyday:hover .resize-handle-bar, +.resize-handle-anyday:active .resize-handle-bar, +.resize-handle-allday:hover .resize-handle-bar, +.resize-handle-allday:active .resize-handle-bar { + background: radial-gradient(circle, var(--weekly-accent, #3b82f6) 1.25px, transparent 1.35px) 2px 1.5px / 6px 5px repeat; + background-color: #d7d7d7; + border-color: #b8b8b8; + opacity: 1; } .all-day-events-header { diff --git a/src/components/SettingsSidebar.tsx b/src/components/SettingsSidebar.tsx index 62b40cb..01e6958 100644 --- a/src/components/SettingsSidebar.tsx +++ b/src/components/SettingsSidebar.tsx @@ -1045,6 +1045,41 @@ function SettingsSidebar({ ))} +
+ +
+ {[ + { value: true, label: profile.language === "de" ? "Anzeigen" : "Show" }, + { value: false, label: profile.language === "de" ? "Ausblenden" : "Hide" }, + ].map(({ value, label }) => { + const isActive = (profile.showHeaderControls !== false) === value; + return ( + + ); + })} +
+
{/* Header Display */}
diff --git a/src/components/WeeklyView.tsx b/src/components/WeeklyView.tsx index 0524497..91667b1 100644 --- a/src/components/WeeklyView.tsx +++ b/src/components/WeeklyView.tsx @@ -128,6 +128,7 @@ type DeviceViewSettingKey = typeof DEVICE_VIEW_SETTINGS_KEYS[number]; // Settings that save to DB (cross-device default) AND to cookie (device override wins on load) const DEVICE_ALSO_COOKIE_KEYS = ["viewStyle", "showTimeGrid", "startDayOffset"]; +const VIEW_SETTINGS_PROFILE_KEYS = ["menuPosition", "showHeaderControls"]; function getCookie(name: string): string | null { if (typeof document === "undefined") return null; @@ -918,6 +919,7 @@ export default function WeeklyView() { // Mobile detection const [isMobile, setIsMobile] = useState(false); const [isPortrait, setIsPortrait] = useState(false); + const [isCompactHeight, setIsCompactHeight] = useState(false); // Tracks the last day column the user interacted with (for "selected day" header display) const [selectedDay, setSelectedDay] = useState(null); @@ -1037,6 +1039,8 @@ export default function WeeklyView() { weatherLon: null, weatherLocation: "", showCalendarProviderIcon: false, + menuPosition: "left", + showHeaderControls: true, }); const [motivationalQuote, setMotivationalQuote] = useState(""); const [showSummary, setShowSummary] = useState(false); @@ -1400,6 +1404,7 @@ export default function WeeklyView() { const check = () => { setIsMobile(window.innerWidth <= 768); setIsPortrait(window.innerHeight > window.innerWidth); + setIsCompactHeight(window.innerHeight <= 720); }; check(); window.addEventListener("resize", check); @@ -1416,7 +1421,12 @@ export default function WeeklyView() { if (res.ok) { const data = await res.json(); if (data && data.user) { - const profileData = data.user; + const viewPrefs = (data.user.viewSettings || {}) as any; + const profileData = { + ...data.user, + menuPosition: viewPrefs.menuPosition ?? data.user.menuPosition ?? "left", + showHeaderControls: viewPrefs.showHeaderControls ?? data.user.showHeaderControls ?? true, + }; setProfile(profileData); // Sync individual states to profile data @@ -2403,6 +2413,22 @@ export default function WeeklyView() { somedayWheelCleanup.current = () => node.removeEventListener("wheel", handler); }, []); const saveSetting = async (key: string, value: any) => { + if (VIEW_SETTINGS_PROFILE_KEYS.includes(key)) { + const updated = { ...(viewSettingsRef.current as any), [key]: value }; + viewSettingsRef.current = updated; + setViewSettings(updated as any); + setProfile((p: any) => ({ ...p, [key]: value })); + try { + await fetch("/api/user/profile", { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ viewSettings: updated }), + }); + } catch (err) { + console.error(`Failed to save setting ${key}:`, err); + } + return; + } // Per-device settings: save to cookie ONLY (not DB) so each device keeps its own value if (DEVICE_SETTINGS_KEYS.includes(key)) { setCookie(`setting_${key}`, String(value)); @@ -5383,6 +5409,10 @@ export default function WeeklyView() { }; const activeTheme = (darkMode ? profile.darkTheme : profile.lightTheme) as Record | null; const useLeftRail = !isMobile && profile.menuPosition !== "top"; + const showHeaderControls = profile.showHeaderControls !== false; + const compactRail = useLeftRail && isCompactHeight; + const collapsedRailWidth = compactRail ? 38 : 44; + const expandedRailWidth = compactRail ? 220 : 240; const containerStyle = { ...(activeTheme ? { @@ -5461,7 +5491,7 @@ export default function WeeklyView() { "--weekly-past-color": activeTheme?.color8 || (darkMode ? invertColor(profile.pastDayColor || "#a6a6a7") : profile.pastDayColor || "#a6a6a7"), - ...(useLeftRail ? { paddingLeft: leftRailExpanded ? "240px" : "44px", transition: "padding-left 0.2s ease" } : {}), + ...(useLeftRail ? { paddingLeft: leftRailExpanded ? `${expandedRailWidth}px` : `${collapsedRailWidth}px`, transition: "padding-left 0.2s ease" } : {}), } as React.CSSProperties; if (isLoading) { @@ -5492,12 +5522,12 @@ export default function WeeklyView() { }); const railIconBtnStyle: React.CSSProperties = { - width: "36px", height: "36px", display: "flex", alignItems: "center", justifyContent: "center", - background: "none", border: "none", cursor: "pointer", borderRadius: "8px", + width: compactRail ? "30px" : "36px", height: compactRail ? "30px" : "36px", display: "flex", alignItems: "center", justifyContent: "center", + background: "none", border: "none", cursor: "pointer", borderRadius: compactRail ? "7px" : "8px", color: darkMode ? "#9ca3af" : "#6b7280", flexShrink: 0, }; const flyoutPanelStyle: React.CSSProperties = { - position: "fixed", left: "48px", + position: "fixed", left: `${collapsedRailWidth + 4}px`, top: `${Math.min(flyoutY, (typeof window !== "undefined" ? window.innerHeight : 800) - 260)}px`, background: darkMode ? "#1a1a2e" : "var(--paper)", border: "1px solid var(--line)", borderRadius: "10px", padding: "12px 14px", @@ -5521,7 +5551,7 @@ export default function WeeklyView() { if (flyoutTimerRef.current) clearTimeout(flyoutTimerRef.current); }; const railSep = ( -
+
); // All-Day Events Section (reusable for above/below positioning) @@ -5535,7 +5565,7 @@ export default function WeeklyView() { const handleOnTop = effectiveAllDayPosition === "below"; const resizeHandle = isAllDayExpanded ? (
startResize(e, 'allday', handleOnTop)} onTouchStart={(e) => startResize(e, 'allday', handleOnTop)} > @@ -5830,10 +5860,10 @@ export default function WeeklyView() { {useLeftRail && ( <> {/* The rail panel */} -
+
{leftRailExpanded ? ( // ── EXPANDED MODE ── -
+
{profile.language === "de" ? "Einstellungen" : "Preferences"}
) : ( // ── COLLAPSED MODE — icon strip ── -
+
{/* Expand toggle */} {railSep} @@ -6243,7 +6273,7 @@ export default function WeeklyView() { )} {/* Desktop Header: Left, Center, Right */} -
+
{/* LEFT SECTION: View Switcher, Days, Hours, Slot Duration — only shown in top-toolbar mode */}
{/* Quick-settings button (top mode only) */} @@ -6503,28 +6533,30 @@ export default function WeeklyView() {
{/* RIGHT SECTION: Navigation & Tools */} -
+
{/* Secondary actions — hidden on tablet, visible on desktop */} -
- - - - - - -
+ {!useLeftRail && ( +
+ + + + + + +
+ )} {/* Navigation Controls — always visible */}
@@ -6540,7 +6572,7 @@ export default function WeeklyView() { {/* Overflow menu — visible on tablet, hidden on desktop */} -
+
@@ -6589,9 +6621,13 @@ export default function WeeklyView() {
{/* Desktop only: Recurring Tasks + New Project */} - - - + {!useLeftRail && ( + <> + + + + + )} {/* User Menu */} startResize(e, 'someday', true)} onTouchStart={(e) => startResize(e, 'someday', true)} > @@ -11782,5 +11818,3 @@ function NotesSidebar({ task, onClose, updateTaskNotes, updateTaskUrl }: NotesSi ); } - -