first commit
This commit is contained in:
@@ -0,0 +1,306 @@
|
||||
<!-- sections/predictive-search.liquid -->
|
||||
<!-- yagor! -->
|
||||
|
||||
{%- liquid
|
||||
comment
|
||||
Get each resource's result count
|
||||
endcomment
|
||||
assign query_count = predictive_search.resources.queries.size
|
||||
assign product_count = predictive_search.resources.products.size
|
||||
assign collection_count = predictive_search.resources.collections.size
|
||||
assign article_count = predictive_search.resources.articles.size
|
||||
assign page_count = predictive_search.resources.pages.size
|
||||
assign collection_counter = 0
|
||||
assign total_results_counter = 0
|
||||
assign terms = predictive_search.terms
|
||||
-%}
|
||||
|
||||
{%- if collection_count > 0 -%}
|
||||
{%- liquid
|
||||
assign exclude_collections_strict = settings.exclude_collections_strict | split: ','
|
||||
assign exclude_collections_contain = settings.exclude_collections_contain | split: ','
|
||||
assign collections_markup = ''
|
||||
-%}
|
||||
|
||||
{%- for collection in predictive_search.resources.collections -%}
|
||||
{%- liquid
|
||||
assign skip_current_collection = false
|
||||
for exclude_collection_strict in exclude_collections_strict
|
||||
assign exclude_collection_strict_stripped = exclude_collection_strict | strip
|
||||
|
||||
if exclude_collection_strict_stripped == collection.handle
|
||||
assign skip_current_collection = true
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
for exclude_collection_contain in exclude_collections_contain
|
||||
assign exclude_collection_contain_stripped = exclude_collection_contain | strip
|
||||
|
||||
if collection.handle contains exclude_collection_contain_stripped
|
||||
assign skip_current_collection = true
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
if skip_current_collection
|
||||
continue
|
||||
endif
|
||||
|
||||
assign collection_counter = collection_counter | plus: 1
|
||||
assign collection_counter_animate = collection_counter | plus: product_count | plus: query_count
|
||||
-%}
|
||||
|
||||
{%- capture collections_markup -%}
|
||||
{{ collections_markup }}
|
||||
|
||||
<div class="other__inline animates" style="animation-delay: {{ collection_counter_animate | times: 90 | plus: 10 }}ms;" id="predictive-search-option-collection-{{ collection_counter }}" role="option" aria-selected="false">
|
||||
<p class="other__inline__title">
|
||||
<a href="{{ collection.url }}">{{ collection.title }}</a>
|
||||
</p>
|
||||
</div>
|
||||
{%- endcapture -%}
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- assign total_results = product_count | plus: query_count | plus: collection_counter | plus: page_count | plus: article_count -%}
|
||||
|
||||
{%- if predictive_search.performed -%}
|
||||
<div class="wrapper">
|
||||
<span class="predictive-search-status visually-hidden" data-predictive-search-live-region-count-value role="status">
|
||||
{%- liquid
|
||||
if total_results == 0
|
||||
echo 'general.search.no_results' | t: terms: terms
|
||||
else
|
||||
echo 'general.search.results_with_count' | t: count: total_results | replace: '**', '' | append: ': '
|
||||
|
||||
if query_count > 0
|
||||
assign count = query_count | plus: collection_counter
|
||||
echo 'general.search.results_suggestions_with_count' | t: count: count | append: ', '
|
||||
endif
|
||||
|
||||
if page_count > 0
|
||||
assign count = page_count | plus: article_count
|
||||
echo 'general.search.results_pages_with_count' | t: count: count | append: ', '
|
||||
endif
|
||||
|
||||
if product_count > 0
|
||||
echo 'general.search.results_products_with_count' | t: count: product_count
|
||||
endif
|
||||
endif
|
||||
-%}
|
||||
</span>
|
||||
<div class="search__results__wrapper" id="predictive-search-results" role="listbox" aria-label="{{ 'general.search.search_for' | t: terms: terms }}">
|
||||
{%- if total_results > 0 -%}
|
||||
<div class="search__results__outer">
|
||||
<div class="search__results__other">
|
||||
{%- if query_count > 0 -%}
|
||||
<div class="search__results__other__list">
|
||||
<div class="search__results__heading">
|
||||
<p class="search__results__title">{{ 'general.search.suggestions' | t }}</p>
|
||||
<span class="badge">{{ query_count }}</span>
|
||||
</div>
|
||||
|
||||
<ul aria-label="{{ 'general.search.suggestions' | t }}" role="group">
|
||||
{%- for query in predictive_search.resources.queries -%}
|
||||
{%- assign total_results_counter = total_results_counter | plus: 1 -%}
|
||||
<li class="other__inline animates" id="predictive-search-option-query-{{ forloop.index }}" role="option" aria-selected="false" style="animation-delay: {{ total_results_counter | times: 90 | plus: 10 }}ms;">
|
||||
<p class="other__inline__title">
|
||||
<a href="{{ query.url }}">
|
||||
<span>{{ query.styled_text }}</span>
|
||||
</a>
|
||||
</p>
|
||||
</li>
|
||||
{%- endfor -%}
|
||||
</ul>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
|
||||
{%- if product_count > 0 -%}
|
||||
<div class="search__results__products">
|
||||
<div class="search__results__products__title">
|
||||
<div id="search_product_results_heading" class="search__results__heading">
|
||||
<p class="search__results__title">{{ 'products.general.products' | t }}</p>
|
||||
<span class="badge">{{ product_count }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul aria-label="{{ 'products.general.products' | t }}" class="search__results__products__list">
|
||||
{%- for product in predictive_search.resources.products -%}
|
||||
{%- liquid
|
||||
assign total_results_counter = total_results_counter | plus: 1
|
||||
assign on_sale = false
|
||||
assign product_price = product.price
|
||||
assign product_compare_at_price = product.compare_at_price
|
||||
|
||||
comment
|
||||
start Yagi app code
|
||||
endcomment
|
||||
assign public_or_tags_matched = true
|
||||
|
||||
if product.metafields.app--168074346497.segment_tags.value.size > 0
|
||||
assign public_or_tags_matched = false
|
||||
endif
|
||||
|
||||
for etag in product.metafields.app--168074346497.segment_tags.value
|
||||
if customer.tags contains etag
|
||||
assign public_or_tags_matched = true
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
if public_or_tags_matched
|
||||
assign product_price = product.metafields.app--168074346497.min_auto_discounted_price.value | default: product.price
|
||||
assign product_compare_at_price = product.compare_at_price
|
||||
|
||||
if shop.metafields.app--168074346497.discount_percentage.value > 0.005
|
||||
assign discount_percentage = shop.metafields.app--168074346497.discount_percentage.value | times: 1.0
|
||||
assign deducted_percentage = 1.0 | minus: discount_percentage
|
||||
assign product_price = product.price | divided_by: 100.0 | times: deducted_percentage | times: 100.0 | ceil
|
||||
endif
|
||||
|
||||
if product_price < product.price and product_compare_at_price == 0 or product_compare_at_price == blank
|
||||
assign product_compare_at_price = product.price
|
||||
endif
|
||||
endif
|
||||
|
||||
comment
|
||||
end Yagi app code
|
||||
endcomment
|
||||
if product_compare_at_price > product_price
|
||||
assign on_sale = true
|
||||
endif
|
||||
|
||||
assign sold_out = true
|
||||
if product.available
|
||||
assign sold_out = false
|
||||
endif
|
||||
-%}
|
||||
|
||||
<div class="product__inline animates" style="animation-delay: {{ total_results_counter | times: 90 | plus: 10 }}ms;" role="option" aria-selected="false">
|
||||
<a href="{{ product.url }}" class="product__inline__link">
|
||||
{%- if product.featured_image != blank -%}
|
||||
<div role="presentation" class="product__inline__image">
|
||||
{% assign alt = product.title | strip_html %}
|
||||
{{ product.featured_image | image_url: width: product.featured_image.width | image_tag: class: 'img-aspect-ratio', alt: alt }}
|
||||
</div>
|
||||
{%- endif -%}
|
||||
|
||||
<div>
|
||||
<p class="product__inline__title">
|
||||
{{ product.title | strip_html }}
|
||||
</p>
|
||||
|
||||
<p class="product__inline__price">
|
||||
<span class="price{% if on_sale %} on-sale{% endif %}{% if sold_out %} sold-out{% endif %}">
|
||||
{%- if settings.currency_code_enable -%}
|
||||
{{ product_price | money_with_currency }}
|
||||
{%- else -%}
|
||||
{{ product_price | money }}
|
||||
{%- endif -%}
|
||||
</span>
|
||||
|
||||
{%- if sold_out -%}
|
||||
<br>
|
||||
<em>{{ 'products.product.sold_out' | t }}</em>
|
||||
{%- endif -%}
|
||||
|
||||
{% if on_sale %}
|
||||
<span class="compare-at">
|
||||
{%- if settings.currency_code_enable -%}
|
||||
{{ product_compare_at_price | money_with_currency }}
|
||||
{%- else -%}
|
||||
{{ product_compare_at_price | money }}
|
||||
{%- endif -%}
|
||||
</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{%- endfor -%}
|
||||
</ul>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
|
||||
{%- if collection_counter > 0 -%}
|
||||
{%- assign total_results_counter = total_results_counter | plus: collection_counter -%}
|
||||
|
||||
<div class="search__results__other__list">
|
||||
<div id="search_collection_results_heading" class="search__results__heading">
|
||||
<p class="search__results__title">{{ 'collections.sidebar.collections' | t }}</p>
|
||||
<span class="badge">{{ collection_counter }}</span>
|
||||
</div>
|
||||
|
||||
<ul aria-label="{{ 'collections.sidebar.collections' | t }}" role="group" class="search__results__collections__list">
|
||||
{{ collections_markup }}
|
||||
</ul>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
|
||||
{%- if article_count > 0 -%}
|
||||
<div class="search__results__other__list">
|
||||
<div id="search_article_results_heading" class="search__results__heading">
|
||||
<p class="search__results__title">{{ 'blogs.article.articles' | t }}</p>
|
||||
<span class="badge">{{ article_count }}</span>
|
||||
</div>
|
||||
|
||||
<ul aria-label="{{ 'blogs.article.articles' | t }}" role="group" class="search__results__articles__list">
|
||||
{%- for article in predictive_search.resources.articles -%}
|
||||
{%- assign total_results_counter = total_results_counter | plus: 1 -%}
|
||||
<li class="other__inline animates" style="animation-delay: {{ total_results_counter | times: 90 | plus: 10 }}ms;" id="predictive-search-option-article-{{ forloop.index }}" role="option" aria-selected="false">
|
||||
<p class="other__inline__title">
|
||||
<a href="{{ article.url }}">{{ article.title }}</a>
|
||||
</p>
|
||||
</li>
|
||||
{%- endfor -%}
|
||||
</ul>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
|
||||
{%- if page_count > 0 -%}
|
||||
<div class="search__results__other__list">
|
||||
<div id="search_page_results_heading" class="search__results__heading">
|
||||
<p class="search__results__title">{{ 'general.page.pages' | t }}</p>
|
||||
<span class="badge">{{ page_count }}</span>
|
||||
</div>
|
||||
|
||||
<ul aria-label="{{ 'general.page.pages' | t }}" role="group" class="search__results__articles__list">
|
||||
{%- for page in predictive_search.resources.pages -%}
|
||||
{%- assign total_results_counter = total_results_counter | plus: 1 -%}
|
||||
<li class="other__inline animates" style="animation-delay: {{ total_results_counter | times: 90 | plus: 10 }}ms;" id="predictive-search-option-page-{{ forloop.index }}" role="option" aria-selected="false">
|
||||
<p class="other__inline__title">
|
||||
<a href="{{ page.url }}">{{ page.title }}</a>
|
||||
</p>
|
||||
</li>
|
||||
{%- endfor -%}
|
||||
</ul>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
</div>
|
||||
{%- else -%}
|
||||
<div class="search__results__empty">
|
||||
<div aria-live="polite">
|
||||
<p>
|
||||
{{ 'general.search.no_results_for' | t }}
|
||||
<em>{{ terms }}</em>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
|
||||
<div class="search__results__actions">
|
||||
<button
|
||||
class="btn btn--outline search__results__btn !flex items-center flex-nowrap gap-r2 whitespace-normal max-w-full"
|
||||
tabindex="-1"
|
||||
role="option"
|
||||
aria-selected="false"
|
||||
>
|
||||
<span class="line-clamp-1 text-ellipsis" data-predictive-search-search-for-text>{{ 'general.search.search_for' | t: terms: terms }}</span>
|
||||
<span class="accent-size-5 [&>svg]:inline-block rtl:-scale-x-100">{%- render 'icon-core-chevron-right' -%}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
@@ -0,0 +1,384 @@
|
||||
<!-- /sections/search.liquid -->
|
||||
|
||||
|
||||
{%- liquid
|
||||
assign has_filters = false
|
||||
if search.filters != empty and section.settings.show_filters
|
||||
assign has_filters = true
|
||||
endif
|
||||
|
||||
assign types = search.types | join: ',' | url_encode
|
||||
assign types = 'type=' | append: types
|
||||
|
||||
assign accordion_initial_state = 'accordion-is-open'
|
||||
assign accordion_open_boolean = true
|
||||
if section.settings.collapse_filters
|
||||
# no class means the accordion is closed
|
||||
assign accordion_initial_state = ''
|
||||
# accordion_open_boolean is used to set aria-expanded and
|
||||
# match js logic for 'display: none' on accordion body elements
|
||||
# which are non-adjacent siblings that cannot easily be styled with CSS
|
||||
assign accordion_open_boolean = false
|
||||
endif
|
||||
-%}
|
||||
|
||||
<section class="page-search {{ section.settings.width }} section-padding"
|
||||
data-section-type="search-page"
|
||||
data-section-id="{{ section.id }}"
|
||||
style="--PT: {{ section.settings.padding_top }}px; --PB: {{ section.settings.padding_bottom }}px;">
|
||||
<div class="search__page__heading">
|
||||
{% render 'search-bar' %}
|
||||
|
||||
{%- if search.terms.size > 0 -%}
|
||||
<div class="search__page__query note">
|
||||
<p>
|
||||
{%- if search.results_count > 0 -%}
|
||||
{{ 'general.search.results_for' | t }} <span class="strong">{{ search.terms | escape }}</span>
|
||||
{%- else -%}
|
||||
{{ 'general.search.no_results_for' | t }} <span class="strong">{{ search.terms | escape }}</span>
|
||||
{%- endif -%}
|
||||
</p>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
|
||||
{% if search.performed %}
|
||||
{%- if has_filters -%}
|
||||
{%- assign filter_count = 0 -%}
|
||||
{%- capture filter_clears -%}
|
||||
{%- for filter in search.filters -%}
|
||||
{%- if filter.type == "price_range" -%}
|
||||
{%- if filter.min_value.value != nil or filter.max_value.value != nil -%}
|
||||
{%- assign filter_count = filter_count | plus: 1 -%}
|
||||
<a class="active__filters__remove" href="{{ filter.url_to_remove }}" data-filter-update-url>
|
||||
{%- assign min_value = filter.min_value.value | default: 0 -%}
|
||||
{%- assign max_value = filter.max_value.value | default: filter.range_max -%}
|
||||
{{ min_value | money_without_trailing_zeros }} - {{ max_value | money_without_trailing_zeros }} X
|
||||
</a>
|
||||
{%- endif -%}
|
||||
{%- else -%}
|
||||
{%- for filter_value in filter.active_values -%}
|
||||
{%- assign filter_count = filter_count | plus: 1 -%}
|
||||
<a class="active__filters__remove" href="{{ filter_value.url_to_remove }}" data-filter-update-url>
|
||||
{{ filter_value.label }} <span class="filter__x">X</span>
|
||||
</a>
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- endcapture -%}
|
||||
|
||||
<nav class="collection__nav" data-collection-tools>
|
||||
<div class="collection__nav__buttons">
|
||||
{% if filter_count > 0 %}
|
||||
{% capture current_filters_count %}
|
||||
<div class="badge">{{ filter_count }}</div>
|
||||
{% endcapture %}
|
||||
{% endif %}
|
||||
|
||||
<button class="collection__filters__toggle filters--default-visible drawer--visible" data-filters-toggle="filters">
|
||||
<span class="hide-filters">{{ 'collections.sidebar.hide_filters' | t }}</span>
|
||||
<span class="show-filters">{{ 'collections.sidebar.filter' | t }}</span>
|
||||
<span data-active-filters-count>{{ current_filters_count }}</span>
|
||||
{% render 'icon-core-filter' %}
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
{%- endif -%}
|
||||
|
||||
<div class="collection__active__filters__wrapper{% unless filter_count > 0 %} is-hidden{% endunless %}">
|
||||
<div class="collection__active__filters" data-active-filters>
|
||||
{%- if filter_count > 1 -%}
|
||||
<a href="{{ routes.search_url }}?{{ types }}&q={{ search.terms | url_encode }}" class="active__filters__clear" data-filter-update-url>{{ 'collections.sidebar.all_tags' | t }}</a>
|
||||
{%- endif -%}
|
||||
{{- filter_clears -}}
|
||||
</div>
|
||||
|
||||
{%- if section.settings.show_products_count -%}
|
||||
<div class="collection__count" data-products-count>
|
||||
{%- if search.results_count -%}
|
||||
{{ 'general.search.results_with_count' | t: count: search.results_count | replace_first: '**', '<strong>' | replace_first: '**', '</strong>' | replace_first: '**', '<strong>' | replace_first: '**', '</strong>' }}
|
||||
{%- endif -%}
|
||||
</div>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
|
||||
<div class="collection__content">
|
||||
|
||||
{%- if has_filters -%}
|
||||
<div class="collection__filters__wrapper filters--default-visible" data-collection-sidebar data-filters="filters" data-default-hide="false">
|
||||
<div class="collection__filters__outer">
|
||||
<div class="drawer__top">
|
||||
<div class="drawer__top__left">
|
||||
<p class="cart__drawer__title">{{ 'collections.sidebar.filter' | t }}</p>
|
||||
</div>
|
||||
|
||||
<button class="drawer__button drawer__close"
|
||||
data-first-focus
|
||||
data-filters-toggle="filters"
|
||||
aria-label="{{ 'general.accessibility.close' | t }}">
|
||||
{% render 'icon-core-x' %}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="collection__filters__inner">
|
||||
<form data-sidebar-filter-form>
|
||||
<input type="hidden" name="q" value="{{ search.terms }}">
|
||||
|
||||
{%- for filter in search.filters -%}
|
||||
{% render 'filters',
|
||||
filter: filter,
|
||||
forloop: forloop,
|
||||
accordion_open_boolean: accordion_open_boolean,
|
||||
accordion_initial_state: accordion_initial_state %}
|
||||
{%- endfor -%}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="drawer__underlay" data-filters-underlay>
|
||||
<span class="drawer__underlay__fill"></span>
|
||||
<span class="drawer__underlay__blur"></span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{%- endif -%}
|
||||
|
||||
<div class="collection__products" data-products-grid>
|
||||
{% paginate search.results by section.settings.pagination_count %}
|
||||
{% for item in search.results %}
|
||||
<div class="search__item__generic">
|
||||
{% if item.image or item.featured_media.preview_image %}
|
||||
|
||||
<div class="search__item__generic__image">
|
||||
<a href="{{ item.url }}" title="{{ item.title | escape }}">
|
||||
{% assign image = item.featured_media.preview_image | default: item.image %}
|
||||
{% assign image_width = 70 | at_most: image.width %}
|
||||
{% assign image_width_2x = image_width | times: 2 | at_most: image.width %}
|
||||
{% assign alt = image.alt %}
|
||||
|
||||
{% capture srcset %}
|
||||
{{ image | image_url: width: image_width_2x }} 2x,
|
||||
{{ image | image_url: width: image_width }}
|
||||
{% endcapture %}
|
||||
|
||||
{%- render 'image',
|
||||
img_object: image,
|
||||
wh_ratio: image.aspect_ratio,
|
||||
width: image_width,
|
||||
srcset: srcset,
|
||||
alt: alt
|
||||
-%}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
<div class="search__item__generic__text">
|
||||
<p class="product__inline__title">
|
||||
{{ item.title | link_to: item.url }}
|
||||
</p>
|
||||
|
||||
{% if item.object_type == 'product' %}
|
||||
<p class="product__inline__price">
|
||||
{%- assign product = item -%}
|
||||
{%- assign on_sale = false -%}
|
||||
{%- assign sold_out = true -%}
|
||||
{%- assign current_variant = product.first_available_variant -%}
|
||||
{% liquid
|
||||
assign product_price = product.price
|
||||
assign product_compare_at_price = product.compare_at_price
|
||||
|
||||
comment
|
||||
start Yagi app code
|
||||
endcomment
|
||||
assign public_or_tags_matched = true
|
||||
|
||||
if product.metafields.app--168074346497.segment_tags.value.size > 0
|
||||
assign public_or_tags_matched = false
|
||||
endif
|
||||
|
||||
for etag in product.metafields.app--168074346497.segment_tags.value
|
||||
if customer.tags contains etag
|
||||
assign public_or_tags_matched = true
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
if public_or_tags_matched
|
||||
assign product_price = product.metafields.app--168074346497.min_auto_discounted_price.value | default: product.price
|
||||
assign product_compare_at_price = product.compare_at_price
|
||||
|
||||
if shop.metafields.app--168074346497.discount_percentage.value > 0.005
|
||||
assign discount_percentage = shop.metafields.app--168074346497.discount_percentage.value | times: 1.0
|
||||
assign deducted_percentage = 1.0 | minus: discount_percentage
|
||||
assign product_price = product.price | divided_by: 100.0 | times: deducted_percentage | times: 100.0 | ceil
|
||||
endif
|
||||
|
||||
if product_price < product.price and product_compare_at_price == 0 or product_compare_at_price == blank
|
||||
assign product_compare_at_price = product.price
|
||||
endif
|
||||
endif
|
||||
|
||||
comment
|
||||
end Yagi app code
|
||||
endcomment
|
||||
%}
|
||||
|
||||
{%- if product_compare_at_price > product_price -%}
|
||||
{%- assign on_sale = true -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- if product.available -%}
|
||||
{%- assign sold_out = false -%}
|
||||
{%- endif -%}
|
||||
|
||||
<span class="price{% if on_sale %} on-sale{% endif %}">
|
||||
{% if product.price_varies %}{{ 'products.general.from' | t }} {% endif %}
|
||||
{% if settings.currency_code_enable %}
|
||||
{{ product_price | money_with_currency }}
|
||||
{% else %}
|
||||
{{ product_price | money }}
|
||||
{% endif %}
|
||||
</span>
|
||||
|
||||
{% if on_sale %}
|
||||
<span class="compare-at">
|
||||
{% if settings.currency_code_enable %}
|
||||
{{ product_compare_at_price | money_with_currency }}
|
||||
{% else %}
|
||||
{{ product_compare_at_price | money }}
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{% if current_variant.unit_price %}
|
||||
{% capture unit_price_separator %}
|
||||
<span aria-hidden="true">/</span><span class="visually-hidden">{{ 'general.accessibility.unit_price_separator' | t }} </span>
|
||||
{% endcapture %}
|
||||
|
||||
{% capture unit_price_base_unit %}
|
||||
{% if current_variant.unit_price_measurement.reference_value != 1 %}
|
||||
{{ current_variant.unit_price_measurement.reference_value }}
|
||||
{% endif %}
|
||||
{{ current_variant.unit_price_measurement.reference_unit }}
|
||||
{% endcapture %}
|
||||
|
||||
<br />
|
||||
<span class="visually-hidden">{{ 'products.product.unit_price_label' | t }}</span>
|
||||
<span class="price-per-unit">{{ current_variant.unit_price | money }}{{ unit_price_separator }}{{ unit_price_base_unit }}</span>
|
||||
{% endif %}
|
||||
|
||||
{% if sold_out %}
|
||||
<br /><em>{{ 'products.product.sold_out' | t }}</em>
|
||||
{% endif %}
|
||||
</p>
|
||||
{%- if settings.product_grid_show_rating and product.metafields.reviews.rating.value != blank -%}
|
||||
<div class="rating__wrapper__search">
|
||||
{% render 'product-rating', product: product, show_rating_count: settings.product_grid_show_rating_count %}
|
||||
</div>
|
||||
{%- endif -%}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% unless forloop.last %}<hr>{% endunless %}
|
||||
{% endfor %}
|
||||
|
||||
{% if paginate.pages > 1 %}
|
||||
<div class="text-center pt-r11">
|
||||
{% render 'pagination-custom', paginate: paginate %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endpaginate %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
{% schema %}
|
||||
{
|
||||
"name": "Search",
|
||||
"settings": [
|
||||
{
|
||||
"type": "header",
|
||||
"content": "Search"
|
||||
},
|
||||
{
|
||||
"type": "checkbox",
|
||||
"id": "show_filters",
|
||||
"default": true,
|
||||
"label": "Show product filters"
|
||||
},
|
||||
{
|
||||
"type": "checkbox",
|
||||
"id": "collapse_filters",
|
||||
"label": "Collapse filter accordions",
|
||||
"info": "Active filters will remain open",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"type": "checkbox",
|
||||
"id": "show_products_count",
|
||||
"label": "Show products count",
|
||||
"info": "Product count will be shown when filters are active",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"type": "range",
|
||||
"id": "pagination_count",
|
||||
"min": 3,
|
||||
"max": 50,
|
||||
"step": 1,
|
||||
"label": "Results per page",
|
||||
"default": 24
|
||||
},
|
||||
{
|
||||
"type": "header",
|
||||
"content": "Section spacing"
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"id": "width",
|
||||
"label": "Width",
|
||||
"default": "wrapper",
|
||||
"options": [
|
||||
{
|
||||
"value": "wrapper--full",
|
||||
"label": "Full width padded"
|
||||
},
|
||||
{
|
||||
"value": "wrapper",
|
||||
"label": "Page width"
|
||||
},
|
||||
{
|
||||
"value": "wrapper--narrow",
|
||||
"label": "Page width narrow"
|
||||
},
|
||||
{
|
||||
"value": "wrapper--tiny",
|
||||
"label": "Page width extra narrow"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "range",
|
||||
"id": "padding_top",
|
||||
"min": 0,
|
||||
"max": 180,
|
||||
"step": 2,
|
||||
"unit": "px",
|
||||
"label": "Padding top",
|
||||
"default": 36
|
||||
},
|
||||
{
|
||||
"type": "range",
|
||||
"id": "padding_bottom",
|
||||
"min": 0,
|
||||
"max": 180,
|
||||
"step": 2,
|
||||
"unit": "px",
|
||||
"label": "Padding bottom",
|
||||
"default": 36
|
||||
}
|
||||
]
|
||||
}
|
||||
{% endschema %}
|
||||
@@ -0,0 +1,173 @@
|
||||
<!-- /snippets/product-buttons.liquid -->
|
||||
|
||||
{%- liquid
|
||||
assign buybutton_setting = false
|
||||
assign gift_card_recipient_feature_active = false
|
||||
assign current_variant = product.selected_or_first_available_variant
|
||||
|
||||
if block.settings.enable_gift_card_recipient and product.gift_card?
|
||||
assign gift_card_recipient_feature_active = true
|
||||
endif
|
||||
|
||||
comment
|
||||
Quick buy buttons are incompatable with gift card products and subscription products
|
||||
endcomment
|
||||
if block.settings.enable_payment_button and gift_card_recipient_feature_active == false
|
||||
assign buybutton_setting = true
|
||||
endif
|
||||
if product.selling_plan_groups.size > 0
|
||||
assign buybutton_setting = false
|
||||
endif
|
||||
-%}
|
||||
|
||||
<div class="product__block__buttons" style="--PB: {{ block.settings.padding_bottom }}px;" {{ block.shopify_attributes }} >
|
||||
<div data-product-form-outer>
|
||||
{% comment %} The [data-product-form] tag distinguishes the product form from upsell instant-add-buttons. {% endcomment %}
|
||||
{%- form 'product', product, id: uniq_id, data-product-form: '', data-product-handle: product.handle -%}
|
||||
{%- unless hidden -%}
|
||||
<div class="product__form__inner" data-form-inner>
|
||||
|
||||
{% comment %}
|
||||
Note: the gift card recipient form is controlled in Checkout and must contain these undocumented line item propeties:
|
||||
* properties[__shopify_send_gift_card_to_recipient] - Hidden - Toggles the feature on/off in checkout, following properties have no effect without it
|
||||
* properties[Recipient email] - The email of the gift card recipient
|
||||
* properties[Recipient name] - The name of the gift card recipient
|
||||
* properties[Message] - Shopify chose a highly generic name for the gift card message, this could easily conflict with an app or custom code
|
||||
{% endcomment %}
|
||||
{%- if gift_card_recipient_feature_active -%}
|
||||
{%- render 'gift-card-recipient-form', product: product, form: form, section: section -%}
|
||||
{%- endif -%}
|
||||
|
||||
<div class="product__submit" data-buttons-wrapper data-add-action-wrapper data-error-boundary>
|
||||
<div data-error-display role='alert' class="add-action-errors"></div>
|
||||
|
||||
{%- assign button_text = 'products.product.add_to_cart' | t -%}
|
||||
{%- if product.metafields.theme.preorder.value == true -%}
|
||||
{% comment %} Add a line item property with 'Sale type: Pre-order' and make the button say 'Pre-order'{% endcomment %}
|
||||
<input type="hidden" data-product-preorder name="properties[{{ 'products.product.sale_type' | t }}]" value="{{ 'products.product.pre_order' | t }}">
|
||||
{%- assign button_text = 'products.product.pre_order' | t -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- render 'sibling-color-as-line-prop' product: product -%}
|
||||
|
||||
{%- if current_variant == null -%}
|
||||
{%- assign button_text = 'products.product.unavailable' | t -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- if current_variant.available == false -%}
|
||||
{%- assign button_text = 'products.product.sold_out' | t -%}
|
||||
{%- endif -%}
|
||||
|
||||
<div class="product__submit__buttons{% if product.has_only_default_variant %} product__submit__buttons--clear{% endif %}">
|
||||
<span class="sr-only" aria-live="polite" x-show="isLoading">
|
||||
{{ 'products.product.adding_to_cart' | t }}
|
||||
</span>
|
||||
<span class="sr-only" aria-live="polite" x-show="isSuccess">
|
||||
{{ 'products.product.added_to_cart' | t }}
|
||||
</span>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
name="add"
|
||||
class="btn--outline btn--full btn--primary btn--add-to-cart"
|
||||
data-add-to-cart
|
||||
{% comment %} Explicitly add aria label so that button text isn't read out in screen readers in uppercase {% endcomment %}
|
||||
aria-label="{{ button_text }}"
|
||||
:class="{
|
||||
'has-success': isSuccess,
|
||||
'loading': isLoading
|
||||
}"
|
||||
{% unless current_variant.available %} disabled="disabled" {% endunless %}
|
||||
>
|
||||
<span class="btn-state-ready flex justify-center">
|
||||
<span data-add-to-cart-text>
|
||||
{{ button_text }}
|
||||
</span>
|
||||
{% if current_variant %}
|
||||
{% liquid
|
||||
comment
|
||||
start Yagi app code
|
||||
endcomment
|
||||
assign current_variant_price = current_variant.price
|
||||
|
||||
assign public_or_tags_matched = true
|
||||
if product.metafields.app--168074346497.segment_tags.value.size > 0
|
||||
assign public_or_tags_matched = false
|
||||
endif
|
||||
|
||||
for etag in product.metafields.app--168074346497.segment_tags.value
|
||||
if customer.tags contains etag
|
||||
assign public_or_tags_matched = true
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
if public_or_tags_matched
|
||||
assign current_variant_price = current_variant.metafields.app--168074346497.auto_discounted_price.value | default: current_variant.price
|
||||
|
||||
if current_variant.metafields.app--168074346497.discount_type.value != nil and current_variant.metafields.app--168074346497.discount_type.value != "fixed" and product.metafields.app--168074346497.discount_percentage.value > 0.01
|
||||
assign deducted_percentage = 1.0 | minus: product.metafields.app--168074346497.discount_percentage.value
|
||||
|
||||
if current_variant.metafields.app--168074346497.discount_percentage.value > 0.01
|
||||
assign deducted_percentage = 1.0 | minus: current_variant.metafields.app--168074346497.discount_percentage.value
|
||||
endif
|
||||
|
||||
assign current_variant_price = current_variant.price | divided_by: 100.0 | times: deducted_percentage | times: 100.0 | ceil
|
||||
endif
|
||||
|
||||
if shop.metafields.app--168074346497.discount_percentage.value > 0.005
|
||||
assign discount_percentage = shop.metafields.app--168074346497.discount_percentage.value | times: 1.0
|
||||
assign deducted_percentage = 1.0 | minus: discount_percentage
|
||||
|
||||
assign current_variant_price = current_variant.price | divided_by: 100.0 | times: deducted_percentage | times: 100.0 | ceil
|
||||
endif
|
||||
endif
|
||||
|
||||
comment
|
||||
end Yagi app code
|
||||
endcomment
|
||||
%}
|
||||
<span class="cta__dot opacity-50">•</span>
|
||||
<span
|
||||
data-button-price
|
||||
{% if public_or_tags_matched %}data-yagi-matched{% endif %}
|
||||
data-yagi-price="{{ current_variant_price }}"
|
||||
x-text="$formatCurrency({{- current_variant_price -}} * Math.max(quantity, 1), { form: 'short', currency: '{{ cart.currency.iso_code }}' })"
|
||||
>
|
||||
{%- if settings.currency_code_enable -%}
|
||||
{{ current_variant_price | money_with_currency }}
|
||||
{%- else -%}
|
||||
{{ current_variant_price | money }}
|
||||
{%- endif -%}
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
|
||||
<span class="btn-state-loading">
|
||||
<svg height="18" width="18" class="svg-loader">
|
||||
<circle r="7" cx="9" cy="9" />
|
||||
<circle stroke-dasharray="87.96459430051421 87.96459430051421" r="7" cx="9" cy="9" />
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
<span class="btn-state-complete"> </span>
|
||||
</button>
|
||||
|
||||
{%- if buybutton_setting -%}
|
||||
<div class="product__submit__quick">
|
||||
{{ form | payment_button }}
|
||||
</div>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% comment %} Shop pay split payment terms {% endcomment %}
|
||||
<div class="shop-pay-terms">{{- form | payment_terms -}}</div>
|
||||
{%- endunless -%}
|
||||
|
||||
{% comment %} The input with name="id" indicates the variant to add to cart {% endcomment %}
|
||||
<input type="hidden" name="id" x-model.fill="variantId" value="{{ current_variant.id }}">
|
||||
{%- endform -%}
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,578 @@
|
||||
<!-- /snippets/product-grid-item-variant.liquid -->
|
||||
|
||||
{% comment %}
|
||||
Inner content for a grid item
|
||||
{% endcomment %}
|
||||
|
||||
{%- liquid
|
||||
assign on_sale = false
|
||||
assign product_price = product.price
|
||||
assign product_compare_at_price = product.compare_at_price
|
||||
|
||||
comment
|
||||
start Yagi app code
|
||||
endcomment
|
||||
assign public_or_tags_matched = true
|
||||
|
||||
if product.metafields.app--168074346497.segment_tags.value.size > 0
|
||||
assign public_or_tags_matched = false
|
||||
endif
|
||||
|
||||
for etag in product.metafields.app--168074346497.segment_tags.value
|
||||
if customer.tags contains etag
|
||||
assign public_or_tags_matched = true
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
if public_or_tags_matched
|
||||
assign product_price = product.metafields.app--168074346497.min_auto_discounted_price.value | default: product.price
|
||||
assign product_compare_at_price = product.compare_at_price
|
||||
|
||||
if shop.metafields.app--168074346497.discount_percentage.value > 0.005
|
||||
assign discount_percentage = shop.metafields.app--168074346497.discount_percentage.value | times: 1.0
|
||||
assign deducted_percentage = 1.0 | minus: discount_percentage
|
||||
assign product_price = product.price | divided_by: 100.0 | times: deducted_percentage | times: 100.0 | ceil
|
||||
endif
|
||||
|
||||
if product_price < product.price and product_compare_at_price == 0 or product_compare_at_price == blank
|
||||
assign product_compare_at_price = product.price
|
||||
endif
|
||||
endif
|
||||
|
||||
comment
|
||||
end Yagi app code
|
||||
endcomment
|
||||
if product_compare_at_price > product_price
|
||||
assign on_sale = true
|
||||
endif
|
||||
|
||||
assign sold_out = true
|
||||
if product.available
|
||||
assign sold_out = false
|
||||
endif
|
||||
|
||||
assign sellout_badge = false
|
||||
if sold_out and settings.badge_sellout
|
||||
assign sellout_badge = true
|
||||
endif
|
||||
|
||||
assign sale_badge = false
|
||||
if on_sale and settings.badge_sale
|
||||
assign sale_badge = true
|
||||
assign sale_badge_content = 'products.product.sale' | t
|
||||
if settings.badge_sale_discount
|
||||
if settings.badge_sale_type == 'dollar'
|
||||
if settings.currency_code_enable
|
||||
assign sale_badge_content = product_compare_at_price | minus: product_price | money_with_currency
|
||||
else
|
||||
assign sale_badge_content = product_compare_at_price | minus: product_price | money_without_trailing_zeros
|
||||
endif
|
||||
else
|
||||
assign difference = product_compare_at_price | minus: product_price
|
||||
assign percent_off = difference | times: 1.0 | divided_by: product_compare_at_price | times: 100
|
||||
assign sale_badge_content = percent_off | floor | append: '%'
|
||||
endif
|
||||
assign save_word = 'products.product.save' | t | append: ' '
|
||||
assign sale_badge_content = sale_badge_content | prepend: save_word
|
||||
endif
|
||||
endif
|
||||
|
||||
assign custom_badge = false
|
||||
if settings.badge_custom
|
||||
if product.metafields.theme.badge != blank and product.metafields.theme.badge.type == 'single_line_text_field'
|
||||
assign custom_badge = true
|
||||
assign custom_badge_content = product.metafields.theme.badge.value
|
||||
endif
|
||||
for tag in product.tags
|
||||
if tag contains "_badge_"
|
||||
assign tag_content = tag | remove: '_badge_' | replace: '_', ' '
|
||||
if tag_content != ''
|
||||
assign custom_badge = true
|
||||
assign custom_badge_content = tag_content
|
||||
endif
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
if badge_string and badge_string != ''
|
||||
assign custom_badge = true
|
||||
assign custom_badge_content = badge_string
|
||||
endif
|
||||
|
||||
assign tagged = false
|
||||
if sellout_badge or sale_badge or custom_badge
|
||||
assign tagged = true
|
||||
endif
|
||||
|
||||
comment
|
||||
Disqualify options that have more than 15 variants or are a combined length of > 90 characters
|
||||
endcomment
|
||||
if inline_variant_buttons.values.size > 15
|
||||
assign inline_variant_buttons = nil
|
||||
endif
|
||||
|
||||
if inline_variant_buttons
|
||||
assign all_characters = inline_variant_buttons.values | join: ""
|
||||
if all_characters.size >= 90
|
||||
assign inline_variant_buttons = nil
|
||||
endif
|
||||
endif
|
||||
|
||||
# Sellign plans can be added along with inline or instand buttons if
|
||||
# the product has exactly 1 selling plan with subscriptions required
|
||||
assign simple_selling_plan = nil
|
||||
|
||||
if inline_variant_buttons or instant_add_button
|
||||
if product.requires_selling_plan and product.selling_plan_groups.size == 1 and product.selling_plan_groups[0].selling_plans.size == 1
|
||||
# one variant, one required subscription, no choices to make
|
||||
assign simple_selling_plan = product.selected_or_first_available_selling_plan_allocation.selling_plan
|
||||
elsif product.selling_plan_groups.size > 0
|
||||
# Abort instant and inline add buttons, subs choices must be made
|
||||
assign inline_variant_buttons = nil
|
||||
assign instant_add_button = nil
|
||||
endif
|
||||
endif
|
||||
|
||||
# Catch case where first sibling has inline variants and subsequent do not
|
||||
if product.has_only_default_variant and inline_variant_buttons
|
||||
assign inline_variant_buttons = nil
|
||||
assign instant_add_button = true
|
||||
endif
|
||||
|
||||
# Allow configuration of image sizing for different numbers of grid columns
|
||||
# Note: desktop/tablet are set to a default of 3 just in case the grid sizes are not set to prevent accidental gigantic full-width images from being loaded
|
||||
assign columns_desktop = columns_desktop | default: section.settings.grid_large | default: 3
|
||||
assign columns_tablet = columns_tablet | default: section.settings.grid_medium | default: columns_desktop | default: 3
|
||||
assign columns_mobile = columns_mobile | default: section.settings.grid_mobile | default: 1
|
||||
assign section_width = section_width | default: section.settings.width | default: null
|
||||
-%}
|
||||
{%- capture badge -%}
|
||||
{%- if tagged %}
|
||||
{%- if custom_badge -%}
|
||||
<div class="product__badge product__badge--custom product__badge--{{ custom_badge_content | strip_html | handle }}">{{ custom_badge_content }}</div>
|
||||
{%- elsif sellout_badge -%}
|
||||
<div class="product__badge product__badge--sold">{{ 'products.product.sold_out' | t }}</div>
|
||||
{%- elsif sale_badge -%}
|
||||
<div class="product__badge product__badge--sale">{{ sale_badge_content }}</div>
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
{%- endcapture -%}
|
||||
|
||||
{%- liquid
|
||||
assign first_image = product.media[0].preview_image
|
||||
assign container_wh_ratio = settings.product_card_wh_ratio
|
||||
assign image_cover = true
|
||||
|
||||
case settings.product_grid_image
|
||||
when 'crop'
|
||||
assign image_wh_ratio = container_wh_ratio
|
||||
when 'uneven'
|
||||
assign container_wh_ratio = first_image.aspect_ratio | default: settings.product_card_wh_ratio
|
||||
when 'scale'
|
||||
assign image_cover = false
|
||||
assign image_wh_ratio = first_image.aspect_ratio | default: settings.product_card_wh_ratio
|
||||
endcase
|
||||
|
||||
# Behavior is inferred based on setting and passed into JS
|
||||
assign image_hover = 'disabled'
|
||||
assign images_limit = settings.cycle_images_limit
|
||||
case images_limit
|
||||
when 1
|
||||
assign image_hover = 'disabled'
|
||||
when 2
|
||||
assign image_hover = 'second_immediately'
|
||||
else
|
||||
assign image_hover = 'cycle_images'
|
||||
endcase
|
||||
-%}
|
||||
|
||||
{%- capture sizes -%}
|
||||
{%- render 'image-grid-sizes',
|
||||
columns_desktop: columns_desktop,
|
||||
columns_tablet: columns_tablet,
|
||||
columns_mobile: columns_mobile,
|
||||
section_width: section_width
|
||||
%}
|
||||
{%- endcapture -%}
|
||||
|
||||
<product-grid-item-variant
|
||||
class="
|
||||
product-grid-item__content{% if on_sale %} on-sale{% endif %}
|
||||
{% if sold_out %} sold-out{% endif %}
|
||||
{% if tagged %} tagged{% endif %}
|
||||
{% comment %} only used to hide badge on hover {% endcomment %}
|
||||
{% if image_hover == 'cycle_images' %} is-slideshow{% endif %}
|
||||
"
|
||||
style="
|
||||
--enter-animation-duration: 225ms;
|
||||
--exit-animation-duration: 400ms;
|
||||
"
|
||||
data-grid-item="{{ product.id }}"
|
||||
data-slideshow-style="{{ image_hover }}"
|
||||
data-grid-item-variant="{{ variant.id }}"
|
||||
{% if visible != true %} hidden {% endif %}
|
||||
aria-label="{{ variant.title }}"
|
||||
>
|
||||
<div class="product-grid-item__container" data-error-boundary>
|
||||
<div data-error-display class="product-grid-item__error-display"> </div>
|
||||
<a href="{{ product.url }}" data-grid-link aria-label="{{ product.title | strip_html | escape }}">
|
||||
<div
|
||||
class="product-grid-item__images aspect-[--wh-ratio]"
|
||||
data-grid-images data-grid-slide
|
||||
style="
|
||||
--wh-ratio: {{ container_wh_ratio }};
|
||||
"
|
||||
>
|
||||
{%- if product.media.size > 0 -%}
|
||||
{% comment %}
|
||||
Manually store and increment this variable since we start skipping images below when we exceed the allowed images which would
|
||||
make using e.g. forloop.index0 not work since the index of the variant image could be greater than the number of images allowed
|
||||
{% endcomment %}
|
||||
{%- assign image_index = 0 -%}
|
||||
|
||||
{%- for media in product.media -%}
|
||||
{%- liquid
|
||||
# If we've already exceeded the number of allowed images, and this is not the variant featured media, skip it
|
||||
if image_index > images_limit
|
||||
if product.selected_variant and product.selected_variant.featured_media.id != media.id
|
||||
continue
|
||||
elsif variant.featured_media.id != media.id
|
||||
continue
|
||||
endif
|
||||
endif
|
||||
|
||||
assign img_object = media.preview_image
|
||||
assign class = "product-grid-item__image"
|
||||
assign loading = 'lazy'
|
||||
assign fetchpriority = "low"
|
||||
assign visible = false
|
||||
assign active_class = 'is-active'
|
||||
assign is_variant_featured_media = false
|
||||
assign is_selected_variant = false
|
||||
assign preload_image = false
|
||||
assign loading_image = 'lazy'
|
||||
|
||||
if variant.featured_media and media.id == variant.featured_media.id
|
||||
# Variant image is not necessarily first image or default image
|
||||
assign is_variant_featured_media = true
|
||||
endif
|
||||
|
||||
if product.selected_variant and product.selected_variant.featured_image
|
||||
if product.selected_variant.featured_media.id == media.id
|
||||
assign is_selected_variant = true
|
||||
endif
|
||||
|
||||
# Show variant image is there is a collection filter applied
|
||||
if is_variant_featured_media and is_selected_variant
|
||||
assign visible = true
|
||||
endif
|
||||
else
|
||||
# If no filters are active show the first image first
|
||||
if forloop.first
|
||||
assign visible = true
|
||||
endif
|
||||
endif
|
||||
|
||||
if visible
|
||||
assign fetchpriority = "high"
|
||||
if preload
|
||||
assign loading = 'eager'
|
||||
assign preload_image = true
|
||||
endif
|
||||
if eagerload
|
||||
assign loading_image = 'eager'
|
||||
endif
|
||||
endif
|
||||
%}
|
||||
|
||||
{%- capture srcset -%}
|
||||
{%- render 'image-grid-srcset',
|
||||
image: img_object,
|
||||
columns_desktop: columns_desktop,
|
||||
columns_tablet: columns_tablet,
|
||||
columns_mobile: columns_mobile,
|
||||
section_width: section_width,
|
||||
wh_ratio: image_wh_ratio,
|
||||
crop: 'center'
|
||||
%}
|
||||
{%- endcapture -%}
|
||||
|
||||
{% comment %} Use a template to prevent hidden images from loading until user begins slideshow{% endcomment %}
|
||||
<product-grid-item-image
|
||||
class="
|
||||
product-grid-item__image-wrapper
|
||||
{% if visible %}{{ active_class }}{% endif %}
|
||||
"
|
||||
data-grid-image="{{ image_index }}"
|
||||
data-grid-image-target="{{ media.id }}"
|
||||
data-variant-id="{{ }}"
|
||||
loading="{{ loading }}"
|
||||
{% if visible %}data-grid-current-image{% endif %}
|
||||
{% if is_selected_variant and visible %}
|
||||
data-slide-for-filter-selected-variant
|
||||
{% endif %}
|
||||
{% if is_variant_featured_media %}
|
||||
data-slide-for-variant-media
|
||||
{% endif %}
|
||||
>
|
||||
{% unless visible %}<template>{% endunless %}
|
||||
|
||||
{% render 'image',
|
||||
cover: image_cover,
|
||||
img_object: img_object,
|
||||
class: class,
|
||||
sizes: sizes,
|
||||
srcset: srcset,
|
||||
preload: preload_image,
|
||||
loading: loading_image,
|
||||
fetchpriority: fetchpriority,
|
||||
wh_ratio: image_wh_ratio,
|
||||
placeholder: placeholder
|
||||
%}
|
||||
{% unless visible %}</template>{% endunless %}
|
||||
</product-grid-item-image>
|
||||
{%- assign image_index = image_index | plus: 1 -%}
|
||||
{%- endfor -%}
|
||||
{% else %}
|
||||
<div class="product-grid-item__image-wrapper is-active">
|
||||
{% render 'image',
|
||||
cover: image_cover,
|
||||
img_object: null,
|
||||
class: class,
|
||||
placeholder: placeholder,
|
||||
wh_ratio: image_wh_ratio
|
||||
%}
|
||||
</div>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
|
||||
{{ badge }}
|
||||
</a>
|
||||
|
||||
{% capture quick_action_toolbar_classes %}
|
||||
group/quick-actions-toolbar
|
||||
absolute
|
||||
flex flex-col justify-end items-end overflow-hidden
|
||||
top-[calc(var(--inner)/2)]
|
||||
right-[calc(var(--inner)/2)]
|
||||
bottom-[calc(var(--inner)/2)]
|
||||
left-[calc(var(--inner)/2)]
|
||||
transition duration-[--exit-animation-duration]
|
||||
md:items-normal
|
||||
md:opacity-0
|
||||
md:translate-y-r4
|
||||
md:group-hover/product-grid-item:opacity-100
|
||||
md:group-hover/product-grid-item:translate-y-0
|
||||
md:group-focus-within/product-grid-item:opacity-100
|
||||
md:group-focus-within/product-grid-item:translate-y-0
|
||||
|
||||
{% comment %}
|
||||
Prevent pointer events on the outer <inline-add-product> element since it covers the whole card
|
||||
and we only want the options menu to show when either the button wrapper or options menu itself
|
||||
are still being hovered
|
||||
{% endcomment %}
|
||||
pointer-events-none
|
||||
{% endcapture %}
|
||||
|
||||
{% capture quick_action_button_classes %}
|
||||
{{ settings.quick_add_button_color }}
|
||||
group/quick-action-button
|
||||
bg-button
|
||||
flex items-center justify-center
|
||||
type-accent font-bold text-r3
|
||||
transition-opacity duration-[--enter-animation-duration]
|
||||
pointer-events-auto
|
||||
w-r12 aspect-square
|
||||
min-w-[40px]
|
||||
min-h-[40px]
|
||||
md:min-h-[48px]
|
||||
md:px-r8 md:py-r5 md:w-full md:aspect-auto
|
||||
{% if sold_out %}opacity-50 !cursor-not-allowed{% endif %}
|
||||
{% endcapture %}
|
||||
|
||||
{%- if instant_add_button %}
|
||||
{% comment %} Allow for shorter default text on longer translations {% endcomment %}
|
||||
{% liquid
|
||||
if product.metafields.theme.preorder.value == true
|
||||
assign button_text = 'products.general.instant_add_pre_order' | t
|
||||
else
|
||||
assign button_text = 'products.general.instant_add' | t
|
||||
endif
|
||||
%}
|
||||
{% capture button %}
|
||||
<button
|
||||
data-add-to-cart
|
||||
type="submit"
|
||||
name="add"
|
||||
class="{{ quick_action_button_classes }}"
|
||||
:class="{
|
||||
'has-success': isSuccess,
|
||||
'loading': isLoading
|
||||
}"
|
||||
title="{% if sold_out %}{{ 'products.product.sold_out' | t }}{% else %}{{ button_text }}{% endif %}"
|
||||
:disabled="{{sold_out}} || isDisabled"
|
||||
aria-label="{{ button_text }}"
|
||||
>
|
||||
<span class="btn-state-ready text-button-contrast group-hover/quick-action-button:text-button-contrast/50 whitespace-nowrap">
|
||||
<span class="hidden md:block">
|
||||
{{ button_text }}
|
||||
</span>
|
||||
<span aria-hidden class="block md:hidden">
|
||||
{% render 'icon-set-classic-cart' %}
|
||||
</span>
|
||||
</span>
|
||||
<span class="btn-state-loading">
|
||||
<svg height="18" width="18" class="svg-loader" style="--border: rgb(var(--rgb-button-contrast) / 50%); --text: rgb(var(--rgb-button-contrast));">
|
||||
<circle r="7" cx="9" cy="9" />
|
||||
<circle stroke-dasharray="87.96459430051421 87.96459430051421" r="7" cx="9" cy="9" />
|
||||
</svg>
|
||||
</span>
|
||||
<span class="btn-state-complete" style="--primary: rgb(var(--rgb-button-contrast));"> </span>
|
||||
</button>
|
||||
{% endcapture %}
|
||||
<div class="{{quick_action_toolbar_classes}}">
|
||||
{% render 'product-add-button-form', variant: variant, selling_plan: simple_selling_plan, button: button, class: "md:w-full" %}
|
||||
</div>
|
||||
{%- elsif inline_variant_buttons %}
|
||||
<div class="{{quick_action_toolbar_classes}}" x-data="productGridItemQuickAddMenu()">
|
||||
<button
|
||||
class="
|
||||
{{ quick_action_button_classes }}
|
||||
transition-opacity
|
||||
"
|
||||
title="{{ 'products.general.inline_add' | t }}"
|
||||
aria-haspopup="true"
|
||||
:aria-expanded="isOpen"
|
||||
:id="$id('quick-add-menu-button')"
|
||||
:aria-controls="$id('quick-add-menu-slideover')"
|
||||
@click.stop="open()"
|
||||
@mouseover="open()"
|
||||
x-ref="button"
|
||||
:class="
|
||||
isOpen ?
|
||||
'duration-[--enter-animation-duration] delay-0 opacity-0 md:opacity-100' :
|
||||
'duration-[--exit-animation-duration] delay-[--exit-animation-duration] opacity-100'
|
||||
"
|
||||
aria-label="{{ 'products.general.inline_add' | t }}"
|
||||
>
|
||||
<span
|
||||
class="
|
||||
whitespace-nowrap
|
||||
text-button-contrast
|
||||
transition translate-y-0 transform
|
||||
"
|
||||
:class="
|
||||
isOpen ?
|
||||
'duration-[--enter-animation-duration] delay-0 translate-y-full opacity-0' :
|
||||
'duration-[--exit-animation-duration] delay-[calc(var(--exit-animation-duration))] translate-y-0 opacity-100'
|
||||
"
|
||||
>
|
||||
<span class="hidden md:block">
|
||||
{{ 'products.general.inline_add' | t }}
|
||||
</span>
|
||||
<span aria-hidden class="block md:hidden">
|
||||
{% render 'icon-set-classic-cart' %}
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="absolute top-0 right-0 bottom-0 left-0 overflow-hidden flex flex-col justify-end"
|
||||
role="popover"
|
||||
x-show="isOpen"
|
||||
x-cloak
|
||||
:aria-labelledby="$id('quick-add-menu-button')"
|
||||
>
|
||||
<div
|
||||
class="
|
||||
{{ settings.quick_add_button_color }}
|
||||
top-0 right-0 bottom-0 left-0 top-auto max-h-full overflow-scroll scrollbar-hide
|
||||
pointer-events-auto
|
||||
transition transform
|
||||
absolute
|
||||
origin-bottom
|
||||
bg-button
|
||||
md:opacity-100
|
||||
"
|
||||
x-show="isOpen"
|
||||
|
||||
{% comment %}
|
||||
Animate outer slider up or down
|
||||
{% endcomment %}
|
||||
x-transition:enter="duration-[calc(var(--enter-animation-duration)*2)] delay-[calc(var(--enter-animation-duration)/2)]"
|
||||
x-transition:enter-start="invisible translate-y-full opacity-0 md:opacity-100"
|
||||
x-transition:enter-end="visible translate-y-0 opacity-100 md:opacity-100"
|
||||
x-transition:leave="duration-[calc(var(--exit-animation-duration)*2)] delay-[calc(var(--exit-animation-duration)/4)]"
|
||||
x-transition:leave-start="visible translate-y-0 opacity-100 md:opacity-100"
|
||||
x-transition:leave-end="invisible translate-y-full opacity-0 md:opacity-100"
|
||||
>
|
||||
<div
|
||||
class="transition transform"
|
||||
x-show="isOpen"
|
||||
|
||||
{% comment %}
|
||||
Stagger inner slider up/down animation so it animates _after_ outer slider
|
||||
{% endcomment %}
|
||||
x-transition:enter="duration-[calc(var(--enter-animation-duration)*2)] delay-[calc(var(--enter-animation-duration)/2)]"
|
||||
x-transition:enter-start="translate-y-1/2 opacity-0"
|
||||
x-transition:enter-end="translate-y-0 opacity-full"
|
||||
x-transition:leave="duration-[calc(var(--exit-animation-duration))] delay-0"
|
||||
x-transition:leave-start="translate-y-0 opacity-full"
|
||||
x-transition:leave-end="translate-y-1/2 opacity-0"
|
||||
>
|
||||
{% render 'product-grid-item-quick-add-toolbar',
|
||||
inline_variants: inline_variants,
|
||||
inline_variant_buttons: inline_variant_buttons,
|
||||
simple_selling_plan: simple_selling_plan
|
||||
%}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{%- elsif settings.quickview_enable -%}
|
||||
<div class="{{quick_action_toolbar_classes}}" x-data="productQuickViewButton({{ product.id }}, '{{ product.handle }}')">
|
||||
<div class="quickview md:w-full" data-quickview-holder="{{ product.id }}" data-add-action-wrapper>
|
||||
<button
|
||||
type="button"
|
||||
class="{{ quick_action_button_classes }}"
|
||||
@click.prevent="clickQuickviewButton"
|
||||
:class="{
|
||||
'loading': isLoading
|
||||
}"
|
||||
title="{% if sold_out %}{{ 'products.product.sold_out' | t }}{% else %}{{ 'products.general.quick_view' | t }}{% endif %}"
|
||||
:disabled="{{sold_out}} || isDisabled"
|
||||
aria-label="{{ 'products.general.quick_view' | t }}"
|
||||
>
|
||||
<span class="btn-state-ready text-button-contrast group-hover/quick-action-button:text-button-contrast/50 whitespace-nowrap">
|
||||
<span class="hidden md:block">
|
||||
{{ 'products.general.quick_view' | t }}
|
||||
</span>
|
||||
<span aria-hidden class="block md:hidden">
|
||||
{% render 'icon-set-classic-cart' %}
|
||||
</span>
|
||||
</span>
|
||||
<span class="btn-state-loading">
|
||||
<svg height="18" width="18" class="svg-loader" style="--border: rgb(var(--rgb-button-contrast) / 50%); --text: rgb(var(--rgb-button-contrast));">
|
||||
<circle r="7" cx="9" cy="9" />
|
||||
<circle stroke-dasharray="87.96459430051421 87.96459430051421" r="7" cx="9" cy="9" />
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<script data-quickview-modal-template type="text/x-template">
|
||||
<div class="drawer drawer--right quickview__modal" data-quickview-modal data-form-holder id="{{ product.id }}" aria-hidden="true">
|
||||
<div class="drawer__content" data-product-quickview-ajax x-section-api='api-product-quickview'></div>
|
||||
|
||||
<span class="drawer__underlay" data-micromodal-close tabindex="-1">
|
||||
<span class="drawer__underlay__fill"></span>
|
||||
<span class="drawer__underlay__blur"></span>
|
||||
</span>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
</product-grid-item-variant>
|
||||
@@ -0,0 +1,670 @@
|
||||
<!-- /snippets/product-grid-item-variant.liquid -->
|
||||
|
||||
{% comment %}
|
||||
A grid item for products used in collection grid view
|
||||
|
||||
* product {object} - The current prodcut
|
||||
|
||||
{% render 'product-grid-item', product: product %}
|
||||
{% endcomment %}
|
||||
|
||||
{%- liquid
|
||||
|
||||
assign on_sale = false
|
||||
assign product_price = product.price
|
||||
assign product_compare_at_price = product.compare_at_price
|
||||
|
||||
comment
|
||||
start Yagi app
|
||||
endcomment
|
||||
assign public_or_tags_matched = true
|
||||
|
||||
if product.metafields.app--168074346497.segment_tags.value.size > 0
|
||||
assign public_or_tags_matched = false
|
||||
endif
|
||||
|
||||
for etag in product.metafields.app--168074346497.segment_tags.value
|
||||
if customer.tags contains etag
|
||||
assign public_or_tags_matched = true
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
if public_or_tags_matched
|
||||
assign product_price = product.metafields.app--168074346497.min_auto_discounted_price.value | default: product.price
|
||||
assign product_compare_at_price = product.compare_at_price
|
||||
|
||||
if shop.metafields.app--168074346497.discount_percentage.value > 0.005
|
||||
assign discount_percentage = shop.metafields.app--168074346497.discount_percentage.value | times: 1.0
|
||||
assign deducted_percentage = 1.0 | minus: discount_percentage
|
||||
assign product_price = product.price | divided_by: 100.0 | times: deducted_percentage | times: 100.0 | ceil
|
||||
endif
|
||||
|
||||
if product_price < product.price and product_compare_at_price == 0 or product_compare_at_price == blank
|
||||
assign product_compare_at_price = product.price
|
||||
endif
|
||||
endif
|
||||
comment
|
||||
end Yagi app
|
||||
endcomment
|
||||
|
||||
if product_compare_at_price > product_price
|
||||
assign on_sale = true
|
||||
endif
|
||||
|
||||
assign sold_out = true
|
||||
if product.available
|
||||
assign sold_out = false
|
||||
endif
|
||||
|
||||
# Siblings are a collection metafield called theme.siblings
|
||||
assign sibs_collection = nil
|
||||
assign has_siblings = false
|
||||
if settings.show_siblings and product.metafields.theme.siblings.value != blank and product.metafields.theme.siblings.type == 'single_line_text_field'
|
||||
assign sibs_collection = collections[product.metafields.theme.siblings.value]
|
||||
# Ensure the collection was set up to contain this product
|
||||
for sib_product in sibs_collection.products
|
||||
if sib_product == product
|
||||
assign has_siblings = true
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
assign swatch_option = nil
|
||||
assign has_swatches = false
|
||||
assign swatch_limit = settings.card_swatch_limit
|
||||
|
||||
if settings.swatches_enable and settings.swatches_collection_enable
|
||||
# Detect new swatch system
|
||||
for option in product.options_with_values
|
||||
if option.values.first.swatch != null
|
||||
assign has_swatches = true
|
||||
assign swatch_option = option
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
if has_swatches == false
|
||||
# Detect old swatch system
|
||||
assign swatch_translation = 'general.swatches.color' | t
|
||||
assign swatch_labels = swatch_translation | append: ',' | split: ','
|
||||
for label in swatch_labels
|
||||
assign sanitized_label = label | lstrip | rstrip
|
||||
if product.options_by_name[sanitized_label].values.size > 0
|
||||
assign has_swatches = true
|
||||
assign swatch_option = product.options_by_name[sanitized_label]
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
endif
|
||||
|
||||
# A button that will add to cart immediately: product with only one default variant or a product with color swatches and no other variants
|
||||
assign instant_add_button = nil
|
||||
|
||||
if settings.instant_add_enable
|
||||
if product.variants.size == 1
|
||||
assign instant_add_button = true
|
||||
elsif has_swatches and product.options.size == 1
|
||||
assign instant_add_button = true
|
||||
endif
|
||||
endif
|
||||
|
||||
# A quick add button that will show short variants like 'size' inside the button.
|
||||
assign inline_variant_buttons = nil
|
||||
|
||||
# Get the non color option
|
||||
# swatch_option.position -> 1 is off by one, it means product.options[0]
|
||||
# In case of siblings, the inline option buttons always use the first option
|
||||
if settings.instant_add_enable
|
||||
if product.options.size == 1
|
||||
assign inline_variant_buttons = product.options_with_values[0]
|
||||
elsif has_swatches and product.options.size == 2
|
||||
if swatch_option.position == 1
|
||||
assign inline_variant_buttons = product.options_with_values[1]
|
||||
else
|
||||
assign inline_variant_buttons = product.options_with_values[0]
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
# Capture the swatch link markup so we only have filter product.variants by color one time
|
||||
assign swatch_link_markup = ''
|
||||
|
||||
# Pass pre-filtered color variants into content, because the logic needs to know about
|
||||
# swatch option positions and swatches need to use the where filter to get the list
|
||||
assign inline_variants = nil
|
||||
|
||||
# Placeholder to be displayed when no product image
|
||||
assign placeholder = placeholder | default: false
|
||||
-%}
|
||||
|
||||
<product-grid-item
|
||||
aria-label="{{ product.title | strip_html | escape }}"
|
||||
class="product-grid-item group/product-grid-item"
|
||||
data-item-id="{{ product.id }}"
|
||||
{{ attributes }}
|
||||
>
|
||||
{% if has_siblings and sibs_collection.products.size > 0 %}
|
||||
{%- liquid
|
||||
# Initialize an empty array to hold visible siblings (i.e. ones under swatch limit)
|
||||
assign visible_siblings = "" | split: ","
|
||||
|
||||
# Initial visible sibling count is 1 to account for the current product
|
||||
assign visible_sibling_count = 1
|
||||
|
||||
# Loop through siblings collection and add the current product, plus other siblings until we hit the swatch_limit
|
||||
for sibling in sibs_collection.products
|
||||
if sibling.id == product.id or visible_sibling_count < swatch_limit
|
||||
# Create a new array with the current value, then concat it into the main array
|
||||
assign new_array = sibling | sort
|
||||
assign visible_siblings = visible_siblings | concat: new_array
|
||||
|
||||
unless sibling.id == product.id
|
||||
assign visible_sibling_count = visible_sibling_count | plus: 1
|
||||
endunless
|
||||
endif
|
||||
endfor
|
||||
-%}
|
||||
|
||||
{%- for sib_product in visible_siblings -%}
|
||||
{% liquid
|
||||
assign visible = false
|
||||
assign first_sibling_variant = sib_product.variants[0]
|
||||
if sib_product.id == product.id
|
||||
assign visible = true
|
||||
endif
|
||||
|
||||
if visible and preload
|
||||
assign preload_variant = true
|
||||
endif
|
||||
if visible and eagerload
|
||||
assign eagerload_variant = true
|
||||
endif
|
||||
|
||||
assign inline_variant_buttons = nil
|
||||
if settings.instant_add_enable and sib_product.options.size == 1
|
||||
assign inline_variant_buttons = sib_product.options_with_values[0]
|
||||
endif
|
||||
|
||||
if inline_variant_buttons
|
||||
assign inline_variants = sib_product.variants
|
||||
endif
|
||||
-%}
|
||||
|
||||
{% render 'product-grid-item-variant',
|
||||
product: sib_product,
|
||||
variant: first_sibling_variant,
|
||||
badge_string: badge_string,
|
||||
visible: visible,
|
||||
instant_add_button: instant_add_button,
|
||||
inline_variant_buttons: inline_variant_buttons,
|
||||
inline_variants: inline_variants,
|
||||
section_width: section_width,
|
||||
eagerload: eagerload_variant,
|
||||
preload: preload_variant,
|
||||
placeholder: placeholder,
|
||||
columns_desktop: columns_desktop,
|
||||
columns_tablet: columns_tablet,
|
||||
columns_mobile: columns_mobile
|
||||
%}
|
||||
{% endfor %}
|
||||
{% elsif has_swatches %}
|
||||
{%- liquid
|
||||
|
||||
# Note: this is a hack based on options having a property named option1, option2, or option3 -- which we rely on below to filter the variants array
|
||||
assign swatch_position_key = 'option' | append: swatch_option.position
|
||||
assign selected_swatch_value = swatch_option.selected_value
|
||||
|
||||
# Initialize an empty array to hold visible siblings (i.e. ones under swatch limit)
|
||||
assign visible_swatches = "" | split: ","
|
||||
|
||||
# If there is a selected swatch, start the visible swatch count at 1 to account for it
|
||||
if selected_swatch_value != blank
|
||||
assign visible_swatch_count = 1
|
||||
else
|
||||
assign visible_swatch_count = 0
|
||||
endif
|
||||
|
||||
# Loop through siblings collection and add the current product, plus other swatches until we hit the swatch limit
|
||||
for swatch_value in swatch_option.values
|
||||
if swatch_value == selected_swatch_value or visible_swatch_count < swatch_limit
|
||||
# Create a new array with the current value, then concat it into the main array
|
||||
assign new_array = swatch_value | sort
|
||||
assign visible_swatches = visible_swatches | concat: new_array
|
||||
|
||||
unless swatch_value == selected_swatch_value
|
||||
assign visible_swatch_count = visible_swatch_count | plus: 1
|
||||
endunless
|
||||
endif
|
||||
endfor
|
||||
-%}
|
||||
|
||||
{%- for swatch_value in visible_swatches -%}
|
||||
{% liquid
|
||||
|
||||
# Limit S|M|L to low-variant products to ensure collection page performance
|
||||
if product.variants.size < 200
|
||||
assign this_color_variants = product.variants | where: swatch_position_key, swatch_value
|
||||
assign in_stock_options = this_color_variants | where: 'available'
|
||||
if inline_variant_buttons
|
||||
assign inline_variants = this_color_variants
|
||||
endif
|
||||
else
|
||||
# Fall back to quickview with section-rendering API variant selection if there's too many variants
|
||||
assign inline_variant_buttons = false
|
||||
assign inline_variants = nil
|
||||
endif
|
||||
|
||||
|
||||
# Use the default variant for the swatch value. If that variant has media attached the image will change.
|
||||
# If not, the variant image will not change when swatch is selected.
|
||||
assign current_variant = swatch_value.variant
|
||||
|
||||
# If there is a variant selected from filters, show that variant first
|
||||
assign visible = false
|
||||
if product.selected_variant.id == current_variant
|
||||
assign visible = true
|
||||
elsif forloop.first
|
||||
assign visible = true
|
||||
endif
|
||||
|
||||
if visible and preload
|
||||
assign preload_variant = true
|
||||
endif
|
||||
if visible and eagerload
|
||||
assign eagerload_variant = true
|
||||
endif
|
||||
-%}
|
||||
|
||||
{% render 'product-grid-item-variant',
|
||||
product: product,
|
||||
variant: current_variant,
|
||||
badge_string: badge_string,
|
||||
visible: visible,
|
||||
instant_add_button: instant_add_button,
|
||||
inline_variant_buttons: inline_variant_buttons,
|
||||
inline_variants: inline_variants,
|
||||
section_width: section_width,
|
||||
swatch_option: swatch_option,
|
||||
eagerload: eagerload_variant,
|
||||
preload: preload_variant,
|
||||
placeholder: placeholder,
|
||||
columns_desktop: columns_desktop,
|
||||
columns_tablet: columns_tablet,
|
||||
columns_mobile: columns_mobile
|
||||
%}
|
||||
|
||||
{% capture swatch_link_markup %}
|
||||
{{ swatch_link_markup }}
|
||||
<radio-swatch title="{{ swatch_value.name }}" class="swatch__button{% if in_stock_options.size == 0 %} sold-out{% endif %}{% if settings.swatches_squares %} swatch__button--square{% endif %}">
|
||||
<a
|
||||
href="{{ current_variant.url }}"
|
||||
class="swatch__label"
|
||||
{% if swatch_value.swatch != null %}
|
||||
data-swatch
|
||||
style="--swatch: {{ swatch_value.swatch.color }}"
|
||||
{% else %}
|
||||
data-swatch="{{ swatch_value | escape_once }}"
|
||||
{% endif %}
|
||||
|
||||
data-grid-item-variant="{{ current_variant.id }}"
|
||||
data-swatch-index="{{ forloop.index0 }}"
|
||||
|
||||
{% if visible %}aria-current="true"{%- endif -%}
|
||||
{% if current_variant.featured_media %}
|
||||
data-swatch-image="{{ current_variant.featured_media.preview_image.src }}"
|
||||
{% endif %}
|
||||
|
||||
{% if current_variant.featured_media %}
|
||||
data-swatch-image-id="{{ current_variant.featured_media.id }}"
|
||||
{% endif %}
|
||||
>
|
||||
{% if swatch_value.swatch.image %}
|
||||
{% render 'image', img_object: swatch_value.swatch.image, width: 34, wh_ratio: 1 %}
|
||||
{% endif %}
|
||||
<span class="visually-hidden">{{ swatch_value }}</span>
|
||||
</a>
|
||||
</radio-swatch>
|
||||
{% endcapture %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
|
||||
{%- liquid
|
||||
|
||||
# Limit S|M|L to low-variant products to ensure collection page performance
|
||||
if inline_variant_buttons and product.variants.size < 200
|
||||
assign inline_variants = product.variants
|
||||
else
|
||||
assign inline_variant_buttons = false
|
||||
assign inline_variants = nil
|
||||
endif
|
||||
|
||||
assign selected_variant = product.selected_variant | default: product.variants[0]
|
||||
|
||||
if preload
|
||||
assign preload_variant = true
|
||||
endif
|
||||
if eagerload
|
||||
assign eagerload_variant = true
|
||||
endif
|
||||
-%}
|
||||
|
||||
{% render 'product-grid-item-variant',
|
||||
product: product,
|
||||
variant: selected_variant,
|
||||
badge_string: badge_string,
|
||||
visible: true,
|
||||
instant_add_button: instant_add_button,
|
||||
inline_variant_buttons: inline_variant_buttons,
|
||||
inline_variants: inline_variants,
|
||||
section_width: section_width,
|
||||
preload: preload_variant,
|
||||
eagerload: eagerload_variant,
|
||||
placeholder: placeholder,
|
||||
columns_desktop: columns_desktop,
|
||||
columns_tablet: columns_tablet,
|
||||
columns_mobile: columns_mobile
|
||||
%}
|
||||
{% endif %}
|
||||
|
||||
<div class="product__grid__info {{ text_align | default: settings.collection_text_alignment | default: 'text-center' }}">
|
||||
<a
|
||||
href="{{ product.url }}" data-grid-link aria-label="{{ product.title | strip_html | escape }}"
|
||||
{% comment %} The link wrapping the product variant image is already focusable, so this does not need to be {% endcomment %}
|
||||
tabindex="-1"
|
||||
>
|
||||
<p class="visually-hidden">{{ product.title | strip_html | escape }}</p>
|
||||
|
||||
<div class="product__grid__title__wrapper">
|
||||
<p id="product-{{ product.id }}-title" class="product__grid__title">
|
||||
{{ product.title | strip_html | escape }}
|
||||
</p>
|
||||
{%- if settings.product_grid_show_rating and product.metafields.reviews.rating.value != blank -%}
|
||||
<div class="rating__wrapper__grid">
|
||||
{% render 'product-rating', product: product, show_rating_count: settings.product_grid_show_rating_count %}
|
||||
</div>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
|
||||
<div class="product__grid__price {% if settings.show_cutline %} product__grid__price--nowrap{% endif %}">
|
||||
{%- if settings.show_cutline -%}
|
||||
<span class="product__grid__cutline">{{ product.metafields.theme.cutline.value }}</span>
|
||||
{%- endif -%}
|
||||
<span class="price{% if on_sale %} on-sale{% endif %}">
|
||||
{% if product.price_varies %}{{ 'products.general.from' | t }} {% endif %}
|
||||
{%- if settings.currency_code_enable -%}
|
||||
{{ product_price | money_with_currency }}
|
||||
{%- else -%}
|
||||
{{ product_price | money }}
|
||||
{%- endif -%}
|
||||
</span>
|
||||
{% if on_sale %}
|
||||
<span class="compare-at">
|
||||
{%- if settings.currency_code_enable -%}
|
||||
{{ product_compare_at_price | money_with_currency }}
|
||||
{%- else -%}
|
||||
{{ product_compare_at_price | money }}
|
||||
{%- endif -%}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if product.selected_or_first_available_variant.unit_price %}
|
||||
{% capture unit_price_separator %}
|
||||
<span aria-hidden="true">/</span><span class="visually-hidden">{{ 'general.accessibility.unit_price_separator' | t }} </span>
|
||||
{% endcapture %}
|
||||
{% capture unit_price_base_unit %}
|
||||
{% if product.selected_or_first_available_variant.unit_price_measurement.reference_value != 1 %}
|
||||
{{ product.selected_or_first_available_variant.unit_price_measurement.reference_value }}
|
||||
{% endif %}
|
||||
{{ product.selected_or_first_available_variant.unit_price_measurement.reference_unit }}
|
||||
{% endcapture %}
|
||||
<p class="product__grid__price__unit">
|
||||
<span class="visually-hidden">{{ 'products.product.unit_price_label' | t }}</span>
|
||||
<span class="price-per-unit">
|
||||
{%- if settings.currency_code_enable -%}
|
||||
{{ product.selected_or_first_available_variant.unit_price | money_with_currency }}
|
||||
{%- else -%}
|
||||
{{ product.selected_or_first_available_variant.unit_price | money }}
|
||||
{%- endif -%}
|
||||
{{ unit_price_separator }}{{ unit_price_base_unit }}
|
||||
</span>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% comment %} {% if sold_out %}
|
||||
<p class="product__grid__price__sold">
|
||||
<em>{{ 'products.product.sold_out' | t }}</em>
|
||||
</p>
|
||||
{% endif %} {% endcomment %}
|
||||
</a>
|
||||
|
||||
{% assign active_variant = product.selected_or_first_available_variant %}
|
||||
|
||||
<div class="product-card__actions mt-2">
|
||||
{%- form 'product', product, class: 'product-card__atc-form', novalidate: 'novalidate', data-type: 'add-to-cart-form', is: 'product-form' -%}
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="id"
|
||||
value="{{ active_variant.id }}"
|
||||
{% if active_variant.available == false %}disabled{% endif %}
|
||||
>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
name="add"
|
||||
class="product-card__atc"
|
||||
{% if active_variant.available == false %}disabled{% endif %}
|
||||
>
|
||||
{%- if active_variant.available -%}
|
||||
{{ 'products.product.add_to_cart' | t }}
|
||||
{%- else -%}
|
||||
{{ 'products.product.sold_out' | t }}
|
||||
{%- endif -%}
|
||||
</button>
|
||||
|
||||
{%- endform -%}
|
||||
</div>
|
||||
|
||||
|
||||
{%- if has_siblings -%}
|
||||
{%- assign sibs_collection = collections[product.metafields.theme.siblings.value] -%}
|
||||
{% if sibs_collection != blank %}
|
||||
{%- assign excess_swatches = sibs_collection.products_count | minus: swatch_limit -%}
|
||||
<div class="product__grid__sibs">
|
||||
<div class="grid__swatch__container">
|
||||
<p class="grid__swatch__placeholder">{{ 'collections.general.swatches_with_count' | t: count: sibs_collection.products_count }}</p>
|
||||
<div class="grid__swatch__hover">
|
||||
<div class="sibs__slider">
|
||||
<div class="sibs__inner">
|
||||
{%- for sib_product in visible_siblings -%}
|
||||
{%- assign title_safe = sib_product.title | strip_html | escape -%}
|
||||
{%- assign color_name = sib_product.metafields.theme.cutline.value | default: title_safe -%}
|
||||
<div class="siblings__link__holder {% if sib_product.available == false %} sold-out{% endif %}">
|
||||
<a
|
||||
href="{{ sib_product.url }}"
|
||||
class="siblings__link"
|
||||
data-sibling-swatch-link
|
||||
data-grid-item-variant="{{ sib_product.variants[0].id }}"
|
||||
data-grid-item-swatch-image="0"
|
||||
title="{{ color_name }}"
|
||||
{% if sib_product.handle == product.handle %}aria-current="true"{%- endif -%}
|
||||
>
|
||||
<div class="siblings__swatch">
|
||||
<div class="sibling__image{% if settings.swatches_squares %} sibling__image--square{% endif %}">
|
||||
{% assign image = sib_product.featured_media.preview_image %}
|
||||
{% assign image_width = 26 %}
|
||||
{% assign image_width_2x = image_width | times: 2 | at_most: image.width %}
|
||||
{% assign alt = image.alt | default: color_name %}
|
||||
|
||||
{% capture srcset %}
|
||||
{{ image | image_url: width: image_width_2x }} 2x,
|
||||
{{ image | image_url: width: image_width }}
|
||||
{% endcapture %}
|
||||
|
||||
{%- render 'image',
|
||||
img_object: image,
|
||||
wh_ratio: 1.0,
|
||||
srcset: srcset,
|
||||
fetchpriority: 'low',
|
||||
width: image_width,
|
||||
placeholder: placeholder,
|
||||
alt: alt
|
||||
-%}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{%- endfor -%}
|
||||
{% if excess_swatches > 0 %} <a class="siblings__more-link" href="{{product.url}}">+{{ excess_swatches }}</a>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{%- elsif has_swatches and swatch_link_markup != '' -%}
|
||||
{%- assign excess_swatches = swatch_option.values.size | minus: swatch_limit -%}
|
||||
|
||||
<div class="grid__swatch__container">
|
||||
<p class="grid__swatch__placeholder">
|
||||
{{ 'collections.general.swatches_with_count' | t: count: swatch_option.values.size }}
|
||||
</p>
|
||||
<div class="grid__swatch__hover" aria-label="Options">
|
||||
{{ swatch_link_markup }} {% if excess_swatches > 0 %}
|
||||
<a class="grid__swatch__more-link" href="{{product.url}}">+{{ excess_swatches }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
</product-grid-item>
|
||||
|
||||
<style>
|
||||
.product-card__actions {
|
||||
position: static !important;
|
||||
margin-top: 1rem;
|
||||
opacity: 1 !important;
|
||||
transform: none !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.product-card__actions {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
.product-card__atc {
|
||||
font-size: 0.9rem;
|
||||
padding: 0.8rem 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.product-card__actions .product-card__atc {
|
||||
width: 100% !important;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #000 !important;
|
||||
color: #fff !important;
|
||||
border: none !important;
|
||||
border-radius: 8px;
|
||||
padding: 0.5rem 1.25rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
transition: all 0.25s ease;
|
||||
box-sizing: border-box;
|
||||
font-size: 0.85rem;
|
||||
min-height: 0 !important;
|
||||
height: auto !important;
|
||||
font-family: var(--FONT-STACK-BODY);
|
||||
font-style: var(--FONT-STYLE-BODY);
|
||||
font-weight: var(--FONT-WEIGHT-BODY);
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.product-card__actions .product-card__atc {
|
||||
padding: 0.45rem 1rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
.product-card__actions .product-card__atc:hover,
|
||||
.product-card__actions .product-card__atc:focus {
|
||||
background-color: #111 !important;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.product-card__actions .product-card__atc[disabled] {
|
||||
background-color: #555 !important;
|
||||
color: #ccc !important;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.product-card:not(.product-card--list) .product-card__actions .btn {
|
||||
opacity: 1 !important;
|
||||
visibility: visible !important;
|
||||
transform: none !important;
|
||||
pointer-events: auto !important;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.product-card__main-actions {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* === Fix product card squishing on small screens === */
|
||||
@media (max-width: 767px) {
|
||||
.collection-products .product-grid,
|
||||
.collection-products .grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.collection-products .product-card {
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.collection-products .product-grid,
|
||||
.collection-products .grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.product-card,
|
||||
.product-card__wrapper {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.product-card__image-wrapper { flex: 0 0 auto; }
|
||||
.product-card__info { flex: 1 1 auto; display: flex; flex-direction: column; }
|
||||
.product-card__actions { margin-top: auto; }
|
||||
|
||||
.product__grid__title {
|
||||
line-height: 1.3;
|
||||
overflow: hidden;
|
||||
min-height: calc(1.3em * 2); /* ensures same vertical space even if short */
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2; /* clamp to 2 lines */
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
@supports not (-webkit-line-clamp: 2) {
|
||||
.product__grid__title { max-height: calc(1.3em * 2); }
|
||||
}
|
||||
|
||||
.product-card__type,
|
||||
.f-price { white-space: normal; word-break: break-word; }
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
<!-- /snippets/product-price.liquid -->
|
||||
{%- liquid
|
||||
assign current_variant = product.selected_or_first_available_variant
|
||||
assign price = current_variant.price
|
||||
assign compare_at_price = current_variant.compare_at_price
|
||||
|
||||
comment
|
||||
start Yagi app code
|
||||
endcomment
|
||||
assign public_or_tags_matched = true
|
||||
|
||||
if product.metafields.app--168074346497.segment_tags.value.size > 0
|
||||
assign public_or_tags_matched = false
|
||||
endif
|
||||
|
||||
for etag in product.metafields.app--168074346497.segment_tags.value
|
||||
if customer.tags contains etag
|
||||
assign public_or_tags_matched = true
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
if public_or_tags_matched
|
||||
assign price = current_variant.metafields.app--168074346497.auto_discounted_price.value | default: current_variant.price
|
||||
assign compare_at_price = current_variant.compare_at_price
|
||||
|
||||
if shop.metafields.app--168074346497.discount_percentage.value > 0.005
|
||||
assign discount_percentage = shop.metafields.app--168074346497.discount_percentage.value | times: 1.0
|
||||
assign deducted_percentage = 1.0 | minus: discount_percentage
|
||||
assign price = current_variant.price | divided_by: 100.0 | times: deducted_percentage | times: 100.0 | ceil
|
||||
endif
|
||||
|
||||
if price < current_variant.price and compare_at_price == 0 or compare_at_price == blank
|
||||
assign compare_at_price = current_variant.price
|
||||
endif
|
||||
endif
|
||||
|
||||
comment
|
||||
end Yagi app code
|
||||
endcomment
|
||||
|
||||
assign sale_type = settings.badge_sale_type
|
||||
|
||||
if block.settings.price_size
|
||||
assign price_size_class = block.settings.price_size | prepend: 'accent-size-'
|
||||
assign price_small_class = block.settings.price_size | minus: 1 | prepend: 'accent-size-'
|
||||
endif
|
||||
|
||||
# Select current selling plan if it's specified in the URL
|
||||
assign selected_selling_plan = product.selected_selling_plan
|
||||
if product.requires_selling_plan
|
||||
# Fallback to the first available selling plan
|
||||
assign selected_selling_plan = product.selected_or_first_available_selling_plan_allocation.selling_plan | default: product.selling_plan_groups[0].selling_plans[0]
|
||||
endif
|
||||
|
||||
assign sale_text = 'products.product.sale' | t
|
||||
|
||||
# Subscription price
|
||||
if selected_selling_plan
|
||||
assign sale_text = 'products.product.subscription' | t
|
||||
|
||||
# Make sure the variant exists(it isn't 'unavailable') and showing a price makes sense
|
||||
if current_variant
|
||||
assign price_adjustment = selected_selling_plan.price_adjustments[0]
|
||||
|
||||
case price_adjustment.value_type
|
||||
when 'percentage'
|
||||
assign sale_type = 'percentage'
|
||||
assign plan_discount_amount = price | times: price_adjustment.value | divided_by: 100
|
||||
assign compare_at_price = price
|
||||
assign price = compare_at_price | minus: plan_discount_amount
|
||||
when 'fixed_amount'
|
||||
assign sale_type = 'dollar'
|
||||
assign compare_at_price = price
|
||||
assign price = compare_at_price | minus: price_adjustment.value
|
||||
when 'price'
|
||||
assign sale_type = 'dollar'
|
||||
assign compare_at_price = price
|
||||
assign price = price_adjustment.value
|
||||
endcase
|
||||
endif
|
||||
endif
|
||||
-%}
|
||||
|
||||
<div class="product__block__price" style="--PB: {{ block.settings.padding_bottom }}px;" {{ block.shopify_attributes }}>
|
||||
<div class="product__price__wrap">
|
||||
<div class="product__price__main" data-price-wrapper>
|
||||
<span class="product__price {{ price_size_class }}">
|
||||
<span data-product-price {% if compare_at_price > price %} class="product__price--sale"{% endif %}>
|
||||
{%- if settings.currency_code_enable -%}
|
||||
{{ price | money_with_currency }}
|
||||
{%- else -%}
|
||||
{{ price | money }}
|
||||
{%- endif -%}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
{% if compare_at_price > price or product.selected_selling_plan %}
|
||||
{% case sale_type %}
|
||||
{% when 'strike' %}
|
||||
<span data-compare-price class="product__price--compare {{ price_size_class }}">
|
||||
{%- if settings.currency_code_enable -%}
|
||||
{{ compare_at_price | money_with_currency }}
|
||||
{%- else -%}
|
||||
{{ compare_at_price | money }}
|
||||
{%- endif -%}
|
||||
</span>
|
||||
{% when 'percentage' %}
|
||||
{% assign difference = compare_at_price | minus: price %}
|
||||
{% assign percent_off = difference | times: 100 | divided_by: compare_at_price %}
|
||||
<span class="product__price--off">
|
||||
<span>{{ sale_text }}</span>
|
||||
{% if percent_off > 0 %}
|
||||
<em>•</em>
|
||||
{{ 'products.product.save' | t }}
|
||||
<span>{{ percent_off | floor | append: '%' }}</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% when 'dollar' %}
|
||||
{% assign amount_off = compare_at_price | minus: price %}
|
||||
<span class="product__price--off">
|
||||
<span>{{ sale_text }}</span>
|
||||
<em>•</em>
|
||||
{{ 'products.product.save' | t }}
|
||||
<span>{{ amount_off | money }}</span>
|
||||
</span>
|
||||
{% endcase %}
|
||||
{% endif %}
|
||||
|
||||
{% if current_variant.unit_price != blank %}
|
||||
{% capture show_units %}
|
||||
{%- unless current_variant.unit_price -%}style="display: none;"{%- endunless -%}
|
||||
{% endcapture %}
|
||||
{% capture unit_price_separator %}
|
||||
<span aria-hidden="true">/</span><span class="visually-hidden">{{ 'general.accessibility.unit_price_separator' | t }} </span>
|
||||
{% endcapture %}
|
||||
{% capture unit_price_base_unit %}
|
||||
<span>
|
||||
{% if current_variant.unit_price_measurement %}
|
||||
{% if current_variant.unit_price_measurement.reference_value != 1 %}
|
||||
{{ current_variant.unit_price_measurement.reference_value }}
|
||||
{%- endif -%}
|
||||
{{ current_variant.unit_price_measurement.reference_unit }}
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endcapture %}
|
||||
<div class="product__price--unit {{ price_small_class }}">
|
||||
<span data-product-unit {{ show_units }}>
|
||||
<span class="visually-hidden visually-hidden--inline">{{ 'products.product.unit_price_label' | t }}</span>
|
||||
<span data-product-unit-price id="unit-price-{{ block.id }}">
|
||||
{%- if settings.currency_code_enable -%}
|
||||
{{ current_variant.unit_price | money_with_currency }}
|
||||
{%- else -%}
|
||||
{{ current_variant.unit_price | money }}
|
||||
{%- endif -%}
|
||||
</span>
|
||||
{{ unit_price_separator }}
|
||||
<span data-product-base id="unit-price-base-{{ block.id }}">{{ unit_price_base_unit }}</span>
|
||||
</span>
|
||||
<span class="hide">
|
||||
{{ 'products.product.each' | t }}
|
||||
<span></span>
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user