Files
Warehouse/snippets/product-variant-selector.liquid

350 lines
20 KiB
Plaintext
Raw Permalink Normal View History

2025-11-25 03:01:55 +08:00
<!-- find the price part, and insert "yagi-hidden" class ot the price container -->
{%- assign selected_variant = product.selected_or_first_available_variant -%}
{%- assign color_label = 'color,colour,couleur,cor,colore,farbe,색,色,カラー,färg,farve,szín,barva' | split: ',' -%}
{%- unless product.has_only_default_variant -%}
<variant-picker handle="{{ product.handle }}" section-id="{{ section.id }}" form-id="{{ product_form_id }}" {% if update_url %}update-url{% endif %} {% if block.settings.hide_sold_out_variants %}hide-sold-out-variants{% endif %} class="product-form__variants" {{ block.shopify_attributes }}>
{%- comment -%}
The variant data is outputted as a JSON, which allows the theme to emit an event with the data when the variant changes. This must not be removed.
{%- endcomment -%}
<script data-variant type="application/json">
{{- product.selected_or_first_available_variant | json -}}
</script>
{%- for option in product.options_with_values -%}
{%- assign downcase_option = option.name | downcase -%}
{%- capture option_name -%}option{{ option.position }}{%- endcapture -%}
{%- assign option_selector_type = block.settings.selector_mode -%}
{%- assign variant_image_options = block.settings.variant_image_options | replace: ', ', ',' | downcase | split: ',' -%}
{%- assign swatch_count = option.values | map: 'swatch' | compact | size -%}
{%- if swatch_count > 0 and block.settings.color_mode == 'swatch' %}
{%- assign option_selector_type = 'swatch' -%}
{%- endif -%}
{% if swatch_count == 0 and color_label contains downcase_option and block.settings.color_mode != 'none' %}
{%- assign option_selector_type = 'swatch' -%}
{%- endif -%}
{%- if variant_image_options contains downcase_option -%}
{%- assign option_selector_type = 'variant' -%}
{%- endif -%}
<div class="product-form__option">
{%- case option_selector_type -%}
{%- when 'swatch' -%}
<span class="product-form__option-name text--strong">{{ option.name }}: <span class="product-form__selected-value">{{ option.selected_value }}</span></span>
<div class="color-swatch-list color-swatch-list--large">
{%- 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 -%}{{ product_form_id }}-{{ option_name }}-{{ forloop.index }}{%- endcapture -%}
<div class="color-swatch {% if downcased_value == 'white' or downcased_value == 'blanc' %}color-swatch--white{% endif %} {% unless value.available %}color-swatch--disabled{% endunless %}">
{%- if value.product_url != blank -%}
{%- if value == option.selected_value -%}
<input type="hidden" name="{{ option_name }}" form="{{ product_form_id }}" value="{{ value.id }}" data-option-position="{{ option.position }}">
{%- endif -%}
<a href="{{ value.product_url }}" class="color-swatch__item {% if value == option.selected_value %}is-selected{% endif %}" 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>
{%- render 'icon', icon: 'cross-sold-out' -%}
</a>
{%- else -%}
<input class="color-swatch__radio product-form__single-selector" type="radio" name="{{ option_name }}" id="{{ color_id }}" value="{{ value.id }}" {% if option.selected_value == value %}checked{% endif %} data-option-position="{{ option.position }}" form="{{ product_form_id }}">
<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>
{%- render 'icon', icon: 'cross-sold-out' -%}
</label>
{%- endif -%}
</div>
{%- endfor -%}
</div>
{%- when 'variant' -%}
<span class="product-form__option-name text--strong">{{ option.name }}: <span class="product-form__selected-value">{{ option.selected_value }}</span></span>
<div class="variant-swatch-list">
{%- for value in option.values -%}
{%- capture variant_swatch_id -%}{{ product_form_id }}-{{ option_name }}-{{ forloop.index }}{%- endcapture -%}
{%- liquid
assign image = value.variant.featured_media
assign option_name = 'option' | append: option.position
if value.variant.featured_media == blank
for variant in product.variants
if variant[option_name] == value
assign image = variant.featured_media
break
endif
endfor
endif
assign image = image | default: product.featured_media
-%}
<div class="variant-swatch {% unless value.available %}variant-swatch--disabled{% endunless %}">
{%- if value.product_url != blank -%}
{%- if value == option.selected_value -%}
<input type="hidden" name="{{ option_name }}" form="{{ product_form_id }}" value="{{ value.id }}" data-option-position="{{ option.position }}">
{%- endif -%}
<a href="{{ value.product_url }}" class="variant-swatch__item {% if value == option.selected_value %}is-selected{% endif %}" title="{{ value | escape }}">
<div class="aspect-ratio" style="padding-bottom: {{ 100.0 | divided_by: image.aspect_ratio }}%">
{{- image | image_url: width: image.width | image_tag: loading: 'lazy', sizes: '120px', widths: '120,240' -}}
</div>
{% render 'icon', icon: 'cross-sold-out' %}
</a>
{%- else -%}
<input class="variant-swatch__radio product-form__single-selector" type="radio" name="{{ option_name }}" id="{{ variant_swatch_id }}" value="{{ value.id }}" form="{{ product_form_id }}" {% if option.selected_value == value %}checked{% endif %} data-option-position="{{ option.position }}">
<label class="variant-swatch__item" for="{{ variant_swatch_id }}" title="{{ value | escape }}">
<div class="aspect-ratio" style="padding-bottom: {{ 100.0 | divided_by: image.aspect_ratio }}%">
{{- image | image_url: width: image.width | image_tag: loading: 'lazy', sizes: '120px', widths: '120,240' -}}
</div>
{% render 'icon', icon: 'cross-sold-out' %}
</label>
{%- endif -%}
</div>
{%- endfor -%}
</div>
{%- when 'block' -%}
<span class="product-form__option-name text--strong">{{ option.name }}: <span class="product-form__selected-value">{{ option.selected_value }}</span></span>
<div class="block-swatch-list">
{%- for value in option.values -%}
{%- capture block_swatch_id -%}{{ product_form_id }}-{{ option_name }}-{{ forloop.index }}{%- endcapture -%}
<div class="block-swatch {% unless value.available %}block-swatch--disabled{% endunless %}">
{%- if value.product_url != blank -%}
{%- if value == option.selected_value -%}
<input type="hidden" name="{{ option_name }}" form="{{ product_form_id }}" value="{{ value.id }}" data-option-position="{{ option.position }}">
{%- endif -%}
<a href="{{ value.product_url }}" class="block-swatch__item {% if value == option.selected_value %}is-selected{% endif %}" title="{{ value | escape }}">
<span class="block-swatch__item-text">{{ value }}</span>
</a>
{%- else -%}
<input class="block-swatch__radio product-form__single-selector" type="radio" name="{{ option_name }}" id="{{ block_swatch_id }}" value="{{ value.id }}" form="{{ product_form_id }}" {% if option.selected_value == value %}checked{% endif %} data-option-position="{{ option.position }}">
<label class="block-swatch__item" for="{{ block_swatch_id }}" title="{{ value | escape }}">
<span class="block-swatch__item-text">{{ value }}</span>
</label>
{%- endif -%}
</div>
{%- endfor -%}
</div>
{%- when 'dropdown' -%}
{%- capture dropdown_id -%}{{ product_form_id }}-{{ option_name }}-{{ forloop.index }}{%- endcapture -%}
<label for="{{ dropdown_id }}" class="product-form__option-name text--strong">{{ option.name }}: <span class="product-form__selected-value">{{ option.selected_value }}</span></label>
<div class="select-wrapper select-wrapper--primary">
{%- render 'icon', icon: 'arrow-bottom' -%}
<select class="product-form__single-selector" name="{{ option_name }}" form="{{ product_form_id }}" id="{{ dropdown_id }}" data-option-position="{{ option.position }}">
{%- for value in option.values -%}
<option {% if value.product_url != blank %}data-update-url="{{ value.product_url | escape }}"{% endif %} value="{{ value.id }}" {% if value == option.selected_value %}selected="selected"{% endif %}>{{ value }}</option>
{%- endfor -%}
</select>
</div>
{%- endcase -%}
</div>
{%- endfor -%}
<div class="no-js product-form__option">
<label class="product-form__option-name text--strong" for="product-select-{{ product.id }}">{{ 'product.form.variant' | t }}</label>
<div class="select-wrapper select-wrapper--primary">
<select id="product-select-{{ product.id }}" name="id">
{%- for variant in product.variants -%}
<option {% if variant == selected_variant %}selected="selected"{% endif %} {% unless variant.available %}disabled="disabled"{% endunless %} value="{{ variant.id }}" data-sku="{{ variant.sku }}">{{ variant.title }} - {{ variant.price | money }}</option>
{%- endfor -%}
</select>
</div>
</div>
</variant-picker>
{%- else -%}
<input type="hidden" name="id" data-sku="{{ selected_variant.sku }}" value="{{ selected_variant.id }}">
{%- endunless -%}
<div class="product-form__info-list">
{%- if product.selected_or_first_available_variant != nil -%}
<div class="product-form__info-item yagi-hidden">
<span class="product-form__info-title text--strong">{{ 'product.form.price' | t }}:</span>
<div class="product-form__info-content" role="region" aria-live="polite">
<div class="price-list">
{%- if selected_variant.compare_at_price > selected_variant.price -%}
<span class="price price--highlight">
<span class="visually-hidden">{{ 'product.general.sale_price' | t }}</span>
{%- if settings.currency_code_enabled -%}
{{- selected_variant.price | money_with_currency -}}
{%- else -%}
{{- selected_variant.price | money -}}
{%- endif -%}
</span>
<span class="price price--compare">
<span class="visually-hidden">{{ 'product.general.regular_price' | t }}</span>
{%- if settings.currency_code_enabled -%}
{{- selected_variant.compare_at_price | money_with_currency -}}
{%- else -%}
{{- selected_variant.compare_at_price | money -}}
{%- endif -%}
</span>
{%- else -%}
<span class="price">
<span class="visually-hidden">{{ 'product.general.sale_price' | t }}</span>
{%- if settings.currency_code_enabled -%}
{{- selected_variant.price | money_with_currency -}}
{%- else -%}
{{- selected_variant.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-form__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 -%}
{{- form | payment_terms -}}
{%- if block.settings.show_taxes_included -%}
{%- if cart.taxes_included or shop.shipping_policy.body != blank -%}
<p class="product-form__price-info">
{%- if cart.taxes_included -%}
{{ 'product.general.include_taxes' | t }}
{%- endif -%}
{%- if shop.shipping_policy.body != blank -%}
{{ 'product.general.shipping_policy_html' | t: link: shop.shipping_policy.url }}
{%- endif -%}
</p>
{%- endif -%}
{%- endif -%}
</div>
</div>
{%- endif -%}
{%- if block.settings.show_inventory_quantity and product.template_suffix != 'pre-order' and product.selected_or_first_available_variant != nil -%}
<div class="product-form__info-item">
<span class="product-form__info-title text--strong">{{ 'product.form.inventory' | t }}:</span>
<div class="product-form__info-content">
{%- if selected_variant.inventory_management -%}
{%- if selected_variant.available -%}
{%- if selected_variant.inventory_quantity <= 0 and selected_variant.requires_shipping -%}
{%- if selected_variant.incoming -%}
{%- capture next_incoming_date -%}{{ selected_variant.next_incoming_date | date: format: 'date' }}{%- endcapture -%}
<span class="product-form__inventory inventory inventory--high">{{ 'product.form.incoming_stock' | t: next_incoming_date: next_incoming_date }}</span>
{%- else -%}
<span class="product-form__inventory inventory inventory--high">{{ 'product.form.oversell_stock' | t }}</span>
{%- endif -%}
{%- elsif block.settings.low_inventory_threshold > 0 -%}
{%- if selected_variant.inventory_quantity <= block.settings.low_inventory_threshold -%}
<span class="product-form__inventory inventory inventory--low">{{ 'product.form.low_stock_with_quantity_count' | t: count: selected_variant.inventory_quantity }}</span>
{%- else -%}
<span class="product-form__inventory inventory inventory--high">{{ 'product.form.in_stock_with_quantity_count' | t: count: selected_variant.inventory_quantity }}</span>
{%- endif -%}
{%- else -%}
<span class="product-form__inventory inventory inventory--high">{{ 'product.form.in_stock' | t }}</span>
{%- endif -%}
{%- else -%}
{%- if selected_variant.incoming -%}
{%- capture next_incoming_date -%}{{ selected_variant.next_incoming_date | date: format: 'date' }}{%- endcapture -%}
<span class="product-form__inventory inventory">{{ 'product.form.incoming_stock' | t: next_incoming_date: next_incoming_date }}</span>
{%- else -%}
<span class="product-form__inventory inventory">{{ 'product.form.sold_out' | t }}</span>
{%- endif -%}
{%- endif -%}
{%- else -%}
<span class="product-form__inventory inventory inventory--high">{{ 'product.form.in_stock' | t }}</span>
{%- endif -%}
{%- for tag in product.tags -%}
{%- if tag contains '__stock:' -%}
{%- assign stock_countdown_max = tag | split: '__stock:' | last | times: 1.0 -%}
{%- break -%}
{%- endif -%}
{%- endfor -%}
{%- comment -%}
Historically the theme used a tag based approach instead of a setting. We keep this for compatibility, but then fallback to the setting,
which allows a simpler setup
{%- endcomment -%}
{%- assign stock_countdown_max = stock_countdown_max | default: block.settings.progress_bar_max_value | times: 1.0 -%}
<progress-bar class="inventory-bar" data-variant-inventory="{{ selected_variant.inventory_quantity }}" data-stock-countdown-max="{{ stock_countdown_max }}">
<span class="inventory-bar__progress" style="width: 100%"></span>
</progress-bar>
</div>
</div>
{%- endif -%}
{%- if product.template_suffix != 'contact' -%}
{%- if block.settings.show_quantity_selector -%}
<div class="product-form__info-item product-form__info-item--quantity">
<label for="{{ section.id }}-{{ product.id }}-quantity" class="product-form__info-title text--strong">{{ 'product.form.quantity' | t }}:</label>
<div class="product-form__info-content">
{%- assign variant = product.selected_or_first_available_variant -%}
<div class="product-form__quantity-with-rules">
<quantity-picker class="quantity-selector quantity-selector--product">
<button type="button" class="quantity-selector__button" data-action="decrease-picker-quantity" aria-label="{{ 'cart.items.decrease_quantity' | t }}" title="{{ 'cart.items.decrease_quantity' | t }}">{% render 'icon', icon: 'minus' %}</button>
<input id="{{ section.id }}-{{ product.id }}-quantity" type="number" name="quantity" aria-label="{{ 'product.form.quantity' | t }}" class="quantity-selector__value" inputmode="numeric" value="{{ variant.quantity_rule.min | default: 1 }}" step="{{ variant.quantity_rule.increment }}" min="{{ variant.quantity_rule.min }}" {% if variant.quantity_rule.max != nil %}max="{{ variant.quantity_rule.max }}"{% endif %}>
<button type="button" class="quantity-selector__button" data-action="increase-picker-quantity" aria-label="{{ 'cart.items.increase_quantity' | t }}" title="{{ 'cart.items.increase_quantity' | t }}">{% render 'icon', icon: 'plus' %}</button>
</quantity-picker>
{%- liquid
assign variant = product.selected_or_first_available_variant
assign quantity_rules = ''
if variant.quantity_rule.min > 1
assign rule = 'product.quantity.minimum_of' | t: min: variant.quantity_rule.min
assign quantity_rules = quantity_rules | append: ' / ' | append: rule
endif
if variant.quantity_rule.max != nil
assign rule = 'product.quantity.maximum_of' | t: max: variant.quantity_rule.max
assign quantity_rules = quantity_rules | append: ' / ' | append: rule
endif
if variant.quantity_rule.increment > 1
assign rule = 'product.quantity.increment_of' | t: step: variant.quantity_rule.increment
assign quantity_rules = quantity_rules | append: ' / ' | append: rule
endif
-%}
{%- if quantity_rules != blank -%}
<p class="product-form__quantity-rules text--small">{{ quantity_rules | remove_first: ' / ' | capitalize }}</p>
{%- endif -%}
</div>
</div>
</div>
{%- else -%}
<input type="hidden" name="quantity" value="1">
{%- endif -%}
{%- endif -%}
</div>