Overview
This allows developers the ability to add custom functionality that is currently not achievable by the bundle builder widget settings. These templates can use any globally accessible functions. Developers can add additional Vue.js/HTML to these templates as needed. As these templates are written in Vue.js they accept and Vue.js attributes such as v-if for conditional rendering.
Setting up Custom Templates
There are a couple of ways these templates can be used. They can target all widgets of that widget type or, they can target a specific widget if you do not want the custom work to be applied to all widgets of that specific widget type.
Typically developers will create a new snippet named something like "rebuy-templates.liquid" and paste the contents of the code in that file. Then render that snippet in the associated file or in the theme.liquid file if they want the template to be globally accessible.
Bundle Builder Default Template
<script id="rebuy-bundle-builder-template" type="text/template">
<div
class="rebuy-widget rebuy-bundle-builder"
v-bind:id="'rebuy-widget-' + id"
>
<div
class="rebuy-bundle-builder__main rebuy-bundle-builder-layout"
v-bind:class="['rebuy-bundle-builder__layout--' + config.layout.type]"
ref="stepTabsWrapper"
>
<!-- Page Header Section -->
<div v-if="hasBundleHeaderSection()" class="rebuy-bundle-builder__main-header">
<h1
v-if="getBundleConfigLabel('super_title')"
class="rebuy-bundle-builder__main-header-super-title"
v-html="getBundleConfigLabel('super_title')"
>
</h1>
<h2
v-if="getBundleConfigLabel('title')"
class="rebuy-bundle-builder__main-header-title"
v-html="getBundleConfigLabel('title')"
>
</h2>
<p
v-if="getBundleConfigLabel('description')"
class="rebuy-bundle-builder__main-header-description"
v-html="getBundleConfigLabel('description')"
>
</p>
</div>
<!-- End Page Header Section -->
<!-- Page Body Section -->
<div class="rebuy-bundle-builder__main-body">
<!-- Steps Section -->
<div class="rebuy-bundle-builder__steps-container">
<!-- Step Tabs Section -->
<div v-if="isBundleBuilderTabLayout()" class="rebuy-bundle-builder__step-tabs rebuy-bundle-builder__step-tabs--sticky">
<div class="rebuy-bundle-builder__step-tabs-wrapper">
<div
v-for="(step, step_index) in config.steps"
class="rebuy-bundle-builder__step-tab"
v-bind:key="'rebuy-bundle-builder__step-tab-' + step_index"
v-bind:class="['rebuy-bundle-builder__step-tab-' + step_index, active_step_index === step_index ? 'active' : '']"
v-bind:aria-label="'Select step tab: ' + step.title"
role="button"
tabindex="0"
v-on:click="handleSelectingBundleStep(step_index)"
v-on:keydown.enter.space="handleSelectingBundleStep(step_index)"
>
<h3
v-if="step.title"
v-html="step.title"
class="rebuy-bundle-builder__step-tab-title">
</h3>
<p
v-if="step.description"
v-html="step.description"
class="rebuy-bundle-builder__step-tab-description">
</p>
</div>
</div>
<button
v-on:click="scrollToTargetBundleElement('main', id)"
class="rebuy-bundle-builder__scroll-to-button"
aria-label="scroll to top"
>
<i class="far fa-chevron-up"></i>
<span>Top</span>
</button>
</div>
<!-- End Step Tabs Section -->
<!-- Step Section -->
<div
v-for="(step, step_index) in config.steps"
class="rebuy-bundle-builder__step-container"
v-bind:key="'rebuy-bundle-builder__step-container-' + step_index"
v-bind:class="[
'rebuy-bundle-builder__step-container-' + step_index,
step.unlocked ? '' : 'locked',
isBundleBuilderCollapsibleLayout() ? 'rebuy-bundle-builder__step-container--collapsible' : '',
!shouldRenderBundleStep(step_index) ? 'rebuy-bundle-builder__step-container--not-active' : 'rebuy-bundle-builder__step-container--active'
]"
aria-label="step"
role="listitem"
>
<!-- Step Header Section -->
<div v-if="!isBundleBuilderTabLayout()" class="rebuy-bundle-builder__step-header">
<div class="rebuy-bundle-builder__step-header-copy-container">
<h3
v-if="step.title"
v-html="step.title"
class="rebuy-bundle-builder__step-header-title">
</h3>
<p
v-if="step.description"
v-html="step.description"
class="rebuy-bundle-builder__step-header-description">
</p>
</div>
<div class="rebuy-bundle-builder__step-header-actions">
<div v-if="isBundleBuilderCollapsibleLayout()">
<button
class="rebuy-bundle-builder__step-header-collapse-button"
v-on:click="handleCollapsingBundleStep(step_index)"
v-bind:aria-label="shouldRenderBundleStep(step_index) ? 'Collapse ' + step.title : 'Expand ' + step.title"
>
<i
class="rebuy-bundle-builder__step-header-collapse-button-icon"
:class="shouldRenderBundleStep(step_index) ? 'far fa-minus' : 'far fa-plus'"
aria-hidden="true">
</i>
</button>
</div>
<button
v-if="!isBundleBuilderCollapsibleLayout()"
v-on:click="scrollToTargetBundleElement('step', id, (step_index - (step_index === 0 ? 0 : 1)))"
:class="[
'rebuy-bundle-builder__scroll-to-button',
step_index === 0 ? 'rebuy-bundle-builder__top-button' : 'rebuy-bundle-builder__prev-button',
'rebuy-bundle-builder__step-button-' + step_index
]"
aria-label="scroll to top"
>
<i class="far fa-chevron-up"></i>
<span v-html="step_index === 0 ? 'Top' : 'Prev'"></span>
</button>
</div>
</div>
<!-- End Step Header Section -->
<!-- Step Body Section -->
<div
class="rebuy-bundle-builder__step-body rebuy-product-grid start-column rebuy-product-grid-step-100"
v-bind:class="[getBundleBuilderLayoutClasses(step), 'rebuy-product-grid__step-' + step_index]"
>
<!-- Step Product Section -->
<div
v-for="(product, product_index) in step.products"
v-bind:class="[product.handle, 'product-id-' + product.id, productTagClasses(product)]"
class="rebuy-product-block"
aria-label="product"
role="listitem"
>
<div v-if="product.type !== 'placeholder'" class="rebuy-bundle-builder__step-product">
<!-- Product Image Section -->
<div class="rebuy-product-media">
<a
class="rebuy-product-image clickable"
v-bind:href="learnMoreURL(product)"
v-on:click="learnMore(product, $event);"
rel="nofollow"
>
<!-- Preload second img in the background hack, so on hover browser can serve the cached image -->
<img
v-if="product.hover_image"
class="rebuy-style__hidden-block"
v-bind:src="sizeImage(product.hover_image.src, '400x400')"
v-bind:alt="'View ' + product.title"
/>
<img
class="rebuy-style__hover-transition"
v-bind:loading="product_index > 1 ? 'lazy' : 'eager'"
v-bind:src="product.is_hovered && product.hover_image ? sizeImage(product.hover_image.src, '400x400') : itemImage(product, product.selected_variant, '400x400')"
v-bind:alt="'View ' + product.title"
v-on:mouseover="handleProductImageHover(product, true)"
v-on:mouseleave="handleProductImageHover(product, false)"
/>
</a>
</div>
<!-- End Product Image Section -->
<!-- Product Info Section -->
<div class="rebuy-product-info">
<a
role="heading"
aria-level="5"
class="rebuy-product-title clickable"
v-bind:href="learnMoreURL(product)"
v-on:click="learnMore(product, $event);"
v-html="product.title"
v-bind:aria-label="'View ' + product.title"
rel="nofollow">
</a>
<div class="rebuy-variant-title" v-if="showVariantTitle(product)" v-html="product.selected_variant.title"></div>
<div class="rebuy-product-review" v-if="hasProductReviews(product)" aria-label="product star rating">
<span class="rebuy-star-rating">
<span
v-if="product.reviews.star_rating"
class="rebuy-star-rating-value sr-only"
v-html="product.reviews.star_rating + ' stars out of 5 stars'"
>
</span>
<span class="rebuy-star-rating-background"></span>
<span class="rebuy-star-rating-foreground" v-bind:style="{ width: productReviewRatingPercentage(product) }"></span>
</span>
<span class="rebuy-review-count" v-html="productReviewCount(product)"></span>
</div>
<div class="rebuy-product-price">
<div v-if="bundleVariantOnSale(product, product.selected_variant)">
<span class="rebuy-money sale">
<span class="sr-only">Sale price</span>
<span tabindex="0" v-html="formatMoney(bundleVariantPrice(product, product.selected_variant))"></span>
</span>
<span class="rebuy-money compare-at">
<span class="sr-only">Original price</span>
<span v-html="formatMoney(bundleVariantCompareAtPrice(product, product.selected_variant))"></span>
</span>
</div>
<div v-if="!(bundleVariantOnSale(product, product.selected_variant))">
<span class="rebuy-money">
<span class="sr-only">Price</span>
<span tabindex="0" v-html="formatMoney(bundleVariantPrice(product, product.selected_variant))"></span>
</span>
</div>
</div>
<div class="rebuy-product-description" v-if="showProductDescription(product)" v-html="text(product.body_html)"></div>
</div>
<!-- End Product Info Section -->
<!-- Product Variant Selection Section -->
<div class="rebuy-product-options" v-if="showVariantSelect(product)">
<select
:id="id + '-' + 'select' + '-' + product_index"
class="rebuy-select"
v-bind:aria-label="'variant of ' + product.title"
v-model="product.selected_variant_id"
v-on:change="selectVariant(product)">
<option v-for="variant in product.variants" v-bind:value="variant.id">{{ variant.title }}</option>
</select>
</div>
<!-- End Product Variant Selection Section -->
<!-- Product CTA Section -->
<div class="rebuy-bundle-builder__product-actions rebuy-product-actions">
<button
v-if="hasProductQuickViewEnabled()"
v-on:click="openVariantModal(product, step, step_index)"
class="rebuy-button secondary rebuy-bundle-builder__product-quick-view"
v-html="getBundleConfigLabel('quick_view_label')"
>
</button>
<div class="rebuy-bundle-builder__product-quantity">
<div v-if="foundStepProductInBundleHolder(product.selected_variant_id, step_index)">
<div class="rebuy-bundle-builder__item-quantity-widget">
<button
class="rebuy-bundle-builder__item-quantity-widget-button"
v-on:click="removeLastAddedProductStep(product.selected_variant_id, step_index)"
v-bind:aria-label="'Decrease quantity of ' + product.title"
>
<i class="fa fa-minus rebuy-grey" aria-hidden="true"></i>
</button>
<span
class="rebuy-bundle-builder__item-quantity-widget-label"
v-html="getBundleVariantQuantity(product.selected_variant_id, step_index)">
</span>
<button
class="rebuy-bundle-builder__item-quantity-widget-button"
v-on:click="addProductToBundle(product, step, step_index)"
v-bind:aria-label="'Increase quantity of ' + product.title"
v-bind:disabled="shouldDisableProductAddToBundle(product, step, step_index)"
>
<i class="fa fa-plus rebuy-grey" aria-hidden="true"></i>
</button>
</div>
</div>
<button
v-if="!foundStepProductInBundleHolder(product.selected_variant_id, step_index)"
class="rebuy-button"
v-bind:disabled="shouldDisableProductAddToBundle(product, step, step_index)"
v-bind:aria-label="buttonAriaLabel(product)"
v-on:click="addProductToBundle(product, step, step_index)"
>
<span v-html="getBundleConfigLabel('add_to_bundle', step)"></span>
</button>
</div>
</div>
<!-- End Product CTA Section -->
</div>
<!-- Product Skeleton Section -->
<div
v-if="product.type === 'placeholder'"
class="rebuy-bundle-builder__step-product-skeleton">
<span></span>
</div>
<!-- End Product Skeleton Section -->
</div>
<!-- End Step Product Section -->
</div>
<!-- End Step Body Section -->
</div>
<!-- End Step Section -->
</div>
<!-- End Steps Section -->
<!-- Product Group Section -->
<div
class="rebuy-bundle-builder__group-container"
v-bind:class="[has_container_expanded ? 'expanded' : 'default']">
<div class="rebuy-bundle-builder__group-container-inner">
<div class="rebuy-bundle-builder__group-container-header">
<div class="rebuy-bundle-builder__group-container-header--left">
<h3
class="rebuy-bundle-builder__group-container-header-title"
v-html="getBundleConfigLabel('bundle_container_title')"
>
</h3>
<span class="rebuy-bundle-builder__group-container-header-title-count">
<span
class="rebuy-bundle-builder__group-container-header-products_count"
v-html="getBundleContainerHeaderValue('products_count')">
</span>
<span class="rebuy-bundle-builder__group-container-header-divider">/</span>
<span
class="rebuy-bundle-builder__group-container-header-container-length"
v-html="getBundleContainerHeaderValue('container_length')">
</span>
</span>
</div>
<button
class="rebuy-bundle-builder__group-container-header-expand"
v-on:click="handleExpandingBundleContainer()"
>
<i v-if="hasBundleContainerExpanded()" class="far fa-chevron-down" aria-hidden="true"></i>
<i v-if="!hasBundleContainerExpanded()" class="far fa-chevron-up" aria-hidden="true"></i>
</button>
</div>
<div v-if="shouldDisplayBundleTierProgressBar()" class="rebuy-bundle-builder__group-container-progress-bar">
<div
class="rebuy-bundle-builder__group-progress-step-wrapper rebuy-bundle-builder__progress-step-wrapper"
v-bind:class="[ 'count-' + config.discount.tiers.length ]"
>
<div
v-for="(tier, tier_index) in config.discount.tiers"
class="rebuy-bundle-builder__progress-step"
v-bind:style="{ left: progressStepPosition(tier) }"
v-bind:class="[
progressTierReached(tier, products) ? 'complete' : '',
'rebuy-bundle-builder__progress-step--' + tier_index
]"
>
<div
class="rebuy-bundle-builder__progress-step-icon"
v-bind:class="[ 'rebuy-bundle-builder__progress-step-icon-type-' + getBundleTierProgressStepType(tier) ]"
>
<span v-if="getBundleTierProgressStepType(tier) !== 'none'" v-html="formatBundleTierValue(tier.discount_value, tier.discount_type)"></span>
</div>
<span
class="rebuy-bundle-builder__progress-step-label"
v-html="formatBundleTierLabel(tier)">
</span>
</div>
</div>
<div class="rebuy-bundle-builder__progress-bar-wrapper">
<div
class="rebuy-bundle-builder__progress-bar-meter"
v-bind:class="[hasTierProgress(products) ? 'has-progress' : 'no-progress']">
<div
class="rebuy-bundle-builder__progress-bar-meter-fill"
v-bind:style="{ width: tiersPercentageComplete(config.discount.tiers, products) }"
role="progressbar"
tabindex="0"
aria-valuemin="0"
aria-valuemax="100"
v-bind:aria-valuenow="tiersPercentageComplete(config.discount.tiers, products)"
aria-labelledby="rebuy-bundle-builder__progress-bar-meter-label"
>
<span class="sr-only"></span>
</div>
</div>
</div>
</div>
<div
class="rebuy-bundle-builder__group-container-body"
v-bind:class="[has_container_expanded ? 'expanded' : 'default']">
<div class="rebuy-bundle-builder__group-container-body-items">
<div
v-for="(product, product_index) in bundleProducts"
class="rebuy-bundle-builder__product-holder"
aria-label="product"
role="listitem"
>
<div
v-if="product.classification === 'placeholder'"
class="rebuy-bundle-builder__product-holder-template"
>
<div class="rebuy-bundle-builder__product-sign-holder">
<span class="rebuy-bundle-builder__product-holder-icon">
<i class="far fa-plus" aria-hidden="true"></i>
</span>
</div>
<div class="rebuy-bundle-builder__product-holder-detail">
<h3
class="rebuy-bundle-builder__product-holder-detail-title"
v-html="getBundleConfigLabel('select_product')">
</h3>
<p
v-if="product.label"
class="rebuy-bundle-builder__product-holder-detail-description"
v-html="product.label">
</p>
</div>
</div>
<div v-if="!product.classification" class="rebuy-bundle-builder__product-holder-data">
<div class="rebuy-bundle-builder__product-holder-info--left">
<!-- Product Image Section -->
<div class="rebuy-bundle-builder__product-holder-media rebuy-product-media">
<img
v-bind:src="itemImage(product, product.selected_variant, '80x80')"
v-bind:alt="'View ' + product.title"
/>
</div>
<!-- End Product Image Section -->
<!-- Product Info Section -->
<div class="rebuy-bundle-builder__product-holder-info rebuy-product-info">
<h3 class="rebuy-bundle-builder__product-holder-info-title rebuy-product-title" v-html="product.title"></h3>
<p
v-if="!shouldHideDefaultVariantTitle(product)"
class="rebuy-bundle-builder__product-holder-info-variant rebuy-variant-title"
v-html="product.selected_variant.title"
>
</p>
<div class="rebuy-bundle-builder__product-holder-info-price rebuy-product-price">
<div v-if="bundleVariantOnSale(product, product.selected_variant, true)">
<span class="rebuy-money sale">
<span class="sr-only">Sale price</span>
<span tabindex="0" v-html="formatMoney(bundleVariantPrice(product, product.selected_variant))"></span>
</span>
<span class="rebuy-money compare-at">
<span class="sr-only">Original price</span>
<span v-html="formatMoney(bundleVariantCompareAtPrice(product, product.selected_variant))"></span>
</span>
<div
v-if="isFixedDiscountBundle()"
class="rebuy-bundle-builder__fixed-discount-line-item-message"
v-html="getBundleConfigLabel('fixed_tier_discount')">
</div>
</div>
<div v-if="!(bundleVariantOnSale(product, product.selected_variant, true))">
<span class="rebuy-money">
<span class="sr-only">Price</span>
<span tabindex="0" v-html="formatMoney(bundleVariantPrice(product, product.selected_variant))"></span>
</span>
</div>
</div>
</div>
</div>
<div class="rebuy-bundle-builder__product-holder-info--right">
<button
class="rebuy-bundle-builder__product-holder-info-delete"
v-on:click="handleRemovingProductFromBundle(product_index)"
>
<span>
<i class="fal fa-times" aria-hidden="true"></i>
</span>
</button>
</div>
<!-- End Product Info Section -->
</div>
</div>
</div>
<!-- Total Savings Amount -->
<div
v-if="hasBundleBuilderTotalSavingsEnabled() && getBundleTotalSavingAmount(products) > 0"
class="rebuy-bundle-builder__total-savings-container">
<span class="rebuy-bundle-builder__total-savings-label">
Total Savings:
</span>
<span
class="rebuy-bundle-builder__total-savings-amount rebuy-money sale"
v-html="formatMoney(getBundleTotalSavingAmount(products))">
</span>
</div>
<div v-if="hasSelectedEnabledBundleSubscription()" class="rebuy-bundle-builder__subscription-options">
<!-- Subscription Radio Box -->
<div
class="rebuy-bundle-builder__action-box-cta rebuy-bundle-builder__action-box-subscription"
v-bind:class="[!hasSellingPlansIntervalList() ? 'rebuy-bundle-builder__action-box-cta--disabled' : '' ]">
<div class="rebuy-bundle-builder__action-box-top">
<label class="rebuy-radio-label">
<input
class="radio-input rebuy-radio rebuy-bundle-builder__action-box-radio"
v-model="selected_purchase_type"
value="subscription"
type="radio"
v-bind:disabled="!hasSellingPlansIntervalList()"
/>
<span
class="rebuy-bundle-builder__action-box-label"
v-html="getBundleConfigLabel('switch_to_subscription_title')"
>
</span>
</label>
<div class="rebuy-bundle-builder__action-box-price">
<span v-if="bundleHasEligibleDiscount(products) || getSubBundleSubtotalSavingPercent(products)">
<span class="rebuy-money compare-at">
<span class="sr-only">Original subtotal price</span>
<span v-html="formatMoney(getBundleSubtotal(products))"></span>
</span>
<span class="rebuy-money sale">
<span class="sr-only">Sale subtotal price</span>
<span tabindex="0" v-html="formatMoney(getSubBundleDiscountedSubtotal(products))"></span>
</span>
</span>
<span v-if="!getSubBundleSubtotalSavingPercent(products) && (!bundleHasEligibleDiscount(products) && getBundleSubtotal(products))" class="rebuy-money sale">
<span class="sr-only">Subtotal price</span>
<span tabindex="0" v-html="formatMoney(getBundleSubtotal(products))"></span>
</span>
</div>
</div>
<div class="rebuy-bundle-builder__action-box-bottom">
<p
v-if="getBundleConfigLabel('switch_to_subscription_description')"
class="rebuy-bundle-builder__action-box-description"
v-html="getBundleConfigLabel('switch_to_subscription_description')">
</p>
<div v-if="hasSellingPlansIntervalList()" class="rebuy-bundle-builder__action-box-details">
<select
id="rebuy-bundle-builder-subscription-interval"
class="rebuy-select rebuy-bundle-builder__action-box-select"
>
<option
v-for="(interval, interval_index) in selling_plan_interval_list"
v-bind:key="'rebuy-bundle-builder-subscription-interval--' + interval_index"
v-bind:value="interval">
{{ interval }}
</option>
</select>
<span v-if="getBundleConfigLabel('save_label') && getSubBundleSubtotalSavingPercent(products)" class="rebuy-bundle-builder__action-box-saving">
<span v-html="getBundleConfigLabel('save_label')"></span>
<span v-html="getSubBundleSubtotalSavingPercent(products)"></span>
</span>
</div>
</div>
</div>
<!-- End Subscription Radio Box -->
<!-- One-time Radio Box -->
<div class="rebuy-bundle-builder__action-box-cta rebuy-bundle-builder__action-box-subscription">
<div class="rebuy-bundle-builder__action-box-top">
<label class="rebuy-radio-label">
<input
class="radio-input rebuy-radio rebuy-bundle-builder__action-box-radio"
v-model="selected_purchase_type"
value="one-time"
type="radio"
/>
<span
v-if="getBundleConfigLabel('one_time_title')"
class="rebuy-bundle-builder__action-box-label"
v-html="getBundleConfigLabel('one_time_title')"
>
</span>
</label>
<div class="rebuy-bundle-builder__action-box-price">
<span v-if="bundleHasEligibleDiscount()">
<span class="rebuy-money compare-at">
<span class="sr-only">Original subtotal price</span>
<span v-html="formatMoney(getBundleSubtotal(products))"></span>
</span>
<span class="rebuy-money sale">
<span class="sr-only">Sale subtotal price</span>
<span tabindex="0" v-html="formatMoney(getBundleDiscountedSubtotal(products))"></span>
</span>
</span>
<span v-if="!bundleHasEligibleDiscount() && getBundleSubtotal(products)" class="rebuy-money sale">
<span class="sr-only">Subtotal price</span>
<span tabindex="0" v-html="formatMoney(getBundleSubtotal(products))"></span>
</span>
</div>
</div>
<div class="rebuy-bundle-builder__action-box-bottom">
<p
v-if="getBundleConfigLabel('one_time_description')"
class="rebuy-bundle-builder__action-box-description"
v-html="getBundleConfigLabel('one_time_description')">
</p>
</div>
</div>
<!-- End One-time Order Radio Box -->
</div>
</div>
<div class="rebuy-bundle-builder__group-container-action">
<div class="rebuy-bundle-builder__cta-container">
<button
class="rebuy-button"
v-on:click="addSelectedProductsToCart()"
v-bind:disabled="shouldDisableAddBundleToCart(products)"
aria-label="Add all selected products to cart"
>
<span v-html="buttonWidgetLabel()"></span>
<span v-if="!hasSelectedEnabledBundleSubscription()">
<span v-if="bundleHasEligibleDiscount()">
<span class="rebuy-money sale">
<span class="sr-only">Sale subtotal price</span>
<span tabindex="0" v-html="formatMoney(getBundleDiscountedSubtotal(products))"></span>
</span>
<span class="rebuy-money compare-at">
<span class="sr-only">Original subtotal price</span>
<span v-html="formatMoney(getBundleSubtotal(products))"></span>
</span>
</span>
<span v-if="!bundleHasEligibleDiscount() && getBundleSubtotal(products)" class="rebuy-money sale">
<span class="sr-only">Subtotal price</span>
<span tabindex="0" v-html="formatMoney(getBundleSubtotal(products))"></span>
</span>
</span>
</button>
</div>
</div>
</div
</div>
<!-- End Product Group Section -->
</div>
<!-- End Body Section -->
</div>
<div
v-if="has_container_expanded"
class="rebuy-bundle-builder__full-screen-background rebuy-full-screen__background"
v-on:click="handleExpandingBundleContainer()">
</div>
</div>
</script>