Apply liquid glass to filter panel in recipe list
This commit is contained in:
@@ -504,6 +504,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.32) !important;
|
border: 1px solid rgba(255, 255, 255, 0.32) !important;
|
||||||
background: rgba(255, 255, 255, 0.2) !important;
|
background: rgba(255, 255, 255, 0.2) !important;
|
||||||
|
background-image: none !important;
|
||||||
color: rgb(var(--text-body-rgb));
|
color: rgb(var(--text-body-rgb));
|
||||||
box-shadow:
|
box-shadow:
|
||||||
inset 0 1px 0 rgba(255, 255, 255, 0.6),
|
inset 0 1px 0 rgba(255, 255, 255, 0.6),
|
||||||
@@ -517,6 +518,7 @@
|
|||||||
.dark #app-bottom-nav .recipe-nav-toggle {
|
.dark #app-bottom-nav .recipe-nav-toggle {
|
||||||
border: 1px solid rgba(255, 255, 255, 0.12) !important;
|
border: 1px solid rgba(255, 255, 255, 0.12) !important;
|
||||||
background: rgba(255, 255, 255, 0.04) !important;
|
background: rgba(255, 255, 255, 0.04) !important;
|
||||||
|
background-image: none !important;
|
||||||
color: rgb(var(--text-body-rgb));
|
color: rgb(var(--text-body-rgb));
|
||||||
box-shadow:
|
box-shadow:
|
||||||
inset 0 1px 0 rgba(255, 255, 255, 0.24),
|
inset 0 1px 0 rgba(255, 255, 255, 0.24),
|
||||||
@@ -841,6 +843,7 @@
|
|||||||
padding: var(--dock-pad);
|
padding: var(--dock-pad);
|
||||||
border-radius: var(--dock-radius);
|
border-radius: var(--dock-radius);
|
||||||
background: rgba(255, 255, 255, 0.2);
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
background-image: none;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.32);
|
border: 1px solid rgba(255, 255, 255, 0.32);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
backdrop-filter: blur(28px) saturate(180%);
|
backdrop-filter: blur(28px) saturate(180%);
|
||||||
@@ -861,6 +864,7 @@
|
|||||||
}
|
}
|
||||||
.dark #app-bottom-nav .bottom-dock {
|
.dark #app-bottom-nav .bottom-dock {
|
||||||
background: rgba(255, 255, 255, 0.04);
|
background: rgba(255, 255, 255, 0.04);
|
||||||
|
background-image: none;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
box-shadow:
|
box-shadow:
|
||||||
inset 0 1px 0 rgba(255, 255, 255, 0.24),
|
inset 0 1px 0 rgba(255, 255, 255, 0.24),
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ import { MEAL_SLOTS } from '../planner/mealSlots.js';
|
|||||||
import { applyFilters, getFilterState } from './RecipeList.js';
|
import { applyFilters, getFilterState } from './RecipeList.js';
|
||||||
|
|
||||||
const FILTER_PANEL_TRANSITION = 'opacity 180ms ease, transform 180ms ease';
|
const FILTER_PANEL_TRANSITION = 'opacity 180ms ease, transform 180ms ease';
|
||||||
const FILTER_SURFACE = 'rgb(var(--sunken-rgb))';
|
const FILTER_SURFACE = 'var(--filter-liquid-panel-bg)';
|
||||||
const FILTER_SURFACE_SOFT = 'rgb(var(--app-bg-rgb))';
|
const FILTER_SURFACE_SOFT = 'var(--filter-liquid-chip-bg)';
|
||||||
const FILTER_BORDER = 'rgb(var(--border-input-rgb))';
|
const FILTER_BORDER = 'var(--filter-liquid-border)';
|
||||||
const FILTER_CHIP_ACTIVE_BG = 'rgb(var(--card-rgb))';
|
const FILTER_CHIP_ACTIVE_BG = 'var(--filter-liquid-chip-active-bg)';
|
||||||
const FILTER_TEXT_SECONDARY = 'rgb(var(--text-body-soft-rgb))';
|
const FILTER_TEXT_SECONDARY = 'var(--filter-liquid-text-secondary)';
|
||||||
const FILTER_TEXT_MUTED = 'rgb(var(--text-muted-rgb))';
|
const FILTER_TEXT_MUTED = 'var(--filter-liquid-text-muted)';
|
||||||
const FILTER_TEXT_ACTIVE = 'rgb(var(--text-emphasis-rgb))';
|
const FILTER_TEXT_ACTIVE = 'var(--filter-liquid-text-active)';
|
||||||
const FILTER_TRACK = 'rgb(var(--card-rgb))';
|
const FILTER_TRACK = 'var(--filter-liquid-track-bg)';
|
||||||
const FILTER_TRACK_FILL = 'rgba(var(--border-input-rgb), 0.58)';
|
const FILTER_TRACK_FILL = 'var(--filter-liquid-accent-bg)';
|
||||||
const PREP_TIME_MIN = 5;
|
const PREP_TIME_MIN = 5;
|
||||||
const PREP_TIME_MAX = 120;
|
const PREP_TIME_MAX = 120;
|
||||||
const PREP_TIME_STEP = 5;
|
const PREP_TIME_STEP = 5;
|
||||||
@@ -57,10 +57,61 @@ function collectAllTags() {
|
|||||||
export function getFilterHTML() {
|
export function getFilterHTML() {
|
||||||
return `
|
return `
|
||||||
<style id="filter-view-styles">
|
<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 {
|
#filter-view.filter-open {
|
||||||
pointer-events: auto;
|
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-track,
|
||||||
#filter-view .prep-time-range-fill {
|
#filter-view .prep-time-range-fill {
|
||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
@@ -71,15 +122,27 @@ export function getFilterHTML() {
|
|||||||
width: 1rem;
|
width: 1rem;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
border: 1px solid rgba(var(--text-emphasis-rgb),0.16);
|
border: 1px solid rgba(255,255,255,0.34);
|
||||||
background: ${FILTER_TRACK_FILL};
|
background: rgba(var(--surface-rgb),0.42);
|
||||||
box-shadow: 0 0 0 1px rgba(var(--overlay-rgb),0.12);
|
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;
|
touch-action: none;
|
||||||
outline: 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>
|
</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-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">
|
<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>
|
<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>
|
<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 color = active ? FILTER_TEXT_ACTIVE : FILTER_TEXT_SECONDARY;
|
||||||
const borderRule = active ? `border:1px solid ${FILTER_BORDER};` : 'border:none;';
|
const borderRule = active ? `border:1px solid ${FILTER_BORDER};` : 'border:none;';
|
||||||
const shadow = active
|
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}`;
|
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;
|
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) {
|
function setActiveTimeHandle(activeHandle) {
|
||||||
const minHandle = document.getElementById('prep-time-min-handle');
|
const minHandle = document.getElementById('prep-time-min-handle');
|
||||||
const maxHandle = document.getElementById('prep-time-max-handle');
|
const maxHandle = document.getElementById('prep-time-max-handle');
|
||||||
@@ -244,26 +313,33 @@ function positionFilterPanel() {
|
|||||||
|
|
||||||
const viewRect = view.getBoundingClientRect();
|
const viewRect = view.getBoundingClientRect();
|
||||||
const anchorRect = (searchShell || button).getBoundingClientRect();
|
const anchorRect = (searchShell || button).getBoundingClientRect();
|
||||||
const gap = 8;
|
const isRecipeContext = activeFilterContext === 'recipes';
|
||||||
|
const gap = isRecipeContext ? 12 : 8;
|
||||||
const margin = 12;
|
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 spaceBelow = viewRect.bottom - anchorRect.bottom - margin;
|
||||||
const preferredMaxHeight = Math.min(420, viewRect.height - margin * 2);
|
const preferredMaxHeight = Math.min(420, viewRect.height - margin * 2);
|
||||||
const spaceAbove = anchorRect.top - viewRect.top - gap - margin;
|
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
|
const maxHeight = opensUpward
|
||||||
? Math.max(220, Math.min(preferredMaxHeight, spaceAbove))
|
? Math.max(220, Math.min(preferredMaxHeight, spaceAbove))
|
||||||
: Math.max(220, viewRect.height - Math.max(margin, anchorRect.bottom - viewRect.top + gap) - margin);
|
: Math.max(220, viewRect.height - Math.max(margin, anchorRect.bottom - viewRect.top + gap) - margin);
|
||||||
const top = opensUpward
|
const leftBase = isRecipeContext
|
||||||
? Math.max(margin, anchorRect.top - viewRect.top - gap - maxHeight)
|
? (viewRect.width - width) / 2
|
||||||
: Math.max(margin, anchorRect.bottom - viewRect.top + gap);
|
: anchorRect.left - viewRect.left;
|
||||||
const left = Math.max(
|
const left = Math.max(margin, Math.min(leftBase, viewRect.width - width - margin));
|
||||||
margin,
|
|
||||||
Math.min(anchorRect.left - viewRect.left, viewRect.width - width - margin),
|
|
||||||
);
|
|
||||||
panel.style.width = `${width}px`;
|
panel.style.width = `${width}px`;
|
||||||
panel.style.left = `${left}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`;
|
panel.style.maxHeight = `${maxHeight}px`;
|
||||||
if (body) body.style.maxHeight = `${maxHeight - 56}px`;
|
if (body) body.style.maxHeight = `${maxHeight - 56}px`;
|
||||||
}
|
}
|
||||||
@@ -283,6 +359,7 @@ function showFilterPanel() {
|
|||||||
view.style.pointerEvents = 'auto';
|
view.style.pointerEvents = 'auto';
|
||||||
view.setAttribute('aria-hidden', 'false');
|
view.setAttribute('aria-hidden', 'false');
|
||||||
positionFilterPanel();
|
positionFilterPanel();
|
||||||
|
panel.style.transform = getClosedPanelTransform(panel);
|
||||||
setRecipeAreaBlur(true);
|
setRecipeAreaBlur(true);
|
||||||
|
|
||||||
syncPanelCount();
|
syncPanelCount();
|
||||||
@@ -303,7 +380,7 @@ function hideFilterPanel() {
|
|||||||
view.style.pointerEvents = 'none';
|
view.style.pointerEvents = 'none';
|
||||||
view.setAttribute('aria-hidden', 'true');
|
view.setAttribute('aria-hidden', 'true');
|
||||||
panel.style.opacity = '0';
|
panel.style.opacity = '0';
|
||||||
panel.style.transform = 'translateY(-0.5rem) scale(0.98)';
|
panel.style.transform = getClosedPanelTransform(panel);
|
||||||
setRecipeAreaBlur(false);
|
setRecipeAreaBlur(false);
|
||||||
syncPanelCount();
|
syncPanelCount();
|
||||||
|
|
||||||
@@ -381,7 +458,7 @@ function syncPanelCount() {
|
|||||||
if (button) {
|
if (button) {
|
||||||
const highlight = isFilterPanelOpen() || count > 0;
|
const highlight = isFilterPanelOpen() || count > 0;
|
||||||
const isRecipeGlassButton = buttonId === 'recipe-filter-btn';
|
const isRecipeGlassButton = buttonId === 'recipe-filter-btn';
|
||||||
if (isRecipeGlassButton && !highlight) {
|
if (isRecipeGlassButton) {
|
||||||
button.style.removeProperty('background');
|
button.style.removeProperty('background');
|
||||||
button.style.removeProperty('border-color');
|
button.style.removeProperty('border-color');
|
||||||
button.style.removeProperty('color');
|
button.style.removeProperty('color');
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export function getRecipeListHTML() {
|
|||||||
<div id="recipe-filter-wrap" class="pointer-events-auto absolute bottom-0 right-4">
|
<div id="recipe-filter-wrap" class="pointer-events-auto absolute bottom-0 right-4">
|
||||||
<button type="button" id="recipe-filter-btn" class="recipe-glass-btn recipe-bottom-action relative flex items-center justify-center transition-all duration-200" aria-label="Otwórz filtry">
|
<button type="button" id="recipe-filter-btn" class="recipe-glass-btn recipe-bottom-action relative flex items-center justify-center transition-all duration-200" aria-label="Otwórz filtry">
|
||||||
<i class="fas fa-sliders-h" aria-hidden="true"></i>
|
<i class="fas fa-sliders-h" aria-hidden="true"></i>
|
||||||
<span id="recipe-filter-count" class="hidden absolute -top-1 -right-1 min-w-[1.1rem] h-[1.1rem] px-1 rounded-full text-[9px] font-bold leading-none items-center justify-center" style="background:rgb(var(--sunken-rgb)); border:1px solid rgb(var(--border-input-rgb)); color:rgb(var(--text-emphasis-rgb));"></span>
|
<span id="recipe-filter-count" class="hidden absolute -top-1 -right-1 min-w-[1.1rem] h-[1.1rem] px-1 rounded-full text-[9px] font-bold leading-none items-center justify-center" style="background:rgba(var(--text-emphasis-rgb),0.86); background-image:none !important; border:1px solid rgba(255,255,255,0.42); color:rgb(var(--app-bg-rgb)); box-shadow:0 2px 6px rgba(var(--overlay-rgb),0.22);"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user