feat: FAB (Floating Action Button) auf allen Unterseiten für Mobile
- Einheitlicher blauer Plus-Button unten rechts auf Mobile (tasks, calendar, notes, contacts, budget) — konsistent mit Dashboard-FAB - Toolbar-"Neu"-Buttons auf Mobile versteckt, auf Desktop weiterhin sichtbar - Wiederverwendbare .page-fab CSS-Klasse in layout.css - Dashboard-FAB Position an neue Nav-Höhe angepasst - Service Worker Cache v13 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -98,6 +98,9 @@ export async function render(container, { user }) {
|
|||||||
<div id="budget-body" style="flex:1;display:flex;flex-direction:column;overflow:hidden;">
|
<div id="budget-body" style="flex:1;display:flex;flex-direction:column;overflow:hidden;">
|
||||||
<div style="padding:2rem;text-align:center;color:var(--color-text-disabled);">Lade…</div>
|
<div style="padding:2rem;text-align:center;color:var(--color-text-disabled);">Lade…</div>
|
||||||
</div>
|
</div>
|
||||||
|
<button class="page-fab" id="fab-new-budget" aria-label="Neuer Eintrag">
|
||||||
|
<i data-lucide="plus" style="width:24px;height:24px"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -131,7 +134,9 @@ function wireNav() {
|
|||||||
renderBody();
|
renderBody();
|
||||||
updateLabel();
|
updateLabel();
|
||||||
});
|
});
|
||||||
_container.querySelector('#budget-add').addEventListener('click', () => openModal({ mode: 'create' }));
|
const addHandler = () => openModal({ mode: 'create' });
|
||||||
|
_container.querySelector('#budget-add').addEventListener('click', addHandler);
|
||||||
|
_container.querySelector('#fab-new-budget').addEventListener('click', addHandler);
|
||||||
updateLabel();
|
updateLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -159,6 +159,9 @@ export async function render(container, { user }) {
|
|||||||
<div class="calendar-page" id="calendar-page">
|
<div class="calendar-page" id="calendar-page">
|
||||||
<div class="cal-toolbar" id="cal-toolbar"></div>
|
<div class="cal-toolbar" id="cal-toolbar"></div>
|
||||||
<div id="cal-body" style="flex:1;display:flex;flex-direction:column;overflow:hidden;"></div>
|
<div id="cal-body" style="flex:1;display:flex;flex-direction:column;overflow:hidden;"></div>
|
||||||
|
<button class="page-fab" id="fab-new-event" aria-label="Neuer Termin">
|
||||||
|
<i data-lucide="plus" style="width:24px;height:24px"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -167,6 +170,8 @@ export async function render(container, { user }) {
|
|||||||
|
|
||||||
renderToolbar();
|
renderToolbar();
|
||||||
renderView();
|
renderView();
|
||||||
|
|
||||||
|
container.querySelector('#fab-new-event')?.addEventListener('click', () => openEventModal({ mode: 'create' }));
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ export async function render(container, { user }) {
|
|||||||
`).join('')}
|
`).join('')}
|
||||||
</div>
|
</div>
|
||||||
<div id="contacts-list" class="contacts-list"></div>
|
<div id="contacts-list" class="contacts-list"></div>
|
||||||
|
<button class="page-fab" id="fab-new-contact" aria-label="Neuer Kontakt">
|
||||||
|
<i data-lucide="plus" style="width:24px;height:24px"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -92,9 +95,9 @@ export async function render(container, { user }) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Neu
|
// Neu
|
||||||
_container.querySelector('#contacts-add-btn').addEventListener('click', () =>
|
const addHandler = () => openModal({ mode: 'create' });
|
||||||
openModal({ mode: 'create' })
|
_container.querySelector('#contacts-add-btn').addEventListener('click', addHandler);
|
||||||
);
|
_container.querySelector('#fab-new-contact').addEventListener('click', addHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ export async function render(container, { user }) {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="notes-grid" class="notes-grid"></div>
|
<div id="notes-grid" class="notes-grid"></div>
|
||||||
|
<button class="page-fab" id="fab-new-note" aria-label="Neue Notiz">
|
||||||
|
<i data-lucide="plus" style="width:24px;height:24px"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -68,7 +71,9 @@ export async function render(container, { user }) {
|
|||||||
}
|
}
|
||||||
renderGrid();
|
renderGrid();
|
||||||
|
|
||||||
_container.querySelector('#notes-add-btn').addEventListener('click', () => openModal({ mode: 'create' }));
|
const addHandler = () => openModal({ mode: 'create' });
|
||||||
|
_container.querySelector('#notes-add-btn').addEventListener('click', addHandler);
|
||||||
|
_container.querySelector('#fab-new-note').addEventListener('click', addHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
|
|||||||
@@ -864,10 +864,12 @@ function wireGroupToggle(container) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function wireNewTaskBtn(container) {
|
function wireNewTaskBtn(container) {
|
||||||
container.querySelector('#btn-new-task')?.addEventListener('click', () => {
|
const handler = () => {
|
||||||
openModal(renderModal({ users: state.users }));
|
openModal(renderModal({ users: state.users }));
|
||||||
wireModalEvents(container);
|
wireModalEvents(container);
|
||||||
});
|
};
|
||||||
|
container.querySelector('#btn-new-task')?.addEventListener('click', handler);
|
||||||
|
container.querySelector('#fab-new-task')?.addEventListener('click', handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
function wireModalEvents(container) {
|
function wireModalEvents(container) {
|
||||||
@@ -974,6 +976,9 @@ export async function render(container, { user }) {
|
|||||||
<div class="skeleton skeleton-line skeleton-line--short" style="height:12px"></div>
|
<div class="skeleton skeleton-line skeleton-line--short" style="height:12px"></div>
|
||||||
</div>`).join('')}
|
</div>`).join('')}
|
||||||
</div>
|
</div>
|
||||||
|
<button class="page-fab" id="fab-new-task" aria-label="Neue Aufgabe">
|
||||||
|
<i data-lucide="plus" style="width:24px;height:24px"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -621,7 +621,7 @@
|
|||||||
* -------------------------------------------------------- */
|
* -------------------------------------------------------- */
|
||||||
.fab-container {
|
.fab-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: calc(var(--nav-height-mobile) + var(--safe-area-inset-bottom) + var(--space-4));
|
bottom: calc(var(--nav-height-mobile) + 24px + var(--safe-area-inset-bottom));
|
||||||
right: var(--space-4);
|
right: var(--space-4);
|
||||||
z-index: calc(var(--z-nav) - 1);
|
z-index: calc(var(--z-nav) - 1);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -177,6 +177,58 @@
|
|||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------
|
||||||
|
* Page FAB — Schwebender Erstellen-Button (nur Mobile/Tablet)
|
||||||
|
*
|
||||||
|
* Einheitlicher FAB auf Unterseiten. Auf Desktop versteckt,
|
||||||
|
* da der Toolbar-Button dort gut erreichbar ist.
|
||||||
|
* -------------------------------------------------------- */
|
||||||
|
.page-fab {
|
||||||
|
position: fixed;
|
||||||
|
bottom: calc(var(--nav-height-mobile) + 24px + var(--safe-area-inset-bottom));
|
||||||
|
right: var(--space-4);
|
||||||
|
width: 52px;
|
||||||
|
height: 52px;
|
||||||
|
border-radius: var(--radius-full);
|
||||||
|
background-color: var(--color-accent);
|
||||||
|
color: #ffffff;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
z-index: calc(var(--z-nav) - 1);
|
||||||
|
transition: transform var(--transition-base), background-color var(--transition-fast);
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-fab:hover {
|
||||||
|
background-color: var(--color-accent-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-fab:active {
|
||||||
|
transform: scale(0.92);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Auf Desktop: FAB verstecken, Toolbar-Button reicht */
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.page-fab {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Auf Mobile: Toolbar-"Neu"-Buttons verstecken, FAB übernimmt */
|
||||||
|
@media (max-width: 1023px) {
|
||||||
|
#btn-new-task,
|
||||||
|
#notes-add-btn,
|
||||||
|
#contacts-add-btn,
|
||||||
|
#budget-add,
|
||||||
|
#cal-add {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
* Sidebar Navigation — Desktop (≥ 1024px)
|
* Sidebar Navigation — Desktop (≥ 1024px)
|
||||||
*
|
*
|
||||||
|
|||||||
+3
-3
@@ -12,9 +12,9 @@
|
|||||||
* API: Immer Netzwerk (kein Caching von Nutzerdaten)
|
* API: Immer Netzwerk (kein Caching von Nutzerdaten)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const SHELL_CACHE = 'oikos-shell-v12';
|
const SHELL_CACHE = 'oikos-shell-v13';
|
||||||
const PAGES_CACHE = 'oikos-pages-v12';
|
const PAGES_CACHE = 'oikos-pages-v13';
|
||||||
const ASSETS_CACHE = 'oikos-assets-v12';
|
const ASSETS_CACHE = 'oikos-assets-v13';
|
||||||
const ALL_CACHES = [SHELL_CACHE, PAGES_CACHE, ASSETS_CACHE];
|
const ALL_CACHES = [SHELL_CACHE, PAGES_CACHE, ASSETS_CACHE];
|
||||||
|
|
||||||
// App-Shell: sofort benötigt für ersten Render
|
// App-Shell: sofort benötigt für ersten Render
|
||||||
|
|||||||
Reference in New Issue
Block a user