feat: transform mobile FAB into quick action menu

FAB now opens a fan menu with 4 options instead of directly adding tasks:
- Task: opens the existing bottom sheet for quick task entry
- Event: opens the calendar event modal
- Focus: enters focus mode
- Search: opens search modal

FAB rotates to X when menu is open, backdrop dismisses on tap.
Each menu item has a colored icon bubble with label.

v1.41.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
mARTin 2026-03-17 10:50:12 +01:00
parent 2d386362ca
commit 18da568b9f
3 changed files with 96 additions and 9 deletions

View File

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

@ -4062,6 +4062,64 @@ h3 {
transform: scale(0.9); transform: scale(0.9);
box-shadow: 0 2px 8px rgba(14, 165, 233, 0.3); box-shadow: 0 2px 8px rgba(14, 165, 233, 0.3);
} }
.mobile-fab.active {
background: #ef4444;
box-shadow: 0 4px 14px rgba(239, 68, 68, 0.4);
}
/* --- FAB Quick Menu --- */
.mobile-fab-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.2);
z-index: 899;
animation: fadeIn 0.15s ease;
}
.mobile-fab-menu {
position: fixed;
bottom: calc(92px + env(safe-area-inset-bottom, 0px));
right: 24px;
display: flex;
flex-direction: column-reverse;
gap: 12px;
z-index: 901;
animation: fabMenuIn 0.2s ease-out;
}
@keyframes fabMenuIn {
from { opacity: 0; transform: translateY(10px) scale(0.95); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
.mobile-fab-menu-item {
display: flex;
align-items: center;
gap: 10px;
flex-direction: row-reverse;
border: none;
background: none;
cursor: pointer;
padding: 0;
-webkit-tap-highlight-color: transparent;
}
.mobile-fab-menu-item span {
background: var(--weekly-bg, #fff);
color: var(--weekly-text, #333);
padding: 6px 14px;
border-radius: 8px;
font-size: 0.85rem;
font-weight: 600;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
white-space: nowrap;
}
.mobile-fab-menu-icon {
width: 44px;
height: 44px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
/* --- Bottom Sheet --- */ /* --- Bottom Sheet --- */
.bottom-sheet-backdrop { .bottom-sheet-backdrop {

View File

@ -1794,6 +1794,7 @@ export default function WeeklyView() {
const [isMobile, setIsMobile] = useState(false); const [isMobile, setIsMobile] = useState(false);
const [showMobileFabSheet, setShowMobileFabSheet] = useState(false); const [showMobileFabSheet, setShowMobileFabSheet] = useState(false);
const [showMobileFabMenu, setShowMobileFabMenu] = useState(false);
const [fabTaskTitle, setFabTaskTitle] = useState(""); const [fabTaskTitle, setFabTaskTitle] = useState("");
const fabTextareaRef = useRef<HTMLTextAreaElement>(null); const fabTextareaRef = useRef<HTMLTextAreaElement>(null);
@ -8352,15 +8353,43 @@ export default function WeeklyView() {
isLoading={isFetchingLists} isLoading={isFetchingLists}
/> />
{/* Mobile: Floating Action Button for quick task creation */} {/* Mobile: Floating Action Button with quick menu */}
{isMobile && !showMobileFabSheet && !showSettings && !showFocusMode && ( {isMobile && !showMobileFabSheet && !showSettings && !showFocusMode && (
<button <>
className="mobile-fab" {showMobileFabMenu && (
onClick={() => setShowMobileFabSheet(true)} <div className="mobile-fab-backdrop" onClick={() => setShowMobileFabMenu(false)} />
title="Add task" )}
> {showMobileFabMenu && (
<Plus size={28} /> <div className="mobile-fab-menu">
</button> <button className="mobile-fab-menu-item" onClick={() => { setShowMobileFabMenu(false); setShowMobileFabSheet(true); }}>
<div className="mobile-fab-menu-icon" style={{ background: "#0ea5e9" }}><Plus size={18} /></div>
<span>{language === "de" ? "Aufgabe" : "Task"}</span>
</button>
<button className="mobile-fab-menu-item" onClick={() => {
setShowMobileFabMenu(false);
const now = new Date();
setCalendarEventModal({ isOpen: true, event: undefined, initialDate: now, initialStartTime: `${String(now.getHours()).padStart(2, "0")}:00` });
}}>
<div className="mobile-fab-menu-icon" style={{ background: "#8b5cf6" }}><Calendar size={18} /></div>
<span>{language === "de" ? "Termin" : "Event"}</span>
</button>
<button className="mobile-fab-menu-item" onClick={() => { setShowMobileFabMenu(false); setShowFocusMode(true); }}>
<div className="mobile-fab-menu-icon" style={{ background: "#f59e0b" }}><Zap size={18} /></div>
<span>{language === "de" ? "Fokus" : "Focus"}</span>
</button>
<button className="mobile-fab-menu-item" onClick={() => { setShowMobileFabMenu(false); setIsSearchOpen(true); }}>
<div className="mobile-fab-menu-icon" style={{ background: "#10b981" }}><Search size={18} /></div>
<span>{language === "de" ? "Suche" : "Search"}</span>
</button>
</div>
)}
<button
className={`mobile-fab ${showMobileFabMenu ? "active" : ""}`}
onClick={() => setShowMobileFabMenu(!showMobileFabMenu)}
>
<Plus size={28} style={{ transform: showMobileFabMenu ? "rotate(45deg)" : "rotate(0deg)", transition: "transform 0.2s ease" }} />
</button>
</>
)} )}
{/* Mobile: Bottom Sheet for task creation */} {/* Mobile: Bottom Sheet for task creation */}