Design changes to pantry
All checks were successful
Build and Deploy / build-and-push (push) Successful in 27s
All checks were successful
Build and Deploy / build-and-push (push) Successful in 27s
This commit is contained in:
@@ -81,6 +81,7 @@ export function setupBottomNav({ refreshPantry, refreshShoppingList } = {}) {
|
|||||||
nav.style.setProperty('--recipe-dock-width', `${dockWidth}px`);
|
nav.style.setProperty('--recipe-dock-width', `${dockWidth}px`);
|
||||||
nav.style.setProperty('--recipe-collapsed-dock-width', `${collapsedDockWidth}px`);
|
nav.style.setProperty('--recipe-collapsed-dock-width', `${collapsedDockWidth}px`);
|
||||||
nav.style.setProperty('--recipe-toggle-size', `${collapsedSlotWidth}px`);
|
nav.style.setProperty('--recipe-toggle-size', `${collapsedSlotWidth}px`);
|
||||||
|
document.documentElement.style.setProperty('--recipe-dock-width', `${dockWidth}px`);
|
||||||
document.documentElement.style.setProperty('--catalog-menu-left', `${dockLeft}px`);
|
document.documentElement.style.setProperty('--catalog-menu-left', `${dockLeft}px`);
|
||||||
document.documentElement.style.setProperty('--catalog-menu-width', `${collapsedDockWidth}px`);
|
document.documentElement.style.setProperty('--catalog-menu-width', `${collapsedDockWidth}px`);
|
||||||
document.documentElement.style.setProperty('--catalog-filter-left', `${filterLeft}px`);
|
document.documentElement.style.setProperty('--catalog-filter-left', `${filterLeft}px`);
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
const STYLE_ID = 'calendar-popover-liquid-styles';
|
||||||
const DEFAULT_POPOVER_CLASS = 'absolute left-0 right-0 top-full mt-2 z-[50] transition-all duration-200 pointer-events-none';
|
const DEFAULT_POPOVER_CLASS = 'absolute left-0 right-0 top-full mt-2 z-[50] transition-all duration-200 pointer-events-none';
|
||||||
const DEFAULT_POPOVER_STYLE = 'opacity:0; transform:translateY(-6px) scale(0.98);';
|
const DEFAULT_POPOVER_STYLE = 'opacity:0; transform:translateY(-6px) scale(0.98);';
|
||||||
const DEFAULT_PANEL_CLASS = 'rounded-[1.35rem] py-3';
|
const DEFAULT_PANEL_CLASS = 'calendar-liquid-panel rounded-[1.35rem] py-3';
|
||||||
const DEFAULT_PANEL_STYLE = 'background:rgb(var(--sunken-rgb)); border:1px solid rgb(var(--border-input-rgb)); box-shadow:var(--shadow-shell);';
|
const DEFAULT_PANEL_STYLE = 'background-image:none !important;';
|
||||||
|
|
||||||
const DEFAULT_OPEN_TRIGGER_STYLE = {
|
const DEFAULT_OPEN_TRIGGER_STYLE = {
|
||||||
background: 'rgb(var(--sunken-rgb))',
|
background: 'rgb(var(--sunken-rgb))',
|
||||||
@@ -13,6 +14,101 @@ const DEFAULT_CLOSED_TRIGGER_STYLE = {
|
|||||||
borderColor: 'rgb(var(--border-card-rgb))',
|
borderColor: 'rgb(var(--border-card-rgb))',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function ensureCalendarPopoverStyles() {
|
||||||
|
if (typeof document === 'undefined') return;
|
||||||
|
if (document.getElementById(STYLE_ID)) return;
|
||||||
|
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.id = STYLE_ID;
|
||||||
|
style.textContent = `
|
||||||
|
.calendar-liquid-panel {
|
||||||
|
background: rgba(255, 255, 255, 0.2) !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.32) !important;
|
||||||
|
box-shadow:
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.6),
|
||||||
|
inset 0 -1px 0 rgba(var(--overlay-rgb), 0.06),
|
||||||
|
0 8px 20px rgba(var(--overlay-rgb), 0.14),
|
||||||
|
0 18px 38px rgba(var(--overlay-rgb), 0.18) !important;
|
||||||
|
backdrop-filter: blur(28px) saturate(180%);
|
||||||
|
-webkit-backdrop-filter: blur(28px) saturate(180%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .calendar-liquid-panel {
|
||||||
|
background: rgba(255, 255, 255, 0.04) !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.12) !important;
|
||||||
|
box-shadow:
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.24),
|
||||||
|
inset 0 -1px 0 rgba(0, 0, 0, 0.22),
|
||||||
|
0 10px 24px rgba(0, 0, 0, 0.3),
|
||||||
|
0 24px 54px rgba(0, 0, 0, 0.38) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-liquid-btn {
|
||||||
|
background: rgba(255, 255, 255, 0.16) !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.24) !important;
|
||||||
|
box-shadow:
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.45),
|
||||||
|
inset 0 -1px 0 rgba(var(--overlay-rgb), 0.08),
|
||||||
|
0 6px 14px rgba(var(--overlay-rgb), 0.14) !important;
|
||||||
|
backdrop-filter: blur(22px) saturate(165%);
|
||||||
|
-webkit-backdrop-filter: blur(22px) saturate(165%);
|
||||||
|
transition: background 180ms ease, border-color 180ms ease, transform 180ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-liquid-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.22) !important;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .calendar-liquid-btn {
|
||||||
|
background: rgba(255, 255, 255, 0.06) !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.12) !important;
|
||||||
|
box-shadow:
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.2),
|
||||||
|
inset 0 -1px 0 rgba(0, 0, 0, 0.2),
|
||||||
|
0 8px 18px rgba(0, 0, 0, 0.28) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .calendar-liquid-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-liquid-btn input,
|
||||||
|
.calendar-liquid-btn input:focus,
|
||||||
|
.calendar-liquid-btn input:active {
|
||||||
|
background: transparent !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
background-image: none !important;
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
outline: none !important;
|
||||||
|
appearance: none !important;
|
||||||
|
-webkit-appearance: none !important;
|
||||||
|
-moz-appearance: textfield !important;
|
||||||
|
backdrop-filter: none !important;
|
||||||
|
-webkit-backdrop-filter: none !important;
|
||||||
|
filter: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-liquid-btn input[type='number']::-webkit-outer-spin-button,
|
||||||
|
.calendar-liquid-btn input[type='number']::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-liquid-btn input:-webkit-autofill,
|
||||||
|
.calendar-liquid-btn input:-webkit-autofill:hover,
|
||||||
|
.calendar-liquid-btn input:-webkit-autofill:focus {
|
||||||
|
-webkit-text-fill-color: rgb(var(--text-body-rgb));
|
||||||
|
-webkit-box-shadow: 0 0 0 1000px transparent inset !important;
|
||||||
|
box-shadow: 0 0 0 1000px transparent inset !important;
|
||||||
|
transition: background-color 9999s ease-in-out 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
`;
|
||||||
|
document.head.appendChild(style);
|
||||||
|
}
|
||||||
|
|
||||||
function byId(idOrElement) {
|
function byId(idOrElement) {
|
||||||
if (!idOrElement) return null;
|
if (!idOrElement) return null;
|
||||||
if (typeof idOrElement === 'string') return document.getElementById(idOrElement);
|
if (typeof idOrElement === 'string') return document.getElementById(idOrElement);
|
||||||
@@ -40,6 +136,7 @@ export function createCalendarPopoverHTML({
|
|||||||
panelStyle = DEFAULT_PANEL_STYLE,
|
panelStyle = DEFAULT_PANEL_STYLE,
|
||||||
wrapInPanel = true,
|
wrapInPanel = true,
|
||||||
}) {
|
}) {
|
||||||
|
ensureCalendarPopoverStyles();
|
||||||
const body = wrapInPanel
|
const body = wrapInPanel
|
||||||
? `<div class="${panelClass}" style="${panelStyle}">${calendarHTML}</div>`
|
? `<div class="${panelClass}" style="${panelStyle}">${calendarHTML}</div>`
|
||||||
: calendarHTML;
|
: calendarHTML;
|
||||||
|
|||||||
@@ -48,6 +48,14 @@ export function ensureFilterPopoverStyles() {
|
|||||||
backdrop-filter: blur(28px) saturate(180%);
|
backdrop-filter: blur(28px) saturate(180%);
|
||||||
-webkit-backdrop-filter: blur(28px) saturate(180%);
|
-webkit-backdrop-filter: blur(28px) saturate(180%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.filter-liquid-surface.filter-liquid-panel-soft {
|
||||||
|
--filter-liquid-panel-bg: rgba(var(--app-bg-rgb), 0.52);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .filter-liquid-surface.filter-liquid-panel-soft {
|
||||||
|
--filter-liquid-panel-bg: rgba(var(--app-bg-rgb), 0.6);
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
updateKitchenItemAmount,
|
updateKitchenItemAmount,
|
||||||
} from '../services/pantryShopping.js?v=2';
|
} from '../services/pantryShopping.js?v=2';
|
||||||
import { showAppToast } from './toast.js';
|
import { showAppToast } from './toast.js';
|
||||||
|
import { ensureCalendarPopoverStyles } from './calendarPopover.js';
|
||||||
|
|
||||||
const CATEGORY_ICONS = {
|
const CATEGORY_ICONS = {
|
||||||
pieczywo: 'fa-bread-slice',
|
pieczywo: 'fa-bread-slice',
|
||||||
@@ -79,7 +80,7 @@ function mediaHtml(image, icon, sizeClass = 'w-9 h-9', radiusClass = 'rounded-lg
|
|||||||
const fit = image.endsWith('.svg') ? 'object-contain' : 'object-cover';
|
const fit = image.endsWith('.svg') ? 'object-contain' : 'object-cover';
|
||||||
return `<img src="${esc(image)}" alt="" class="${sizeClass} ${radiusClass} ${fit} shrink-0">`;
|
return `<img src="${esc(image)}" alt="" class="${sizeClass} ${radiusClass} ${fit} shrink-0">`;
|
||||||
}
|
}
|
||||||
return `<div class="${sizeClass} ${radiusClass} flex items-center justify-center shrink-0" style="background:rgb(var(--card-soft-rgb));"><i class="fas ${icon} text-sm" style="color:rgb(var(--text-faint-rgb));"></i></div>`;
|
return `<div class="${sizeClass} ${radiusClass} flex items-center justify-center shrink-0" style="background:transparent;"><i class="fas ${icon} text-sm" style="color:rgb(var(--text-faint-rgb));"></i></div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function compactMetaText(text, tone = 'default') {
|
function compactMetaText(text, tone = 'default') {
|
||||||
@@ -123,6 +124,11 @@ function formatPackCount(amount, packSize) {
|
|||||||
return `${formatPreciseQty((Number(amount) || 0) / Number(packSize))} opak.`;
|
return `${formatPreciseQty((Number(amount) || 0) / Number(packSize))} opak.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseQtyInput(value) {
|
||||||
|
const normalized = String(value ?? '').trim().replace(',', '.');
|
||||||
|
return normalizeQty(Number(normalized) || 0);
|
||||||
|
}
|
||||||
|
|
||||||
function getQtyStepMeta(def, product = null) {
|
function getQtyStepMeta(def, product = null) {
|
||||||
const productPackSize = Number(product?.packSize);
|
const productPackSize = Number(product?.packSize);
|
||||||
if (Number.isFinite(productPackSize) && productPackSize > 0) {
|
if (Number.isFinite(productPackSize) && productPackSize > 0) {
|
||||||
@@ -152,17 +158,18 @@ function getQtyStepMeta(def, product = null) {
|
|||||||
export function getIngredientCardHTML({
|
export function getIngredientCardHTML({
|
||||||
idBase,
|
idBase,
|
||||||
overlayClass = 'fixed inset-0 z-[70] hidden opacity-0 transition-opacity duration-200 flex items-center justify-center p-5',
|
overlayClass = 'fixed inset-0 z-[70] hidden opacity-0 transition-opacity duration-200 flex items-center justify-center p-5',
|
||||||
overlayStyle = 'pointer-events:none; background:rgba(var(--overlay-rgb),0.5);',
|
overlayStyle = 'pointer-events:none;',
|
||||||
cardClass = 'relative w-full max-w-xs rounded-2xl shadow-2xl overflow-hidden',
|
cardClass = 'calendar-liquid-panel relative w-full max-w-xs rounded-2xl overflow-hidden',
|
||||||
cardStyle = 'background:rgb(var(--app-bg-rgb)); pointer-events:auto; max-height:85vh; overflow-y:auto; transform:translateY(0.75rem); opacity:0; transition:transform 220ms ease, opacity 220ms ease;',
|
cardStyle = 'pointer-events:auto; max-height:85vh; overflow-y:auto; transform:translateY(0.75rem); opacity:0; transition:transform 220ms ease, opacity 220ms ease;',
|
||||||
} = {}) {
|
} = {}) {
|
||||||
if (!idBase) throw new Error('getIngredientCardHTML requires idBase');
|
if (!idBase) throw new Error('getIngredientCardHTML requires idBase');
|
||||||
|
ensureCalendarPopoverStyles();
|
||||||
return `
|
return `
|
||||||
<div id="${idBase}-overlay" class="${overlayClass}" style="${overlayStyle}">
|
<div id="${idBase}-overlay" class="${overlayClass}" style="${overlayStyle}">
|
||||||
<div id="${idBase}" class="${cardClass}" style="${cardStyle}">
|
<div id="${idBase}" class="${cardClass}" style="${cardStyle}">
|
||||||
<div class="relative px-4 pt-4 pb-2">
|
<div class="relative px-4 pt-4 pb-2">
|
||||||
<div class="flex items-start gap-3 pr-10">
|
<div class="flex items-start gap-3 pr-10">
|
||||||
<div id="${idBase}-hero" class="relative w-20 h-20 rounded-2xl flex items-center justify-center shrink-0 overflow-hidden" style="background:rgb(var(--card-rgb));">
|
<div id="${idBase}-hero" class="relative w-20 h-20 rounded-2xl flex items-center justify-center shrink-0 overflow-hidden" style="background:transparent;">
|
||||||
<img id="${idBase}-img" class="w-full h-full hidden" alt="" />
|
<img id="${idBase}-img" class="w-full h-full hidden" alt="" />
|
||||||
<div id="${idBase}-fallback" class="absolute inset-0 flex items-center justify-center">
|
<div id="${idBase}-fallback" class="absolute inset-0 flex items-center justify-center">
|
||||||
<i id="${idBase}-fallback-icon" class="fas fa-box-open text-2xl" style="color:rgb(var(--text-subdued-rgb));"></i>
|
<i id="${idBase}-fallback-icon" class="fas fa-box-open text-2xl" style="color:rgb(var(--text-subdued-rgb));"></i>
|
||||||
@@ -170,7 +177,7 @@ export function getIngredientCardHTML({
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex-1 min-w-0 pt-0.5">
|
<div class="flex-1 min-w-0 pt-0.5">
|
||||||
<div class="flex items-center gap-1.5">
|
<div class="flex items-center gap-1.5">
|
||||||
<button type="button" id="${idBase}-back" class="hidden w-5 h-5 rounded-full items-center justify-center shrink-0" style="background:rgb(var(--card-rgb)); color:rgb(var(--text-body-rgb));" aria-label="Wróć do składnika">
|
<button type="button" id="${idBase}-back" class="calendar-liquid-btn hidden w-5 h-5 rounded-full items-center justify-center shrink-0" style="color:rgb(var(--text-body-rgb));" aria-label="Wróć do składnika">
|
||||||
<i class="fas fa-chevron-left text-[9px]"></i>
|
<i class="fas fa-chevron-left text-[9px]"></i>
|
||||||
</button>
|
</button>
|
||||||
<p id="${idBase}-category" class="text-[10px] font-semibold uppercase tracking-wider truncate" style="color:rgb(var(--success-rgb));"></p>
|
<p id="${idBase}-category" class="text-[10px] font-semibold uppercase tracking-wider truncate" style="color:rgb(var(--success-rgb));"></p>
|
||||||
@@ -179,7 +186,7 @@ export function getIngredientCardHTML({
|
|||||||
<p id="${idBase}-subtitle" class="text-[11px] mt-0.5 hidden" style="color:rgb(var(--text-dim-rgb));"></p>
|
<p id="${idBase}-subtitle" class="text-[11px] mt-0.5 hidden" style="color:rgb(var(--text-dim-rgb));"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" id="${idBase}-close" class="absolute top-3 right-3 w-8 h-8 rounded-full flex items-center justify-center" style="background:rgb(var(--card-rgb)); color:rgb(var(--text-body-rgb));" aria-label="Zamknij">
|
<button type="button" id="${idBase}-close" class="calendar-liquid-btn absolute top-3 right-3 w-8 h-8 rounded-full flex items-center justify-center" style="color:rgb(var(--text-body-rgb));" aria-label="Zamknij">
|
||||||
<i class="fas fa-times text-sm"></i>
|
<i class="fas fa-times text-sm"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -371,28 +378,28 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
|
|||||||
<p class="text-[16px] font-bold tabular-nums" style="color:rgb(var(--success-rgb));">${esc(stockValueLabel)}</p>
|
<p class="text-[16px] font-bold tabular-nums" style="color:rgb(var(--success-rgb));">${esc(stockValueLabel)}</p>
|
||||||
${stockSubLabel ? `<p class="text-[11px] mt-1" style="color:rgb(var(--text-dim-rgb));">${esc(stockSubLabel)}</p>` : ''}
|
${stockSubLabel ? `<p class="text-[11px] mt-1" style="color:rgb(var(--text-dim-rgb));">${esc(stockSubLabel)}</p>` : ''}
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="ingredient-card-stock-toggle inline-flex items-center rounded-full px-2.5 py-1 text-[10px] font-semibold shrink-0" style="background:${state.stockEditorOpen ? 'rgb(var(--sunken-rgb))' : 'rgb(var(--card-soft-rgb))'}; color:${state.stockEditorOpen ? 'rgb(var(--text-emphasis-rgb))' : 'rgb(var(--text-body-soft-rgb))'};">
|
<button type="button" class="calendar-liquid-btn ingredient-card-stock-toggle inline-flex items-center rounded-full px-2.5 py-1 text-[10px] font-semibold shrink-0" style="color:${state.stockEditorOpen ? 'rgb(var(--text-emphasis-rgb))' : 'rgb(var(--text-body-soft-rgb))'};">
|
||||||
${esc(actionLabel)}
|
${esc(actionLabel)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
${state.stockEditorOpen ? `
|
${state.stockEditorOpen ? `
|
||||||
<div class="mt-3 pt-3 border-t" style="border-color:rgb(var(--card-strong-rgb));">
|
<div class="mt-3 pt-3 border-t" style="border-color:rgb(var(--card-strong-rgb));">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<button type="button" class="ingredient-card-stock-step w-9 h-9 rounded-xl flex items-center justify-center shrink-0" style="background:rgb(var(--card-soft-rgb)); color:rgb(var(--text-body-soft-rgb));" data-dir="-1" aria-label="Zmniejsz szkic zapasu">
|
<button type="button" class="calendar-liquid-btn ingredient-card-stock-step w-9 h-9 rounded-xl flex items-center justify-center shrink-0" style="color:rgb(var(--text-body-soft-rgb));" data-dir="-1" aria-label="Zmniejsz szkic zapasu">
|
||||||
<i class="fas fa-minus text-xs"></i>
|
<i class="fas fa-minus text-xs"></i>
|
||||||
</button>
|
</button>
|
||||||
<label class="flex-1 rounded-xl px-3 py-2 flex items-center justify-center gap-2" style="background:rgb(var(--card-soft-rgb));">
|
<label class="calendar-liquid-btn flex-1 rounded-xl px-3 py-2 flex items-center justify-center gap-2">
|
||||||
<input type="number" min="0" step="${usesPackStep ? '1' : step}" value="${draftInputValue}" class="ingredient-card-stock-input w-20 bg-transparent text-center text-[14px] font-semibold tabular-nums outline-none appearance-none" style="color:rgb(var(--text-body-rgb)); background:transparent !important; border:none !important; box-shadow:none !important; -webkit-appearance:none; -moz-appearance:textfield;">
|
<input type="text" inputmode="decimal" value="${draftInputValue}" class="ingredient-card-stock-input w-20 bg-transparent text-center text-[14px] font-semibold tabular-nums outline-none appearance-none" style="color:rgb(var(--text-body-rgb)); background:transparent !important; border:none !important; box-shadow:none !important; -webkit-appearance:none; -moz-appearance:textfield;">
|
||||||
<span class="text-[12px] font-medium shrink-0" style="color:rgb(var(--text-dim-rgb));">${esc(draftInputUnit)}</span>
|
<span class="text-[12px] font-medium shrink-0" style="color:rgb(var(--text-dim-rgb));">${esc(draftInputUnit)}</span>
|
||||||
</label>
|
</label>
|
||||||
<button type="button" class="ingredient-card-stock-step w-9 h-9 rounded-xl flex items-center justify-center shrink-0" style="background:rgb(var(--card-soft-rgb)); color:rgb(var(--text-body-soft-rgb));" data-dir="1" aria-label="Zwiększ szkic zapasu">
|
<button type="button" class="calendar-liquid-btn ingredient-card-stock-step w-9 h-9 rounded-xl flex items-center justify-center shrink-0" style="color:rgb(var(--text-body-soft-rgb));" data-dir="1" aria-label="Zwiększ szkic zapasu">
|
||||||
<i class="fas fa-plus text-xs"></i>
|
<i class="fas fa-plus text-xs"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
${usesPackStep ? `<p class="text-[10px] mt-2 text-right" style="color:rgb(var(--text-dim-rgb));">${esc(formatQtyWithUnit(draftQty, def.pantryUnit))}</p>` : ''}
|
${usesPackStep ? `<p class="text-[10px] mt-2 text-right" style="color:rgb(var(--text-dim-rgb));">${esc(formatQtyWithUnit(draftQty, def.pantryUnit))}</p>` : ''}
|
||||||
<div class="flex items-center justify-between gap-3 mt-3">
|
<div class="flex items-center justify-between gap-3 mt-3">
|
||||||
<button type="button" class="ingredient-card-stock-clear text-[11px] font-semibold" style="color:rgb(var(--text-dim-rgb));">Wyzeruj</button>
|
<button type="button" class="ingredient-card-stock-clear text-[11px] font-semibold" style="color:rgb(var(--text-dim-rgb));">Wyzeruj</button>
|
||||||
<button type="button" class="ingredient-card-stock-save inline-flex items-center rounded-full px-3 py-1.5 text-[11px] font-semibold" style="background:rgb(var(--text-body-rgb)); color:rgb(var(--app-bg-rgb));">Zapisz</button>
|
<button type="button" class="calendar-liquid-btn ingredient-card-stock-save inline-flex items-center rounded-full px-3 py-1.5 text-[11px] font-semibold" style="color:rgb(var(--text-emphasis-rgb));">Zapisz</button>
|
||||||
</div>
|
</div>
|
||||||
</div>` : ''}
|
</div>` : ''}
|
||||||
</div>`;
|
</div>`;
|
||||||
@@ -420,8 +427,8 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
|
|||||||
|
|
||||||
wrap.querySelector('.ingredient-card-stock-input')?.addEventListener('input', (event) => {
|
wrap.querySelector('.ingredient-card-stock-input')?.addEventListener('input', (event) => {
|
||||||
state.stockDraftQty = usesPackStep
|
state.stockDraftQty = usesPackStep
|
||||||
? normalizeQty((Number(event.target.value) || 0) * step)
|
? normalizeQty(parseQtyInput(event.target.value) * step)
|
||||||
: normalizeQty(event.target.value);
|
: parseQtyInput(event.target.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
wrap.querySelector('.ingredient-card-stock-clear')?.addEventListener('click', () => {
|
wrap.querySelector('.ingredient-card-stock-clear')?.addEventListener('click', () => {
|
||||||
@@ -432,8 +439,8 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
|
|||||||
wrap.querySelector('.ingredient-card-stock-save')?.addEventListener('click', () => {
|
wrap.querySelector('.ingredient-card-stock-save')?.addEventListener('click', () => {
|
||||||
const input = wrap.querySelector('.ingredient-card-stock-input');
|
const input = wrap.querySelector('.ingredient-card-stock-input');
|
||||||
const nextQty = usesPackStep
|
const nextQty = usesPackStep
|
||||||
? normalizeQty((Number(input?.value) || 0) * step)
|
? normalizeQty(parseQtyInput(input?.value) * step)
|
||||||
: normalizeQty(input?.value ?? state.stockDraftQty ?? qty);
|
: parseQtyInput(input?.value ?? state.stockDraftQty ?? qty);
|
||||||
if (product) {
|
if (product) {
|
||||||
setPantryProductQty(def.id, product.id, nextQty);
|
setPantryProductQty(def.id, product.id, nextQty);
|
||||||
} else {
|
} else {
|
||||||
@@ -480,21 +487,21 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
|
|||||||
<p class="text-[16px] font-bold tabular-nums" style="color:${hasShoppingItem ? 'rgb(var(--text-body-rgb))' : 'rgb(var(--text-dim-rgb))'};">${esc(shopValueLabel)}</p>
|
<p class="text-[16px] font-bold tabular-nums" style="color:${hasShoppingItem ? 'rgb(var(--text-body-rgb))' : 'rgb(var(--text-dim-rgb))'};">${esc(shopValueLabel)}</p>
|
||||||
${shopSubLabel ? `<p class="text-[11px] mt-1" style="color:rgb(var(--text-dim-rgb));">${esc(shopSubLabel)}</p>` : ''}
|
${shopSubLabel ? `<p class="text-[11px] mt-1" style="color:rgb(var(--text-dim-rgb));">${esc(shopSubLabel)}</p>` : ''}
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="ingredient-card-shop-toggle inline-flex items-center rounded-full px-2.5 py-1 text-[10px] font-semibold shrink-0" style="background:${state.shopEditorOpen ? 'rgb(var(--sunken-rgb))' : 'rgb(var(--card-soft-rgb))'}; color:${state.shopEditorOpen ? 'rgb(var(--text-emphasis-rgb))' : 'rgb(var(--text-body-soft-rgb))'};">
|
<button type="button" class="calendar-liquid-btn ingredient-card-shop-toggle inline-flex items-center rounded-full px-2.5 py-1 text-[10px] font-semibold shrink-0" style="color:${state.shopEditorOpen ? 'rgb(var(--text-emphasis-rgb))' : 'rgb(var(--text-body-soft-rgb))'};">
|
||||||
${esc(actionLabel)}
|
${esc(actionLabel)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
${state.shopEditorOpen ? `
|
${state.shopEditorOpen ? `
|
||||||
<div class="mt-3 pt-3 border-t" style="border-color:rgb(var(--card-strong-rgb));">
|
<div class="mt-3 pt-3 border-t" style="border-color:rgb(var(--card-strong-rgb));">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<button type="button" class="ingredient-card-shop-step w-9 h-9 rounded-xl flex items-center justify-center shrink-0" style="background:rgb(var(--card-soft-rgb)); color:rgb(var(--text-body-soft-rgb));" data-dir="-1" aria-label="Zmniejsz ilość na liście">
|
<button type="button" class="calendar-liquid-btn ingredient-card-shop-step w-9 h-9 rounded-xl flex items-center justify-center shrink-0" style="color:rgb(var(--text-body-soft-rgb));" data-dir="-1" aria-label="Zmniejsz ilość na liście">
|
||||||
<i class="fas fa-minus text-xs"></i>
|
<i class="fas fa-minus text-xs"></i>
|
||||||
</button>
|
</button>
|
||||||
<label class="flex-1 rounded-xl px-3 py-2 flex items-center justify-center gap-2" style="background:rgb(var(--card-soft-rgb));">
|
<label class="calendar-liquid-btn flex-1 rounded-xl px-3 py-2 flex items-center justify-center gap-2">
|
||||||
<input type="number" min="0" step="${usesPackStep ? '1' : defaultAmount}" value="${shopInputValue}" class="ingredient-card-shop-input w-20 bg-transparent text-center text-[14px] font-semibold tabular-nums outline-none appearance-none" style="color:rgb(var(--text-body-rgb)); background:transparent !important; border:none !important; box-shadow:none !important; -webkit-appearance:none; -moz-appearance:textfield;">
|
<input type="text" inputmode="decimal" value="${shopInputValue}" class="ingredient-card-shop-input w-20 bg-transparent text-center text-[14px] font-semibold tabular-nums outline-none appearance-none" style="color:rgb(var(--text-body-rgb)); background:transparent !important; border:none !important; box-shadow:none !important; -webkit-appearance:none; -moz-appearance:textfield;">
|
||||||
<span class="text-[12px] font-medium shrink-0" style="color:rgb(var(--text-dim-rgb));">${esc(shopInputUnit)}</span>
|
<span class="text-[12px] font-medium shrink-0" style="color:rgb(var(--text-dim-rgb));">${esc(shopInputUnit)}</span>
|
||||||
</label>
|
</label>
|
||||||
<button type="button" class="ingredient-card-shop-step w-9 h-9 rounded-xl flex items-center justify-center shrink-0" style="background:rgb(var(--card-soft-rgb)); color:rgb(var(--text-body-soft-rgb));" data-dir="1" aria-label="Zwiększ ilość na liście">
|
<button type="button" class="calendar-liquid-btn ingredient-card-shop-step w-9 h-9 rounded-xl flex items-center justify-center shrink-0" style="color:rgb(var(--text-body-soft-rgb));" data-dir="1" aria-label="Zwiększ ilość na liście">
|
||||||
<i class="fas fa-plus text-xs"></i>
|
<i class="fas fa-plus text-xs"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -503,7 +510,7 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
|
|||||||
${hasShoppingItem
|
${hasShoppingItem
|
||||||
? '<button type="button" class="ingredient-card-shop-remove text-[11px] font-semibold" style="color:rgb(var(--text-dim-rgb));">Usuń z listy</button>'
|
? '<button type="button" class="ingredient-card-shop-remove text-[11px] font-semibold" style="color:rgb(var(--text-dim-rgb));">Usuń z listy</button>'
|
||||||
: '<span></span>'}
|
: '<span></span>'}
|
||||||
<button type="button" class="ingredient-card-shop-save inline-flex items-center rounded-full px-3 py-1.5 text-[11px] font-semibold" style="background:rgb(var(--text-body-rgb)); color:rgb(var(--app-bg-rgb));">Zapisz</button>
|
<button type="button" class="calendar-liquid-btn ingredient-card-shop-save inline-flex items-center rounded-full px-3 py-1.5 text-[11px] font-semibold" style="color:rgb(var(--text-emphasis-rgb));">Zapisz</button>
|
||||||
</div>
|
</div>
|
||||||
</div>` : ''}
|
</div>` : ''}
|
||||||
</div>`;
|
</div>`;
|
||||||
@@ -531,8 +538,8 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
|
|||||||
|
|
||||||
wrap.querySelector('.ingredient-card-shop-input')?.addEventListener('input', (event) => {
|
wrap.querySelector('.ingredient-card-shop-input')?.addEventListener('input', (event) => {
|
||||||
state.shopDraftQty = usesPackStep
|
state.shopDraftQty = usesPackStep
|
||||||
? normalizeQty((Number(event.target.value) || 0) * step)
|
? normalizeQty(parseQtyInput(event.target.value) * step)
|
||||||
: normalizeQty(event.target.value);
|
: parseQtyInput(event.target.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
wrap.querySelector('.ingredient-card-shop-remove')?.addEventListener('click', () => {
|
wrap.querySelector('.ingredient-card-shop-remove')?.addEventListener('click', () => {
|
||||||
@@ -549,8 +556,8 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
|
|||||||
wrap.querySelector('.ingredient-card-shop-save')?.addEventListener('click', () => {
|
wrap.querySelector('.ingredient-card-shop-save')?.addEventListener('click', () => {
|
||||||
const input = wrap.querySelector('.ingredient-card-shop-input');
|
const input = wrap.querySelector('.ingredient-card-shop-input');
|
||||||
const nextAmount = usesPackStep
|
const nextAmount = usesPackStep
|
||||||
? normalizeQty((Number(input?.value) || 0) * step)
|
? normalizeQty(parseQtyInput(input?.value) * step)
|
||||||
: normalizeQty(input?.value ?? state.shopDraftQty ?? defaultAmount);
|
: parseQtyInput(input?.value ?? state.shopDraftQty ?? defaultAmount);
|
||||||
let toastText = null;
|
let toastText = null;
|
||||||
if (shoppingItem) {
|
if (shoppingItem) {
|
||||||
updateKitchenItemAmount(KITCHEN_LIST_ID, shoppingItem.id, nextAmount);
|
updateKitchenItemAmount(KITCHEN_LIST_ID, shoppingItem.id, nextAmount);
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ const MONTHS_SHORT = ['sty', 'lut', 'mar', 'kwi', 'maj', 'cze', 'lip', 'sie', 'w
|
|||||||
const DEFAULT_HORIZON_DAYS = 7;
|
const DEFAULT_HORIZON_DAYS = 7;
|
||||||
const SHORTFALL_ACCENT = 'rgb(var(--danger-rgb))';
|
const SHORTFALL_ACCENT = 'rgb(var(--danger-rgb))';
|
||||||
const PANTRY_CALENDAR_THEME = {
|
const PANTRY_CALENDAR_THEME = {
|
||||||
bg: 'rgb(var(--app-bg-rgb))',
|
bg: 'rgba(255,255,255,0.08)',
|
||||||
border: 'rgb(var(--card-raised-rgb))',
|
border: 'rgb(var(--card-raised-rgb))',
|
||||||
text: 'rgb(var(--text-body-soft-rgb))',
|
text: 'rgb(var(--text-body-soft-rgb))',
|
||||||
dimText: 'rgb(var(--text-faint-rgb))',
|
dimText: 'rgb(var(--text-faint-rgb))',
|
||||||
@@ -158,6 +158,7 @@ function photoStripMedia(image, icon, accentBg) {
|
|||||||
|
|
||||||
export function getPantryHTML() {
|
export function getPantryHTML() {
|
||||||
return `
|
return `
|
||||||
|
<div id="pantry-view" class="hidden flex flex-col h-full absolute inset-0 overflow-hidden z-10" style="background:rgb(var(--app-bg-rgb)) !important;">
|
||||||
<style id="pantry-view-styles">
|
<style id="pantry-view-styles">
|
||||||
.pv2-tile {
|
.pv2-tile {
|
||||||
background: rgba(var(--surface-rgb), 0.62) !important;
|
background: rgba(var(--surface-rgb), 0.62) !important;
|
||||||
@@ -189,31 +190,23 @@ export function getPantryHTML() {
|
|||||||
background: rgba(255, 255, 255, 0.1);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div id="pantry-view" class="hidden flex flex-col h-full absolute inset-0 overflow-hidden z-10" style="background:rgb(var(--app-bg-rgb)) !important;">
|
|
||||||
|
|
||||||
<!-- ── floating top bar (calendar only) ── -->
|
<!-- ── floating horizon pill — mirrors bottom search-field structure ── -->
|
||||||
<div id="pantry-topbar-outer" class="pointer-events-none absolute inset-x-0 top-0 z-[12] px-4 pt-4 pb-4" style="background:rgb(var(--app-bg-rgb)) !important; border:none !important;">
|
<div id="pantry-top-controls" class="pointer-events-none absolute inset-x-0 z-[24]" style="top:1rem; height:var(--recipe-control-size, 3.05rem); background:transparent !important; border:none !important; box-shadow:none !important; backdrop-filter:none !important; -webkit-backdrop-filter:none !important;">
|
||||||
<div class="pointer-events-auto relative z-[1] w-full">
|
<div id="pantry-horizon-wrap" class="pointer-events-auto absolute top-0" style="left:var(--catalog-menu-left, 1rem); width:var(--recipe-dock-width, calc(100% - 2rem)); height:var(--recipe-control-size, 3.05rem);">
|
||||||
<div id="pantry-topbar" class="relative min-h-12">
|
<button type="button" id="pantry-horizon-compact" class="recipe-glass-btn w-full h-full rounded-full flex items-center gap-1.5 px-3">
|
||||||
<div id="pantry-default-row" class="flex min-h-12 items-center justify-end gap-2">
|
|
||||||
<div id="pantry-horizon-wrap" class="relative shrink">
|
|
||||||
<button type="button" id="pantry-horizon-compact" class="min-w-0 max-w-[12rem] h-10 rounded-full flex items-center gap-1.5 px-2.5 transition-all" style="background:rgb(var(--card-rgb)) !important; border:1px solid rgb(var(--border-card-rgb)) !important; box-shadow:var(--shadow-shell) !important;">
|
|
||||||
<span id="pantry-horizon-compact-label" class="min-w-0 flex-1 text-left text-[13px] font-normal truncate" style="color:rgb(var(--text-body-rgb));"></span>
|
<span id="pantry-horizon-compact-label" class="min-w-0 flex-1 text-left text-[13px] font-normal truncate" style="color:rgb(var(--text-body-rgb));"></span>
|
||||||
<i id="pantry-horizon-chevron" class="fas fa-chevron-down text-[10px] shrink-0 transition-transform duration-200" style="color:rgb(var(--text-dim-rgb));"></i>
|
<i id="pantry-horizon-chevron" class="fas fa-chevron-down text-[10px] shrink-0 transition-transform duration-200" style="color:rgb(var(--text-dim-rgb));"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
${createCalendarPopoverHTML({
|
${createCalendarPopoverHTML({
|
||||||
id: 'pantry-calendar-popover',
|
id: 'pantry-calendar-popover',
|
||||||
calendarHTML: createSwipePopoverCalendarHTML({ idPrefix: 'pantry-cal' }),
|
calendarHTML: createSwipePopoverCalendarHTML({ idPrefix: 'pantry-cal' }),
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ── scrollable content ── -->
|
<!-- ── scrollable content ── -->
|
||||||
<div id="pantry-scroll" class="flex-1 overflow-y-auto no-scrollbar px-4 pt-[5.35rem] pb-24" style="background:rgb(var(--app-bg-rgb)) !important;">
|
<div id="pantry-scroll" class="flex-1 overflow-y-auto no-scrollbar px-4 pt-4 pb-24" style="background:rgb(var(--app-bg-rgb)) !important; scroll-padding-top:calc(1rem + var(--recipe-control-size, 3.05rem) + 0.5rem);">
|
||||||
<div id="pantry-board"></div>
|
<div id="pantry-board"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -270,6 +263,8 @@ function syncHorizonUI() {
|
|||||||
isOpen: isCalendarOpen,
|
isOpen: isCalendarOpen,
|
||||||
chevron,
|
chevron,
|
||||||
trigger: compactPill,
|
trigger: compactPill,
|
||||||
|
openTriggerStyle: {},
|
||||||
|
closedTriggerStyle: {},
|
||||||
triggerImportant: true,
|
triggerImportant: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -509,8 +504,8 @@ function classifyIngredients(searchQuery) {
|
|||||||
/* ══════════════════════ TILE RENDERING ══════════════════════ */
|
/* ══════════════════════ TILE RENDERING ══════════════════════ */
|
||||||
|
|
||||||
function tileIconHtml(item, size = 'sm') {
|
function tileIconHtml(item, size = 'sm') {
|
||||||
const wrap = size === 'lg' ? 'w-9 h-9' : 'w-6 h-6';
|
const wrap = size === 'lg' ? 'w-11 h-11' : 'w-7 h-7';
|
||||||
const iconSize = size === 'lg' ? 'text-[18px]' : 'text-[12px]';
|
const iconSize = size === 'lg' ? 'text-[22px]' : 'text-[15px]';
|
||||||
if (item.image) {
|
if (item.image) {
|
||||||
return `<div class="${wrap} shrink-0 overflow-hidden"><img src="${esc(item.image)}" alt="" class="w-full h-full object-contain"></div>`;
|
return `<div class="${wrap} shrink-0 overflow-hidden"><img src="${esc(item.image)}" alt="" class="w-full h-full object-contain"></div>`;
|
||||||
}
|
}
|
||||||
@@ -665,18 +660,6 @@ export function setupPantry() {
|
|||||||
syncHorizonUI();
|
syncHorizonUI();
|
||||||
renderBoard();
|
renderBoard();
|
||||||
|
|
||||||
// Scroll shadow under top bar
|
|
||||||
const pantryScroll = document.getElementById('pantry-scroll');
|
|
||||||
const topbarOuter = document.getElementById('pantry-topbar-outer');
|
|
||||||
if (pantryScroll && topbarOuter) {
|
|
||||||
const shadow = document.createElement('div');
|
|
||||||
shadow.style.cssText = 'position:absolute;left:0;right:0;bottom:-8px;height:8px;background:linear-gradient(to bottom,rgba(var(--overlay-rgb),0.25),transparent);opacity:0;transition:opacity 0.2s;pointer-events:none;';
|
|
||||||
topbarOuter.appendChild(shadow);
|
|
||||||
pantryScroll.addEventListener('scroll', () => {
|
|
||||||
shadow.style.opacity = pantryScroll.scrollTop > 2 ? '1' : '0';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
document.getElementById('pantry-search-input')?.addEventListener('input', () => renderBoard());
|
document.getElementById('pantry-search-input')?.addEventListener('input', () => renderBoard());
|
||||||
document.getElementById('pantry-search-input')?.addEventListener('keydown', (event) => {
|
document.getElementById('pantry-search-input')?.addEventListener('keydown', (event) => {
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ function initShoppingCalendar() {
|
|||||||
selectedText: 'rgb(var(--text-emphasis-rgb))',
|
selectedText: 'rgb(var(--text-emphasis-rgb))',
|
||||||
selectedDot: 'rgb(var(--text-emphasis-rgb))',
|
selectedDot: 'rgb(var(--text-emphasis-rgb))',
|
||||||
selectedShadow: '0 0 0 1px rgba(var(--text-emphasis-rgb),0.10)',
|
selectedShadow: '0 0 0 1px rgba(var(--text-emphasis-rgb),0.10)',
|
||||||
bg: 'rgb(var(--app-bg-rgb))',
|
bg: 'rgba(255,255,255,0.08)',
|
||||||
border: 'transparent',
|
border: 'transparent',
|
||||||
text: 'rgb(var(--text-body-soft-rgb))',
|
text: 'rgb(var(--text-body-soft-rgb))',
|
||||||
dimmedBg: 'transparent',
|
dimmedBg: 'transparent',
|
||||||
|
|||||||
Reference in New Issue
Block a user