This commit is contained in:
66
index.html
66
index.html
@@ -23,46 +23,46 @@
|
|||||||
:root {
|
:root {
|
||||||
color-scheme: light;
|
color-scheme: light;
|
||||||
--app-font: 'Plus Jakarta Sans', 'Segoe UI', sans-serif;
|
--app-font: 'Plus Jakarta Sans', 'Segoe UI', sans-serif;
|
||||||
--app-bg-rgb: 243, 239, 233; /* #f3efe9 — warm cream base */
|
--app-bg-rgb: 226, 242, 241; /* #e2f2f1 — cool mint base */
|
||||||
--surface-rgb: 255, 255, 255; /* #ffffff — white panels (existing tokens) */
|
--surface-rgb: 255, 255, 255; /* #ffffff — white panels */
|
||||||
--surface-soft-rgb: 249, 245, 238; /* #f9f5ee */
|
--surface-soft-rgb: 235, 246, 245; /* #ebf6f5 */
|
||||||
--surface-strong-rgb: 235, 229, 220; /* #ebe5dc */
|
--surface-strong-rgb: 215, 234, 232; /* #d7eae8 */
|
||||||
--line-rgb: 69, 58, 48; /* #453a30 — divider base (warm brown) */
|
--line-rgb: 40, 58, 58; /* #283a3a — cool neutral divider base */
|
||||||
--text-primary-rgb: 28, 24, 21; /* #1c1815 */
|
--text-primary-rgb: 26, 31, 31; /* #1a1f1f */
|
||||||
--text-secondary-rgb: 102, 92, 83; /* #665c53 */
|
--text-secondary-rgb: 92, 100, 100; /* #5c6464 */
|
||||||
--text-tertiary-rgb: 140, 130, 118; /* #8c8276 */
|
--text-tertiary-rgb: 125, 133, 133; /* #7d8585 */
|
||||||
--warm-rgb: 183, 142, 88; /* #b78e58 — caramel */
|
--warm-rgb: 183, 142, 88; /* #b78e58 — caramel */
|
||||||
--success-rgb: 47, 133, 82; /* #2f8552 — deep emerald (AA on white) */
|
--success-rgb: 47, 133, 82; /* #2f8552 — deep emerald (harmonizes with mint) */
|
||||||
--danger-rgb: 187, 75, 75; /* #bb4b4b — deep rose (AA on white) */
|
--danger-rgb: 187, 75, 75; /* #bb4b4b — deep rose */
|
||||||
--panel-shadow: 0 18px 40px rgba(24, 17, 11, 0.08);
|
--panel-shadow: 0 18px 40px rgba(20, 35, 35, 0.08);
|
||||||
--panel-shadow-strong: 0 24px 60px rgba(24, 17, 11, 0.16);
|
--panel-shadow-strong: 0 24px 60px rgba(20, 35, 35, 0.16);
|
||||||
--dock-shadow: 0 24px 56px rgba(24, 17, 11, 0.18);
|
--dock-shadow: 0 24px 56px rgba(20, 35, 35, 0.18);
|
||||||
--highlight-top: transparent;
|
--highlight-top: transparent;
|
||||||
--highlight-bottom: transparent;
|
--highlight-bottom: transparent;
|
||||||
|
|
||||||
/* Extended light palette — mirrors dark hierarchy: sunken < app-bg < card. */
|
/* Extended light palette — mirrors dark hierarchy: sunken < app-bg < card. */
|
||||||
--card-rgb: 255, 255, 255; /* #ffffff — primary elevated card (raised via shadow) */
|
--card-rgb: 255, 255, 255; /* #ffffff — primary elevated card (raised via shadow) */
|
||||||
--card-soft-rgb: 239, 233, 221; /* #efe9dd — nested/medium surface on white */
|
--card-soft-rgb: 220, 238, 236; /* #dceeec — nested/medium surface on white */
|
||||||
--card-strong-rgb: 228, 219, 205; /* #e4dbcd — strong surface / stroke */
|
--card-strong-rgb: 206, 228, 225; /* #cee4e1 — strong surface / stroke */
|
||||||
--card-raised-rgb: 252, 249, 242; /* #fcf9f2 — subtle raise (chip on card) */
|
--card-raised-rgb: 244, 250, 250; /* #f4fafa — subtle raise (chip on card) */
|
||||||
--sunken-rgb: 235, 229, 217; /* #ebe5d9 — deep inputs / search shell */
|
--sunken-rgb: 213, 232, 230; /* #d5e8e6 — deep inputs / search shell */
|
||||||
--sunken-deep-rgb: 219, 211, 196; /* #dbd3c4 — deepest well */
|
--sunken-deep-rgb: 193, 220, 218; /* #c1dcda — deepest well */
|
||||||
--border-card-rgb: 224, 215, 200; /* #e0d7c8 — subtle card/dock border */
|
--border-card-rgb: 205, 225, 222; /* #cde1de — subtle card/dock border */
|
||||||
--border-input-rgb: 180, 168, 150; /* #b4a896 — input / active border */
|
--border-input-rgb: 148, 180, 177; /* #94b4b1 — input / active border */
|
||||||
--text-emphasis-rgb: 14, 12, 10; /* #0e0c0a — brightest interactive text */
|
--text-emphasis-rgb: 12, 18, 18; /* #0c1212 — brightest interactive text */
|
||||||
--text-body-rgb: 48, 40, 33; /* #302821 — primary body text (most common) */
|
--text-body-rgb: 42, 48, 48; /* #2a3030 — primary body text (most common) */
|
||||||
--text-body-soft-rgb: 72, 62, 52; /* #483e34 — body soft */
|
--text-body-soft-rgb: 63, 70, 70; /* #3f4646 — body soft */
|
||||||
--text-muted-rgb: 102, 92, 83; /* #665c53 — muted (matches --text-secondary) */
|
--text-muted-rgb: 92, 100, 100; /* #5c6464 — muted (matches --text-secondary) */
|
||||||
--text-dim-rgb: 140, 130, 118; /* #8c8276 — dim / placeholder */
|
--text-dim-rgb: 125, 133, 133; /* #7d8585 — dim / placeholder */
|
||||||
--text-faint-rgb: 156, 146, 137; /* #9c9289 — faint icon color */
|
--text-faint-rgb: 143, 150, 150; /* #8f9696 — faint icon color */
|
||||||
--text-subdued-rgb: 176, 166, 154; /* #b0a69a — subdued */
|
--text-subdued-rgb: 163, 169, 169; /* #a3a9a9 — subdued */
|
||||||
--skeleton-rgb: 226, 220, 210; /* #e2dcd2 — image loading placeholder */
|
--skeleton-rgb: 214, 228, 226; /* #d6e4e2 — image loading placeholder */
|
||||||
--on-accent-rgb: 255, 255, 255; /* white text on accent backgrounds */
|
--on-accent-rgb: 255, 255, 255; /* white text on accent backgrounds */
|
||||||
--overlay-rgb: 24, 17, 11; /* #18110b — scrim / shadow base (warm brown-black) */
|
--overlay-rgb: 20, 35, 35; /* #142323 — scrim / shadow base (cool dark) */
|
||||||
--shadow-card: 0 2px 8px rgba(24, 17, 11, 0.08);
|
--shadow-card: 0 2px 8px rgba(20, 35, 35, 0.08);
|
||||||
--shadow-shell: 0 5px 10px rgba(24, 17, 11, 0.08), 0 14px 22px rgba(24, 17, 11, 0.12), 0 22px 34px rgba(24, 17, 11, 0.10), inset 0 1px 0 rgba(255, 255, 255, 0.5);
|
--shadow-shell: 0 5px 10px rgba(20, 35, 35, 0.08), 0 14px 22px rgba(20, 35, 35, 0.12), 0 22px 34px rgba(20, 35, 35, 0.10), inset 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||||
--icon-watermark: rgba(24, 17, 11, 0.08);
|
--icon-watermark: rgba(20, 35, 35, 0.08);
|
||||||
--hover-overlay: rgba(24, 17, 11, 0.05);
|
--hover-overlay: rgba(20, 35, 35, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
|
|||||||
@@ -37,11 +37,11 @@ function getCalendarDayHTML(day, meta, dayState, dayAttr, theme = {}) {
|
|||||||
let borderClass = 'border';
|
let borderClass = 'border';
|
||||||
|
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
bg = theme.selectedBg || 'rgb(var(--sunken-rgb))';
|
bg = theme.selectedBg || 'rgb(var(--card-rgb))';
|
||||||
borderColor = theme.selectedBorder || 'rgb(var(--border-input-rgb))';
|
borderColor = theme.selectedBorder || 'rgb(var(--border-input-rgb))';
|
||||||
text = theme.selectedText || 'rgb(var(--text-emphasis-rgb))';
|
text = theme.selectedText || 'rgb(var(--text-emphasis-rgb))';
|
||||||
} else if (isDimmed) {
|
} else if (isDimmed) {
|
||||||
bg = theme.dimmedBg ?? theme.bg ?? defaultBg;
|
bg = theme.dimmedBg ?? 'transparent';
|
||||||
text = theme.dimText || 'rgb(var(--text-faint-rgb))';
|
text = theme.dimText || 'rgb(var(--text-faint-rgb))';
|
||||||
borderClass = 'border-0';
|
borderClass = 'border-0';
|
||||||
} else {
|
} else {
|
||||||
@@ -52,7 +52,7 @@ function getCalendarDayHTML(day, meta, dayState, dayAttr, theme = {}) {
|
|||||||
|
|
||||||
const dot = isSelected ? (theme.selectedDot || 'rgb(var(--text-emphasis-rgb))') : (theme.dot || 'rgb(var(--text-faint-rgb))');
|
const dot = isSelected ? (theme.selectedDot || 'rgb(var(--text-emphasis-rgb))') : (theme.dot || 'rgb(var(--text-faint-rgb))');
|
||||||
const opacity = isDimmed ? String(theme.dimOpacity ?? 0.72) : '1';
|
const opacity = isDimmed ? String(theme.dimOpacity ?? 0.72) : '1';
|
||||||
const borderStyle = isDimmed ? 'border:none;' : `border-color:${borderColor};`;
|
const borderStyle = borderColor ? `border-color:${borderColor};` : 'border:none;';
|
||||||
const outerClass = `${mode === 'month' ? 'mx-auto ' : ''}flex h-[2.05rem] w-full min-w-0 max-w-full items-center justify-center rounded-full ${borderClass} text-xs font-medium transition-colors leading-tight overflow-hidden`;
|
const outerClass = `${mode === 'month' ? 'mx-auto ' : ''}flex h-[2.05rem] w-full min-w-0 max-w-full items-center justify-center rounded-full ${borderClass} text-xs font-medium transition-colors leading-tight overflow-hidden`;
|
||||||
const innerClass = mode === 'month'
|
const innerClass = mode === 'month'
|
||||||
? 'relative flex h-full w-full flex-col items-center justify-center'
|
? 'relative flex h-full w-full flex-col items-center justify-center'
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { MEAL_SLOTS } from '../planner/mealSlots.js';
|
|||||||
import {
|
import {
|
||||||
addMonths,
|
addMonths,
|
||||||
addWeeks,
|
addWeeks,
|
||||||
|
sameDay,
|
||||||
sameMonth,
|
sameMonth,
|
||||||
startOfDay,
|
startOfDay,
|
||||||
startOfMonth,
|
startOfMonth,
|
||||||
@@ -90,9 +91,9 @@ export function getMealPlannerHTML() {
|
|||||||
${createCalendarWeekdayHeaderHTML()}
|
${createCalendarWeekdayHeaderHTML()}
|
||||||
<div id="calendar-month-grid" class="grid grid-cols-7 gap-1.5"></div>
|
<div id="calendar-month-grid" class="grid grid-cols-7 gap-1.5"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="calendar-drag-handle" class="flex items-center justify-center pb-2 pt-0.5">
|
<button id="calendar-mode-toggle" type="button" class="w-full flex items-center justify-center py-1 pb-2 pt-0.5 text-[rgb(var(--text-faint-rgb))] hover:text-[rgb(var(--text-body-soft-rgb))] transition-colors" aria-label="Przełącz widok kalendarza">
|
||||||
<span id="calendar-handle-icon" class="${CALENDAR_HANDLE_CLASS}" aria-hidden="true"></span>
|
<i id="calendar-handle-icon" class="fas fa-chevron-down text-[10px]"></i>
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -191,8 +192,9 @@ function syncModeToggle(mode) {
|
|||||||
mode,
|
mode,
|
||||||
weekWrapEl: document.getElementById('calendar-week-wrap'),
|
weekWrapEl: document.getElementById('calendar-week-wrap'),
|
||||||
monthWrapEl: document.getElementById('calendar-month-wrap'),
|
monthWrapEl: document.getElementById('calendar-month-wrap'),
|
||||||
handleEl: document.getElementById('calendar-handle-icon'),
|
|
||||||
});
|
});
|
||||||
|
const icon = document.getElementById('calendar-handle-icon');
|
||||||
|
if (icon) icon.className = mode === 'month' ? 'fas fa-chevron-up text-[10px]' : 'fas fa-chevron-down text-[10px]';
|
||||||
}
|
}
|
||||||
|
|
||||||
function bindCalendarSwipeGesture(state, rerender) {
|
function bindCalendarSwipeGesture(state, rerender) {
|
||||||
@@ -1131,18 +1133,24 @@ export function setupMealPlanner() {
|
|||||||
const rerender = () => {
|
const rerender = () => {
|
||||||
syncModeToggle(state.mode);
|
syncModeToggle(state.mode);
|
||||||
syncTodayButton(state.mode, state.weekStart, state.monthAnchor, state.selected);
|
syncTodayButton(state.mode, state.weekStart, state.monthAnchor, state.selected);
|
||||||
|
const today = startOfDay(new Date());
|
||||||
renderCollapsibleCalendar({
|
renderCollapsibleCalendar({
|
||||||
weekGridEl: weekGrid,
|
weekGridEl: weekGrid,
|
||||||
monthGridEl: monthGrid,
|
monthGridEl: monthGrid,
|
||||||
weekAnchorDate: state.weekStart,
|
weekAnchorDate: state.weekStart,
|
||||||
monthAnchorDate: state.monthAnchor,
|
monthAnchorDate: state.monthAnchor,
|
||||||
selectedDate: state.selected,
|
selectedDate: state.selected,
|
||||||
resolveDayState: (day, meta) => ({
|
resolveDayState: (day, meta) => {
|
||||||
dimmed: meta.mode === 'month' && !meta.inCurrentMonth,
|
const isSelected = sameDay(day, state.selected);
|
||||||
showIndicator: meta.mode === 'month'
|
const isPast = day.getTime() < today.getTime();
|
||||||
? meta.inCurrentMonth && dayHasAnyMeal(state.plans, day)
|
return {
|
||||||
: dayHasAnyMeal(state.plans, day),
|
disabled: isPast && !isSelected,
|
||||||
}),
|
dimmed: (isPast || (meta.mode === 'month' && !meta.inCurrentMonth)) && !isSelected,
|
||||||
|
showIndicator: meta.mode === 'month'
|
||||||
|
? meta.inCurrentMonth && dayHasAnyMeal(state.plans, day)
|
||||||
|
: dayHasAnyMeal(state.plans, day),
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
renderDayContent(state, persist);
|
renderDayContent(state, persist);
|
||||||
};
|
};
|
||||||
@@ -1422,6 +1430,17 @@ export function setupMealPlanner() {
|
|||||||
|
|
||||||
bindCalendarSwipeGesture(state, rerender);
|
bindCalendarSwipeGesture(state, rerender);
|
||||||
|
|
||||||
|
document.getElementById('calendar-mode-toggle')?.addEventListener('click', () => {
|
||||||
|
if (state.mode === 'week') {
|
||||||
|
state.mode = 'month';
|
||||||
|
state.monthAnchor = startOfMonth(state.selected);
|
||||||
|
} else {
|
||||||
|
state.mode = 'week';
|
||||||
|
state.weekStart = startOfWeekMonday(state.selected);
|
||||||
|
}
|
||||||
|
rerender();
|
||||||
|
});
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
const ww = document.getElementById('calendar-week-wrap');
|
const ww = document.getElementById('calendar-week-wrap');
|
||||||
const mw = document.getElementById('calendar-month-wrap');
|
const mw = document.getElementById('calendar-month-wrap');
|
||||||
|
|||||||
@@ -726,7 +726,8 @@ export function setupPantry() {
|
|||||||
document.getElementById('pantry-search-toggle')?.addEventListener('click', () => openSearch());
|
document.getElementById('pantry-search-toggle')?.addEventListener('click', () => openSearch());
|
||||||
document.getElementById('pantry-search-close')?.addEventListener('click', () => closeSearch());
|
document.getElementById('pantry-search-close')?.addEventListener('click', () => closeSearch());
|
||||||
document.getElementById('pantry-filter-toggle')?.addEventListener('click', () => toggleFilterPanel());
|
document.getElementById('pantry-filter-toggle')?.addEventListener('click', () => toggleFilterPanel());
|
||||||
document.getElementById('pantry-filter-clear')?.addEventListener('click', () => {
|
document.getElementById('pantry-filter-clear')?.addEventListener('click', (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
pantryFilters = { categories: [], sections: [] };
|
pantryFilters = { categories: [], sections: [] };
|
||||||
syncHorizonUI();
|
syncHorizonUI();
|
||||||
renderBoard();
|
renderBoard();
|
||||||
@@ -738,6 +739,8 @@ export function setupPantry() {
|
|||||||
const chip = target.closest('[data-pantry-filter-kind]');
|
const chip = target.closest('[data-pantry-filter-kind]');
|
||||||
if (!(chip instanceof Element)) return;
|
if (!(chip instanceof Element)) return;
|
||||||
|
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
const kind = chip.getAttribute('data-pantry-filter-kind');
|
const kind = chip.getAttribute('data-pantry-filter-kind');
|
||||||
const value = chip.getAttribute('data-pantry-filter-value');
|
const value = chip.getAttribute('data-pantry-filter-value');
|
||||||
if (!kind || !value) return;
|
if (!kind || !value) return;
|
||||||
|
|||||||
Reference in New Issue
Block a user