first commit

This commit is contained in:
Axel
2026-04-30 03:13:31 +08:00
commit b9f58a20af
6 changed files with 2279 additions and 0 deletions
+306
View File
@@ -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 -%}
+384
View File
@@ -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 }}&nbsp;</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 %}