Apply liquid glass to filter panel in recipe list
This commit is contained in:
@@ -3,15 +3,15 @@ import { MEAL_SLOTS } from '../planner/mealSlots.js';
|
||||
import { applyFilters, getFilterState } from './RecipeList.js';
|
||||
|
||||
const FILTER_PANEL_TRANSITION = 'opacity 180ms ease, transform 180ms ease';
|
||||
const FILTER_SURFACE = 'rgb(var(--sunken-rgb))';
|
||||
const FILTER_SURFACE_SOFT = 'rgb(var(--app-bg-rgb))';
|
||||
const FILTER_BORDER = 'rgb(var(--border-input-rgb))';
|
||||
const FILTER_CHIP_ACTIVE_BG = 'rgb(var(--card-rgb))';
|
||||
const FILTER_TEXT_SECONDARY = 'rgb(var(--text-body-soft-rgb))';
|
||||
const FILTER_TEXT_MUTED = 'rgb(var(--text-muted-rgb))';
|
||||
const FILTER_TEXT_ACTIVE = 'rgb(var(--text-emphasis-rgb))';
|
||||
const FILTER_TRACK = 'rgb(var(--card-rgb))';
|
||||
const FILTER_TRACK_FILL = 'rgba(var(--border-input-rgb), 0.58)';
|
||||
const FILTER_SURFACE = 'var(--filter-liquid-panel-bg)';
|
||||
const FILTER_SURFACE_SOFT = 'var(--filter-liquid-chip-bg)';
|
||||
const FILTER_BORDER = 'var(--filter-liquid-border)';
|
||||
const FILTER_CHIP_ACTIVE_BG = 'var(--filter-liquid-chip-active-bg)';
|
||||
const FILTER_TEXT_SECONDARY = 'var(--filter-liquid-text-secondary)';
|
||||
const FILTER_TEXT_MUTED = 'var(--filter-liquid-text-muted)';
|
||||
const FILTER_TEXT_ACTIVE = 'var(--filter-liquid-text-active)';
|
||||
const FILTER_TRACK = 'var(--filter-liquid-track-bg)';
|
||||
const FILTER_TRACK_FILL = 'var(--filter-liquid-accent-bg)';
|
||||
const PREP_TIME_MIN = 5;
|
||||
const PREP_TIME_MAX = 120;
|
||||
const PREP_TIME_STEP = 5;
|
||||
@@ -57,10 +57,61 @@ function collectAllTags() {
|
||||
export function getFilterHTML() {
|
||||
return `
|
||||
<style id="filter-view-styles">
|
||||
#filter-view {
|
||||
--filter-liquid-panel-bg: rgba(var(--app-bg-rgb), 0.78);
|
||||
--filter-liquid-panel-shine: none;
|
||||
--filter-liquid-border: rgba(255, 255, 255, 0.32);
|
||||
--filter-liquid-chip-bg: rgba(var(--surface-rgb), 0.68);
|
||||
--filter-liquid-chip-active-bg: rgba(var(--surface-rgb), 0.92);
|
||||
--filter-liquid-track-bg: rgba(var(--overlay-rgb), 0.16);
|
||||
--filter-liquid-accent-bg: rgba(255, 255, 255, 0.72);
|
||||
--filter-liquid-text-active: rgb(var(--text-emphasis-rgb));
|
||||
--filter-liquid-text-secondary: rgb(var(--text-body-soft-rgb));
|
||||
--filter-liquid-text-muted: rgb(var(--text-muted-rgb));
|
||||
--filter-liquid-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.16),
|
||||
0 22px 52px rgba(var(--overlay-rgb), 0.24);
|
||||
}
|
||||
|
||||
.dark #filter-view {
|
||||
--filter-liquid-panel-bg: rgba(var(--app-bg-rgb), 0.82);
|
||||
--filter-liquid-border: rgba(255, 255, 255, 0.12);
|
||||
--filter-liquid-chip-bg: rgba(255, 255, 255, 0.08);
|
||||
--filter-liquid-chip-active-bg: rgba(255, 255, 255, 0.16);
|
||||
--filter-liquid-track-bg: rgba(255, 255, 255, 0.1);
|
||||
--filter-liquid-accent-bg: rgba(255, 255, 255, 0.32);
|
||||
--filter-liquid-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.36),
|
||||
0 26px 60px rgba(0, 0, 0, 0.46);
|
||||
}
|
||||
|
||||
#filter-view.filter-open {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#filter-panel {
|
||||
isolation: isolate;
|
||||
background: ${FILTER_SURFACE} !important;
|
||||
background-image: none !important;
|
||||
border: 1px solid ${FILTER_BORDER} !important;
|
||||
box-shadow: var(--filter-liquid-shadow) !important;
|
||||
backdrop-filter: blur(28px) saturate(180%);
|
||||
-webkit-backdrop-filter: blur(28px) saturate(180%);
|
||||
}
|
||||
|
||||
#filter-panel::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#filter-panel > * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#filter-view .prep-time-range-track,
|
||||
#filter-view .prep-time-range-fill {
|
||||
border-radius: 9999px;
|
||||
@@ -71,15 +122,27 @@ export function getFilterHTML() {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border-radius: 9999px;
|
||||
border: 1px solid rgba(var(--text-emphasis-rgb),0.16);
|
||||
background: ${FILTER_TRACK_FILL};
|
||||
box-shadow: 0 0 0 1px rgba(var(--overlay-rgb),0.12);
|
||||
border: 1px solid rgba(255,255,255,0.34);
|
||||
background: rgba(var(--surface-rgb),0.42);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255,255,255,0.42),
|
||||
0 0 0 1px rgba(var(--overlay-rgb),0.1),
|
||||
0 4px 10px rgba(var(--overlay-rgb),0.18);
|
||||
touch-action: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.dark #filter-view .prep-time-range-handle {
|
||||
border-color: rgba(255,255,255,0.18);
|
||||
background: rgba(255,255,255,0.26);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255,255,255,0.24),
|
||||
0 0 0 1px rgba(0,0,0,0.22),
|
||||
0 4px 12px rgba(0,0,0,0.34);
|
||||
}
|
||||
</style>
|
||||
<div id="filter-view" class="absolute inset-0 z-[70] hidden opacity-0 transition-opacity duration-150" style="pointer-events:none; background:transparent !important; background-image:none !important;" aria-hidden="true">
|
||||
<div id="filter-panel" class="absolute flex flex-col overflow-hidden rounded-[1.35rem]" style="background:${FILTER_SURFACE} !important; background-image:none !important; border:1px solid ${FILTER_BORDER} !important; opacity:0; transform:translateY(-0.5rem) scale(0.98); transform-origin:top center; transition:${FILTER_PANEL_TRANSITION}; box-shadow:var(--shadow-shell); width:min(calc(100% - 1.5rem), 22rem);">
|
||||
<div id="filter-panel" class="absolute flex flex-col overflow-hidden rounded-[1.35rem]" style="opacity:0; transform:translateY(0.65rem) scale(0.98); transform-origin:bottom center; transition:${FILTER_PANEL_TRANSITION}; width:min(calc(100% - 1.5rem), 22rem);">
|
||||
<div class="shrink-0 px-3 pt-3 pb-2 flex items-center justify-between gap-3">
|
||||
<p class="text-[11px] font-semibold leading-none" style="color:${FILTER_TEXT_ACTIVE};">Filtry</p>
|
||||
<button id="filter-clear-btn" type="button" class="h-8 px-2 rounded-full text-[11px] font-semibold transition-colors" style="background:transparent; border:none; color:${FILTER_TEXT_MUTED};">Wyczyść</button>
|
||||
@@ -179,8 +242,8 @@ function getChipStyle(active) {
|
||||
const color = active ? FILTER_TEXT_ACTIVE : FILTER_TEXT_SECONDARY;
|
||||
const borderRule = active ? `border:1px solid ${FILTER_BORDER};` : 'border:none;';
|
||||
const shadow = active
|
||||
? 'box-shadow:inset 0 1px 0 rgba(255,255,255,0.04), 0 0 0 1px rgba(var(--overlay-rgb),0.08);'
|
||||
: '';
|
||||
? 'box-shadow:0 1px 2px rgba(var(--overlay-rgb),0.08);'
|
||||
: 'box-shadow:none;';
|
||||
return `background:${background}; ${borderRule} color:${color}; ${shadow}`;
|
||||
}
|
||||
|
||||
@@ -197,6 +260,12 @@ function getSliderPercent(value) {
|
||||
return ((value - PREP_TIME_MIN) / (PREP_TIME_MAX - PREP_TIME_MIN)) * 100;
|
||||
}
|
||||
|
||||
function getClosedPanelTransform(panel) {
|
||||
return panel?.dataset.placement === 'below'
|
||||
? 'translateY(-0.5rem) scale(0.98)'
|
||||
: 'translateY(0.65rem) scale(0.98)';
|
||||
}
|
||||
|
||||
function setActiveTimeHandle(activeHandle) {
|
||||
const minHandle = document.getElementById('prep-time-min-handle');
|
||||
const maxHandle = document.getElementById('prep-time-max-handle');
|
||||
@@ -244,26 +313,33 @@ function positionFilterPanel() {
|
||||
|
||||
const viewRect = view.getBoundingClientRect();
|
||||
const anchorRect = (searchShell || button).getBoundingClientRect();
|
||||
const gap = 8;
|
||||
const isRecipeContext = activeFilterContext === 'recipes';
|
||||
const gap = isRecipeContext ? 12 : 8;
|
||||
const margin = 12;
|
||||
const width = Math.min(anchorRect.width, viewRect.width - margin * 2);
|
||||
const maxPanelWidth = isRecipeContext ? 352 : anchorRect.width;
|
||||
const width = Math.min(maxPanelWidth, viewRect.width - margin * 2);
|
||||
const spaceBelow = viewRect.bottom - anchorRect.bottom - margin;
|
||||
const preferredMaxHeight = Math.min(420, viewRect.height - margin * 2);
|
||||
const spaceAbove = anchorRect.top - viewRect.top - gap - margin;
|
||||
const opensUpward = spaceBelow < 260 && anchorRect.top - viewRect.top > spaceBelow;
|
||||
const opensUpward = isRecipeContext || (spaceBelow < 260 && anchorRect.top - viewRect.top > spaceBelow);
|
||||
const maxHeight = opensUpward
|
||||
? Math.max(220, Math.min(preferredMaxHeight, spaceAbove))
|
||||
: Math.max(220, viewRect.height - Math.max(margin, anchorRect.bottom - viewRect.top + gap) - margin);
|
||||
const top = opensUpward
|
||||
? Math.max(margin, anchorRect.top - viewRect.top - gap - maxHeight)
|
||||
: Math.max(margin, anchorRect.bottom - viewRect.top + gap);
|
||||
const left = Math.max(
|
||||
margin,
|
||||
Math.min(anchorRect.left - viewRect.left, viewRect.width - width - margin),
|
||||
);
|
||||
const leftBase = isRecipeContext
|
||||
? (viewRect.width - width) / 2
|
||||
: anchorRect.left - viewRect.left;
|
||||
const left = Math.max(margin, Math.min(leftBase, viewRect.width - width - margin));
|
||||
panel.style.width = `${width}px`;
|
||||
panel.style.left = `${left}px`;
|
||||
panel.style.top = `${top}px`;
|
||||
panel.style.transformOrigin = opensUpward ? 'bottom center' : 'top center';
|
||||
panel.dataset.placement = opensUpward ? 'above' : 'below';
|
||||
if (opensUpward) {
|
||||
panel.style.top = 'auto';
|
||||
panel.style.bottom = `${Math.max(margin, viewRect.bottom - anchorRect.top + gap)}px`;
|
||||
} else {
|
||||
panel.style.top = `${Math.max(margin, anchorRect.bottom - viewRect.top + gap)}px`;
|
||||
panel.style.bottom = 'auto';
|
||||
}
|
||||
panel.style.maxHeight = `${maxHeight}px`;
|
||||
if (body) body.style.maxHeight = `${maxHeight - 56}px`;
|
||||
}
|
||||
@@ -283,6 +359,7 @@ function showFilterPanel() {
|
||||
view.style.pointerEvents = 'auto';
|
||||
view.setAttribute('aria-hidden', 'false');
|
||||
positionFilterPanel();
|
||||
panel.style.transform = getClosedPanelTransform(panel);
|
||||
setRecipeAreaBlur(true);
|
||||
|
||||
syncPanelCount();
|
||||
@@ -303,7 +380,7 @@ function hideFilterPanel() {
|
||||
view.style.pointerEvents = 'none';
|
||||
view.setAttribute('aria-hidden', 'true');
|
||||
panel.style.opacity = '0';
|
||||
panel.style.transform = 'translateY(-0.5rem) scale(0.98)';
|
||||
panel.style.transform = getClosedPanelTransform(panel);
|
||||
setRecipeAreaBlur(false);
|
||||
syncPanelCount();
|
||||
|
||||
@@ -381,7 +458,7 @@ function syncPanelCount() {
|
||||
if (button) {
|
||||
const highlight = isFilterPanelOpen() || count > 0;
|
||||
const isRecipeGlassButton = buttonId === 'recipe-filter-btn';
|
||||
if (isRecipeGlassButton && !highlight) {
|
||||
if (isRecipeGlassButton) {
|
||||
button.style.removeProperty('background');
|
||||
button.style.removeProperty('border-color');
|
||||
button.style.removeProperty('color');
|
||||
|
||||
Reference in New Issue
Block a user