From b9f58a20af76ccaa9a428c6516a7e73ecab39cfc Mon Sep 17 00:00:00 2001 From: Axel Date: Thu, 30 Apr 2026 03:13:31 +0800 Subject: [PATCH] first commit --- sections/predictive-search.liquid | 306 ++++++++++ sections/search.liquid | 384 +++++++++++++ snippets/product-buttons.liquid | 173 ++++++ snippets/product-grid-item-variant.liquid | 578 +++++++++++++++++++ snippets/product-grid-item.liquid | 670 ++++++++++++++++++++++ snippets/product-price.liquid | 168 ++++++ 6 files changed, 2279 insertions(+) create mode 100644 sections/predictive-search.liquid create mode 100644 sections/search.liquid create mode 100644 snippets/product-buttons.liquid create mode 100644 snippets/product-grid-item-variant.liquid create mode 100644 snippets/product-grid-item.liquid create mode 100644 snippets/product-price.liquid diff --git a/sections/predictive-search.liquid b/sections/predictive-search.liquid new file mode 100644 index 0000000..c95c4c8 --- /dev/null +++ b/sections/predictive-search.liquid @@ -0,0 +1,306 @@ + + + +{%- 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 }} + +
+

+ {{ collection.title }} +

+
+ {%- endcapture -%} + {%- endfor -%} +{%- endif -%} + +{%- assign total_results = product_count | plus: query_count | plus: collection_counter | plus: page_count | plus: article_count -%} + +{%- if predictive_search.performed -%} +
+ + {%- 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 + -%} + +
+ {%- if total_results > 0 -%} +
+
+ {%- if query_count > 0 -%} +
+
+

{{ 'general.search.suggestions' | t }}

+ {{ query_count }} +
+ +
    + {%- for query in predictive_search.resources.queries -%} + {%- assign total_results_counter = total_results_counter | plus: 1 -%} +
  • +

    + + {{ query.styled_text }} + +

    +
  • + {%- endfor -%} +
+
+ {%- endif -%} + + {%- if product_count > 0 -%} +
+
+
+

{{ 'products.general.products' | t }}

+ {{ product_count }} +
+
+ + +
+ {%- endif -%} + + {%- if collection_counter > 0 -%} + {%- assign total_results_counter = total_results_counter | plus: collection_counter -%} + +
+
+

{{ 'collections.sidebar.collections' | t }}

+ {{ collection_counter }} +
+ +
    + {{ collections_markup }} +
+
+ {%- endif -%} + + {%- if article_count > 0 -%} +
+
+

{{ 'blogs.article.articles' | t }}

+ {{ article_count }} +
+ +
    + {%- for article in predictive_search.resources.articles -%} + {%- assign total_results_counter = total_results_counter | plus: 1 -%} +
  • +

    + {{ article.title }} +

    +
  • + {%- endfor -%} +
+
+ {%- endif -%} + + {%- if page_count > 0 -%} +
+
+

{{ 'general.page.pages' | t }}

+ {{ page_count }} +
+ +
    + {%- for page in predictive_search.resources.pages -%} + {%- assign total_results_counter = total_results_counter | plus: 1 -%} +
  • +

    + {{ page.title }} +

    +
  • + {%- endfor -%} +
+
+ {%- endif -%} +
+
+ {%- else -%} +
+
+

+ {{ 'general.search.no_results_for' | t }} + {{ terms }} +

+
+
+ {%- endif -%} + +
+ +
+
+
+{%- endif -%} diff --git a/sections/search.liquid b/sections/search.liquid new file mode 100644 index 0000000..48ea04d --- /dev/null +++ b/sections/search.liquid @@ -0,0 +1,384 @@ + + + +{%- 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 +-%} + + + +{% 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 %} diff --git a/snippets/product-buttons.liquid b/snippets/product-buttons.liquid new file mode 100644 index 0000000..06c9243 --- /dev/null +++ b/snippets/product-buttons.liquid @@ -0,0 +1,173 @@ + + +{%- liquid + assign buybutton_setting = false + assign gift_card_recipient_feature_active = false + assign current_variant = product.selected_or_first_available_variant + + if block.settings.enable_gift_card_recipient and product.gift_card? + assign gift_card_recipient_feature_active = true + endif + + comment + Quick buy buttons are incompatable with gift card products and subscription products + endcomment + if block.settings.enable_payment_button and gift_card_recipient_feature_active == false + assign buybutton_setting = true + endif + if product.selling_plan_groups.size > 0 + assign buybutton_setting = false + endif +-%} + +
+
+ {% comment %} The [data-product-form] tag distinguishes the product form from upsell instant-add-buttons. {% endcomment %} + {%- form 'product', product, id: uniq_id, data-product-form: '', data-product-handle: product.handle -%} + {%- unless hidden -%} +
+ + {% comment %} + Note: the gift card recipient form is controlled in Checkout and must contain these undocumented line item propeties: + * properties[__shopify_send_gift_card_to_recipient] - Hidden - Toggles the feature on/off in checkout, following properties have no effect without it + * properties[Recipient email] - The email of the gift card recipient + * properties[Recipient name] - The name of the gift card recipient + * properties[Message] - Shopify chose a highly generic name for the gift card message, this could easily conflict with an app or custom code + {% endcomment %} + {%- if gift_card_recipient_feature_active -%} + {%- render 'gift-card-recipient-form', product: product, form: form, section: section -%} + {%- endif -%} + +
+ + + {%- assign button_text = 'products.product.add_to_cart' | t -%} + {%- if product.metafields.theme.preorder.value == true -%} + {% comment %} Add a line item property with 'Sale type: Pre-order' and make the button say 'Pre-order'{% endcomment %} + + {%- assign button_text = 'products.product.pre_order' | t -%} + {%- endif -%} + + {%- render 'sibling-color-as-line-prop' product: product -%} + + {%- if current_variant == null -%} + {%- assign button_text = 'products.product.unavailable' | t -%} + {%- endif -%} + + {%- if current_variant.available == false -%} + {%- assign button_text = 'products.product.sold_out' | t -%} + {%- endif -%} + +
+ + {{ 'products.product.adding_to_cart' | t }} + + + {{ 'products.product.added_to_cart' | t }} + + + + + {%- if buybutton_setting -%} +
+ {{ form | payment_button }} +
+ {%- endif -%} +
+
+
+ + {% comment %} Shop pay split payment terms {% endcomment %} +
{{- form | payment_terms -}}
+ {%- endunless -%} + + {% comment %} The input with name="id" indicates the variant to add to cart {% endcomment %} + + {%- endform -%} +
+
diff --git a/snippets/product-grid-item-variant.liquid b/snippets/product-grid-item-variant.liquid new file mode 100644 index 0000000..0f0f393 --- /dev/null +++ b/snippets/product-grid-item-variant.liquid @@ -0,0 +1,578 @@ + + +{% comment %} + Inner content for a grid item +{% endcomment %} + +{%- liquid + 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 + + assign sellout_badge = false + if sold_out and settings.badge_sellout + assign sellout_badge = true + endif + + assign sale_badge = false + if on_sale and settings.badge_sale + assign sale_badge = true + assign sale_badge_content = 'products.product.sale' | t + if settings.badge_sale_discount + if settings.badge_sale_type == 'dollar' + if settings.currency_code_enable + assign sale_badge_content = product_compare_at_price | minus: product_price | money_with_currency + else + assign sale_badge_content = product_compare_at_price | minus: product_price | money_without_trailing_zeros + endif + else + assign difference = product_compare_at_price | minus: product_price + assign percent_off = difference | times: 1.0 | divided_by: product_compare_at_price | times: 100 + assign sale_badge_content = percent_off | floor | append: '%' + endif + assign save_word = 'products.product.save' | t | append: ' ' + assign sale_badge_content = sale_badge_content | prepend: save_word + endif + endif + + assign custom_badge = false + if settings.badge_custom + if product.metafields.theme.badge != blank and product.metafields.theme.badge.type == 'single_line_text_field' + assign custom_badge = true + assign custom_badge_content = product.metafields.theme.badge.value + endif + for tag in product.tags + if tag contains "_badge_" + assign tag_content = tag | remove: '_badge_' | replace: '_', ' ' + if tag_content != '' + assign custom_badge = true + assign custom_badge_content = tag_content + endif + break + endif + endfor + endif + + if badge_string and badge_string != '' + assign custom_badge = true + assign custom_badge_content = badge_string + endif + + assign tagged = false + if sellout_badge or sale_badge or custom_badge + assign tagged = true + endif + + comment + Disqualify options that have more than 15 variants or are a combined length of > 90 characters + endcomment + if inline_variant_buttons.values.size > 15 + assign inline_variant_buttons = nil + endif + + if inline_variant_buttons + assign all_characters = inline_variant_buttons.values | join: "" + if all_characters.size >= 90 + assign inline_variant_buttons = nil + endif + endif + + # Sellign plans can be added along with inline or instand buttons if + # the product has exactly 1 selling plan with subscriptions required + assign simple_selling_plan = nil + + if inline_variant_buttons or instant_add_button + if product.requires_selling_plan and product.selling_plan_groups.size == 1 and product.selling_plan_groups[0].selling_plans.size == 1 + # one variant, one required subscription, no choices to make + assign simple_selling_plan = product.selected_or_first_available_selling_plan_allocation.selling_plan + elsif product.selling_plan_groups.size > 0 + # Abort instant and inline add buttons, subs choices must be made + assign inline_variant_buttons = nil + assign instant_add_button = nil + endif + endif + + # Catch case where first sibling has inline variants and subsequent do not + if product.has_only_default_variant and inline_variant_buttons + assign inline_variant_buttons = nil + assign instant_add_button = true + endif + + # Allow configuration of image sizing for different numbers of grid columns + # Note: desktop/tablet are set to a default of 3 just in case the grid sizes are not set to prevent accidental gigantic full-width images from being loaded + assign columns_desktop = columns_desktop | default: section.settings.grid_large | default: 3 + assign columns_tablet = columns_tablet | default: section.settings.grid_medium | default: columns_desktop | default: 3 + assign columns_mobile = columns_mobile | default: section.settings.grid_mobile | default: 1 + assign section_width = section_width | default: section.settings.width | default: null +-%} +{%- capture badge -%} + {%- if tagged %} + {%- if custom_badge -%} +
{{ custom_badge_content }}
+ {%- elsif sellout_badge -%} +
{{ 'products.product.sold_out' | t }}
+ {%- elsif sale_badge -%} +
{{ sale_badge_content }}
+ {%- endif -%} + {%- endif -%} +{%- endcapture -%} + +{%- liquid + assign first_image = product.media[0].preview_image + assign container_wh_ratio = settings.product_card_wh_ratio + assign image_cover = true + + case settings.product_grid_image + when 'crop' + assign image_wh_ratio = container_wh_ratio + when 'uneven' + assign container_wh_ratio = first_image.aspect_ratio | default: settings.product_card_wh_ratio + when 'scale' + assign image_cover = false + assign image_wh_ratio = first_image.aspect_ratio | default: settings.product_card_wh_ratio + endcase + + # Behavior is inferred based on setting and passed into JS + assign image_hover = 'disabled' + assign images_limit = settings.cycle_images_limit + case images_limit + when 1 + assign image_hover = 'disabled' + when 2 + assign image_hover = 'second_immediately' + else + assign image_hover = 'cycle_images' + endcase +-%} + +{%- capture sizes -%} + {%- render 'image-grid-sizes', + columns_desktop: columns_desktop, + columns_tablet: columns_tablet, + columns_mobile: columns_mobile, + section_width: section_width + %} +{%- endcapture -%} + + diff --git a/snippets/product-grid-item.liquid b/snippets/product-grid-item.liquid new file mode 100644 index 0000000..75e5903 --- /dev/null +++ b/snippets/product-grid-item.liquid @@ -0,0 +1,670 @@ + + +{% comment %} + A grid item for products used in collection grid view + + * product {object} - The current prodcut + + {% render 'product-grid-item', product: product %} +{% endcomment %} + +{%- liquid + +assign on_sale = false +assign product_price = product.price +assign product_compare_at_price = product.compare_at_price + +comment + start Yagi app +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 +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 + +# Siblings are a collection metafield called theme.siblings +assign sibs_collection = nil +assign has_siblings = false +if settings.show_siblings and product.metafields.theme.siblings.value != blank and product.metafields.theme.siblings.type == 'single_line_text_field' + assign sibs_collection = collections[product.metafields.theme.siblings.value] + # Ensure the collection was set up to contain this product + for sib_product in sibs_collection.products + if sib_product == product + assign has_siblings = true + endif + endfor +endif + +assign swatch_option = nil +assign has_swatches = false +assign swatch_limit = settings.card_swatch_limit + +if settings.swatches_enable and settings.swatches_collection_enable + # Detect new swatch system + for option in product.options_with_values + if option.values.first.swatch != null + assign has_swatches = true + assign swatch_option = option + break + endif + endfor + + if has_swatches == false + # Detect old swatch system + assign swatch_translation = 'general.swatches.color' | t + assign swatch_labels = swatch_translation | append: ',' | split: ',' + for label in swatch_labels + assign sanitized_label = label | lstrip | rstrip + if product.options_by_name[sanitized_label].values.size > 0 + assign has_swatches = true + assign swatch_option = product.options_by_name[sanitized_label] + break + endif + endfor + endif +endif + +# A button that will add to cart immediately: product with only one default variant or a product with color swatches and no other variants +assign instant_add_button = nil + +if settings.instant_add_enable + if product.variants.size == 1 + assign instant_add_button = true + elsif has_swatches and product.options.size == 1 + assign instant_add_button = true + endif +endif + +# A quick add button that will show short variants like 'size' inside the button. +assign inline_variant_buttons = nil + +# Get the non color option +# swatch_option.position -> 1 is off by one, it means product.options[0] +# In case of siblings, the inline option buttons always use the first option +if settings.instant_add_enable + if product.options.size == 1 + assign inline_variant_buttons = product.options_with_values[0] + elsif has_swatches and product.options.size == 2 + if swatch_option.position == 1 + assign inline_variant_buttons = product.options_with_values[1] + else + assign inline_variant_buttons = product.options_with_values[0] + endif + endif +endif + + +# Capture the swatch link markup so we only have filter product.variants by color one time +assign swatch_link_markup = '' + +# Pass pre-filtered color variants into content, because the logic needs to know about +# swatch option positions and swatches need to use the where filter to get the list +assign inline_variants = nil + +# Placeholder to be displayed when no product image +assign placeholder = placeholder | default: false +-%} + + + {% if has_siblings and sibs_collection.products.size > 0 %} + {%- liquid + # Initialize an empty array to hold visible siblings (i.e. ones under swatch limit) + assign visible_siblings = "" | split: "," + + # Initial visible sibling count is 1 to account for the current product + assign visible_sibling_count = 1 + + # Loop through siblings collection and add the current product, plus other siblings until we hit the swatch_limit + for sibling in sibs_collection.products + if sibling.id == product.id or visible_sibling_count < swatch_limit + # Create a new array with the current value, then concat it into the main array + assign new_array = sibling | sort + assign visible_siblings = visible_siblings | concat: new_array + + unless sibling.id == product.id + assign visible_sibling_count = visible_sibling_count | plus: 1 + endunless + endif + endfor + -%} + + {%- for sib_product in visible_siblings -%} + {% liquid + assign visible = false + assign first_sibling_variant = sib_product.variants[0] + if sib_product.id == product.id + assign visible = true + endif + + if visible and preload + assign preload_variant = true + endif + if visible and eagerload + assign eagerload_variant = true + endif + + assign inline_variant_buttons = nil + if settings.instant_add_enable and sib_product.options.size == 1 + assign inline_variant_buttons = sib_product.options_with_values[0] + endif + + if inline_variant_buttons + assign inline_variants = sib_product.variants + endif + -%} + + {% render 'product-grid-item-variant', + product: sib_product, + variant: first_sibling_variant, + badge_string: badge_string, + visible: visible, + instant_add_button: instant_add_button, + inline_variant_buttons: inline_variant_buttons, + inline_variants: inline_variants, + section_width: section_width, + eagerload: eagerload_variant, + preload: preload_variant, + placeholder: placeholder, + columns_desktop: columns_desktop, + columns_tablet: columns_tablet, + columns_mobile: columns_mobile + %} + {% endfor %} + {% elsif has_swatches %} + {%- liquid + + # Note: this is a hack based on options having a property named option1, option2, or option3 -- which we rely on below to filter the variants array + assign swatch_position_key = 'option' | append: swatch_option.position + assign selected_swatch_value = swatch_option.selected_value + + # Initialize an empty array to hold visible siblings (i.e. ones under swatch limit) + assign visible_swatches = "" | split: "," + + # If there is a selected swatch, start the visible swatch count at 1 to account for it + if selected_swatch_value != blank + assign visible_swatch_count = 1 + else + assign visible_swatch_count = 0 + endif + + # Loop through siblings collection and add the current product, plus other swatches until we hit the swatch limit + for swatch_value in swatch_option.values + if swatch_value == selected_swatch_value or visible_swatch_count < swatch_limit + # Create a new array with the current value, then concat it into the main array + assign new_array = swatch_value | sort + assign visible_swatches = visible_swatches | concat: new_array + + unless swatch_value == selected_swatch_value + assign visible_swatch_count = visible_swatch_count | plus: 1 + endunless + endif + endfor + -%} + + {%- for swatch_value in visible_swatches -%} + {% liquid + + # Limit S|M|L to low-variant products to ensure collection page performance + if product.variants.size < 200 + assign this_color_variants = product.variants | where: swatch_position_key, swatch_value + assign in_stock_options = this_color_variants | where: 'available' + if inline_variant_buttons + assign inline_variants = this_color_variants + endif + else + # Fall back to quickview with section-rendering API variant selection if there's too many variants + assign inline_variant_buttons = false + assign inline_variants = nil + endif + + + # Use the default variant for the swatch value. If that variant has media attached the image will change. + # If not, the variant image will not change when swatch is selected. + assign current_variant = swatch_value.variant + + # If there is a variant selected from filters, show that variant first + assign visible = false + if product.selected_variant.id == current_variant + assign visible = true + elsif forloop.first + assign visible = true + endif + + if visible and preload + assign preload_variant = true + endif + if visible and eagerload + assign eagerload_variant = true + endif + -%} + + {% render 'product-grid-item-variant', + product: product, + variant: current_variant, + badge_string: badge_string, + visible: visible, + instant_add_button: instant_add_button, + inline_variant_buttons: inline_variant_buttons, + inline_variants: inline_variants, + section_width: section_width, + swatch_option: swatch_option, + eagerload: eagerload_variant, + preload: preload_variant, + placeholder: placeholder, + columns_desktop: columns_desktop, + columns_tablet: columns_tablet, + columns_mobile: columns_mobile + %} + + {% capture swatch_link_markup %} + {{ swatch_link_markup }} + + + {% if swatch_value.swatch.image %} + {% render 'image', img_object: swatch_value.swatch.image, width: 34, wh_ratio: 1 %} + {% endif %} + {{ swatch_value }} + + + {% endcapture %} + {% endfor %} + {% else %} + + {%- liquid + + # Limit S|M|L to low-variant products to ensure collection page performance + if inline_variant_buttons and product.variants.size < 200 + assign inline_variants = product.variants + else + assign inline_variant_buttons = false + assign inline_variants = nil + endif + + assign selected_variant = product.selected_variant | default: product.variants[0] + + if preload + assign preload_variant = true + endif + if eagerload + assign eagerload_variant = true + endif + -%} + + {% render 'product-grid-item-variant', + product: product, + variant: selected_variant, + badge_string: badge_string, + visible: true, + instant_add_button: instant_add_button, + inline_variant_buttons: inline_variant_buttons, + inline_variants: inline_variants, + section_width: section_width, + preload: preload_variant, + eagerload: eagerload_variant, + placeholder: placeholder, + columns_desktop: columns_desktop, + columns_tablet: columns_tablet, + columns_mobile: columns_mobile + %} + {% endif %} + +
+ +

{{ product.title | strip_html | escape }}

+ +
+

+ {{ product.title | strip_html | escape }} +

+ {%- if settings.product_grid_show_rating and product.metafields.reviews.rating.value != blank -%} +
+ {% render 'product-rating', product: product, show_rating_count: settings.product_grid_show_rating_count %} +
+ {%- endif -%} +
+ +
+ {%- if settings.show_cutline -%} + {{ product.metafields.theme.cutline.value }} + {%- 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 -%} + + {% if on_sale %} + + {%- if settings.currency_code_enable -%} + {{ product_compare_at_price | money_with_currency }} + {%- else -%} + {{ product_compare_at_price | money }} + {%- endif -%} + + {% endif %} +
+ {% if product.selected_or_first_available_variant.unit_price %} + {% capture unit_price_separator %} + {{ 'general.accessibility.unit_price_separator' | t }}  + {% endcapture %} + {% capture unit_price_base_unit %} + {% if product.selected_or_first_available_variant.unit_price_measurement.reference_value != 1 %} + {{ product.selected_or_first_available_variant.unit_price_measurement.reference_value }} + {% endif %} + {{ product.selected_or_first_available_variant.unit_price_measurement.reference_unit }} + {% endcapture %} +

+ {{ 'products.product.unit_price_label' | t }} + + {%- if settings.currency_code_enable -%} + {{ product.selected_or_first_available_variant.unit_price | money_with_currency }} + {%- else -%} + {{ product.selected_or_first_available_variant.unit_price | money }} + {%- endif -%} + {{ unit_price_separator }}{{ unit_price_base_unit }} + +

+ {% endif %} + {% comment %} {% if sold_out %} +

+ {{ 'products.product.sold_out' | t }} +

+ {% endif %} {% endcomment %} +
+ + {% assign active_variant = product.selected_or_first_available_variant %} + +
+ {%- form 'product', product, class: 'product-card__atc-form', novalidate: 'novalidate', data-type: 'add-to-cart-form', is: 'product-form' -%} + + + + + + {%- endform -%} +
+ + + {%- if has_siblings -%} + {%- assign sibs_collection = collections[product.metafields.theme.siblings.value] -%} + {% if sibs_collection != blank %} + {%- assign excess_swatches = sibs_collection.products_count | minus: swatch_limit -%} +
+
+

{{ 'collections.general.swatches_with_count' | t: count: sibs_collection.products_count }}

+ +
+
+ {% endif %} + {%- elsif has_swatches and swatch_link_markup != '' -%} + {%- assign excess_swatches = swatch_option.values.size | minus: swatch_limit -%} + +
+

+ {{ 'collections.general.swatches_with_count' | t: count: swatch_option.values.size }} +

+
+ {{ swatch_link_markup }} {% if excess_swatches > 0 %} + +{{ excess_swatches }} + {% endif %} +
+
+ {%- endif -%} +
+
+ + + diff --git a/snippets/product-price.liquid b/snippets/product-price.liquid new file mode 100644 index 0000000..70b9a41 --- /dev/null +++ b/snippets/product-price.liquid @@ -0,0 +1,168 @@ + +{%- liquid + assign current_variant = product.selected_or_first_available_variant + assign price = current_variant.price + assign compare_at_price = current_variant.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 price = current_variant.metafields.app--168074346497.auto_discounted_price.value | default: current_variant.price + assign compare_at_price = current_variant.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 price = current_variant.price | divided_by: 100.0 | times: deducted_percentage | times: 100.0 | ceil + endif + + if price < current_variant.price and compare_at_price == 0 or compare_at_price == blank + assign compare_at_price = current_variant.price + endif + endif + + comment + end Yagi app code + endcomment + + assign sale_type = settings.badge_sale_type + + if block.settings.price_size + assign price_size_class = block.settings.price_size | prepend: 'accent-size-' + assign price_small_class = block.settings.price_size | minus: 1 | prepend: 'accent-size-' + endif + + # Select current selling plan if it's specified in the URL + assign selected_selling_plan = product.selected_selling_plan + if product.requires_selling_plan + # Fallback to the first available selling plan + assign selected_selling_plan = product.selected_or_first_available_selling_plan_allocation.selling_plan | default: product.selling_plan_groups[0].selling_plans[0] + endif + + assign sale_text = 'products.product.sale' | t + + # Subscription price + if selected_selling_plan + assign sale_text = 'products.product.subscription' | t + + # Make sure the variant exists(it isn't 'unavailable') and showing a price makes sense + if current_variant + assign price_adjustment = selected_selling_plan.price_adjustments[0] + + case price_adjustment.value_type + when 'percentage' + assign sale_type = 'percentage' + assign plan_discount_amount = price | times: price_adjustment.value | divided_by: 100 + assign compare_at_price = price + assign price = compare_at_price | minus: plan_discount_amount + when 'fixed_amount' + assign sale_type = 'dollar' + assign compare_at_price = price + assign price = compare_at_price | minus: price_adjustment.value + when 'price' + assign sale_type = 'dollar' + assign compare_at_price = price + assign price = price_adjustment.value + endcase + endif + endif +-%} + +
+
+
+ + price %} class="product__price--sale"{% endif %}> + {%- if settings.currency_code_enable -%} + {{ price | money_with_currency }} + {%- else -%} + {{ price | money }} + {%- endif -%} + + + + {% if compare_at_price > price or product.selected_selling_plan %} + {% case sale_type %} + {% when 'strike' %} + + {%- if settings.currency_code_enable -%} + {{ compare_at_price | money_with_currency }} + {%- else -%} + {{ compare_at_price | money }} + {%- endif -%} + + {% when 'percentage' %} + {% assign difference = compare_at_price | minus: price %} + {% assign percent_off = difference | times: 100 | divided_by: compare_at_price %} + + {{ sale_text }} + {% if percent_off > 0 %} + + {{ 'products.product.save' | t }} + {{ percent_off | floor | append: '%' }} + {% endif %} + + {% when 'dollar' %} + {% assign amount_off = compare_at_price | minus: price %} + + {{ sale_text }} + + {{ 'products.product.save' | t }} + {{ amount_off | money }} + + {% endcase %} + {% endif %} + + {% if current_variant.unit_price != blank %} + {% capture show_units %} + {%- unless current_variant.unit_price -%}style="display: none;"{%- endunless -%} + {% endcapture %} + {% capture unit_price_separator %} + {{ 'general.accessibility.unit_price_separator' | t }}  + {% endcapture %} + {% capture unit_price_base_unit %} + + {% if current_variant.unit_price_measurement %} + {% if current_variant.unit_price_measurement.reference_value != 1 %} + {{ current_variant.unit_price_measurement.reference_value }} + {%- endif -%} + {{ current_variant.unit_price_measurement.reference_unit }} + {% endif %} + + {% endcapture %} +
+ + {{ 'products.product.unit_price_label' | t }} + + {%- if settings.currency_code_enable -%} + {{ current_variant.unit_price | money_with_currency }} + {%- else -%} + {{ current_variant.unit_price | money }} + {%- endif -%} + + {{ unit_price_separator }} + {{ unit_price_base_unit }} + + + {{ 'products.product.each' | t }} + + +
+ {% endif %} +
+
+