commit 909f3e7675eb16344887dcdd65817f11e5bad164 Author: Axel Date: Tue Nov 25 03:01:55 2025 +0800 first commit diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..828824b Binary files /dev/null and b/.DS_Store differ diff --git a/snippets/product-item.liquid b/snippets/product-item.liquid new file mode 100644 index 0000000..aae2464 --- /dev/null +++ b/snippets/product-item.liquid @@ -0,0 +1,385 @@ +{%- 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 +-%} + +
+ {%- capture product_labels -%} + {%- unless hide_labels -%} + {%- assign custom_badges = product.metafields.custom.badges.value -%} + + {%- for badge in custom_badges -%} + {{ badge }} + {%- endfor -%} + + {%- for tag in product.tags -%} + {%- if tag contains '__label:' -%} + {{ tag | split: '__label:' | last }} + {%- endif -%} + + {%- if tag contains '__label1:' -%} + {{ tag | split: '__label1:' | last }} + {%- endif -%} + + {%- if tag contains '__label2:' -%} + {{ tag | split: '__label2:' | last }} + {%- 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 -%}{{ product_compare_at_price | minus: product_price | money }}{%- endcapture -%} + {%- endif -%} + + {{ 'collection.product.discount_html' | t: savings: savings }} + {%- endif -%} + {%- endunless -%} + {%- endcapture -%} + + {%- if product_labels != blank -%} +
+ {{- product_labels -}} +
+ {%- 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 -%} + + +
+ {{- 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 -%} +
+
+ {%- endif -%} + +
+
+ {%- capture 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 -%} + + + {{ 'product.general.sale_price' | t }} + {{- 'collection.product.from_price_html' | t: price_min: price_min, price_max: price_max -}} + + + + {{ 'product.general.regular_price' | t }} + + {%- if settings.currency_code_enabled -%} + {{- product_compare_at_price_min | money_with_currency -}} + {%- else -%} + {{- product_compare_at_price_min | money -}} + {%- endif -%} + + {%- else -%} + + {{ 'product.general.sale_price' | t }} + + {%- if settings.currency_code_enabled -%} + {{- product_price | money_with_currency -}} + {%- else -%} + {{- product_price | money -}} + {%- endif -%} + + + + {{ 'product.general.regular_price' | t }} + {%- if settings.currency_code_enabled -%} + {{- product_compare_at_price | money_with_currency -}} + {%- else -%} + {{- product_compare_at_price | money -}} + {%- endif -%} + + {%- 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 -%} + + + {{ 'product.general.sale_price' | t }} + {{- 'collection.product.from_price_html' | t: price_min: price_min, price_max: price_max -}} + + {%- else -%} + + {{ 'product.general.sale_price' | t }} + {%- if settings.currency_code_enabled -%} + {{- product_price | money_with_currency -}} + {%- else -%} + {{- product_price | money -}} + {%- endif -%} + + {%- endif -%} +
+ + {%- assign variant_to_use = selected_variant | default: product.selected_or_first_available_variant -%} + + {%- if variant_to_use.unit_price -%} +
+
+ {{- variant_to_use.unit_price | unit_price_with_measurement: variant_to_use.unit_price_measurement -}} +
+
+ {%- 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? -%} + {{ product.vendor }} + {%- else -%} + {{ product.vendor }} + {%- endunless -%} + {%- endif -%} + {%- endcapture -%} + + {%- if settings.product_price_position == 'before_title' -%} + {{ price_list }} + {%- endif -%} + + {%- if settings.product_price_position == 'after_title' -%} + {{ vendor }} + {%- endif -%} + + {{ product.title }} + + {%- 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: '
' -%} + + {%- 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 -%} + +
+ {%- if variant_for_value.featured_media -%} + {%- assign image_url = variant_for_value.featured_media | image_url: width: 800 -%} + {%- endif -%} + + + + +{{ option.values.size | minus: forloop.index0 }} +
+ {%- endfor -%} + {%- endif -%} + {%- endfor -%} + {%- endcapture -%} + + {%- if color_swatch != blank -%} +
+
+ {{ color_swatch }} +
+
+ {%- endif -%} + {%- endif -%} + + {%- if settings.product_price_position == 'after_title' -%} + {{ price_list }} + {%- endif -%} + + {%- if settings.show_reviews_badge -%} + + {%- render 'product-rating', product: product -%} + + {%- 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 -%} + {{ 'collection.product.low_stock_with_quantity_count' | t: count: all_inventory }} + {%- else -%} + {{ 'collection.product.in_stock_with_quantity_count' | t: count: all_inventory }} + {%- endif -%} + {%- else -%} + {%- if product.variants.first.inventory_policy == 'continue' and product.variants.first.inventory_quantity <= 0 and product.variants.first.requires_shipping -%} + {{ 'collection.product.oversell_stock' | t }} + {%- else -%} + {{ 'collection.product.in_stock' | t }} + {%- endif -%} + {%- endif -%} + {%- else -%} + {{ 'collection.product.sold_out' | t }} + {%- endif -%} + {%- endif -%} +
+ + {%- 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' -%} + + {%- else -%} + {%- capture quick_buy_form -%} + {%- form 'product', product, id: form_id, class: product_form_classes -%} + + + + {%- 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 -%} + + + + {%- else -%} + {{ 'collection.product.choose_options' | t }} + {%- endif -%} + {%- else -%} + + {%- 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 -%} + + + {%- endform -%} + {%- endcapture -%} + + {%- if complementary_products != true -%} + {{- quick_buy_form -}} + {%- endif -%} + {%- endif -%} + {%- endif -%} +
+ + {%- 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 -%} +
\ No newline at end of file diff --git a/snippets/product-variant-selector.liquid b/snippets/product-variant-selector.liquid new file mode 100644 index 0000000..b71e710 --- /dev/null +++ b/snippets/product-variant-selector.liquid @@ -0,0 +1,349 @@ + + +{%- 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 -%} + + {%- 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 -%} + + + {%- 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 -%} + +
+ {%- case option_selector_type -%} + {%- when 'swatch' -%} + {{ option.name }}: {{ option.selected_value }} + +
+ {%- assign color_swatch_config = settings.color_swatch_config | newline_to_br | split: '
' -%} + + {%- for value in option.values -%} + {%- assign downcased_value = value | downcase -%} + {%- capture color_id -%}{{ product_form_id }}-{{ option_name }}-{{ forloop.index }}{%- endcapture -%} + +
+ {%- if value.product_url != blank -%} + {%- if value == option.selected_value -%} + + {%- endif -%} + + + {{ value }} + {%- render 'icon', icon: 'cross-sold-out' -%} + + {%- else -%} + + + {%- endif -%} +
+ {%- endfor -%} +
+ + {%- when 'variant' -%} + {{ option.name }}: {{ option.selected_value }} + +
+ {%- 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 + -%} + +
+ {%- if value.product_url != blank -%} + {%- if value == option.selected_value -%} + + {%- endif -%} + + +
+ {{- image | image_url: width: image.width | image_tag: loading: 'lazy', sizes: '120px', widths: '120,240' -}} +
+ + {% render 'icon', icon: 'cross-sold-out' %} +
+ {%- else -%} + + + + {%- endif -%} +
+ {%- endfor -%} +
+ + {%- when 'block' -%} + {{ option.name }}: {{ option.selected_value }} + +
+ {%- for value in option.values -%} + {%- capture block_swatch_id -%}{{ product_form_id }}-{{ option_name }}-{{ forloop.index }}{%- endcapture -%} + +
+ {%- if value.product_url != blank -%} + {%- if value == option.selected_value -%} + + {%- endif -%} + + + {{ value }} + + {%- else -%} + + + {%- endif -%} +
+ {%- endfor -%} +
+ + {%- when 'dropdown' -%} + {%- capture dropdown_id -%}{{ product_form_id }}-{{ option_name }}-{{ forloop.index }}{%- endcapture -%} + + + +
+ {%- render 'icon', icon: 'arrow-bottom' -%} + + +
+ {%- endcase -%} +
+ {%- endfor -%} + +
+ + +
+ +
+
+
+{%- else -%} + +{%- endunless -%} + +
+ {%- if product.selected_or_first_available_variant != nil -%} +
+ {{ 'product.form.price' | t }}: + +
+
+ {%- if selected_variant.compare_at_price > selected_variant.price -%} + + {{ 'product.general.sale_price' | t }} + + {%- if settings.currency_code_enabled -%} + {{- selected_variant.price | money_with_currency -}} + {%- else -%} + {{- selected_variant.price | money -}} + {%- endif -%} + + + + {{ 'product.general.regular_price' | t }} + + {%- if settings.currency_code_enabled -%} + {{- selected_variant.compare_at_price | money_with_currency -}} + {%- else -%} + {{- selected_variant.compare_at_price | money -}} + {%- endif -%} + + {%- else -%} + + {{ 'product.general.sale_price' | t }} + + {%- if settings.currency_code_enabled -%} + {{- selected_variant.price | money_with_currency -}} + {%- else -%} + {{- selected_variant.price | money -}} + {%- endif -%} + + {%- endif -%} +
+ + {%- assign variant_to_use = selected_variant | default: product.selected_or_first_available_variant -%} + + {%- if variant_to_use.unit_price -%} +
+
+ {{- variant_to_use.unit_price | unit_price_with_measurement: variant_to_use.unit_price_measurement -}} +
+
+ {%- endif -%} + + {{- form | payment_terms -}} + + {%- if block.settings.show_taxes_included -%} + {%- if cart.taxes_included or shop.shipping_policy.body != blank -%} +

+ {%- 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 -%} +

+ {%- endif -%} + {%- endif -%} +
+
+ {%- endif -%} + + {%- if block.settings.show_inventory_quantity and product.template_suffix != 'pre-order' and product.selected_or_first_available_variant != nil -%} +
+ {{ 'product.form.inventory' | t }}: + +
+ {%- 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 -%} + {{ 'product.form.incoming_stock' | t: next_incoming_date: next_incoming_date }} + {%- else -%} + {{ 'product.form.oversell_stock' | t }} + {%- endif -%} + {%- elsif block.settings.low_inventory_threshold > 0 -%} + {%- if selected_variant.inventory_quantity <= block.settings.low_inventory_threshold -%} + {{ 'product.form.low_stock_with_quantity_count' | t: count: selected_variant.inventory_quantity }} + {%- else -%} + {{ 'product.form.in_stock_with_quantity_count' | t: count: selected_variant.inventory_quantity }} + {%- endif -%} + {%- else -%} + {{ 'product.form.in_stock' | t }} + {%- endif -%} + {%- else -%} + {%- if selected_variant.incoming -%} + {%- capture next_incoming_date -%}{{ selected_variant.next_incoming_date | date: format: 'date' }}{%- endcapture -%} + {{ 'product.form.incoming_stock' | t: next_incoming_date: next_incoming_date }} + {%- else -%} + {{ 'product.form.sold_out' | t }} + {%- endif -%} + {%- endif -%} + {%- else -%} + {{ 'product.form.in_stock' | t }} + {%- 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 -%} + + + + +
+
+ {%- endif -%} + + {%- if product.template_suffix != 'contact' -%} + {%- if block.settings.show_quantity_selector -%} +
+ + +
+ {%- assign variant = product.selected_or_first_available_variant -%} + +
+ + + + + + + {%- 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 -%} +

{{ quantity_rules | remove_first: ' / ' | capitalize }}

+ {%- endif -%} +
+
+
+ {%- else -%} + + {%- endif -%} + {%- endif -%} +