"Notify Me Button" Snippet

Read this article to learn more about the snippet usage.


Usage:

{% render 'back-in-stock' %}

Code:

{% comment %}
  Back In Stock snippet (for themes where the app does not support app blocks).

  Usage (minimal):
  {% render 'back-in-stock', product: product %}

  Usage (overriding any text/option):
  {% render 'back-in-stock',
    product: product,
    button_text: 'Notify me when available',
    already_subscribed_text: 'You are subscribed on this product',
    disable_already_subscribed: false,
    modal_title: 'Notify me when available',
    modal_subtitle: '<p>Get notified when product back in stock.</p>',
    email_placeholder: 'Your Email',
    prefill_customer_email: true,
    show_variant_selector: true,
    modal_description: 'Your info stays private and will never be shared with anyone.',
    subscribe_button_text: 'Notify me when available',
    thank_you: '<p>Thank you!</p>',
    button_text_signed: "You're signed up! We'll notify you.",
    we_will_notify: '<p>We will notify you when the product is available.</p>',
    error_email_text: 'Your e-mail address is wrong.',
    error_text: 'Something went wrong, please try again later.'
  %}

  Required prop: product
  All text props are optional. When a text prop is omitted, the default is read
  from the translation files (locales/en.default.json) via the `t` filter, e.g.
  'back_in_stock.settings.button_text.default'.

  Note: the custom element, CSS classes and JS are namespaced as
  `r-back-in-stock-snippet` so they never collide with the official app block
  (`r-back-in-stock`) if both are present on the same page.
{% endcomment %}

{% comment %}
  Back In Stock snippet (for themes where the app does not support app blocks).

  Usage (minimal):
  {% render 'back-in-stock', product: product %}

  Usage (overriding any text/option):
  {% render 'back-in-stock',
    product: product,
    button_text: 'Notify me when available',
    already_subscribed_text: 'You are subscribed on this product',
    disable_already_subscribed: false,
    modal_title: 'Notify me when available',
    modal_subtitle: '<p>Get notified when product back in stock.</p>',
    email_placeholder: 'Your Email',
    prefill_customer_email: true,
    show_variant_selector: true,
    modal_description: 'Your info stays private and will never be shared with anyone.',
    subscribe_button_text: 'Notify me when available',
    thank_you: '<p>Thank you!</p>',
    button_text_signed: "You're signed up! We'll notify you.",
    we_will_notify: '<p>We will notify you when the product is available.</p>',
    error_email_text: 'Your e-mail address is wrong.',
    error_text: 'Something went wrong, please try again later.'
  %}

  Required prop: product
  All text props are optional. When a text prop is omitted, the default is read
  from the translation files (locales/en.default.json) via the `t` filter, e.g.
  'back_in_stock.settings.button_text.default'.

  Note: the custom element, CSS classes and JS are namespaced as
  `r-back-in-stock-snippet` so they never collide with the official app block
  (`r-back-in-stock`) if both are present on the same page.
{% endcomment %}

{%- liquid
  assign p = product | default: product
  assign _content_style = content_style | default: 'text'

  # Prop defaults (fall back to translations in locales/en.default.json)
  assign _button_text = 'back_in_stock.settings.button_text.default' | t
  if button_text != blank
    assign _button_text = button_text
  endif

  assign _already_subscribed_text = 'back_in_stock.settings.already_subscribed_text.default' | t
  if already_subscribed_text != blank
    assign _already_subscribed_text = already_subscribed_text
  endif

  assign _modal_title = 'back_in_stock.settings.modal_title.default' | t
  if modal_title != blank
    assign _modal_title = modal_title
  endif

  assign _email_placeholder = 'back_in_stock.settings.email_placeholder.default' | t
  if email_placeholder != blank
    assign _email_placeholder = email_placeholder
  endif

  assign _subscribe_button_text = 'back_in_stock.settings.subscribe_button_text.default' | t
  if subscribe_button_text != blank
    assign _subscribe_button_text = subscribe_button_text
  endif

  assign _button_text_signed = 'back_in_stock.settings.button_text_signed.default' | t
  if button_text_signed != blank
    assign _button_text_signed = button_text_signed
  endif

  assign _modal_subtitle = 'back_in_stock.settings.modal_subtitle.default' | t
  if modal_subtitle != blank
    assign _modal_subtitle = modal_subtitle
  endif

  assign _modal_description = 'back_in_stock.settings.modal_description.default' | t
  if modal_description != blank
    assign _modal_description = modal_description
  endif

  assign _thank_you = 'back_in_stock.settings.thank_you.default' | t
  if thank_you != blank
    assign _thank_you = thank_you
  endif

  assign _we_will_notify = 'back_in_stock.settings.we_will_notify.default' | t
  if we_will_notify != blank
    assign _we_will_notify = we_will_notify
  endif

  assign _error_email_text = 'back_in_stock.settings.error_email_text.default' | t
  if error_email_text != blank
    assign _error_email_text = error_email_text
  endif

  assign _already_subscribed_error_text = 'back_in_stock.settings.already_subscribed_error_text.default' | t
  if already_subscribed_error_text != blank
    assign _already_subscribed_error_text = already_subscribed_error_text
  endif

  assign _error_text = 'back_in_stock.settings.error_text.default' | t
  if error_text != blank
    assign _error_text = error_text
  endif

  # checkbox props: nil => default true/false
  if prefill_customer_email == null
    assign _prefill_customer_email = true
  else
    assign _prefill_customer_email = prefill_customer_email
  endif

  if show_variant_selector == null
    assign _show_variant_selector = true
  else
    assign _show_variant_selector = show_variant_selector
  endif

  if disable_already_subscribed == null
    assign _disable_already_subscribed = false
  else
    assign _disable_already_subscribed = disable_already_subscribed
  endif
-%}

{%- assign is_app_blocked = shop.metafields.back_in_stock.app_blocked -%}
{%- assign public_admin_settings = shop.metafields.refactor_back_in_stock.settings.value -%}
{%- assign subscribed_variants = customer.metafields.refactor_back_in_stock['subscribed-variants'].value -%}
{%- assign current_variant_id = p.selected_or_first_available_variant.id | append: '' -%}
{%- assign customer_is_subscribed = false -%}
{%- assign subscribed_ids = '' -%}

{%- if subscribed_variants -%}
  {%- for variant in subscribed_variants -%}
    {%- assign subscribed_ids = subscribed_ids | append: variant[0] | append: ',' -%}
  {%- endfor -%}
{%- endif -%}

{%- if subscribed_variants[current_variant_id] -%}
  {%- assign customer_is_subscribed = true -%}
{%- endif -%}

{%- liquid
  assign locale_code = request.locale.iso_code | split: '-' | first | strip
  assign dir_value = 'ltr'
  if locale_code == 'ar' or locale_code == 'he' or locale_code == 'fa' or locale_code == 'ur'
    assign dir_value = 'rtl'
  endif
-%}

{%- unless is_app_blocked and request.design_mode == false -%}
  <style>
    .r-back-in-stock-snippet { display: none; }
    .r-back-in-stock-snippet.is-active { display: block; }
    .r-back-in-stock-snippet__notify-btn {
      display: flex;
      justify-content: center;
      align-items: center;
      gap: 6px;
      cursor: pointer;
    }
    .r-back-in-stock-snippet__notify-btn--disabled { pointer-events: none; }
    .r-back-in-stock-snippet__form { display: flex; flex-direction: column; }
    .r-back-in-stock-snippet__form.is-loading { opacity: 0.5; pointer-events: none; }
    .r-back-in-stock-snippet__text { font-size: 16px; font-weight: 600; margin: 0; }
    .r-back-in-stock-snippet__label {
      font-size: 16px;
      display: flex;
      flex-direction: column;
      gap: 12px;
    }
    .r-back-in-stock-snippet__modal {
      display: flex;
      position: fixed;
      top: 0; bottom: 0; left: 0; right: 0;
      align-items: center;
      justify-content: center;
      z-index: 10000;
    }
    .r-back-in-stock-snippet__modal p { margin: 0; }
    .r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-overlay {
      display: flex;
      width: 100%;
      height: 100%;
      background: rgba(27, 27, 27, 0.6);
      position: absolute;
      z-index: 1;
      cursor: pointer;
    }
    .r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content {
      margin: 0 15px;
      border-radius: 4px;
      padding: 24px;
      position: relative;
      z-index: 2;
      background: #fff;
      width: 480px;
      display: flex;
      flex-direction: column;
      gap: 24px;
    }
    .r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content header {
      padding-bottom: 16px;
      border-bottom: 1px solid #d7d7d7;
    }
    .r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content h3 {
      margin: 0 0 2px 0;
      font-weight: 700;
      font-size: 20px;
      line-height: 1.4;
    }
    .r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__modal-text {
      margin: 0;
      font-size: 14px;
    }
    .r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__name {
      font-weight: 700;
      font-size: 16px;
      line-height: 1.4;
    }
    .r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__variant-select {
      font-size: 14px;
      padding: 16px 14px;
      border: 1px solid #888;
      border-radius: 4px;
      outline: none;
      box-shadow: none;
      width: 100%;
      appearance: none;
      -webkit-appearance: none;
      font-family: inherit;
      cursor: pointer;
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='currentColor' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
      background-repeat: no-repeat;
      background-position: right 1rem center;
    }
    .r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__input {
      font-size: 14px;
      padding: 16px 14px;
      border: 1px solid #888;
      border-radius: 4px;
      outline: none;
      box-shadow: none;
    }
    .r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__input-info {
      font-size: 13px;
      font-weight: normal;
      line-height: 1;
    }
    .r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__button {
      margin-top: 24px;
      width: 100%;
    }
    .r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__thanks {
      max-width: 280px;
      margin: 0 auto 20px;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 20px;
    }
    .r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__thanks img,
    .r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__thanks .r-back-in-stock-snippet__checkmark { max-width: 100px; height: auto; }
    .r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__thanks-texts {
      display: flex;
      flex-direction: column;
      gap: 4px;
      text-align: center;
    }
    .r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__thanks-texts .r-back-in-stock-snippet__thanks-header {
      font-size: 20px;
      font-weight: 700;
      margin: 0;
      line-height: 1.4;
    }
    .r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__thanks-texts .r-back-in-stock-snippet__thanks-header p {
      text-align: center !important;
    }
    .r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__thanks-texts .r-back-in-stock-snippet__thanks-info {
      font-size: 16px;
      font-weight: 400;
      margin: 0;
      line-height: 1.4;
    }
    .r-back-in-stock-snippet__label:has(.r-back-in-stock-snippet__message.is-error, .r-back-in-stock-snippet__message.is-success) .r-back-in-stock-snippet__input {
      border-color: #b71600;
      color: #b71600;
    }
    .r-back-in-stock-snippet__label:has(.r-back-in-stock-snippet__message.is-error, .r-back-in-stock-snippet__message.is-success) .r-back-in-stock-snippet__input::placeholder {
      color: #b71600;
    }
    .r-back-in-stock-snippet__message {
      font-size: 14px;
      font-weight: 400;
      display: none;
    }
    .r-back-in-stock-snippet__message.is-error {
      display: block;
      color: #b71600;
    }
    [dir="rtl"].r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__variant-select {
      background-position: left 1rem center;
    }
    .r-back-in-stock-snippet__blocked-message {
      display: flex;
      align-items: center;
      justify-content: center;
      margin-top: 8px;
    }
    .r-back-in-stock-snippet__blocked-message span {
      display: flex;
      align-items: center;
      justify-content: center;
      text-align: center;
      padding: 8px 12px;
      border: 1px solid #b71600;
      color: #b71600;
    }

    /* helpers (from styles.css) */
    .r-back-in-stock-snippet .hidden,
    .r-back-in-stock-snippet__modal.hidden { display: none !important; }
    .r-back-in-stock-snippet__modal .close-btn {
      position: absolute;
      top: 8px;
      right: 8px;
      width: 24px;
      height: 24px;
      cursor: pointer;
      display: inline-block;
    }
    [dir="rtl"] .r-back-in-stock-snippet__modal .close-btn { right: auto; left: 8px; }
    .r-back-in-stock-snippet__modal .close-btn::before,
    .r-back-in-stock-snippet__modal .close-btn::after {
      content: "";
      position: absolute;
      top: 50%;
      left: 50%;
      width: 13px;
      height: 1.5px;
      background-color: rgb(27, 27, 27);
      transform-origin: center;
      transition: background-color 0.2s ease;
    }
    .r-back-in-stock-snippet__modal .close-btn::before { transform: translate(-50%, -50%) rotate(45deg); }
    .r-back-in-stock-snippet__modal .close-btn::after { transform: translate(-50%, -50%) rotate(-45deg); }
    .r-back-in-stock-snippet__modal .close-btn:hover::before,
    .r-back-in-stock-snippet__modal .close-btn:hover::after { background-color: #333; }
  </style>

  <r-back-in-stock-snippet
    dir="{{ dir_value }}"
    class="r-back-in-stock-snippet {% unless p.selected_or_first_available_variant.available %}is-active{% endunless %}"
    data-product-id="{{ p.id }}"
    data-subscribed-variants="{{ subscribed_ids }}"
    data-disable-already-subscribed="{{ _disable_already_subscribed }}"
    data-show-if-continue-selling="{{ public_admin_settings.showIfContinueSelling }}"
    data-show-if-track-quantity-disabled="{{ public_admin_settings.showIfTrackQuantityDisabled }}"
  >
    <div
      class="r-back-in-stock-snippet__notify-btn r-back-in-stock-snippet__notify-btn--{{ _content_style }} {% if _disable_already_subscribed %}r-back-in-stock-snippet__notify-btn--disabled{% endif %}"
    >
      {% if _content_style == 'icon' %}
        <span class="r-back-in-stock-snippet__notify-icon" aria-hidden="true">
      {% endif %}
      <svg
        class="r-back-in-stock-snippet__icon"
        xmlns="http://www.w3.org/2000/svg"
        width="24"
        height="24"
        viewBox="0 0 24 24"
        fill="none"
        aria-hidden="true"
      >
        <path d="M21.75 6.75V17.25C21.75 17.6478 21.592 18.0294 21.3107 18.3107C21.0294 18.592 20.6478 18.75 20.25 18.75H3.75C3.35218 18.75 2.97064 18.592 2.68934 18.3107C2.40804 18.0294 2.25 17.6478 2.25 17.25V6.75M21.75 6.75C21.75 6.35218 21.592 5.97064 21.3107 5.68934C21.0294 5.40804 20.6478 5.25 20.25 5.25H3.75C3.35218 5.25 2.97064 5.40804 2.68934 5.68934C2.40804 5.97064 2.25 6.35218 2.25 6.75M21.75 6.75L12.8535 12.909C12.6027 13.0825 12.305 13.1755 12 13.1755C11.695 13.1755 11.3973 13.0825 11.1465 12.909L2.25 6.75" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>
      {% if _content_style == 'icon' %}
        </span>
      {% endif %}

      <span
        class="r-back-in-stock-snippet__unsigned"
        data-default-text="{{ _button_text }}"
        data-subscribed-text="{{ _already_subscribed_text }}"
      >
        {% if _content_style == 'text' %}
          <span class="r-back-in-stock-snippet__unsigned-label">
            {% if customer_is_subscribed %}
              {{ _already_subscribed_text }}
            {% else %}
              {{ _button_text }}
            {% endif %}
          </span>
        {% endif %}
      </span>

      <span class="r-back-in-stock-snippet__signed hidden">
        {% if _content_style == 'icon' %}
          <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true">
            <path d="M20 6L9 17L4 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
          </svg>
        {% else %}
          {{ _button_text_signed }}
        {% endif %}
      </span>
    </div>

    {% if is_app_blocked %}
      <div class="r-back-in-stock-snippet__blocked-message">
        <span
          >R+ Back In Stock is inactive.
          <br>
          Please activate a subscription to continue using the app.</span
        >
      </div>
    {% endif %}

    <div dir="{{ dir_value }}" class="r-back-in-stock-snippet__modal hidden">
      <div class="r-back-in-stock-snippet__modal-overlay"></div>
      <div class="r-back-in-stock-snippet__modal-content">
        <span class="close-btn"></span>
        <header>
          <h3>{{- _modal_title -}}</h3>
          {% if _modal_subtitle != empty and _modal_subtitle != null %}
            <p class="r-back-in-stock-snippet__modal-text">{{- _modal_subtitle -}}</p>
          {% endif %}
        </header>

        <form class="r-back-in-stock-snippet__form js-r-back-in-stock-snippet__form">
          {% unless _show_variant_selector and p.variants.size > 1 %}
            {% assign fallback_variant = null %}

            {% for variant in p.variants %}
              {% unless variant.available %}
                {% assign fallback_variant = variant %}
                {% break %}
              {% endunless %}
            {% endfor %}

            <input name="variant_id" value="{{ fallback_variant.id }}" type="hidden">
          {% endunless %}
          {% if customer %}
            <input name="customer_id" value="{{ customer.id }}" type="hidden">
          {% endif %}
          <label class="r-back-in-stock-snippet__label">
            <span class="r-back-in-stock-snippet__name">
              {{- p.title -}}
              -
              {{- p.selected_or_first_available_variant.title -}}
            </span>

            {% if _show_variant_selector %}
              {% assign variants = p.variants %}
              {% if variants.size > 1 %}
                <div class="r-back-in-stock-snippet__variant-selector">
                  <select name="variant_id" class="r-back-in-stock-snippet__variant-select">
                    {% assign first_found = false %}

                    {% for variant in variants %}
                      {% assign show_variant = false %}

                      {% if variant.available == false %}
                        {% assign show_variant = true %}
                      {% endif %}

                      {% if public_admin_settings.showIfContinueSelling
                        and variant.inventory_policy == 'continue'
                        and variant.inventory_quantity < 1
                      %}
                        {% assign show_variant = true %}
                      {% endif %}

                      {% if public_admin_settings.showIfTrackQuantityDisabled and variant.inventory_management == nil %}
                        {% assign show_variant = true %}
                      {% endif %}

                      {% if show_variant %}
                        <option
                          value="{{ variant.id }}"
                          {% if variant.id == p.selected_or_first_available_variant.id %}
                            selected
                            {% assign first_found = true %}
                          {% elsif first_found == false %}
                            selected
                            {% assign first_found = true %}
                          {% endif %}
                        >
                          {{ variant.title }}
                        </option>
                      {% endif %}
                    {% endfor %}
                  </select>
                </div>
              {% endif %}
            {% endif %}

            <input
              name="email"
              class="r-back-in-stock-snippet__input"
              autocomplete="email"
              type="text"
              placeholder="{{ _email_placeholder }}"
              {% if _prefill_customer_email and customer %}
                value="{{ customer.email }}"
              {% endif %}
            >
            <span class="r-back-in-stock-snippet__message js-r-back-in-stock-snippet__message"></span>
            {% if _modal_description != empty and _modal_description != null %}
              <span class="r-back-in-stock-snippet__input-info">{{- _modal_description -}}</span>
            {% endif %}
          </label>

          <button type="submit" class="button r-back-in-stock-snippet__button">
            {{- _subscribe_button_text -}}
          </button>
        </form>

        <div class="r-back-in-stock-snippet__thanks hidden">
          <svg
            class="r-back-in-stock-snippet__checkmark"
            xmlns="http://www.w3.org/2000/svg"
            width="100"
            height="100"
            viewBox="0 0 100 100"
            fill="none"
            role="img"
            aria-label="success"
          >
            <circle cx="50" cy="50" r="46" fill="#E7F6EC" stroke="#2E7D32" stroke-width="3"/>
            <path d="M30 51.5L44 65.5L71 36.5" stroke="#2E7D32" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
          </svg>

          <div class="r-back-in-stock-snippet__thanks-texts">
            <div class="r-back-in-stock-snippet__thanks-header">
              {% if _thank_you != empty and _thank_you != null %}
                {{- _thank_you -}}
              {% endif %}
            </div>
            <div class="r-back-in-stock-snippet__thanks-info">
              {% if _we_will_notify != empty and _we_will_notify != null %}
                {{- _we_will_notify -}}
              {% endif %}
            </div>
          </div>
        </div>
      </div>
    </div>

    {%- assign _error_email_text_final = _error_email_text -%}
    {%- assign _already_subscribed_error_text_final = _already_subscribed_error_text -%}
    {%- assign _error_text_final = _error_text -%}

    {%- if _error_email_text_final contains 'missing translation' -%}
      {%- assign _error_email_text_final = "Your e-mail address is wrong." -%}
    {%- endif -%}

    {%- if _already_subscribed_error_text_final contains 'missing translation' -%}
      {%- assign _already_subscribed_error_text_final = "You are already subscribed to this product." -%}
    {%- endif -%}

    {%- if _error_text_final contains 'missing translation' -%}
      {%- assign _error_text_final = "Something went wrong, reload the page and try again." -%}
    {%- endif -%}

    <template class="t-r-back-in-stock-snippet__error-email">{{- _error_email_text_final -}}</template>
    <template class="t-r-back-in-stock-snippet__error-already-subscribed">{{- _already_subscribed_error_text_final -}}</template>
    <template class="t-r-back-in-stock-snippet__error">{{- _error_text_final -}}</template>

    <template class="t-r-back-in-stock-snippet__variants">
      [
      {% for variant in p.variants %}
        {
          "id": {{ variant.id | json }},
          "name": {{ variant.name | json }},
          "title": {{ variant.title | json }},
          "available": {{ variant.available | json }},
          "inventory_quantity": {{ variant.inventory_quantity | json }},
          "inventory_policy": {{ variant.inventory_policy | json }},
          "inventory_management": {{ variant.inventory_management | json }}
        }{% unless forloop.last %},{% endunless %}
      {% endfor %}
      ]
    </template>
  </r-back-in-stock-snippet>

  <script>
    (() => {
      function initRBackInStock() {
        if (customElements.get('r-back-in-stock-snippet')) return;

        class RBackInStock extends HTMLElement {
          constructor() {
            super();
            this.boundedThemeAddToCartHandler = this.themeAddToCartHandler.bind(this);
            this.boundedFormHandler = this.formHandler.bind(this);
            this.boundedShowPopup = this.showPopup.bind(this);
            this.boundedHandlePopupClick = this.handlePopupClick.bind(this);
            this.boundedUpdateVariantId = this.updateVariantId.bind(this);
            this.boundedResetMessage = this.resetMessage.bind(this);
            this.boundedSyncVariantSelect = this.syncVariantSelect.bind(this);

            this.signedVariants = {};
            this.sessionSignedVariants = {};
          }

          async connectedCallback() {
            this.initElements();
            await this.updateVariantId();
            this.addEventListeners();
          }

          disconnectedCallback() {
            this.removeEventListeners();
          }

          initElements() {
            this.labelNameEl = this.querySelector('.r-back-in-stock-snippet__name');
            this.form = this.querySelector('.js-r-back-in-stock-snippet__form');
            this.unsignedText = this.querySelector('.r-back-in-stock-snippet__unsigned');
            this.signedText = this.querySelector('.r-back-in-stock-snippet__signed');
            this.thanksEl = this.querySelector('.r-back-in-stock-snippet__thanks');
            this.inputEl = this.querySelector('.r-back-in-stock-snippet__input');
            this.errorEmailText = this.querySelector('.t-r-back-in-stock-snippet__error-email');
            this.errorAlreadySubscribedText = this.querySelector('.t-r-back-in-stock-snippet__error-already-subscribed');
            this.errorText = this.querySelector('.t-r-back-in-stock-snippet__error');
            this.message = this.querySelector('.js-r-back-in-stock-snippet__message');
            this.addToCartForm = document.querySelector("form[action='/cart/add']");
            this.productId = this.dataset.productId ?? null;
            this.variantsJSON = JSON.parse(this.querySelector('.t-r-back-in-stock-snippet__variants').innerHTML);

            this.shopifyVariantIdEl = document.querySelector("input[name='id']");

            this.btnTrigger = this.querySelector('.r-back-in-stock-snippet__notify-btn');
            this.popupEl = this.querySelector('.r-back-in-stock-snippet__modal');
            this.variantSelect = this.popupEl?.querySelector('.r-back-in-stock-snippet__variant-select');

            if (this.popupEl) {
              document.body.appendChild(this.popupEl);
            }

            const subscribedVariantsAttr = this.dataset.subscribedVariants;

            if (subscribedVariantsAttr) {
              subscribedVariantsAttr
                .split(',')
                .filter(Boolean)
                .forEach((id) => {
                  this.signedVariants[Number(id)] = true;
                });
            }

            this.observeVariantValue();
          }

          observeVariantValue() {
            const el = this.shopifyVariantIdEl;

            if (!el || el.__rSnippetValuePatched) {
              return;
            }

            const prototypeDescriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');

            Object.defineProperty(el, 'value', {
              get: function () {
                return prototypeDescriptor.get.call(this);
              },
              set: (newValue) => {
                prototypeDescriptor.set.call(el, newValue);
                this.boundedUpdateVariantId();
              },
              configurable: true,
            });

            el.__rSnippetValuePatched = true;
          }

          addEventListeners() {
            this.form?.addEventListener('submit', this.boundedFormHandler);
            this.addToCartForm?.addEventListener('change', this.boundedThemeAddToCartHandler);

            this.btnTrigger?.addEventListener('click', this.boundedShowPopup);
            this.popupEl?.addEventListener('click', this.boundedHandlePopupClick);
            this.inputEl?.addEventListener('input', this.boundedResetMessage);

            document.addEventListener('variant:change', this.boundedUpdateVariantId);

            this.shopifyVariantIdEl?.addEventListener('change', this.boundedUpdateVariantId);
            this.variantSelect?.addEventListener('change', this.boundedSyncVariantSelect);
          }

          removeEventListeners() {
            this.form?.removeEventListener('submit', this.boundedFormHandler);
            this.addToCartForm?.removeEventListener('change', this.boundedThemeAddToCartHandler);
            this.btnTrigger?.removeEventListener('click', this.boundedShowPopup);
            this.popupEl?.removeEventListener('click', this.boundedHandlePopupClick);
            this.inputEl?.removeEventListener('input', this.boundedResetMessage);

            document.removeEventListener('variant:change', this.boundedUpdateVariantId);

            this.shopifyVariantIdEl?.removeEventListener('change', this.boundedUpdateVariantId);
            this.variantSelect?.removeEventListener('change', this.boundedSyncVariantSelect);
          }

          async handlePopupClick(e) {
            const target = e?.target;
            const classList = target?.classList;

            if (!classList) {
              return;
            }

            const isCloseModal =
              classList.contains('r-back-in-stock-snippet__modal-overlay') || classList.contains('close-btn');

            if (isCloseModal) {
              this.hidePopup();
            }
          }

          showPopup() {
            if (!this.popupEl) return;

            this.variantId = this.getVariantId();

            if (this.variantSelect) {
              this.variantSelect.value = this.variantId;
            }

            const variantData = this.variantsJSON.find((el) => el.id === parseInt(this.variantId, 10));
            if (this.labelNameEl && variantData) {
              this.labelNameEl.innerText = variantData.name;
            }

            this.hideThanksBlock();
            this.resetMessage();
            this.popupEl.classList.remove('hidden');
          }

          hidePopup() {
            if (!this.popupEl) return;
            this.popupEl.classList.add('hidden');
            this.hideThanksBlock();
            this.resetMessage();
          }

          updateVariantId() {
            this.variantId = this.getVariantId();
            this.updateButton();
          }

          updateButton() {
            this.updateButtonText();
            this.updateButtonSettings();
          }

          updateButtonText() {
            const label = this.unsignedText.querySelector('.r-back-in-stock-snippet__unsigned-label');

            if (this.sessionSignedVariants[this.variantId]) {
              this.signedText.classList.remove('hidden');
              this.unsignedText.classList.add('hidden');
            } else {
              this.signedText.classList.add('hidden');
              this.unsignedText.classList.remove('hidden');

              if (label) {
                label.textContent = this.signedVariants[this.variantId]
                  ? this.unsignedText.dataset.subscribedText
                  : this.unsignedText.dataset.defaultText;
              }
            }
          }

          updateButtonSettings() {
            this.updateVisibility();

            if (this.dataset.disableAlreadySubscribed === 'true') {
              if (this.signedVariants[this.variantId]) {
                this.btnTrigger.classList.add('r-back-in-stock-snippet__notify-btn--disabled');
              } else {
                this.btnTrigger.classList.remove('r-back-in-stock-snippet__notify-btn--disabled');
              }
            }
          }

          getVariantId() {
            if (this.variantSelect && !this.popupEl.classList.contains('hidden')) {
              return Number(this.variantSelect.value);
            }

            const shopifyVariantId = this.shopifyVariantIdEl?.value;
            const customVariantIdEl = this.querySelector(".r-back-in-stock-snippet__form input[name='variant_id']");
            const params = new URLSearchParams(window.location.search);
            const variantIdFromParams = params.get('variant');

            return Number(shopifyVariantId || variantIdFromParams || customVariantIdEl?.value);
          }

          themeAddToCartHandler() {
            this.updateVariantId();
            this.resetMessage();
          }

          resetMessage() {
            this.message.innerHTML = '';
            this.message.classList.remove('is-error', 'is-success');
          }

          formHandler(event) {
            event.preventDefault();

            const payload = Object.fromEntries(new FormData(this.form));

            const emailValidRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;

            if (typeof payload.email === 'string' && payload.email.match(emailValidRegex)) {
              this.sendSubscriptionRequest(payload);
            } else {
              this.showMessage(this.errorEmailText.innerHTML);
              return;
            }
          }

          async sendSubscriptionRequest(payload) {
            this.form.classList.add('is-loading');

            const variantId = this.getVariantId() || payload.variant_id;
            const res = await window.refactor_apps.back_in_stock.subscribeOnTheProduct({
              productId: this.productId,
              variantId: variantId,
              email: payload.email,
              customerId: payload.customer_id,
            });

            if (res.success) {
              this.showThanksBlock();
              this.signedVariants[this.variantId] = true;
              this.sessionSignedVariants[this.variantId] = true;
              this.updateButtonText();
            } else if (res.message?.already_subscribed) {
              const errors = this.errorAlreadySubscribedText.innerHTML;
              this.showMessage(errors);
            } else {
              const errors = this.errorText.innerHTML;
              this.showMessage(errors);
            }

            this.form.classList.remove('is-loading');
          }

          showThanksBlock() {
            this.thanksEl.classList.remove('hidden');
            this.form.classList.add('hidden');
          }

          hideThanksBlock() {
            this.thanksEl.classList.add('hidden');
            this.form.classList.remove('hidden');
          }

          showMessage(message) {
            this.message.innerHTML = message;
            this.message.classList.add('is-error');
          }

          syncVariantSelect() {
            const selectedId = Number(this.variantSelect.value);
            this.variantId = selectedId;
            const variantData = this.variantsJSON.find((el) => el.id === selectedId);

            if (this.labelNameEl && variantData) {
              this.labelNameEl.innerText = variantData.name;
            }

            this.hideThanksBlock();
            this.resetMessage();
          }

          shouldShowSubscribeButton(variant) {
            if (!variant) return false;

            const showIfContinueSelling = this.dataset.showIfContinueSelling === 'true';
            const showIfTrackQuantityDisabled = this.dataset.showIfTrackQuantityDisabled === 'true';

            const inventoryQuantity = Number(variant.inventory_quantity ?? 0);

            if (!variant.available) {
              return true;
            }

            if (showIfContinueSelling && inventoryQuantity < 1 && variant.inventory_policy === 'continue') {
              return true;
            }

            if (showIfTrackQuantityDisabled && !variant.inventory_management) {
              return true;
            }

            return false;
          }

          updateVisibility() {
            const variantData = this.variantsJSON.find((variant) => variant.id === Number(this.variantId));

            if (this.shouldShowSubscribeButton(variantData)) {
              this.classList.add('is-active');
            } else {
              this.classList.remove('is-active');
            }
          }
        }

        customElements.define('r-back-in-stock-snippet', RBackInStock);
      }

      if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initRBackInStock);
      } else {
        initRBackInStock();
      }
    })();
  </script>
{%- endunless -%}

Do you need help?

If you have any questions or run into issues, please contact us — we’re happy to help

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.

Still need help? Contact Us Contact Us