<div class="customize js-customize customize--core-clip-on customize--core-clip-on">
    <div class="customize__options">
        <div class="customize__option customize__option--square js-customize-option">
            <div class="customize__option-label">
                Frame Color: <span class="customize__option-value js-customize-option-value">Light Brown</span>
                <button class="customize__tooltip-trigger js-tooltip-open-link">
          <svg class="icon icon--question-mark-circle">
  <use xlink:href="/assets/icons/icons.svg#question-mark-circle"></use>
</svg>

        </button>
                <div class="tooltip">Morbi semper sed elit eu tempus.</div>
            </div>
            <ul class="customize__items">
                <li class="customize__item">
                    <div class="form-item form-item--radio">
                        <input type="radio" id="fc-light-brown" value="10" name="frame-color" class="radio radio--alternate customize__item-input js-customize-item-input" data-value="MJO2204 (Light Brown)" data-frame-style="mjo2204-79ar" data-frame-color="79ar" checked="true">
                        <label for="fc-light-brown" class=" customize__item-label">
    <span class="radio__alternate-fill" style="background: #b38353;"></span>
    
    <span class="radio__label-text u-visually-hidden">Light Brown</span>
  </label>
                    </div>

                </li>
                <li class="customize__item">
                    <div class="form-item form-item--radio">
                        <input type="radio" id="fc-black" value="20" name="frame-color" class="radio radio--alternate customize__item-input js-customize-item-input" data-value="MJO2204 (Black)" data-frame-style="mjo2204-80ar" data-frame-color="80ar">
                        <label for="fc-black" class=" customize__item-label">
    <span class="radio__alternate-fill" style="background: #303032;"></span>
    
    <span class="radio__label-text u-visually-hidden">false</span>
  </label>
                    </div>

                </li>
                <li class="customize__item">
                    <div class="form-item form-item--radio">
                        <input type="radio" id="fc-dark-brown" value="30" name="frame-color" class="radio radio--alternate customize__item-input js-customize-item-input" data-value="MJO2204 (Dark Brown)" data-frame-style="mjo2204-81ar" data-frame-color="81ar">
                        <label for="fc-dark-brown" class=" customize__item-label">
    <span class="radio__alternate-fill" style="background: #4A2C21;"></span>
    
    <span class="radio__label-text u-visually-hidden">Dark Brown</span>
  </label>
                    </div>

                </li>
            </ul>
        </div>
        <div class="customize__option customize__option--switch js-customize-option">
            <div class="customize__option-label">
                Clip-On
            </div>
            <ul class="customize__items">
                <li class="customize__item">
                    <div class="form-item form-item--checkbox">
                        <input type="checkbox" id="clip-on" name="clip-on" class="checkbox checkbox--switch">
                        <label for="clip-on">
    <img class="checkbox__image" src="/assets/images/dummy-products/mjo2204-79ar_front.jpg">Clip-On
    
  </label>
                    </div>

                </li>
            </ul>
        </div>
    </div>
</div>
<div class="customize js-customize customize--{{modifier}} customize--{{_self.name}}">
  <div class="customize__options">
    {{#each options}}
    <div class="customize__option{{#each modifiers}} customize__option--{{this}}{{/each}} js-customize-option">
      <div class="customize__option-label">
        {{label}}{{#if valueInLabel}}: <span class="customize__option-value js-customize-option-value">{{#each items}}{{#if context.checked}}{{context.label}}{{/if}}{{/each}}</span>{{/if}}
        {{#if tooltip}}
        <button class="customize__tooltip-trigger js-tooltip-open-link">
          {{render '@icons--question-mark-circle'}}
        </button>
        {{> @tooltip tooltip}}
        {{/if}}
      </div>
      <ul class="customize__items{{#if disabled}} js-customize-disabled{{/if}}">
        {{#each items}}
        <li class="customize__item{{#if disabled}} js-customize-disabled{{/if}}">
          {{#if input}}
          {{render (dynamicVariant component variant) context}}
          {{/if}}
          {{#if options}}
          <div class="select-wrapper">
            <select id="{{id}}" name="{{name}}" class="select js-select">
              {{#each options}}
              <option value="{{@key}}">{{this}}</option>
              {{/each}}
            </select>
          </div>
          {{/if}}
        </li>
        {{/each}}
      </ul>
    </div>
    {{/each}}
    {{#if customizeOptions}}
    <div class="customize__options-help">
      {{#each customizeOptions}}
      {{render (dynamicComponent component) context}}
      {{/each}}
    </div>
    {{/if}}
  </div>
</div>
{
  "pageCss": [
    "products"
  ],
  "modifier": "core-clip-on",
  "options": [
    {
      "name": "frame-color",
      "label": "Frame Color",
      "modifiers": [
        "square"
      ],
      "valueInLabel": true,
      "tooltip": {
        "value": "Morbi semper sed elit eu tempus."
      },
      "items": [
        {
          "input": true,
          "component": "radio",
          "variant": "alternate-fill",
          "context": {
            "id": "fc-light-brown",
            "name": "frame-color",
            "value": "10",
            "modifier": "alternate",
            "background": "#b38353",
            "label": "Light Brown",
            "checked": true,
            "dataAttributes": [
              {
                "name": "value",
                "value": "MJO2204 (Light Brown)"
              },
              {
                "name": "frame-style",
                "value": "mjo2204-79ar"
              },
              {
                "name": "frame-color",
                "value": "79ar"
              }
            ],
            "inputClasses": [
              "customize__item-input",
              "js-customize-item-input"
            ],
            "labelClasses": [
              "customize__item-label"
            ],
            "labelTextClasses": [
              "u-visually-hidden"
            ]
          }
        },
        {
          "input": true,
          "component": "radio",
          "variant": "alternate-fill",
          "context": {
            "id": "fc-black",
            "name": "frame-color",
            "value": "20",
            "modifier": "alternate",
            "background": "#303032",
            "label": "false",
            "checked": null,
            "dataAttributes": [
              {
                "name": "value",
                "value": "MJO2204 (Black)"
              },
              {
                "name": "frame-style",
                "value": "mjo2204-80ar"
              },
              {
                "name": "frame-color",
                "value": "80ar"
              }
            ],
            "inputClasses": [
              "customize__item-input",
              "js-customize-item-input"
            ],
            "labelClasses": [
              "customize__item-label"
            ],
            "labelTextClasses": [
              "u-visually-hidden"
            ]
          }
        },
        {
          "input": true,
          "component": "radio",
          "variant": "alternate-fill",
          "context": {
            "id": "fc-dark-brown",
            "name": "frame-color",
            "value": "30",
            "modifier": "alternate",
            "background": "#4A2C21",
            "label": "Dark Brown",
            "checked": null,
            "dataAttributes": [
              {
                "name": "value",
                "value": "MJO2204 (Dark Brown)"
              },
              {
                "name": "frame-style",
                "value": "mjo2204-81ar"
              },
              {
                "name": "frame-color",
                "value": "81ar"
              }
            ],
            "inputClasses": [
              "customize__item-input",
              "js-customize-item-input"
            ],
            "labelClasses": [
              "customize__item-label"
            ],
            "labelTextClasses": [
              "u-visually-hidden"
            ]
          }
        }
      ]
    },
    {
      "name": "clip-on",
      "label": "Clip-On",
      "valueInLabel": null,
      "modifiers": [
        "switch"
      ],
      "items": [
        {
          "input": true,
          "component": "checkbox",
          "variant": "switch",
          "context": {
            "id": "clip-on",
            "name": "clip-on",
            "modifier": "switch",
            "label": "Clip-On",
            "image": "/assets/images/dummy-products/mjo2204-79ar_front.jpg"
          }
        }
      ]
    }
  ]
}
  • Content:
    (function (window) {
      'use strict';
    
      function camelCase(string) {
        return string.replace(/-([a-z])/g, (original, match) => (match[0].toUpperCase()));
      }
    
      function getElementData(el) {
        const attributes = [...el.attributes];
        const data = {};
    
        for (let i = 0; i < attributes.length; i++) {
          if (/^data-/.test(attributes[i].name)) {
            data[camelCase(attributes[i].name)] = attributes[i].value;
          }
        }
    
        return data;
      }
    
      function handleInputChange(e) {
        const input = e.target;
        const option = input.closest('.js-mymaui-customize-option, .js-customize-option');
    
        if (option) {
          const optionValue = option.querySelector('.js-mymaui-customize-option-value, .js-customize-option-value');
          const inputValue = input.getAttribute('data-value');
    
          if (optionValue && inputValue) {
            optionValue.innerText = inputValue;
          }
        }
      }
    
      // Note that this function is not sophisticated enough to cope with e.g. TWO
      // completely separate customization components on one page.
      //
      // Currently, there is only one multi-customization configuration, namely
      // the Rimless Optical products where the user must select both Shape and
      // Color.
      //
      // The function is meant to satisfy the requirements of that specific config
      // AND single-configuration situations only.
      function customizationsNotify(e) {
        const selections = document.querySelectorAll('.js-customize input[type=radio]:checked');
        const customizations = {};
        const rimless = document.querySelector('.js-customize.customize--rimless');
        const clipon = document.querySelector('.js-customize.customize--core-clip-on');
    
    
        for (let i = 0; i < selections.length; i++) {
          const data = getElementData(selections[i]);
    
          if (data.hasOwnProperty('dataFrameColor') && selections.length > 1) {
            delete data.dataValue;
          }
    
          Object.assign(customizations, data);
        }
    
        if (rimless) {
          customizations.dataFrameStyle = customizations.dataFrameStyle.replace(/-[a-z0-9]+$/i, '')
          customizations.rimless = true;
        }
    
        if (clipon) {
          customizations.clipon = true;
        }
    
        componentEvents.emitEvent('customization-options-notify', [customizations]);
      }
    
      function init() {
        const instances = document.querySelectorAll('.js-mymaui-customize, .js-customize');
    
        for (let i = 0; i < instances.length; i++) {
          if (!instances[i].classList.contains('js-component-init')) {
            const instance = instances[i];
            const inputs = instance.querySelectorAll('.js-mymaui-customize-item-input, .js-customize-item-input');
    
            for (let i = 0; i < inputs.length; i++) {
              inputs[i].addEventListener('change', handleInputChange);
    
              if (inputs[i].type === 'radio') {
                inputs[i].addEventListener('click', customizationsNotify);
              }
            }
    
            // Prevent an instance from being initialized more than once.
            instance.classList.add('js-component-init');
          }
        }
    
        // Establish the current state of customizations on page load.
        window.addEventListener('load', customizationsNotify);
      }
    
      // Allow application JS to reinitialize any instances added with Ajax, etc.
      if (typeof componentEvents !== 'undefined') {
        componentEvents.on('component-init', init);
      }
    
      init();
    
    })(this);
    
  • URL: /components/raw/customize/customize.js
  • Filesystem Path: src/components/02-components/customize/customize.js
  • Size: 3.3 KB
  • Content:
    .customize,
    .mymaui-customize {
      position: relative;
    
      &:before {
        position: absolute;
        right: $gutter-width-half;
        bottom: 0;
        left: $gutter-width-half;
        width: calc(100% - 20px);
        border-bottom: 1px solid $color-light;
        content: '';
        z-index: 1;
      }
    
      @include breakpoint($breakpoint-xl) {
        &:before {
          content: none;
        }
      }
    }
    
    .customize__options,
    .mymaui-customize__options {
      padding: 0 20px;
      border-top: 1px solid $color-lighter;
    
      @include breakpoint($breakpoint-xl) {
        padding: 0;
        border: 0;
      }
    }
    
    .mymaui-customize__options {
      padding-bottom: 30px;
    }
    
    .customize__option,
    .mymaui-customize__option {
      @include clearfix;
    
      &:last-child {
        .tooltip {
          top: auto;
          right: 25px;
          bottom: -5px;
        }
    
        .tooltip::before {
          top: auto;
          bottom: 5px;
        }
      }
    
      @include breakpoint($breakpoint-xl) {
        &:last-child {
          .tooltip {
            bottom: 0;
          }
        }
      }
    }
    
    .customize__option-label,
    .mymaui-customize__option-label {
      position: relative;
      margin: 20px 0 5px;
      color: $color-dark;
      font-size: 1.4rem;
      font-weight: $font-weight-bold;
      letter-spacing: normal;
      line-height: 1.7rem;
    
      .tooltip {
        display: none;
        position: absolute;
        top: -10px;
        right: 25px;
    
        &:before {
          top: 5px;
          right: -5px;
          left: auto;
          border-top: 10px solid transparent;
          border-bottom: 10px solid transparent;
          border-left: 5px solid #0db287;
          border-right-style: none;
        }
      }
    
      .js-tooltip--open {
        display: block;
      }
    
      @include breakpoint($breakpoint-xl) {
        font-size: 1.9rem;
        line-height: 2.3rem;
      }
    }
    
    .customize__tooltip-trigger,
    .mymaui-customize__tooltip-trigger {
      position: absolute;
      right: 0;
      margin: 0;
      padding: 0;
      border-style: none;
      background: transparent;
      cursor: pointer;
      fill: $color-medium-dark;
    
      @include breakpoint($breakpoint-xl) {
        top: 5px;
      }
    }
    
    .customize__option-value,
    .mymaui-customize__option-value {
      font-weight: $font-weight-normal;
    }
    
    .customize__items,
    .mymaui-customize__items {
      @include reset-list;
    }
    
    .customize__item,
    .mymaui-customize__item {
      margin-right: 10px;
      float: left;
    
      .customize__option--select &,
      .mymaui-customize__option--select & {
        width: 100%;
      }
    
      .customize__option--label &,
      .mymaui-customize__option--label & {
        width: 50%;
        margin: 0;
      }
    
      @include breakpoint($breakpoint-md) {
        margin-right: 40px;
    
        .customize__option--select &,
        .mymaui-customize__option--select & {
          width: 50%;
        }
      }
    
      @include breakpoint($breakpoint-xl) {
        margin-right: 30px;
    
        .customize__option--select &,
        .mymaui-customize__option--select & {
          width: 100%;
        }
      }
    }
    
    .radio + .mymaui-customize__item-label {
      height: 40px;
      line-height: 40px;
    
      &:before {
        top: 7px;
        left: 7px;
        width: 26px;
        height: 26px;
      }
    
      &:after {
        width: 40px;
        height: 40px;
        border-width: 3px;
      }
    
      .customize__option--square &,
      .mymaui-customize__option--square & {
        &:before,
        &:after {
          border-radius: 0;
        }
      }
    
      .customize__option--label &,
      .mymaui-customize__option--label & {
        padding-left: 50px;
      }
    
      @include breakpoint($breakpoint-md) {
        height: 60px;
        line-height: 60px;
    
        &:before {
          top: 10px;
          left: 10px;
          width: 40px;
          height: 40px;
        }
    
        &:after {
          width: 60px;
          height: 60px;
        }
    
        .customize__option--label &,
        .mymaui-customize__option--label & {
          padding-left: 70px;
        }
      }
    }
    
    .radio:checked + .customize__item-label,
    .radio:checked + .mymaui-customize__item-label {
      font-weight: $font-weight-bold;
    }
    
    .mymaui-customize__item-alternate-fill {
      position: absolute;
      top: 7px;
      left: 7px;
      width: 26px;
      height: 26px;
      z-index: 1;
      box-sizing: border-box;
    
      .customize__option--round &,
      .mymaui-customize__option--round & {
        border-radius: 50%;
      }
    
      @include breakpoint($breakpoint-md) {
        top: 10px;
        left: 10px;
        width: 40px;
        height: 40px;
      }
    }
    
    .js-customize-disabled {
      display: none;
    }
    
    .customize__option-value {
      font-weight: $font-weight-normal;
    }
    
    .customize__items {
      display: flex;
      flex-wrap: wrap;
      padding: 0;
    }
    
    .customize__item {
      flex: 0 0 85px;
      margin: 0 15px 15px 0;
      list-style-type: none;
    
      .form-item--radio {
        margin: 0;
      }
    
      .radio__image {
        margin: 0;
      }
    
      @include breakpoint($breakpoint-lg) {
        flex: 0 0 100px;
      }
    }
    
    .customize__options-help {
      margin-top: 35px;
      padding-bottom: 20px;
    
      .paragraph {
        font-size: 1.4rem;
        line-height: 2.0rem;
      }
    }
    
    .customize__option {
      .customize__items {
        margin-top: 15px;
      }
    
      .customize__item {
        flex: 0 0 30px;
        margin-right: 15px;
    
        @include breakpoint($breakpoint-md) {
          flex: 0 0 60px;
          margin-right: 20px;
        }
      }
    
      .customize__item-label {
        width: 30px;
        height: 30px;
        padding-left: 0;
    
        @include breakpoint($breakpoint-md) {
          width: 60px;
          height: 60px;
        }
      }
    
      .radio--alternate ~ label {
        &:before {
          display: none;
        }
    
        &:after {
          @include breakpoint($breakpoint-md) {
            width: 60px;
            height: 60px;
            border-width: 3px;
          }
        }
    
        .radio__alternate-fill {
          @include breakpoint($breakpoint-md) {
            top: 10px;
            left: 10px;
            width: 40px;
            height: 40px;
          }
        }
      }
    
      .radio--alternate:not(:checked) ~ label {
        .radio__alternate-fill {
          opacity: 1;
        }
      }
    }
    
    // Note:
    //
    // .customize__option--round has no special styles because:
    //
    // a) The radio--alternate-fill variant itself is round, and
    // b) Customizations of size etc for this context are common to round and
    //    square (so found in .customize__option).
    .customize__option--square {
      .radio--alternate ~ label {
        &:after,
        .radio__alternate-fill {
          border-radius: 0;
        }
      }
    }
    
    .customize__option--switch {
      border-top: 1px solid $color-light;
    
      .customize__items {
        display: block;
      }
    
      .customize__item {
        display: block;
        width: 100%;
        margin-right: 0;
      }
    }
    
    .customize--core-clip-on {
      .customize__option--square {
        margin-bottom: 25px;
      }
    
      .customize__items {
        margin-top: 5px;
      }
    }
    
    .customize--rimless {
      .customize__item {
        flex: 0 0 83px;
        margin: 0 $gutter-width-half $gutter-width-half 0;
    
        @include breakpoint($breakpoint-md) {
          flex: 0 0 100px;
        }
      }
    
      .radio__image {
        width: 55px;
        padding: 14px 12px;
    
        @include breakpoint($breakpoint-md) {
          width: 80px;
          padding: 19px 8px;
        }
      }
    
      .radio__image--narrow {
        width: 75px;
        padding: 3px 2px;
    
        @include breakpoint($breakpoint-md) {
          width: 92px;
        }
      }
    }
    
    .customize--rimless-colors {
      .customize__option-label {
        display: none;
    
        @include breakpoint($breakpoint-lg) {
          display: block;
        }
      }
    }
    
  • URL: /components/raw/customize/customize.scss
  • Filesystem Path: src/components/02-components/customize/customize.scss
  • Size: 6.9 KB

Customize Events

The Customize component emits an event, customization-options-notify, on the load of a page containing the component, and subsequently, each time one of the component’s child radio buttons is clicked.

Various other components listen for this type of event and modify their own internal states or that of other elements in the dom.

The event should contain an object with the following properties:

  • dataFrameStyle: that part of the product sku designating the frame, e.g. MJO2001. Specified by the value attribute of a radio with the name attribute set to lens-shape.
  • dataFrameColor: that part of the product sku designating the color, e.g. 30. Specified by the value attribute of a radiowith thenameattribute set toframe-color`.
  • dataValue: the name of the frame, as used in e.g. the Product Detail component. Specified by the data-value attribute of a radio.

The event object could also contain the following property:

  • dataTempleLength: that part of the product sku containing the temple length.

These properties will be combined using the @product-detail component’s data-sku-form attribute.