326 lines
17 KiB
Plaintext
326 lines
17 KiB
Plaintext
{%- comment -%}
|
|
----------------------------------------------------------------------------------------------------------------------
|
|
PRODUCT CARD COMPONENT
|
|
----------------------------------------------------------------------------------------------------------------------
|
|
|
|
This component is used in collection and featured collection to render a single product as a card
|
|
|
|
********************************************
|
|
Supported variables
|
|
********************************************
|
|
|
|
* product: the product to render
|
|
* stacked: define if the product is stacked or not on mobile
|
|
* reveal: if set to true, the card will be revealed on scroll through animation
|
|
* show_badges: show or not the badges (default to true if nothing is set).
|
|
* show_rating: show or not the rating. If nothing is set, the theme uses the default product card setting
|
|
* show_vendor: show or not the vendor. If nothing is set, the theme uses the default product card setting
|
|
* show_quick_buy: show or not the quick buy (which open a modal if the product contains more than 1 choice)
|
|
* show_secondary_image: show or not the secondary media on hover. If nothing is set, the theme uses the default product card setting
|
|
* show_swatches: show or not the swatches. The swatch type is inferred from setting, and will default to true if nothing is set.
|
|
* hide_product_information: if set to true, all content (vendor, badge, title...) are not rendered, to create image-based grid
|
|
* quick_buy_context: a unique text to dissociate quick buy if the same product is embedded multiple times
|
|
{%- endcomment -%}
|
|
{%- liquid
|
|
if hide_product_information
|
|
assign show_badges = false
|
|
assign show_rating = false
|
|
assign show_vendor = false
|
|
assign show_title = false
|
|
assign show_prices = false
|
|
assign show_swatches = false
|
|
assign show_quick_buy = show_quick_buy | default: settings.show_quick_buy, allow_false: true
|
|
assign show_secondary_image = show_secondary_image | default: settings.show_secondary_image, allow_false: true
|
|
else
|
|
assign show_badges = show_badges | default: true, allow_false: true
|
|
assign show_rating = show_rating | default: settings.show_product_rating, allow_false: true
|
|
assign show_vendor = show_vendor | default: settings.show_vendor, allow_false: true
|
|
assign show_quick_buy = show_quick_buy | default: settings.show_quick_buy, allow_false: true
|
|
assign show_title = true
|
|
assign show_prices = true
|
|
assign show_secondary_image = show_secondary_image | default: settings.show_secondary_image, allow_false: true
|
|
assign show_swatches = show_swatches | default: true, allow_false: true
|
|
endif
|
|
-%}
|
|
|
|
<product-card class="product-card" {% if reveal %}reveal-on-scroll="true"{% endif %} handle="{{ product.handle | escape }}">
|
|
{%- comment -%}
|
|
--------------------------------------------------------------------------------------------------------------------
|
|
PRODUCT MEDIA
|
|
--------------------------------------------------------------------------------------------------------------------
|
|
{%- endcomment -%}
|
|
|
|
{%- if product.media.size > 0 -%}
|
|
<div class="product-card__figure">
|
|
{%- if show_badges -%}
|
|
{%- render 'product-badges', product: product, vertical: true -%}
|
|
{%- endif -%}
|
|
|
|
<a href="{{ product.url }}" class="product-card__media" data-instant>
|
|
{%- capture sizes -%}
|
|
{%- if stacked -%}
|
|
(max-width: 699px) calc(100vw / {{ section.settings.products_per_row_mobile }}), (max-width: 999px) calc(100vw / {{ 3 | at_most: section.settings.products_per_row_desktop | default: 3 }} - 64px), calc((100vw - 96px) / {{ section.settings.products_per_row_desktop | default: 3 }} - (24px / {{ section.settings.products_per_row_desktop | default: 3 }} * {{ section.settings.products_per_row_desktop | default: 3 | minus: 1 }}))
|
|
{%- else -%}
|
|
(max-width: 699px) 74vw, (max-width: 999px) 38vw, calc((100vw - 96px) / {{ section.settings.products_per_row_desktop | default: 3 }} - (24px / {{ section.settings.products_per_row_desktop | default: 3 }} * {{ section.settings.products_per_row_desktop | default: 3 | minus: 1 }}))
|
|
{%- endif -%}
|
|
{%- endcapture -%}
|
|
|
|
{%- capture main_image_classes -%}product-card__image product-card__image--primary {% if settings.product_image_aspect_ratio contains 'crop' %}object-cover{% endif %} aspect-{{ settings.product_image_aspect_ratio | split: '_' | first }}{%- endcapture -%}
|
|
{{- product.featured_media | image_url: width: product.featured_media.width | image_tag: loading: 'lazy', sizes: sizes, widths: '200,300,400,500,600,700,800,1000,1200,1400,1600,1800', class: main_image_classes -}}
|
|
|
|
{%- if show_secondary_image and product.media.size > 1 -%}
|
|
{%- assign next_media = product.media[product.featured_media.position] | default: product.media[1] -%}
|
|
{{- next_media | image_url: width: next_media.width | image_tag: class: 'product-card__image product-card__image--secondary', loading: 'lazy', fetchpriority: 'low', sizes: sizes, widths: '200,300,400,500,600,700,800,1000,1200,1400,1600,1800' -}}
|
|
{%- endif -%}
|
|
</a>
|
|
|
|
{%- if show_quick_buy and product.available -%}
|
|
{%- if product.variants.size == 1 and product.selling_plan_groups.size == 0 -%}
|
|
{%- form 'product', product, is: 'product-form' -%}
|
|
<input type="hidden" name="on_success" value="force_open_drawer">
|
|
<input type="hidden" name="quantity" value="1">
|
|
<input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
|
|
{% render 'collection-add-to-cart', product: product %}
|
|
{%- endform -%}
|
|
{%- else -%}
|
|
{%- capture quick_buy_id -%}product-quick-buy-{{ section.id }}-{{ block.id }}-{{ quick_buy_context }}-{{ product.id }}{%- endcapture -%}
|
|
|
|
{% render 'collection-add-to-cart', product: product %}
|
|
|
|
<quick-buy-modal handle="{{ product.handle }}" class="quick-buy-modal modal" id="{{ quick_buy_id }}">
|
|
</quick-buy-modal>
|
|
{%- endif -%}
|
|
{%- endif -%}
|
|
</div>
|
|
{%- endif -%}
|
|
|
|
{%- comment -%}
|
|
--------------------------------------------------------------------------------------------------------------------
|
|
PRODUCT INFO
|
|
--------------------------------------------------------------------------------------------------------------------
|
|
{%- endcomment -%}
|
|
|
|
<div class="product-card__info empty:hidden">
|
|
{%- assign text_class = '' -%}
|
|
|
|
{%- if settings.product_card_text_font == 'heading' -%}
|
|
{%- assign text_class = 'h5' -%}
|
|
{%- endif -%}
|
|
|
|
{%- if show_title or show_prices or show_vendor and product.vendor != blank -%}
|
|
<div class="v-stack justify-items-center gap-2">
|
|
{%- if show_vendor and product.vendor != blank -%}
|
|
{%- capture vendor_class -%}smallcaps {% if settings.product_card_text_font == 'heading' %}heading{% endif %}{% endcapture %}
|
|
{%- render 'vendor' with product.vendor, class: vendor_class -%}
|
|
{%- endif -%}
|
|
|
|
<div class="v-stack justify-items-center gap-1">
|
|
{%- if show_title -%}
|
|
<a href="{{ product.url }}" class="product-title {% if text_class != blank %}{{ text_class }}{% endif %} {% if settings.product_title_max_lines > 0 %}line-clamp{% endif %}" {% if settings.product_title_max_lines > 0 %}style="--line-clamp-count: {{ settings.product_title_max_lines }}"{% endif %} data-instant>
|
|
{{- product.title -}}
|
|
</a>
|
|
{%- endif -%}
|
|
|
|
{%- if show_prices and product.template_suffix != 'quote' -%}
|
|
{% assign v = product.selected_or_first_available_variant %}
|
|
{% assign variant_price = v.price %}
|
|
{% assign variant_compare_at_price = v.compare_at_price %}
|
|
{% if v.metafields.app--168074346497.discount_type.value != nil and v.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 v.metafields.app--168074346497.discount_percentage.value > 0.01 %}
|
|
{% assign deducted_percentage = 1.0 | minus: v.metafields.app--168074346497.discount_percentage.value %}
|
|
{% endif %}
|
|
|
|
{% assign variant_price = v.price | divided_by: 100.0 | times: deducted_percentage | times: 100.0 | ceil %}
|
|
{% assign variant_compare_at_price = v.price %}
|
|
{% if v.compare_at_price > variant_compare_at_price %}
|
|
{% assign variant_compare_at_price = v.compare_at_price %}
|
|
{% endif %}
|
|
{% endif %}
|
|
<div style="display: flex; gap: 0.25em; font-size: var(--text-h4);
|
|
line-height: 1.6;
|
|
font-weight: var(--heading-font-weight);
|
|
font-style: var(--heading-font-style);
|
|
letter-spacing: var(--heading-letter-spacing);
|
|
text-transform: var(--heading-text-transform);
|
|
overflow-wrap: anywhere;">
|
|
<div class="product-price">
|
|
{{ variant_price | money }}
|
|
</div>
|
|
|
|
{% if variant_compare_at_price %}
|
|
<compare-at-price class="text-subdued line-through">
|
|
{{ variant_compare_at_price | money }}
|
|
</compare-at-price>
|
|
{% endif %}
|
|
</div>
|
|
{%- endif -%}
|
|
</div>
|
|
</div>
|
|
{%- endif -%}
|
|
|
|
{%- if show_swatches and settings.product_color_display != 'hide' -%}
|
|
{%- assign translated_color_label = 'general.label.color' | t | downcase -%}
|
|
{%- assign original_color_label = 'Color' | downcase -%}
|
|
{%- assign color_labels = translated_color_label | append: ',' | append: original_color_label | split: ',' -%}
|
|
|
|
{%- for color_label in color_labels -%}
|
|
{%- if product.options_by_name[color_label] != blank -%}
|
|
{%- assign product_option = product.options_by_name[color_label] -%}
|
|
{%- capture name -%}swatch-{{ quick_buy_context }}-{{ section.id }}-{{ product.id }}-{{ product_option.position }}{%- endcapture -%}
|
|
|
|
{%- case settings.product_color_display -%}
|
|
{%- when 'count' -%}
|
|
<p class="smallcaps text-subdued">{{- 'product.general.available_colors_count' | t: count: product_option.values.size -}}</p>
|
|
|
|
{%- when 'swatch' -%}
|
|
<fieldset class="h-stack wrap justify-center gap-1" data-option-position="{{ product_option.position }}">
|
|
{%- for option_value in product_option.values -%}
|
|
{%- if forloop.first or product.selected_or_first_available_variant.matched and option_value == product_option.selected_value -%}
|
|
{%- assign selected = true -%}
|
|
{%- else -%}
|
|
{% assign selected = false %}
|
|
{%- endif -%}
|
|
{%- unless product.template_suffix == 'quote' -%}
|
|
{%- render 'swatch', type: 'colors', value: option_value, name: name, selected: selected, size: 'sm', show_tooltip: true -%}
|
|
{%- endunless -%}
|
|
{%- endfor %}
|
|
</fieldset>
|
|
{%- endcase -%}
|
|
|
|
{%- assign type_option = product.options_with_values[1] -%}
|
|
{%- if type_option and type_option.values.size > 1 and type_option.name != 'Color' -%}
|
|
|
|
<!-- Type (Option 2) as Dropdown -->
|
|
{%- unless product.template_suffix == 'quote' -%}
|
|
<div class="product-card__option">
|
|
<label for="option-{{ type_option.name }}">{{ type_option.name }}</label>
|
|
<select name="option2" id="option-{{ type_option.name }}" class="product-card__dropdown">
|
|
{%- for value in type_option.values -%}
|
|
<option value='{{ value }}' {%- if forloop.first -%} selected {%-endif-%} >{{ value }}</option>
|
|
{%- endfor %}
|
|
</select>
|
|
</div>
|
|
{%- endunless -%}
|
|
{%- endif -%}
|
|
<!-- Option 3 -->
|
|
{% if product.options.size > 2 %}
|
|
<div class="product-card__option">
|
|
<label for="option3">{{ product.options[2] }}</label>
|
|
<select id="option3" name="Option3" class="product-card__dropdown product-card__dropdown3">
|
|
{% for value in product.options_with_values[2].values %}
|
|
<option value='{{ value }}' {%- if forloop.first -%} selected {%-endif-%} >{{ value }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
{% endif %}
|
|
{%- break -%}
|
|
{%- endif -%}
|
|
{%- endfor -%}
|
|
|
|
<!-- Handle Letter-based options (e.g., 1Letter, 2Letter, 3Letter) -->
|
|
{% assign alphabet = 'A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z' | split: ',' %}
|
|
|
|
{% if product.template_suffix == "1Letter" %}
|
|
<div class="product-card__option letter-option">
|
|
<label for="letter-{{ product.id }}">{{ 'product.general.letter' | t }}</label>
|
|
<select id="letter-{{ product.id }}" name="properties[Letter-{{ product.id }}]" class="product-card__dropdown">
|
|
{% for letter in alphabet %}
|
|
<option value="{{ letter }}">{{ letter }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
{% elsif product.template_suffix == "2Letter" %}
|
|
{% for i in (1..2) %}
|
|
<div class="product-card__option letter-option">
|
|
<label for="letter-{{ i }}-{{ product.id }}">{{ 'product.general.letter_' | append: i | t }}</label>
|
|
<select id="letter-{{ i }}-{{ product.id }}" name="properties[Letter-{{ i }}-{{ product.id }}]" class="product-card__dropdown">
|
|
{% for letter in alphabet %}
|
|
<option value="{{ letter }}">{{ letter }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
{% endfor %}
|
|
|
|
{% elsif product.template_suffix == "3Letter" %}
|
|
{% for i in (1..3) %}
|
|
<div class="product-card__option letter-option">
|
|
<label for="letter-{{ i }}-{{ product.id }}">{{ 'product.general.letter_' | append: i | t }}</label>
|
|
<select id="letter-{{ i }}-{{ product.id }}" name="properties[Letter-{{ i }}-{{ product.id }}]" class="product-card__dropdown">
|
|
{% for letter in alphabet %}
|
|
<option value="{{ letter }}">{{ letter }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
|
|
<!-- Handle Date input option -->
|
|
{% if product.template_suffix == "1Date" %}
|
|
<div class="product-card__option date-option">
|
|
<label for="date-{{ product.id }}">{{ 'product.general.date' | t }}</label>
|
|
<input type="date" id="date-{{ product.id }}" name="properties[Date-{{ product.id }}]" class="product-card__dropdown product-card__input--date" placeholder="DD-MM-YYYY" required>
|
|
</div>
|
|
{% endif %}
|
|
{%- endif -%}
|
|
|
|
<!-- Rating and other optional product information -->
|
|
{%- if show_rating -%}
|
|
{%- render 'product-rating', product: product, show_empty: settings.show_product_rating_if_empty, display_mode: settings.product_rating_mode -%}
|
|
{%- endif -%}
|
|
</div>
|
|
</product-card>
|
|
|
|
<script>
|
|
$(document).on('change', 'product-card[handle="{{ product.handle }}"]', function() {
|
|
let selectedOptions = [];
|
|
|
|
// Capture selected color if available
|
|
const option1 = $(this).find('input:checked').val();
|
|
if(option1) selectedOptions.push(option1);
|
|
|
|
// Capture selected size, length, or letter
|
|
const option2 = $(this).find('select.product-card__dropdown').val();
|
|
if(option2) selectedOptions.push(option2);
|
|
|
|
// Capture selected option3
|
|
const option3 = $(this).find('select.product-card__dropdown3').val();
|
|
if(option3) selectedOptions.push(option3);
|
|
|
|
// Capture selected date if applicable
|
|
const selectedDate = $(this).find('input[type="date"]').val();
|
|
if(selectedDate) selectedOptions.push(selectedDate);
|
|
|
|
// Fetch available product variants from the product JSON
|
|
let variants = {{ product.variants | json }};
|
|
|
|
// Match selected options to find the corresponding variant
|
|
let selectedVariant = variants.find(variant => {
|
|
return variant.options.every(function(option, index) {
|
|
return option === selectedOptions[index];
|
|
});
|
|
});
|
|
|
|
if (selectedVariant) {
|
|
// Update hidden input with the correct variant ID
|
|
$('#selected-variant-id-{{ product.id }}').val(selectedVariant.id);
|
|
|
|
{% assign deducted_percentage = 1.0 %}
|
|
{% if product.metafields.app--168074346497.discount_percentage.value > 0.01 %}
|
|
{% assign deducted_percentage = 1.0 | minus: product.metafields.app--168074346497.discount_percentage.value %}
|
|
{% endif %}
|
|
|
|
// Update displayed price
|
|
let locale = Shopify.locale;
|
|
let currency = Shopify.currency.active;
|
|
let country = Shopify.country;
|
|
let amount = new Intl.NumberFormat(`${locale}-${country}`, { style: "currency", currency: currency }).format(selectedVariant.price / 100 * {{ deducted_percentage }});
|
|
$(this).find('.product-price').text(amount);
|
|
} else {
|
|
console.log('No matching variant found for the selected options.');
|
|
}
|
|
});
|
|
</script>
|