Я не хочу, чтобы образец был поповером.
Я хочу, чтобы образцы отображались так, как они упоминали в https://help.shopify.com/en/themes/customization/products/features/add-color-swatches
Я следовал инструкциям на сайте. Мне не удалось найти функцию selectCallback во всех файлах темы. Я застрял здесь. Пожалуйста, помогите мне решить эту проблему.
Вот код файла js.
function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", {
value: true
});
/* harmony import */
var __WEBPACK_IMPORTED_MODULE_0__components_Popover__ = __webpack_require__(9);
/* harmony import */
var __WEBPACK_IMPORTED_MODULE_1__helper_Dom__ = __webpack_require__(0);
/* harmony import */
var __WEBPACK_IMPORTED_MODULE_2__components_VariantSelector__ = __webpack_require__(16);
/* harmony import */
var __WEBPACK_IMPORTED_MODULE_3__helper_Currency__ = __webpack_require__(5);
/**
* This component handles all the logic of switching variant, updating product meta...
*/
var ProductVariants = function() {
function ProductVariants(container, options) {
var _this5 = this;
_classCallCheck(this, ProductVariants);
this.element = container;
this.delegateElement = new domDelegate.Delegate(this.element);
this.options = options;
var jsonData = JSON.parse(this.element.querySelector('[data-product-json]').innerHTML);
this.productData = jsonData['product'];
this.variantsInventories = jsonData['inventories'] || {};
this.masterSelector = this.element.querySelector('#product-select-' + this.productData['id']);
// We init value with the first selected variant
this.productData['variants'].forEach(function(variant) {
if (variant['id'] === jsonData['selected_variant_id']) {
_this5.currentVariant = variant;
_this5.option1 = variant['option1'];
_this5.option2 = variant['option2'];
_this5.option3 = variant['option3'];
}
});
this._attachListeners();
this._createSelectors();
}
_createClass(ProductVariants, [{
key: 'destroy',
value: function destroy() {
this.delegateElement.off('click');
this.formPopovers.forEach(function(popover) {
return popover.destroy();
});
this.formVariantSelectors.forEach(function(selector) {
return selector.destroy();
});
}
}, {
key: '_attachListeners',
value: function _attachListeners() {
this.delegateElement.on('click', '[data-action="add-to-cart"]', this._addToCart.bind(this));
this.delegateElement.on('click', '[data-action="decrease-quantity"]', this._decreaseQuantity.bind(this));
this.delegateElement.on('click', '[data-action="increase-quantity"]', this._increaseQuantity.bind(this));
this.delegateElement.on('change', '[name="quantity"]', this._validateQuantity.bind(this));
}
/**
* Selectors can either be popovers or dedicated variant selectors. We therefore pre-create them all here
*/
}, {
key: '_createSelectors',
value: function _createSelectors() {
var _this6 = this;
// Create the instances for each selector
this.formPopovers = [];
this.formVariantSelectors = [];
__WEBPACK_IMPORTED_MODULE_1__helper_Dom__["default"].nodeListToArray(this.element.querySelectorAll('.OptionSelector')).forEach(function(item) {
var popover = new __WEBPACK_IMPORTED_MODULE_0__components_Popover__["default"](item, {
preferredPosition: 'left',
onValueChanged: _this6._onOptionChanged.bind(_this6)
});
_this6.formPopovers.push(popover);
});
__WEBPACK_IMPORTED_MODULE_1__helper_Dom__["default"].nodeListToArray(this.element.querySelectorAll('.VariantSelector')).forEach(function(item) {
var variantSelector = new __WEBPACK_IMPORTED_MODULE_2__components_VariantSelector__["default"](item, {
onValueChanged: _this6._onOptionChanged.bind(_this6)
});
_this6.formVariantSelectors.push(variantSelector);
});
}
/**
* ---------------------------------------------------------------------------------------------------
* CODE THAT HANDLE VARIANT CHANGES IN THE FRONT
*
* Please note that this code is highly dependant on the markup and classes, so make sure to NOT
* edit this code
* ---------------------------------------------------------------------------------------------------
*/
/**
* This callback is called whenever the variant changes and allows to update data about the active variant
*/
}, {
key: '_onVariantChanged',
value: function _onVariantChanged(previousVariant, newVariant) {
// 1st: the prices
this._updateProductPrices(newVariant, previousVariant);
// 2th: update inventory
this._updateInventory(newVariant, previousVariant);
// 3th: the add to cart button
this._updateAddToCartButton(newVariant, previousVariant);
if (window.theme.currencyConversionEnabled) {
__WEBPACK_IMPORTED_MODULE_3__helper_Currency__["default"].convertAll(this.element);
}
// Finally, we send an event so that other system could hook and do their own logic
this.element.dispatchEvent(new CustomEvent('variant:changed', {
bubbles: true,
detail: {
variant: newVariant,
previousVariant: previousVariant
}
}));
}
/**
* Update the prices (optionally showing compare at price)
*/
}, {
key: '_updateProductPrices',
value: function _updateProductPrices(newVariant, previousVariant) {
var productMetaPrices = this.element.querySelector('.ProductMeta__PriceList');
if (!newVariant) {
productMetaPrices.style.display = 'none';
} else {
if (previousVariant && previousVariant['price'] === newVariant['price'] && previousVariant['compare_at_price'] === newVariant['compare_at_price']) {
return; // The price do not have changed so let's return to avoid changing the DOM for nothing
}
productMetaPrices.innerHTML = '';
if (newVariant['compare_at_price'] > newVariant['price']) {
productMetaPrices.innerHTML += '<span class="ProductMeta__Price Price Price--highlight Text--subdued u-h4" data-money-convertible>' + __WEBPACK_IMPORTED_MODULE_3__helper_Currency__["default"].formatMoney(newVariant['price'], window.theme.moneyFormat) + '</span>';
productMetaPrices.innerHTML += '<span class="ProductMeta__Price Price Price--compareAt Text--subdued u-h4" data-money-convertible>' + __WEBPACK_IMPORTED_MODULE_3__helper_Currency__["default"].formatMoney(newVariant['compare_at_price'], window.theme.moneyFormat) + '</span>';
} else {
productMetaPrices.innerHTML += '<span class="ProductMeta__Price Price Text--subdued u-h4" data-money-convertible>' + __WEBPACK_IMPORTED_MODULE_3__helper_Currency__["default"].formatMoney(newVariant['price'], window.theme.moneyFormat) + '</span>';
}
productMetaPrices.style.display = '';
}
}
/**
* Update the inventory (if needed)
*/
}, {
key: '_updateInventory',
value: function _updateInventory(newVariant) {
if (!this.options['showInventoryQuantity']) {
return;
}
var productFormInventory = this.element.querySelector('.ProductForm__Inventory'),
variantInventory = newVariant ? this.variantsInventories[newVariant['id']] : null;
if (!newVariant || null === variantInventory['inventory_management'] || variantInventory['inventory_quantity'] <= 0 || this.options['inventoryQuantityThreshold'] > 0 && variantInventory['inventory_quantity'] > this.options['inventoryQuantityThreshold']) {
productFormInventory.style.display = 'none';
} else {
productFormInventory.textContent = variantInventory['inventory_message'];
productFormInventory.style.display = '';
}
}
/**
* Update the add to cart
*/
}, {
key: '_updateAddToCartButton',
value: function _updateAddToCartButton(newVariant) {
var addToCartButton = this.element.querySelector('.ProductForm__AddToCart'),
shopifyPaymentButton = this.element.querySelector('.shopify-payment-button'),
newButton = document.createElement('button');
newButton.setAttribute('type', 'submit');
newButton.className = 'ProductForm__AddToCart Button Button--full';
if (!newVariant) {
newButton.setAttribute('disabled', 'disabled');
newButton.removeAttribute('data-action');
newButton.classList.add('Button--secondary');
newButton.innerHTML = window.languages.productFormUnavailable;
} else {
if (newVariant['available']) {
newButton.removeAttribute('disabled');
newButton.classList.add(this.options['showPaymentButton'] ? 'Button--secondary' : 'Button--primary');
newButton.setAttribute('data-action', 'add-to-cart');
if (this.options['showPriceInButton']) {
newButton.innerHTML = '\n <span>' + window.languages.productFormAddToCart + '</span>\n <span class="Button__SeparatorDot"></span>\n <span data-money-convertible>' + __WEBPACK_IMPORTED_MODULE_3__helper_Currency__["default"].formatMoney(newVariant['price'], window.theme.moneyFormat) + '</span>\n ';
} else {
newButton.innerHTML = '<span>' + window.languages.productFormAddToCart + '</span>';
}
} else {
newButton.setAttribute('disabled', 'disabled');
newButton.classList.add('Button--secondary');
newButton.removeAttribute('data-action');
newButton.innerHTML = window.languages.productFormSoldOut;
}
}
if (this.options['showPaymentButton'] && shopifyPaymentButton) {
if (!newVariant || !newVariant['available']) {
shopifyPaymentButton.style.display = 'none';
} else {
shopifyPaymentButton.style.display = 'block';
}
}
// We replace the HTML instead of editing as it prevents for the CSS transition to show up
addToCartButton.parentNode.replaceChild(newButton, addToCartButton);
}
/**
* ---------------------------------------------------------------------------------------------------
* INTERNAL CODE THAT HANDLE VARIANT CHANGES
* ---------------------------------------------------------------------------------------------------
*/
/**
* Whenever an option is changed, this code fetch the corresponding active variant
*/
}, {
key: '_onOptionChanged',
value: function _onOptionChanged(newValue, target, activator) {
this['option' + target.getAttribute('data-option-position')] = newValue;
// We change the value associated with the activator, and check if we have a color swatch
var selectedValueElement = activator.querySelector('.ProductForm__SelectedValue'),
colorSwatchElement = activator.querySelector('.ProductForm__ColorSwatch');
selectedValueElement.innerHTML = newValue;
if (colorSwatchElement) {
// If the browser does not support a given color, it won't change it back to initial value
if (!window.CSS || window.CSS && window.CSS.supports('background-color: ' + target.getAttribute('data-background-color'))) {
colorSwatchElement.style.backgroundColor = target.getAttribute('data-background-color');
} else {
colorSwatchElement.style.backgroundColor = 'transparent';
}
colorSwatchElement.style.backgroundImage = 'url(' + target.getAttribute('data-background-image') + ')';
if (newValue.toLowerCase() === 'white') {
colorSwatchElement.classList.add('ProductForm__ColorSwatch--white');
} else {
colorSwatchElement.classList.remove('ProductForm__ColorSwatch--white');
}
}
// Finally, we get the new variant
var previousVariant = this.currentVariant;
this.currentVariant = this._getCurrentVariantFromOptions();
this._onVariantChanged(previousVariant, this.currentVariant);
if (this.currentVariant) {
if (this.options['enableHistoryState']) {
this._updateHistoryState(this.currentVariant);
}
// We need to modify the hidden select that contain the id attribute as well
this.masterSelector.querySelector('[selected]').removeAttribute('selected');
this.masterSelector.querySelector('[value="' + this.currentVariant['id'] + '"]').setAttribute('selected', 'selected');
}
}
/**
* Get the active variant based on the options
*/
}, {
key: '_getCurrentVariantFromOptions',
value: function _getCurrentVariantFromOptions() {
var _this7 = this;
var found = false;
this.productData['variants'].forEach(function(variant) {
if (variant['option1'] === _this7.option1 && variant['option2'] === _this7.option2 && variant['option3'] === _this7.option3) {
found = variant;
}
});
return found || null;
}
/**
* Update the history state for browsers that support it
*/
}, {
key: '_updateHistoryState',
value: function _updateHistoryState(variant) {
if (!history.replaceState) {
return;
}
var newUrl = window.location.protocol + '//' + window.location.host + window.location.pathname + '?variant=' + variant.id;
window.history.replaceState({
path: newUrl
}, '', newUrl);
}
/**
* ---------------------------------------------------------------------------------------------------
* INTERNAL CODE THAT HANDLE PRODUCT ADD TO CART
* ---------------------------------------------------------------------------------------------------
*/
}, {
key: '_addToCart',
value: function _addToCart(event) {
var _this8 = this;
if (!this.options['useAjaxCart']) {
return; // When using a cart type of page, we just simply redirect to the cart page
}
event.preventDefault(); // Prevent form to be submitted
var addToCartButton = this.element.querySelector('.ProductForm__AddToCart');
// First, we switch the status of the button
addToCartButton.setAttribute('disabled', 'disabled');
document.dispatchEvent(new CustomEvent('theme:loading:start'));
// Then we add the product in Ajax
var formElement = this.element.querySelector('form[action^="/cart/add"]'),
formData = new FormData(formElement);
/*
fetch('/cart/add.js', {
credentials: 'same-origin',
method: 'POST',
headers: {
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest' // This is needed as currently there is a bug in Shopify that assumes this header
},
body: formData
}).then((response) => {
document.dispatchEvent(new CustomEvent('theme:loading:end'));
if (response.ok) {
addToCartButton.removeAttribute('disabled');
// We simply trigger an event so the mini-cart can re-render
this.element.dispatchEvent(new CustomEvent('product:added', {
bubbles: true,
detail: {
variant: this.currentVariant,
quantity: parseInt(formElement.querySelector('[name="quantity"]').value)
}
}));
} else {
response.json().then((content) => {
let errorMessageElement = document.createElement('span');
errorMessageElement.className = 'ProductForm__Error Alert Alert--error';
errorMessageElement.innerHTML = content['description'];
addToCartButton.removeAttribute('disabled');
addToCartButton.insertAdjacentElement('afterend', errorMessageElement);
setTimeout(function() {
errorMessageElement.remove();
}, 2500);
});
}
});*/
// Unfortunately Shopify currently does not support fetch API for the Facebook Pixel. We therefore had to revert
// to old style Ajax calls until this is tackled by the dev team
function formSerialize(form) {
var i = void 0,
j = void 0,
len = void 0,
jLen = void 0,
formElement = void 0,
q = [];
function urlencode(str) {
return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
}
function addNameValue(name, value) {
q.push(urlencode(name) + '=' + urlencode(value));
}
if (!form || !form.nodeName || form.nodeName.toLowerCase() !== 'form') {
throw 'You must supply a form element';
}
for (i = 0, len = form.elements.length; i < len; i++) {
formElement = form.elements[i];
if (formElement.name === '' || formElement.disabled) {
continue;
}
switch (formElement.nodeName.toLowerCase()) {
case 'input':
switch (formElement.type) {
case 'text':
case 'hidden':
case 'password':
case 'button': // Not submitted when submitting form manually, though jQuery does serialize this and it can be an HTML4 successful control
case 'submit':
addNameValue(formElement.name, formElement.value);
break;
case 'checkbox':
case 'radio':
if (formElement.checked) {
addNameValue(formElement.name, formElement.value);
}
break;
case 'file':
// addNameValue(formElement.name, formElement.value); // Will work and part of HTML4 "successful controls", but not used in jQuery
break;
case 'reset':
break;
}
break;
case 'textarea':
addNameValue(formElement.name, formElement.value);
break;
case 'select':
switch (formElement.type) {
case 'select-one':
addNameValue(formElement.name, formElement.value);
break;
case 'select-multiple':
for (j = 0, jLen = formElement.options.length; j < jLen; j++) {
if (formElement.options[j].selected) {
addNameValue(formElement.name, formElement.options[j].value);
}
}
break;
}
break;
case 'button':
// jQuery does not submit these, though it is an HTML4 successful control
switch (formElement.type) {
case 'reset':
case 'submit':
case 'button':
addNameValue(formElement.name, formElement.value);
break;
}
break;
}
}
return q.join('&');
}
var httpRequest = new XMLHttpRequest();
httpRequest.open('POST', '/cart/add.js', true);
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
httpRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
httpRequest.send(formSerialize(formElement));
httpRequest.onreadystatechange = function() {
// Process the server response here.
if (httpRequest.readyState === XMLHttpRequest.DONE) {
document.dispatchEvent(new CustomEvent('theme:loading:end'));
if (httpRequest.status === 200) {
addToCartButton.removeAttribute('disabled');
// We simply trigger an event so the mini-cart can re-render
_this8.element.dispatchEvent(new CustomEvent('product:added', {
bubbles: true,
detail: {
variant: _this8.currentVariant,
quantity: parseInt(formElement.querySelector('[name="quantity"]').value)
}
}));
} else {
var errors = JSON.parse(httpRequest['responseText']);
var errorMessageElement = document.createElement('span');
errorMessageElement.className = 'ProductForm__Error Alert Alert--error';
errorMessageElement.innerHTML = errors['description'];
addToCartButton.removeAttribute('disabled');
addToCartButton.insertAdjacentElement('afterend', errorMessageElement);
setTimeout(function() {
errorMessageElement.remove();
}, 2500);
}
}
};
event.preventDefault();
}
/**
* ---------------------------------------------------------------------------------------------------
* OTHER
* ---------------------------------------------------------------------------------------------------
*/
/**
* When using the quantity selector, this can be used to decrease the quantity (be ensuring it won't be lower than 1)
*/
}, {
key: '_decreaseQuantity',
value: function _decreaseQuantity(event, target) {
target.nextElementSibling.value = Math.max(parseInt(target.nextElementSibling.value) - 1, 1);
}
/**
* When using the quantity selector, this can be used to increase the quantity
*/
}, {
key: '_increaseQuantity',
value: function _increaseQuantity(event, target) {
target.previousElementSibling.value = parseInt(target.previousElementSibling.value) + 1;
}
/**
* Make sure the quantity does not go below when manually changed
*/
}, {
key: '_validateQuantity',
value: function _validateQuantity(event, target) {
target.value = Math.max(parseInt(target.value) || 1, 1);
}
}]);
return ProductVariants;
}();
/* harmony export (immutable) */
__webpack_exports__["default"] = ProductVariants;
/***/
}