Files
Warehouse/snippets/product-item.liquid

385 lines
19 KiB
Plaintext
Raw Permalink Normal View History

2025-11-25 03:01:55 +08:00
{%- comment -%}Color labels used to detect what is a color swatch{%- endcomment -%}
{%- assign color_label = 'color,colour,couleur,cor,colore,farbe,색,色,カラー,färg,farve,szín,barva' | split: ',' -%}
{%- liquid
assign product_price = product.metafields.app--168074346497.min_auto_discounted_price.value | default: product.price
assign product_price_min = product.price
assign product_price_max = product.price_max
assign product_compare_at_price = product.compare_at_price
assign product_compare_at_price_min = product.compare_at_price_min
if product.metafields.app--168074346497.discount_percentage.value > 0.01
assign deducted_percentage = 1.0 | minus: product.metafields.app--168074346497.discount_percentage.value
assign product_price = product.price | divided_by: 100.0 | times: deducted_percentage | times: 100.0 | ceil
assign product_price_min = product_price
assign product_price_max = product.price_max | divided_by: 100.0 | times: deducted_percentage | times: 100.0 | ceil
assign product_compare_at_price = product.price
assign product_compare_at_price_min = product_compare_at_price
if product.compare_at_price > product_compare_at_price
assign product_compare_at_price = product.compare_at_price
assign product_compare_at_price_min = product_compare_at_price
endif
endif
if product_price < product.price and product_compare_at_price == blank
assign product_compare_at_price = product.price
assign product_compare_at_price_min = product.price
endif
-%}
<div class="product-item {% if horizontal %}product-item--horizontal{% elsif list %}product-item--list{% else %}product-item--vertical{% endif %} {% if complementary_products %}product-item--complementary{% endif %} {% if section.id == 'blog-sidebar' %}product-item--compact{% endif %} {{ grid_classes }}">
{%- capture product_labels -%}
{%- unless hide_labels -%}
{%- assign custom_badges = product.metafields.custom.badges.value -%}
{%- for badge in custom_badges -%}
<span class="product-label product-label--custom1">{{ badge }}</span>
{%- endfor -%}
{%- for tag in product.tags -%}
{%- if tag contains '__label:' -%}
<span class="product-label product-label--custom1">{{ tag | split: '__label:' | last }}</span>
{%- endif -%}
{%- if tag contains '__label1:' -%}
<span class="product-label product-label--custom1">{{ tag | split: '__label1:' | last }}</span>
{%- endif -%}
{%- if tag contains '__label2:' -%}
<span class="product-label product-label--custom2">{{ tag | split: '__label2:' | last }}</span>
{%- endif -%}
{%- endfor -%}
{%- if settings.show_discount and product_price < product_compare_at_price -%}
{%- if settings.discount_mode == 'percentage' -%}
{%- assign savings = product_compare_at_price | minus: product_price | times: 100.0 | divided_by: product_compare_at_price | round | append: '%' -%}
{%- else -%}
{%- capture savings -%}<span>{{ product_compare_at_price | minus: product_price | money }}</span>{%- endcapture -%}
{%- endif -%}
<span class="product-label product-label--on-sale">{{ 'collection.product.discount_html' | t: savings: savings }}</span>
{%- endif -%}
{%- endunless -%}
{%- endcapture -%}
{%- if product_labels != blank -%}
<div class="product-item__label-list">
{{- product_labels -}}
</div>
{%- endif -%}
{%- if settings.show_secondary_image and product.media.size > 1 -%}
{%- assign show_secondary_media = true -%}
{%- else -%}
{%- assign show_secondary_media = false -%}
{%- endif -%}
{%- assign filtered_variant = '' -%}
{%- if product.media.size > 0 -%}
{%- assign primary_media = product.featured_media -%}
<a href="{{ product.url }}" class="product-item__image-wrapper {% if show_secondary_media %}product-item__image-wrapper--with-secondary{% endif %}">
<div class="aspect-ratio {% if settings.product_image_size != 'natural' %}aspect-ratio--{{ settings.product_image_size }}{% endif %}" style="padding-bottom: {{ 100.0 | divided_by: primary_media.preview_image.aspect_ratio }}%">
{{- primary_media | image_url: width: primary_media.width | image_tag: loading: 'lazy', sizes: '(max-width: 699px) 100vw, 600px', widths: '200,300,400,600,800,1000,1200,1400,1600', data-media-id: primary_media.id, class: 'product-item__primary-image' -}}
{%- if show_secondary_media -%}
{{- product.media[1] | image_url: width: product.media[1].width | image_tag: loading: 'lazy', sizes: '(max-width: 699px) 100vw, 600px', widths: '200,300,400,600,800,1000,1200,1400,1600', class: 'product-item__secondary-image' -}}
{%- endif -%}
</div>
</a>
{%- endif -%}
<div class="product-item__info">
<div class="product-item__info-inner">
{%- capture price_list -%}
<spark-product-card parent-id="{{ product.id }}"></spark-product-card>
<div data-spark="b2c-only" class="product-item__price-list price-list">
{%- if product_price < product_compare_at_price -%}
{%- if product.price_varies -%}
{%- if settings.currency_code_enabled -%}
{%- capture price_min -%}{{ product_price_min | money_with_currency }}{%- endcapture -%}
{%- capture price_max -%}{{ product_price_max | money_with_currency }}{%- endcapture -%}
{%- else -%}
{%- capture price_min -%}{{ product_price_min | money }}{%- endcapture -%}
{%- capture price_max -%}{{ product_price_max | money }}{%- endcapture -%}
{%- endif -%}
<span class="price price--highlight">
<span class="visually-hidden">{{ 'product.general.sale_price' | t }}</span>
{{- 'collection.product.from_price_html' | t: price_min: price_min, price_max: price_max -}}
</span>
<span class="price price--compare">
<span class="visually-hidden">{{ 'product.general.regular_price' | t }}</span>
{%- if settings.currency_code_enabled -%}
{{- product_compare_at_price_min | money_with_currency -}}
{%- else -%}
{{- product_compare_at_price_min | money -}}
{%- endif -%}
</span>
{%- else -%}
<span class="price price--highlight">
<span class="visually-hidden">{{ 'product.general.sale_price' | t }}</span>
{%- if settings.currency_code_enabled -%}
{{- product_price | money_with_currency -}}
{%- else -%}
{{- product_price | money -}}
{%- endif -%}
</span>
<span class="price price--compare">
<span class="visually-hidden">{{ 'product.general.regular_price' | t }}</span>
{%- if settings.currency_code_enabled -%}
{{- product_compare_at_price | money_with_currency -}}
{%- else -%}
{{- product_compare_at_price | money -}}
{%- endif -%}
</span>
{%- endif -%}
{%- elsif product.price_varies -%}
{%- if settings.currency_code_enabled -%}
{%- capture price_min -%}{{ product_price_min | money_with_currency }}{%- endcapture -%}
{%- capture price_max -%}{{ product_price_max | money_with_currency }}{%- endcapture -%}
{%- else -%}
{%- capture price_min -%}{{ product_price_min | money }}{%- endcapture -%}
{%- capture price_max -%}{{ product_price_max | money }}{%- endcapture -%}
{%- endif -%}
<span class="price">
<span class="visually-hidden">{{ 'product.general.sale_price' | t }}</span>
{{- 'collection.product.from_price_html' | t: price_min: price_min, price_max: price_max -}}
</span>
{%- else -%}
<span class="price">
<span class="visually-hidden">{{ 'product.general.sale_price' | t }}</span>
{%- if settings.currency_code_enabled -%}
{{- product_price | money_with_currency -}}
{%- else -%}
{{- product_price | money -}}
{%- endif -%}
</span>
{%- endif -%}
</div>
{%- assign variant_to_use = selected_variant | default: product.selected_or_first_available_variant -%}
{%- if variant_to_use.unit_price -%}
<div class="product-item__price-info">
<div class="unit-price-measurement">
{{- variant_to_use.unit_price | unit_price_with_measurement: variant_to_use.unit_price_measurement -}}
</div>
</div>
{%- endif -%}
{%- endcapture -%}
{%- capture vendor -%}
{%- if settings.show_vendor -%}
{%- assign vendor_handle = product.vendor | handle -%}
{%- assign collection_for_vendor = collections[vendor_handle] -%}
{%- unless collection_for_vendor.empty? -%}
<a class="product-item__vendor link" href="{{ collection_for_vendor.url }}">{{ product.vendor }}</a>
{%- else -%}
<a class="product-item__vendor link" href="{{ product.vendor | url_for_vendor }}">{{ product.vendor }}</a>
{%- endunless -%}
{%- endif -%}
{%- endcapture -%}
{%- if settings.product_price_position == 'before_title' -%}
{{ price_list }}
{%- endif -%}
{%- if settings.product_price_position == 'after_title' -%}
{{ vendor }}
{%- endif -%}
<a href="{{ filtered_variant.url | default: product.url }}" class="product-item__title text--strong link">{{ product.title }}</a>
{%- if settings.product_price_position == 'before_title' -%}
{{ vendor }}
{%- endif -%}
{%- if settings.show_color_swatch and template != 'blog' -%}
{%- capture color_swatch -%}
{%- capture color_name -%}{{ section.id }}-{{ product.id }}{%- endcapture -%}
{%- for option in product.options_with_values -%}
{%- assign downcased_option = option.name | downcase -%}
{%- if color_label contains downcased_option -%}
{%- assign variant_option = 'option' | append: forloop.index -%}
{%- assign value_to_match = filtered_variant[variant_option] | default: option.selected_value -%}
{%- assign color_swatch_config = settings.color_swatch_config | newline_to_br | split: '<br />' -%}
{%- for value in option.values -%}
{%- assign downcased_value = value | downcase -%}
{%- capture color_id -%}{{ color_name }}-{{ forloop.index }}{%- endcapture -%}
{%- for variant in product.variants -%}
{%- if variant[variant_option] == value -%}
{%- assign variant_for_value = variant -%}
{%- break -%}
{%- endif -%}
{%- endfor -%}
<div class="color-swatch {% if downcased_value == 'white' or downcased_value == 'blanc' %}color-swatch--white{% endif %}">
{%- if variant_for_value.featured_media -%}
{%- assign image_url = variant_for_value.featured_media | image_url: width: 800 -%}
{%- endif -%}
<input class="color-swatch__radio" type="radio" name="{{ color_name }}" id="{{ color_id }}" value="{{ value | escape }}" {% if value_to_match == value %}checked="checked"{% endif %} data-variant-url="{{ variant_for_value.url }}" {% if variant_for_value.featured_media %}data-media-id="{{ variant_for_value.featured_media.id }}" data-image-url="{{ image_url }}" data-image-aspect-ratio="{{ variant_for_value.featured_media.preview_image.aspect_ratio }}"{% endif %} aria-label="{{ value | escape }}">
<label class="color-swatch__item" for="{{ color_id }}" style="{% render 'color-swatch-style', swatch: value.swatch, color_swatch_config: color_swatch_config, value: downcased_value %}" title="{{ value | escape }}">
<span class="visually-hidden">{{ value }}</span>
</label>
<a href="{{ product.url }}" class="color-swatch__item-link">+{{ option.values.size | minus: forloop.index0 }}</a>
</div>
{%- endfor -%}
{%- endif -%}
{%- endfor -%}
{%- endcapture -%}
{%- if color_swatch != blank -%}
<div class="product-item__swatch-list">
<div class="color-swatch-list">
{{ color_swatch }}
</div>
</div>
{%- endif -%}
{%- endif -%}
{%- if settings.product_price_position == 'after_title' -%}
{{ price_list }}
{%- endif -%}
{%- if settings.show_reviews_badge -%}
<a class="product-item__reviews-badge link" href="{{ product.url }}#product-reviews">
{%- render 'product-rating', product: product -%}
</a>
{%- endif -%}
{%- if settings.show_inventory_quantity and product.variants.first.inventory_management != blank and product.template_suffix != 'pre-order' -%}
{%- if product.available -%}
{%- assign should_calculate_inventory = true -%}
{%- for variant in product.variants -%}
{%- comment -%}
If we have one variant that is set to "continue" or that do not have any inventory management, then we skip the calculation of inventory as this
means at least one of the variant is always purchasable
{%- endcomment -%}
{%- if variant.inventory_policy == 'continue' or variant.inventory_management == null -%}
{%- assign should_calculate_inventory = false -%}
{%- break -%}
{%- endif -%}
{%- endfor -%}
{%- if should_calculate_inventory and settings.low_inventory_threshold > 0 -%}
{%- assign all_inventory = 0 -%}
{%- for variant in product.variants -%}
{%- if variant.inventory_management -%}
{%- assign all_inventory = variant.inventory_quantity | at_least: 0 | plus: all_inventory -%}
{%- endif -%}
{%- endfor -%}
{%- if all_inventory <= settings.low_inventory_threshold -%}
<span class="product-item__inventory inventory inventory--low">{{ 'collection.product.low_stock_with_quantity_count' | t: count: all_inventory }}</span>
{%- else -%}
<span class="product-item__inventory inventory inventory--high">{{ 'collection.product.in_stock_with_quantity_count' | t: count: all_inventory }}</span>
{%- endif -%}
{%- else -%}
{%- if product.variants.first.inventory_policy == 'continue' and product.variants.first.inventory_quantity <= 0 and product.variants.first.requires_shipping -%}
<span class="product-item__inventory inventory inventory--high">{{ 'collection.product.oversell_stock' | t }}</span>
{%- else -%}
<span class="product-item__inventory inventory inventory--high">{{ 'collection.product.in_stock' | t }}</span>
{%- endif -%}
{%- endif -%}
{%- else -%}
<span class="product-item__inventory inventory">{{ 'collection.product.sold_out' | t }}</span>
{%- endif -%}
{%- endif -%}
</div>
{%- if request.page_type == 'collection' or request.page_type == 'search' or show_add_to_cart -%}
{%- if section.settings.show_quick_view == 'list_grid' or section.settings.show_quick_buy == 'list_grid' or show_add_to_cart -%}
{%- assign product_form_classes = 'product-item__action-list button-stack' -%}
{%- else -%}
{%- assign product_form_classes = 'product-item__action-list product-item__action-list--list-view-only button-stack' -%}
{%- endif -%}
{%- assign form_id = 'product_form_id_' | append: product.id | append: '_' | append: section.id -%}
{%- if product.template_suffix == 'contact' -%}
<div data-spark="b2c-only" class="{{ product_form_classes }}">
<a href="mailto:{{ shop.email }}" class="product-item__action-button button button--small button--primary">
{{- 'product.form.contact_us' | t -}}
</a>
</div>
{%- else -%}
{%- capture quick_buy_form -%}
{%- form 'product', product, id: form_id, class: product_form_classes -%}
<input type="hidden" name="quantity" value="1">
<input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
{%- if section.settings.show_quick_buy == 'list_grid' or show_add_to_cart -%}
{%- if complementary_products -%}
{%- assign quick_buy_classes = 'product-item__action-button button button--small button--ternary' -%}
{%- else -%}
{%- assign quick_buy_classes = 'product-item__action-button button button--small button--primary' -%}
{%- endif -%}
{%- else -%}
{%- assign quick_buy_classes = 'product-item__action-button product-item__action-button--list-view-only button button--small button--primary' -%}
{%- endif -%}
{%- if product.available -%}
{%- if product.variants.size == 1 -%}
<buy-button data-spark="b2c-only" class="buy-button">
<button type="submit" class="{{ quick_buy_classes }}">
{%- if product.template_suffix == 'pre-order' -%}
{{- 'collection.product.pre_order' | t -}}
{%- else -%}
{{- 'collection.product.add_to_cart' | t -}}
{%- endif -%}
</button>
</buy-button>
{%- else -%}
<a href="{{ product.url }}" class="{{ quick_buy_classes }}">{{ 'collection.product.choose_options' | t }}</a>
{%- endif -%}
{%- else -%}
<button class="{{ quick_buy_classes | replace: 'button--primary', 'button--disabled' }}" disabled>{{ 'collection.product.sold_out' | t }}</button>
{%- endif -%}
{%- if section.settings.show_quick_view == 'list_grid' -%}
{%- assign quick_view_classes = 'product-item__action-button button button--small button--ternary hidden-phone' -%}
{%- else -%}
{%- assign quick_view_classes = 'product-item__action-button product-item__action-button--list-view-only button button--small button--ternary hidden-phone' -%}
{%- endif -%}
<button type="button" class="{{ quick_view_classes }}" data-action="open-modal" data-secondary-action="open-quick-view" aria-controls="modal-quick-view-{{ section.id }}" data-product-url="{{ product.url }}">{{ 'collection.product.quick_view' | t }}</button>
{%- endform -%}
{%- endcapture -%}
{%- if complementary_products != true -%}
{{- quick_buy_form -}}
{%- endif -%}
{%- endif -%}
{%- endif -%}
</div>
{%- comment -%}
The complementary product has an exception, where the form is shown outside
{%- endcomment -%}
{%- if complementary_products and show_add_to_cart -%}
{{- quick_buy_form -}}
{%- endif -%}
</div>