Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion BLOCKS_LAYOUT.md
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ Displays an Eleventy collection as a card grid or horizontal slider.
| `masonry` | boolean | `false` | If true, renders as a masonry grid using uWrap for zero-reflow height prediction. |
| `filter` | object | — | Filter object: `{property, includes, equals}`. `property` is a dot-notation path (e.g. `"url"`, `"data.title"`). When the resolved value is an array, the operator runs against each element (per-element exact match for `equals`, per-element substring for `includes`). `includes` matches substring; `equals` matches exact value. |
| `image_aspect_ratio` | string | — | Aspect ratio for images, e.g. `"16/9"`, `"1/1"`, `"4/3"`. |
| `filter_ui_collection` | string | — | Optional name of a collection providing the client-side filter UI. When the collection is keyed by `page.fileSlug` (e.g. `categoryListingFilterUI`), the matching entry is used. Otherwise the collection itself is treated as a flat filter UI (e.g. `filteredProductPagesListingFilterUI`). When set, wraps the items in the products-layout sidebar. |
| `filter_ui_collection` | string | — | Optional name of a collection providing the client-side filter UI. When the collection is keyed by `page.fileSlug` (e.g. `categoryListingFilterUI`), the matching entry is used. Otherwise the collection itself is treated as a flat filter UI (e.g. `filteredProductPagesListingFilterUI`). When set, prefixes the items with the filter row. |

---

Expand Down
6 changes: 2 additions & 4 deletions src/_includes/design-system/blocks/category-products.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{%- comment -%}
Category products block: lists every product tagged with the current
category and wraps them in the products-layout sidebar with the matching
client-side filter UI.
category, prefixed by the matching client-side filter UI row.

Equivalent to an `items` block with `collection: products`,
`filter: { property: "data.categories", equals: "<page.fileSlug>" }`, and
Expand All @@ -12,7 +11,6 @@

{%- assign blockItems = collections.products | getProductsByCategory: page.fileSlug -%}
{%- assign filterUI = collections.categoryListingFilterUI[page.fileSlug] -%}
{%- include "products-layout-start.html", filterUI: filterUI -%}
{%- include "products-filter.html", filterUI: filterUI -%}
{%- include "design-system/block-intro.html" -%}
{%- include "items.html", items: blockItems, horizontal: block.horizontal, masonry: block.masonry, image_aspect_ratio: block.image_aspect_ratio -%}
{%- include "products-layout-end.html" -%}
11 changes: 4 additions & 7 deletions src/_includes/design-system/blocks/items.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,14 @@
includes: string the property must contain
equals: exact value the property must match
filter_ui_collection - Optional name of a collection mapping page.fileSlug to
a filter UI object. When set, wraps the items in the
products-layout sidebar with the matching filter UI.
a filter UI object. When set, prefixes the items with
the matching client-side filter UI row.
{%- endcomment -%}

{%- assign blockItems = collections[block.collection] -%}
{%- if block.filter_ui_collection -%}
{%- assign filterUIRoot = collections[block.filter_ui_collection] -%}
{%- assign filterUI = filterUIRoot[page.fileSlug] | default: filterUIRoot -%}
{%- include "products-layout-start.html", filterUI: filterUI -%}
{%- include "design-system/render-items-block.html", block: block, image_aspect_ratio: block.image_aspect_ratio -%}
{%- include "products-layout-end.html" -%}
{%- else -%}
{%- include "design-system/render-items-block.html", block: block, image_aspect_ratio: block.image_aspect_ratio -%}
{%- include "products-filter.html", filterUI: filterUI -%}
{%- endif -%}
{%- include "design-system/render-items-block.html", block: block, image_aspect_ratio: block.image_aspect_ratio -%}
7 changes: 0 additions & 7 deletions src/_includes/filtered-items-section.html

This file was deleted.

4 changes: 0 additions & 4 deletions src/_includes/products-layout-end.html

This file was deleted.

6 changes: 0 additions & 6 deletions src/_includes/products-layout-start.html

This file was deleted.

3 changes: 1 addition & 2 deletions src/_layouts/products.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
{%- include "products-header.html" -%}

{%- assign filterUI = collections.filteredProductPagesListingFilterUI -%}
{%- include "products-layout-start.html", filterUI: filterUI -%}
{%- include "products-filter.html", filterUI: filterUI -%}
<div class="prose">{{- content -}}</div>
{% assign products = collections.products %}
{%- include "items.html", items: products -%}
{%- include "products-layout-end.html" -%}
2 changes: 1 addition & 1 deletion src/_lib/public/ui/category-filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ onReady(() => {
const container = document.querySelector("[data-filter-container]");
if (!container) return;

const list = container.closest(".products-layout")?.querySelector(".items");
const list = container.parentElement?.querySelector(".items");
if (!list) return;

const lis = list.querySelectorAll("li[data-filter-item]");
Expand Down
2 changes: 1 addition & 1 deletion src/_lib/utils/block-schema/items.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const fields = {
filter_ui_collection: {
...str("Filter UI Collection"),
description:
"Optional name of a collection providing the client-side filter UI. When the collection is keyed by `page.fileSlug` (e.g. `categoryListingFilterUI`), the matching entry is used. Otherwise the collection itself is treated as a flat filter UI (e.g. `filteredProductPagesListingFilterUI`). When set, wraps the items in the products-layout sidebar.",
"Optional name of a collection providing the client-side filter UI. When the collection is keyed by `page.fileSlug` (e.g. `categoryListingFilterUI`), the matching entry is used. Otherwise the collection itself is treated as a flat filter UI (e.g. `filteredProductPagesListingFilterUI`). When set, prefixes the items with the filter row.",
},
};

Expand Down
2 changes: 1 addition & 1 deletion src/css/bundle.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
@use "slider";
@use "stripe-checkout";
@use "noscript";
@use "products-filter";
@use "item-filter";
@use "image-popup";
@use "guide";
@use "tabs";
Expand Down
1 change: 1 addition & 0 deletions src/css/design-system-bundle.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
@use "cart";
@use "quote-steps";
@use "image-popup";
@use "item-filter";
@use "theme"; // client theme overrides - must be last
153 changes: 153 additions & 0 deletions src/css/item-filter.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
@use "variables" as *;
@use "mixins" as *;
@use "breakpoints" as breakpoint;

// =============================================================================
// ITEM FILTER
// =============================================================================
// Horizontal row of filter groups + sort dropdown. Active filter pills appear
// above the row when any filter is set. Built on the design-system tokens so
// the component blends in regardless of whether it is rendered inside a
// `.design-system` wrapper or in the legacy `.content` article.

.item-filter {
@include flex-col($space-sm);

margin-bottom: $space-lg;

ul {
margin: 0;
padding: 0;
list-style: none;
}

.filter-active {
@include flex-wrap($space-xs);

align-items: center;

&:empty {
display: none;
}

li {
@include flex-row($space-xs);

padding: $space-xs $space-sm;
border-radius: $radius-full;

font-size: $font-size-sm;
color: var(--color-bg);

background: var(--color-link);

a {
color: inherit;
font-weight: 700;
text-decoration: none;

&:hover {
opacity: 0.8;
}
}

&:last-child {
padding: 0;
background: transparent;

a {
@include link-plain;

font-weight: 600;
}
}
}
}

.filter-groups {
@include flex-wrap($space-md);

align-items: center;

> li {
@include flex-row($space-xs);

flex-wrap: wrap;

> span {
font-size: $font-size-sm;
font-weight: 600;
}

> ul {
@include flex-wrap($space-xs);

li {
font-size: $font-size-sm;

a {
display: block;

padding: $space-xs $space-sm;
border: $border-light;
border-radius: $radius-md;

color: var(--color-text);
text-decoration: none;

transition:
background $transition-fast,
border-color $transition-fast,
color $transition-fast;

&:hover {
border-color: var(--color-link);
color: var(--color-link);
}
}

&.active a {
border-color: var(--color-link);
color: var(--color-bg);

background: var(--color-link);
}
}
}
}
}

.sort-dropdown {
@include flex-row($space-xs);

margin: 0;

span {
font-size: $font-size-sm;
font-weight: 600;
}

select {
width: auto;
margin: 0;
padding: $space-xs $space-sm;
border: $border-light;
border-radius: $radius-md;

font-family: inherit;
font-size: $font-size-sm;
color: var(--color-text);

background: var(--color-bg);
}
}

noscript {
display: contents;

p {
margin: 0;
font-size: $font-size-sm;
}
}
}
Loading
Loading