This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
import { INGREDIENTS, RECIPES, PRODUCTS, getProductsForIngredient } from '../data/catalog.js?v=9';
|
||||
import { MEAL_SLOTS } from '../planner/mealSlots.js';
|
||||
import {
|
||||
addDays,
|
||||
addMonths,
|
||||
sameDay,
|
||||
sameMonth,
|
||||
startOfDay,
|
||||
startOfWeekMonday,
|
||||
} from '../services/dateUtils.js';
|
||||
@@ -17,14 +14,17 @@ import {
|
||||
import { dayHasAnyMeal, autoSelectProducts, saveLastProductSelection } from '../services/planIngredients.js?v=4';
|
||||
import { loadPantry } from '../services/pantryShopping.js?v=2';
|
||||
import {
|
||||
bindCollapsibleCalendarSwipeGesture,
|
||||
bindCalendarDayClicks,
|
||||
bindCalendarHorizontalSwipe,
|
||||
createCollapsibleCalendarHTML,
|
||||
createCalendarTopbarHTML,
|
||||
createCalendarWeekdayHeaderHTML,
|
||||
formatCalendarPeriodLabel,
|
||||
isCalendarOnToday,
|
||||
renderCalendarGrid,
|
||||
renderCollapsibleCalendar,
|
||||
syncCalendarTodayButton,
|
||||
} from './mealCalendar.js?v=14';
|
||||
syncCollapsibleCalendarMode,
|
||||
syncCollapsibleCalendarToggleIcon,
|
||||
} from './mealCalendar.js?v=15';
|
||||
import { createIngredientCardController, getIngredientCardHTML } from './ingredientCard.js?v=20260417-116';
|
||||
|
||||
function esc(s) {
|
||||
@@ -52,25 +52,38 @@ export function getMealPlanEditorHTML() {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mpe-cal-wrap" class="hidden relative z-[1] shrink-0 px-5 pt-3 pb-3 bg-[rgb(var(--app-bg-rgb))]" style="background:rgb(var(--app-bg-rgb)) !important; background-image:none !important;">
|
||||
<div id="mpe-cal-wrap" class="hidden relative z-[2] shrink-0 px-5 pt-3 pb-3 bg-[rgb(var(--app-bg-rgb))]" style="background:rgb(var(--app-bg-rgb)) !important; background-image:none !important;">
|
||||
<div id="mpe-top-shadow" class="pointer-events-none absolute inset-x-0 -bottom-3 h-3 opacity-0 transition-opacity duration-200" style="background:linear-gradient(to bottom, rgba(var(--overlay-rgb),0.12), rgba(var(--overlay-rgb),0.03), rgba(var(--overlay-rgb),0));"></div>
|
||||
<div id="mpe-cal-section" class="hidden">
|
||||
${createCalendarTopbarHTML({
|
||||
todayId: 'mpe-cal-today',
|
||||
wrapperClass: 'mb-2 flex items-center justify-end gap-3',
|
||||
})}
|
||||
${createCalendarWeekdayHeaderHTML()}
|
||||
<div id="mpe-cal-grid" class="grid grid-cols-7 gap-1.5"></div>
|
||||
<button id="mpe-cal-toggle" type="button" class="w-full flex items-center justify-center py-1 mt-1 text-gray-400 hover:text-gray-600 transition-colors"><i id="mpe-cal-toggle-icon" class="fas fa-chevron-down text-[10px]"></i></button>
|
||||
<p class="text-[10px] font-bold text-gray-400 uppercase tracking-wider mt-3 mb-2">Pora posiłku</p>
|
||||
<div id="mpe-slot-chips" class="flex flex-wrap gap-1.5"></div>
|
||||
${createCollapsibleCalendarHTML({
|
||||
idPrefix: 'mpe-cal',
|
||||
swipeZoneId: 'mpe-cal-swipe-zone',
|
||||
weekWrapId: 'mpe-cal-week-wrap',
|
||||
weekGridId: 'mpe-cal-week-grid',
|
||||
monthWrapId: 'mpe-cal-month-wrap',
|
||||
monthGridId: 'mpe-cal-month-grid',
|
||||
toggleId: 'mpe-cal-toggle',
|
||||
iconId: 'mpe-cal-toggle-icon',
|
||||
weekWrapClass: 'overflow-x-hidden bg-[rgb(var(--app-bg-rgb))]',
|
||||
monthWrapClass: 'bg-[rgb(var(--app-bg-rgb))]',
|
||||
weekWrapStyle: 'overflow: hidden; max-height: 10rem; opacity: 1; padding-bottom: 0.25rem',
|
||||
toggleClass: 'w-full flex items-center justify-center py-1 mt-1 text-gray-400 hover:text-gray-600 transition-colors',
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div id="mpe-summary-wrap" class="relative z-[1] shrink-0 px-5 pb-3 bg-[rgb(var(--app-bg-rgb))]" style="background:rgb(var(--app-bg-rgb)) !important; background-image:none !important;">
|
||||
<div id="mpe-nutrition-section"></div>
|
||||
<div id="mpe-servings-row" class="mt-3"></div>
|
||||
<div id="mpe-top-shadow" class="pointer-events-none absolute inset-x-0 -bottom-3 h-3 opacity-0 transition-opacity duration-200" style="background:linear-gradient(to bottom, rgba(var(--overlay-rgb),0.12), rgba(var(--overlay-rgb),0.03), rgba(var(--overlay-rgb),0));"></div>
|
||||
</div>
|
||||
<div id="mpe-ing-scroll" class="flex-1 min-h-0 overflow-y-auto no-scrollbar px-5 bg-[rgb(var(--app-bg-rgb))]" style="background:rgb(var(--app-bg-rgb)) !important; background-image:none !important; padding-bottom:calc(1.5rem + env(safe-area-inset-bottom));">
|
||||
<div id="mpe-slot-section" class="pt-3 pb-1 hidden">
|
||||
<p class="text-[10px] font-bold text-gray-400 uppercase tracking-wider mb-2">Pora posiłku</p>
|
||||
<div id="mpe-slot-chips" class="flex flex-wrap gap-1.5"></div>
|
||||
</div>
|
||||
<div id="mpe-summary-wrap" class="relative z-[1] pt-3 pb-3 bg-[rgb(var(--app-bg-rgb))]" style="background:rgb(var(--app-bg-rgb)) !important; background-image:none !important;">
|
||||
<div id="mpe-nutrition-section"></div>
|
||||
<div id="mpe-servings-row" class="mt-3"></div>
|
||||
</div>
|
||||
<div id="mpe-ing-section" class="mb-4">
|
||||
<p class="text-[10px] font-bold text-gray-400 uppercase tracking-wider mb-2">Składniki</p>
|
||||
<div id="mpe-ing-list" class="space-y-1.5"></div>
|
||||
@@ -155,6 +168,18 @@ export function setupMealPlanEditor() {
|
||||
|
||||
/* ── Calendar ──────────────────────────────────── */
|
||||
|
||||
function resolveCalendarDayState(day, meta, plans = loadPlans(), today = startOfDay(new Date())) {
|
||||
const isSelected = S.date && sameDay(day, S.date);
|
||||
const isPast = day.getTime() < today.getTime();
|
||||
return {
|
||||
disabled: isPast && !isSelected,
|
||||
dimmed: (isPast || (meta.mode === 'month' && !meta.inCurrentMonth)) && !isSelected,
|
||||
showIndicator: meta.mode === 'month'
|
||||
? meta.inCurrentMonth && dayHasAnyMeal(plans, day)
|
||||
: dayHasAnyMeal(plans, day),
|
||||
};
|
||||
}
|
||||
|
||||
function renderCal() {
|
||||
const wrap = document.getElementById('mpe-cal-wrap');
|
||||
const sec = document.getElementById('mpe-cal-section');
|
||||
@@ -169,45 +194,46 @@ export function setupMealPlanEditor() {
|
||||
}
|
||||
wrap.classList.remove('hidden');
|
||||
sec.classList.remove('hidden');
|
||||
const grid = document.getElementById('mpe-cal-grid');
|
||||
const weekGrid = document.getElementById('mpe-cal-week-grid');
|
||||
const monthGrid = document.getElementById('mpe-cal-month-grid');
|
||||
const todayBtn = document.getElementById('mpe-cal-today');
|
||||
const icon = document.getElementById('mpe-cal-toggle-icon');
|
||||
if (!grid) return;
|
||||
if (!weekGrid || !monthGrid) return;
|
||||
const today = startOfDay(new Date());
|
||||
const plans = loadPlans();
|
||||
const mode = S.calExpanded ? 'month' : 'week';
|
||||
|
||||
if (icon) {
|
||||
icon.className = S.calExpanded ? 'fas fa-chevron-up text-[10px]' : 'fas fa-chevron-down text-[10px]';
|
||||
}
|
||||
syncCollapsibleCalendarMode({
|
||||
mode,
|
||||
weekWrapEl: document.getElementById('mpe-cal-week-wrap'),
|
||||
monthWrapEl: document.getElementById('mpe-cal-month-wrap'),
|
||||
activePaddingBottom: '0.25rem',
|
||||
});
|
||||
syncCollapsibleCalendarToggleIcon(icon, mode);
|
||||
syncCalendarTodayButton(
|
||||
todayBtn,
|
||||
isCalendarOnToday(mode, startOfWeekMonday(S.calDate), S.calDate, S.date),
|
||||
S.date,
|
||||
{
|
||||
labelText: formatCalendarPeriodLabel(mode, S.calDate, S.calDate),
|
||||
},
|
||||
);
|
||||
|
||||
renderCalendarGrid({
|
||||
gridEl: grid,
|
||||
mode,
|
||||
anchorDate: S.calDate,
|
||||
renderCollapsibleCalendar({
|
||||
weekGridEl: weekGrid,
|
||||
monthGridEl: monthGrid,
|
||||
weekAnchorDate: S.calDate,
|
||||
monthAnchorDate: S.calDate,
|
||||
selectedDate: S.date,
|
||||
resolveDayState: (day, meta) => {
|
||||
const isSelected = S.date && sameDay(day, S.date);
|
||||
const isPast = day.getTime() < today.getTime();
|
||||
return {
|
||||
disabled: isPast && !isSelected,
|
||||
dimmed: (isPast || (meta.mode === 'month' && !meta.inCurrentMonth)) && !isSelected,
|
||||
showIndicator: meta.mode === 'month'
|
||||
? meta.inCurrentMonth && dayHasAnyMeal(plans, day)
|
||||
: dayHasAnyMeal(plans, day),
|
||||
};
|
||||
},
|
||||
resolveDayState: (day, meta) => resolveCalendarDayState(day, meta, plans, today),
|
||||
});
|
||||
syncScrollShadows();
|
||||
}
|
||||
|
||||
function renderSlots() {
|
||||
const el = document.getElementById('mpe-slot-chips');
|
||||
const sec = document.getElementById('mpe-slot-section');
|
||||
if (sec) sec.classList.toggle('hidden', !S.showCal);
|
||||
if (!el || !S.showCal) return;
|
||||
const r = RECIPES[S.recipeId];
|
||||
if (!r) return;
|
||||
@@ -611,50 +637,39 @@ export function setupMealPlanEditor() {
|
||||
});
|
||||
}
|
||||
document.getElementById('mpe-confirm-btn')?.addEventListener('click', handleConfirm);
|
||||
bindCalendarDayClicks(document.getElementById('mpe-cal-grid'), (date) => {
|
||||
const selectCalendarDate = (date) => {
|
||||
S.date = date;
|
||||
S.calDate = new Date(date);
|
||||
renderCal();
|
||||
});
|
||||
};
|
||||
|
||||
bindCalendarHorizontalSwipe(document.getElementById('mpe-cal-grid'), {
|
||||
onPrev: () => {
|
||||
if (!S.showCal) return;
|
||||
S.calDate = S.calExpanded ? addMonths(S.calDate, -1) : addDays(S.calDate, -7);
|
||||
renderCal();
|
||||
bindCalendarDayClicks(document.getElementById('mpe-cal-week-grid'), selectCalendarDate);
|
||||
bindCalendarDayClicks(document.getElementById('mpe-cal-month-grid'), selectCalendarDate);
|
||||
|
||||
bindCollapsibleCalendarSwipeGesture({
|
||||
zoneEl: document.getElementById('mpe-cal-swipe-zone'),
|
||||
weekWrapEl: document.getElementById('mpe-cal-week-wrap'),
|
||||
monthWrapEl: document.getElementById('mpe-cal-month-wrap'),
|
||||
getMode: () => (S.calExpanded ? 'month' : 'week'),
|
||||
setMode: (mode) => {
|
||||
S.calExpanded = mode === 'month';
|
||||
},
|
||||
onNext: () => {
|
||||
if (!S.showCal) return;
|
||||
S.calDate = S.calExpanded ? addMonths(S.calDate, 1) : addDays(S.calDate, 7);
|
||||
renderCal();
|
||||
getWeekAnchor: () => S.calDate,
|
||||
setWeekAnchor: (date) => {
|
||||
S.calDate = startOfDay(date);
|
||||
},
|
||||
renderGhost: (ghostGrid, direction) => {
|
||||
if (!S.showCal) return false;
|
||||
const sign = direction === 'prev' ? -1 : 1;
|
||||
const mode = S.calExpanded ? 'month' : 'week';
|
||||
const anchor = S.calExpanded
|
||||
? addMonths(S.calDate, sign)
|
||||
: addDays(S.calDate, 7 * sign);
|
||||
const today = startOfDay(new Date());
|
||||
const plans = loadPlans();
|
||||
renderCalendarGrid({
|
||||
gridEl: ghostGrid,
|
||||
mode,
|
||||
anchorDate: anchor,
|
||||
selectedDate: S.date,
|
||||
resolveDayState: (day, meta) => {
|
||||
const isSelected = S.date && sameDay(day, S.date);
|
||||
const isPast = day.getTime() < today.getTime();
|
||||
return {
|
||||
disabled: isPast && !isSelected,
|
||||
dimmed: (isPast || (meta.mode === 'month' && !meta.inCurrentMonth)) && !isSelected,
|
||||
showIndicator: meta.mode === 'month'
|
||||
? meta.inCurrentMonth && dayHasAnyMeal(plans, day)
|
||||
: dayHasAnyMeal(plans, day),
|
||||
};
|
||||
},
|
||||
});
|
||||
getMonthAnchor: () => S.calDate,
|
||||
setMonthAnchor: (date) => {
|
||||
S.calDate = startOfDay(date);
|
||||
},
|
||||
getSelectedDate: () => S.date,
|
||||
setSelectedDate: (date) => {
|
||||
S.date = startOfDay(date);
|
||||
},
|
||||
rerender: renderCal,
|
||||
resolveDayState: (day, meta) => resolveCalendarDayState(day, meta),
|
||||
selectOnNavigateOutside: false,
|
||||
enableVerticalModeSwipe: false,
|
||||
});
|
||||
document.getElementById('mpe-cal-today')?.addEventListener('click', () => {
|
||||
const today = startOfDay(new Date());
|
||||
@@ -824,7 +839,10 @@ export function setupMealPlanEditor() {
|
||||
if (e.target.closest('#mpe-add-btn')) {
|
||||
S.addOpen = true; S.addQuery = '';
|
||||
renderAddArea();
|
||||
document.getElementById('mpe-add-search')?.focus();
|
||||
requestAnimationFrame(() => {
|
||||
document.getElementById('mpe-add-area')?.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
||||
document.getElementById('mpe-add-search')?.focus({ preventScroll: true });
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user