Redesign controls in recipe list
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
export function getBottomNavHTML() {
|
||||
return `
|
||||
<nav id="app-bottom-nav" aria-label="Główna nawigacja">
|
||||
<div class="bottom-dock">
|
||||
<div id="app-bottom-nav-dock" class="bottom-dock">
|
||||
<button type="button" id="recipe-nav-toggle" class="recipe-nav-toggle" aria-label="Otwórz menu katalogu" aria-expanded="false" aria-controls="app-bottom-nav-dock">
|
||||
<i class="fas fa-book-open" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="nav-slot">
|
||||
<button type="button" data-tab="planner" id="nav-planner" class="nav-tab is-active" aria-label="Planer" aria-current="page">
|
||||
<i class="far fa-calendar-alt" aria-hidden="true"></i>
|
||||
@@ -10,7 +13,7 @@ export function getBottomNavHTML() {
|
||||
</div>
|
||||
<div class="nav-slot">
|
||||
<button type="button" data-tab="recipes" id="nav-recipes" class="nav-tab" aria-label="Katalog">
|
||||
<i class="fas fa-search" aria-hidden="true"></i>
|
||||
<i class="fas fa-book-open" aria-hidden="true"></i>
|
||||
<span class="nav-label">Katalog</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -40,12 +43,86 @@ export function setupBottomNav({ refreshPantry, refreshShoppingList } = {}) {
|
||||
if (!main || !planner || !pantry || !shopping || !nav) return;
|
||||
|
||||
const TABS = ['recipes', 'planner', 'pantry', 'shopping'];
|
||||
let isRecipeMenuOpen = false;
|
||||
let previousTab = 'planner';
|
||||
let collapseTimer = null;
|
||||
|
||||
const syncRecipeNavMetrics = () => {
|
||||
const rootFontSize = parseFloat(window.getComputedStyle(document.documentElement).fontSize) || 16;
|
||||
const navStyles = window.getComputedStyle(nav);
|
||||
const navPadLeft = parseFloat(navStyles.paddingLeft) || 0;
|
||||
const navPadRight = parseFloat(navStyles.paddingRight) || 0;
|
||||
const navContentWidth = nav.clientWidth - navPadLeft - navPadRight;
|
||||
const isCompact = window.matchMedia('(max-width: 380px)').matches;
|
||||
const dockInset = (isCompact ? 1.6 : 2.4) * rootFontSize;
|
||||
const dockMax = (isCompact ? 22.5 : 24.5) * rootFontSize;
|
||||
const dockWidth = Math.min(navContentWidth - dockInset, dockMax);
|
||||
if (dockWidth <= 0) return;
|
||||
|
||||
const padLeft = (isCompact ? 0.38 : 0.42) * rootFontSize;
|
||||
const padRight = (isCompact ? 0.38 : 0.42) * rootFontSize;
|
||||
const columnGap = (isCompact ? 0.05 : 0.06) * rootFontSize;
|
||||
const slotWidth = Math.max(44, (dockWidth - padLeft - padRight - (columnGap * 3)) / 4);
|
||||
const collapsedSlotWidth = Math.max(42, Math.min(slotWidth, isCompact ? 44 : 46));
|
||||
const collapsedDockWidth = collapsedSlotWidth + padLeft + padRight;
|
||||
const dockLeft = navPadLeft + ((navContentWidth - dockWidth) / 2);
|
||||
const controlSize = (isCompact ? 2.95 : 3.05) * rootFontSize;
|
||||
const expandedDockHeight = (isCompact ? 3.48 : 3.72) * rootFontSize;
|
||||
const controlGap = 0.5 * rootFontSize;
|
||||
const controlsLift = Math.max(0, (expandedDockHeight - controlSize) / 2);
|
||||
const filterLeft = Math.max(
|
||||
dockLeft + collapsedDockWidth + controlGap,
|
||||
dockLeft + dockWidth - padRight - controlSize,
|
||||
);
|
||||
const searchRight = Math.max(16, nav.clientWidth - filterLeft + controlGap);
|
||||
|
||||
nav.style.setProperty('--recipe-dock-width', `${dockWidth}px`);
|
||||
nav.style.setProperty('--recipe-collapsed-dock-width', `${collapsedDockWidth}px`);
|
||||
nav.style.setProperty('--recipe-toggle-size', `${collapsedSlotWidth}px`);
|
||||
document.documentElement.style.setProperty('--catalog-menu-left', `${dockLeft}px`);
|
||||
document.documentElement.style.setProperty('--catalog-menu-width', `${collapsedDockWidth}px`);
|
||||
document.documentElement.style.setProperty('--catalog-filter-left', `${filterLeft}px`);
|
||||
document.documentElement.style.setProperty('--catalog-search-right', `${searchRight}px`);
|
||||
document.documentElement.style.setProperty('--recipe-control-size', `${controlSize}px`);
|
||||
document.documentElement.style.setProperty('--recipe-controls-lift', `${controlsLift}px`);
|
||||
};
|
||||
|
||||
const setRecipeMenuOpen = (open) => {
|
||||
syncRecipeNavMetrics();
|
||||
window.clearTimeout(collapseTimer);
|
||||
isRecipeMenuOpen = open;
|
||||
nav.classList.remove('is-recipes-collapsing');
|
||||
nav.classList.toggle('is-recipes-menu-open', open);
|
||||
document.documentElement.classList.toggle('is-recipes-menu-open', open);
|
||||
document.getElementById('recipe-nav-toggle')?.setAttribute('aria-expanded', open ? 'true' : 'false');
|
||||
};
|
||||
|
||||
const apply = (tab) => {
|
||||
const wasRecipeTab = previousTab === 'recipes';
|
||||
main.classList.toggle('hidden', tab !== 'recipes');
|
||||
planner.classList.toggle('hidden', tab !== 'planner');
|
||||
pantry.classList.toggle('hidden', tab !== 'pantry');
|
||||
shopping.classList.toggle('hidden', tab !== 'shopping');
|
||||
nav.classList.toggle('is-recipes-tab', tab === 'recipes');
|
||||
syncRecipeNavMetrics();
|
||||
setRecipeMenuOpen(false);
|
||||
|
||||
if (tab === 'recipes' && !wasRecipeTab) {
|
||||
nav.classList.add('is-recipes-menu-open', 'is-recipes-collapsing');
|
||||
document.documentElement.classList.add('is-recipes-menu-open');
|
||||
document.getElementById('recipe-nav-toggle')?.setAttribute('aria-expanded', 'false');
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
nav.classList.remove('is-recipes-menu-open');
|
||||
document.documentElement.classList.remove('is-recipes-menu-open');
|
||||
collapseTimer = window.setTimeout(() => {
|
||||
nav.classList.remove('is-recipes-collapsing');
|
||||
}, 500);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (tab === 'pantry' && typeof refreshPantry === 'function') refreshPantry();
|
||||
if (tab === 'shopping' && typeof refreshShoppingList === 'function') refreshShoppingList();
|
||||
@@ -60,17 +137,45 @@ export function setupBottomNav({ refreshPantry, refreshShoppingList } = {}) {
|
||||
else btn.removeAttribute('aria-current');
|
||||
}
|
||||
});
|
||||
|
||||
window.dispatchEvent(new CustomEvent('app-tab-change', { detail: { tab } }));
|
||||
previousTab = tab;
|
||||
};
|
||||
|
||||
nav.addEventListener('click', (e) => {
|
||||
const toggle = e.target.closest('#recipe-nav-toggle');
|
||||
if (toggle) {
|
||||
e.stopPropagation();
|
||||
setRecipeMenuOpen(!isRecipeMenuOpen);
|
||||
window.closeRecipeSearch?.();
|
||||
window.closeFilters?.();
|
||||
return;
|
||||
}
|
||||
|
||||
const btn = e.target.closest('.nav-tab[data-tab]');
|
||||
if (!btn || btn.hasAttribute('disabled')) return;
|
||||
const tab = btn.getAttribute('data-tab');
|
||||
if (TABS.includes(tab)) apply(tab);
|
||||
});
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!isRecipeMenuOpen || !nav.classList.contains('is-recipes-tab')) return;
|
||||
if (e.composedPath().includes(nav)) return;
|
||||
setRecipeMenuOpen(false);
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && isRecipeMenuOpen) setRecipeMenuOpen(false);
|
||||
});
|
||||
|
||||
window.addEventListener('resize', syncRecipeNavMetrics);
|
||||
|
||||
apply('planner');
|
||||
|
||||
window.switchAppTab = (tab) => {
|
||||
if (TABS.includes(tab)) apply(tab);
|
||||
};
|
||||
|
||||
window.refreshStockViews = () => {
|
||||
if (typeof refreshPantry === 'function') refreshPantry();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user