Przejdź do głównej treści

Odwiedź nasze salony | Wrocław: pl. Strzelecki 25 | Gdynia: ul. Orłowska 66

Darmowa już od 199zł ZOBACZ

Dostawa już od 199zł ZOBACZ
Produkty w koszyku: 0. Zobacz szczegóły
Twój koszyk jest pusty
język/waluta
PL / PLN
  • 7dfe44ae4b079cd81b5cc7c4732b7947
  • a98ed8f1ec4b442cf6c979fdc0042931
  • 44976d94489ba0e873d05d74117c2768

Rebel Walls R21682 Botanical Gardens Meadow Bouquet Black - mural w bukiety

Czas wysyłki: 14 - 21 dni roboczych
Klej gratis
Rebel Walls
Przejdź do sekcji Opinie
Cena 237,00 zł
m2

Tapeta tworzona pod wymiar ściany

Zapytaj o produkt

Płać nawet 30 dni po zakupie

const TapProductSample = { settings: { selectors: { root: '.js__tap-product-sample', button: '.js__tap-product-sample-button', nativeBuyButton: 'buy-button', basketProduct: '.basket-product', basketStepper: 'h-input-stepper', basketStepperButton: 'h-button-stepper', basketQuantityInput: '.input-stepper__value_input' }, classes: { loading: 'tap-product-sample__button_is-loading', modalActive: 'tap-product-sample-modal_is-active' }, storefront: null, basketUpdaterApi: null, basketProductsApi: null, basketProducts: [], basketWatcherBound: false, basketApisPromise: null, basketDomObserver: null, basketDomObserverQueued: false, basketInteractionGuardBound: false, protectionStyleInjected: false }, init: (storefront) => { TapProductSample.settings.storefront = storefront; TapProductSample.injectProtectionStyles(); TapProductSample.bindBasketInteractionGuard(); TapProductSample.ensureBasketDomObserver(); TapProductSample.load(); // Prewarmujemy koszyk tylko tam, gdzie moduł może go potrzebować. // Rejestracja dynamiczna zachowuje możliwość pierwszego dodania próbki // również wtedy, gdy użytkownik nie ma jeszcze sesji koszyka. if (TapProductSample.shouldPrepareBasketApis()) { TapProductSample.ensureBasketApis().catch((error) => { console.warn('[TapProductSample] Basket prewarm failed.', error); }); } if (storefront.eventBus && typeof storefront.eventBus.on === 'function') { storefront.eventBus.on('basket.initialized', () => { TapProductSample.ensureBasketApis().catch((error) => { console.warn( '[TapProductSample] Basket API resolve after basket.initialized failed.', error ); }); TapProductSample.scheduleBasketDomProtection(); }); } }, shouldPrepareBasketApis: () => { return Boolean( document.querySelector(TapProductSample.settings.selectors.root) || window.location.pathname.includes('/basket') ); }, ensureBasketApis: async () => { if ( TapProductSample.settings.basketUpdaterApi && TapProductSample.settings.basketProductsApi ) { return true; } if (TapProductSample.settings.basketApisPromise) { return TapProductSample.settings.basketApisPromise; } TapProductSample.settings.basketApisPromise = (async () => { const storefront = TapProductSample.settings.storefront; if (!storefront) { throw new Error('Storefront API is not available.'); } let basketUpdaterApi = typeof storefront.getApiSync === 'function' ? storefront.getApiSync('basketUpdaterApi') : null; let basketProductsApi = typeof storefront.getApiSync === 'function' ? storefront.getApiSync('basketProductsApi') : null; // Gdy nie ma jeszcze sesji koszyka, feature "basket" może nie być // zarejestrowany. Rejestrujemy go PRZED próbą pobrania API. if (!basketUpdaterApi || !basketProductsApi) { const featureSystemApi = typeof storefront.getApiSync === 'function' ? storefront.getApiSync('FeatureSystemApi') : null; if ( !featureSystemApi || typeof featureSystemApi.registerDynamic !== 'function' ) { throw new Error('FeatureSystemApi is not available.'); } try { await featureSystemApi.registerDynamic('basket', true); } catch (error) { // Inny moduł może zarejestrować basket w tym samym momencie. // Błąd rejestracji ignorujemy i od razu ponawiamy pobranie API. } basketUpdaterApi = typeof storefront.getApiSync === 'function' ? storefront.getApiSync('basketUpdaterApi') : null; basketProductsApi = typeof storefront.getApiSync === 'function' ? storefront.getApiSync('basketProductsApi') : null; } if (!basketUpdaterApi && typeof storefront.getApi === 'function') { try { basketUpdaterApi = await storefront.getApi('basketUpdaterApi'); } catch (error) { console.warn( '[TapProductSample] basketUpdaterApi getApi() failed.', error ); } } if (!basketProductsApi && typeof storefront.getApi === 'function') { try { basketProductsApi = await storefront.getApi('basketProductsApi'); } catch (error) { console.warn( '[TapProductSample] basketProductsApi getApi() failed.', error ); } } if (!basketUpdaterApi || !basketProductsApi) { throw new Error('Basket APIs could not be initialized.'); } TapProductSample.settings.basketUpdaterApi = basketUpdaterApi; TapProductSample.settings.basketProductsApi = basketProductsApi; TapProductSample.watchBasket(); return true; })(); try { return await TapProductSample.settings.basketApisPromise; } catch (error) { TapProductSample.settings.basketApisPromise = null; throw error; } }, waitForBasket: async () => { return TapProductSample.ensureBasketApis(); }, load: () => { TapProductSample.injectProtectionStyles(); TapProductSample.bindBasketInteractionGuard(); TapProductSample.ensureBasketDomObserver(); document .querySelectorAll(TapProductSample.settings.selectors.root) .forEach((root) => { if (root.dataset.tapProductSampleReady !== '1') { root.dataset.tapProductSampleReady = '1'; const button = root.querySelector( TapProductSample.settings.selectors.button ); if (button) { button.addEventListener('click', () => { TapProductSample.handleClick(root, button); }); } } TapProductSample.bindNativeSampleProductGuard(root); TapProductSample.protectNativeSampleProductQuantity(root); }); TapProductSample.scheduleBasketDomProtection(); }, injectProtectionStyles: () => { if (TapProductSample.settings.protectionStyleInjected) { return; } const style = document.createElement('style'); style.id = 'tap-product-sample-protection-styles'; style.textContent = ` .tap-product-sample-basket-item h-input-stepper h-button-stepper[type="increment"] { display: none !important; } .tap-product-sample-basket-item_quantity-valid h-input-stepper h-button-stepper[type="decrement"] { display: none !important; } .tap-product-sample-basket-item .input-stepper__value_input, .tap-product-sample-native-quantity-locked .input-stepper__value_input { pointer-events: none !important; user-select: none !important; caret-color: transparent !important; } .tap-product-sample-native-quantity-locked h-button-stepper { display: none !important; } `; document.head.appendChild(style); TapProductSample.settings.protectionStyleInjected = true; }, ensureBasketDomObserver: () => { if ( TapProductSample.settings.basketDomObserver || !document.body || typeof MutationObserver !== 'function' ) { return; } TapProductSample.settings.basketDomObserver = new MutationObserver( (mutations) => { const shouldProtect = mutations.some((mutation) => { if (mutation.type === 'childList') { return mutation.addedNodes && mutation.addedNodes.length > 0; } if (mutation.type === 'attributes') { return Boolean( mutation.target.closest?.( TapProductSample.settings.selectors.basketProduct ) ); } return false; }); if (shouldProtect) { TapProductSample.scheduleBasketDomProtection(); } } ); TapProductSample.settings.basketDomObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['value'] }); }, scheduleBasketDomProtection: () => { if (TapProductSample.settings.basketDomObserverQueued) { return; } TapProductSample.settings.basketDomObserverQueued = true; window.requestAnimationFrame(() => { TapProductSample.settings.basketDomObserverQueued = false; TapProductSample.protectBasketDom(); }); }, bindBasketInteractionGuard: () => { if (TapProductSample.settings.basketInteractionGuardBound) { return; } TapProductSample.settings.basketInteractionGuardBound = true; document.addEventListener( 'click', (event) => { const stepperButton = event.target.closest( TapProductSample.settings.selectors.basketStepperButton ); if (!stepperButton) { return; } const basketProduct = stepperButton.closest( TapProductSample.settings.selectors.basketProduct ); if ( !basketProduct || !TapProductSample.isBasketProductElementSample(basketProduct) ) { return; } const buttonType = stepperButton.getAttribute('type'); const quantity = TapProductSample.getBasketProductDomQuantity( basketProduct ); // Jeżeli przed wdrożeniem próbka miała już błędnie ilość > 1, // pozwalamy tylko ją zmniejszyć do 1. Zwiększanie jest zawsze blokowane. if (buttonType === 'decrement' && quantity > 1) { return; } event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); }, true ); document.addEventListener( 'keydown', (event) => { const input = event.target.closest( TapProductSample.settings.selectors.basketQuantityInput ); if (!input) { return; } const basketProduct = input.closest( TapProductSample.settings.selectors.basketProduct ); if ( !basketProduct || !TapProductSample.isBasketProductElementSample(basketProduct) ) { return; } event.preventDefault(); event.stopPropagation(); }, true ); document.addEventListener( 'beforeinput', (event) => { const input = event.target.closest( TapProductSample.settings.selectors.basketQuantityInput ); if (!input) { return; } const basketProduct = input.closest( TapProductSample.settings.selectors.basketProduct ); if ( !basketProduct || !TapProductSample.isBasketProductElementSample(basketProduct) ) { return; } event.preventDefault(); event.stopPropagation(); }, true ); }, watchBasket: () => { const basketProductsApi = TapProductSample.settings.basketProductsApi; if (!basketProductsApi || TapProductSample.settings.basketWatcherBound) { return; } TapProductSample.settings.basketWatcherBound = true; if (typeof basketProductsApi.get === 'function') { try { const products = basketProductsApi.get(); const items = TapProductSample.getBasketItemsFromBasketObject(products); if (items.length || Array.isArray(products)) { TapProductSample.settings.basketProducts = items; } } catch (error) { console.warn( '[TapProductSample] Initial basketProductsApi.get() failed.', error ); } } if (typeof basketProductsApi.select$ !== 'function') { return; } const basketProducts$ = basketProductsApi.select$(); if (basketProducts$ && typeof basketProducts$.subscribe === 'function') { basketProducts$.subscribe((products) => { TapProductSample.settings.basketProducts = TapProductSample.getBasketItemsFromBasketObject(products); TapProductSample.scheduleBasketDomProtection(); }); } }, splitIds: (value) => { return String(value || '') .split(/[\s,;]+/) .map((item) => item.trim()) .filter(Boolean); }, normalizeId: (value) => { return String(value || '').trim(); }, normalizeText: (value) => { return String(value || '') .trim() .toLocaleLowerCase('pl-PL') .normalize('NFD') .replace(/[\u0300-\u036f]/g, ''); }, getKnownSampleProductIdsFromDom: () => { const ids = new Set(); document .querySelectorAll(TapProductSample.settings.selectors.root) .forEach((root) => { TapProductSample.splitIds(root.dataset.allSampleProductIds).forEach( (id) => ids.add(id) ); TapProductSample.getSampleCategoryProductIds(root).forEach( (id) => ids.add(id) ); }); return Array.from(ids); }, getBasketProductOriginalProductId: (basketProduct) => { const optionNodes = basketProduct.querySelectorAll( '.basket-product__options [data-option-name]' ); for (const optionNode of optionNodes) { if ( TapProductSample.normalizeText(optionNode.dataset.optionName) === 'id produktu' ) { return TapProductSample.normalizeId(optionNode.dataset.optionValue); } } return ''; }, getBasketProductDisplayName: (basketProduct) => { const link = basketProduct.querySelector( '.basket-product__link, .basket-product__name a, .basket-product__name' ); return TapProductSample.normalizeText(link?.textContent || ''); }, isBasketProductElementSample: (basketProduct) => { if (!basketProduct) { return false; } // Próbki dodawane przez ten moduł mają w koszyku opcję "ID produktu". if (TapProductSample.getBasketProductOriginalProductId(basketProduct)) { return true; } const productId = TapProductSample.normalizeId( basketProduct.dataset.productId ); if ( productId && TapProductSample.getKnownSampleProductIdsFromDom().includes(productId) ) { return true; } // Na stronie koszyka skrypt modułu jest ładowany globalnie, ale nie ma tam // jego Twiga z konfiguracją. Produkty z kategorii /probniki rozpoznajemy // dodatkowo po ich nazwach ("Próbka...", "Zestaw próbek..."). const name = TapProductSample.getBasketProductDisplayName(basketProduct); return ( name.includes('probka') || name.includes('probek') || name.includes('probki') ); }, getBasketProductDomQuantity: (basketProduct) => { const stepper = basketProduct.querySelector( TapProductSample.settings.selectors.basketStepper ); const value = stepper?.getAttribute('value') || '1'; const quantity = parseInt(value, 10); return Number.isFinite(quantity) && quantity > 0 ? quantity : 1; }, countSamplesInDom: () => { return Array.from( document.querySelectorAll( TapProductSample.settings.selectors.basketProduct ) ).reduce((sum, basketProduct) => { if (!TapProductSample.isBasketProductElementSample(basketProduct)) { return sum; } return sum + TapProductSample.getBasketProductDomQuantity(basketProduct); }, 0); }, hasDuplicateSampleInDom: (root) => { const productId = TapProductSample.normalizeId(root?.dataset?.productId); if (!productId) { return false; } return Array.from( document.querySelectorAll( TapProductSample.settings.selectors.basketProduct ) ).some( (basketProduct) => TapProductSample.getBasketProductOriginalProductId(basketProduct) === productId ); }, hasNativeSampleProductInDom: (root) => { const productId = TapProductSample.normalizeId(root?.dataset?.productId); if (!productId) { return false; } return Array.from( document.querySelectorAll( TapProductSample.settings.selectors.basketProduct ) ).some( (basketProduct) => TapProductSample.normalizeId(basketProduct.dataset.productId) === productId ); }, lockBasketProductQuantity: (basketProduct) => { if (!basketProduct) { return; } basketProduct.classList.add('tap-product-sample-basket-item'); const stepper = basketProduct.querySelector( TapProductSample.settings.selectors.basketStepper ); if (!stepper) { return; } const quantity = TapProductSample.getBasketProductDomQuantity(basketProduct); const isValidQuantity = quantity <= 1; basketProduct.classList.toggle( 'tap-product-sample-basket-item_quantity-valid', isValidQuantity ); stepper.setAttribute('min', '1'); if (isValidQuantity) { stepper.setAttribute('max', '1'); } stepper .querySelectorAll(TapProductSample.settings.selectors.basketStepperButton) .forEach((button) => { const buttonType = button.getAttribute('type'); const canRepairQuantity = buttonType === 'decrement' && quantity > 1; if (canRepairQuantity) { button.removeAttribute('aria-disabled'); button.setAttribute('tabindex', '0'); return; } button.setAttribute('aria-disabled', 'true'); button.setAttribute('tabindex', '-1'); }); const input = stepper.querySelector( TapProductSample.settings.selectors.basketQuantityInput ); if (input) { input.readOnly = true; input.setAttribute('aria-readonly', 'true'); input.setAttribute('tabindex', '-1'); } }, protectBasketDom: () => { document .querySelectorAll(TapProductSample.settings.selectors.basketProduct) .forEach((basketProduct) => { if (TapProductSample.isBasketProductElementSample(basketProduct)) { TapProductSample.lockBasketProductQuantity(basketProduct); } }); }, protectNativeSampleProductQuantity: (root) => { if (root?.dataset?.isSampleCategoryProduct !== '1') { return; } const productId = TapProductSample.normalizeId(root.dataset.productId); if (!productId) { return; } document .querySelectorAll(`product-quantity[product-id="${productId}"]`) .forEach((productQuantity) => { productQuantity.classList.add( 'tap-product-sample-native-quantity-locked' ); productQuantity.setAttribute('value', '1'); const stepper = productQuantity.querySelector( TapProductSample.settings.selectors.basketStepper ); if (stepper) { stepper.setAttribute('min', '1'); stepper.setAttribute('max', '1'); stepper.setAttribute('value', '1'); stepper .querySelectorAll( TapProductSample.settings.selectors.basketStepperButton ) .forEach((button) => { button.setAttribute('aria-disabled', 'true'); button.setAttribute('tabindex', '-1'); }); const input = stepper.querySelector( TapProductSample.settings.selectors.basketQuantityInput ); if (input) { input.readOnly = true; input.setAttribute('aria-readonly', 'true'); input.setAttribute('tabindex', '-1'); } } }); document .querySelectorAll(`buy-button[product-id="${productId}"]`) .forEach((buyButton) => { buyButton.setAttribute('quantity', '1'); }); }, getSampleLimit: (root) => { const limit = parseInt(root?.dataset?.sampleLimit || '5', 10); return Number.isFinite(limit) && limit > 0 ? limit : 5; }, getSampleCategoryProductIds: (root) => { return TapProductSample.splitIds(root?.dataset?.sampleCategoryProductIds); }, getNativeBuyButtonQuantity: (buyButton) => { const quantity = parseInt(buyButton?.getAttribute('quantity') || '1', 10); return Number.isFinite(quantity) && quantity > 0 ? quantity : 1; }, getQuantity: (item) => { const quantity = item?.quantity ?? item?.count ?? item?.amount ?? item?.qty ?? 1; const parsed = parseInt(quantity, 10); return Number.isFinite(parsed) && parsed > 0 ? parsed : 1; }, getItemProductId: (item) => { return TapProductSample.normalizeId( item?.productId ?? item?.product_id ?? item?.product?.id ?? item?.product?.productId ?? item?.id ?? '' ); }, getItemVariantId: (item) => { return TapProductSample.normalizeId( item?.variantId ?? item?.variant_id ?? item?.stockId ?? item?.stock_id ?? item?.variant?.id ?? item?.stock?.id ?? '' ); }, collectStrings: (value, output = [], depth = 0) => { if (depth > 6 || value === null || value === undefined) { return output; } if (typeof value === 'string' || typeof value === 'number') { output.push(String(value)); return output; } if (Array.isArray(value)) { value.forEach((item) => { TapProductSample.collectStrings(item, output, depth + 1); }); return output; } if (typeof value === 'object') { Object.keys(value).forEach((key) => { output.push(String(key)); TapProductSample.collectStrings(value[key], output, depth + 1); }); } return output; }, getBasketItemsFromBasketObject: (basket) => { if (!basket) { return []; } if (Array.isArray(basket)) { return basket; } const candidates = [ basket.products?.list, basket.products, basket.items, basket.lines, basket.positions, basket.basket?.products?.list, basket.basket?.products, basket.basket?.items, basket.basket?.lines, basket.data?.products?.list, basket.data?.products, basket.data?.items ]; for (const candidate of candidates) { if (Array.isArray(candidate)) { return candidate; } } return []; }, getBasketItems: async () => { await TapProductSample.waitForBasket(); const basketProductsApi = TapProductSample.settings.basketProductsApi; const basketUpdaterApi = TapProductSample.settings.basketUpdaterApi; // Przy każdym sprawdzeniu limitu próbujemy pobrać najświeższy stan koszyka. // Poprzednio cache z subskrypcji mógł być jeszcze nieaktualny po szybkich zmianach. if (basketUpdaterApi && typeof basketUpdaterApi.getCurrentBasket === 'function') { try { const basket = await basketUpdaterApi.getCurrentBasket(); const items = TapProductSample.getBasketItemsFromBasketObject(basket); TapProductSample.settings.basketProducts = items; return items; } catch (error) {} } if (basketProductsApi && typeof basketProductsApi.get === 'function') { try { const products = basketProductsApi.get(); const items = TapProductSample.getBasketItemsFromBasketObject(products); if (items.length || Array.isArray(products)) { TapProductSample.settings.basketProducts = items; return items; } } catch (error) {} } return TapProductSample.settings.basketProducts; }, isSampleItem: ( item, sampleProductIds, sampleCategoryProductIds, sampleProductId, sampleVariantId ) => { const itemProductId = TapProductSample.getItemProductId(item); const itemVariantId = TapProductSample.getItemVariantId(item); const strings = TapProductSample.collectStrings(item); if (itemProductId && sampleProductIds.includes(itemProductId)) { return true; } if (itemProductId && sampleCategoryProductIds.includes(itemProductId)) { return true; } if (sampleProductId && itemProductId === sampleProductId) { return true; } if (sampleVariantId && itemVariantId === sampleVariantId) { return true; } if (sampleProductIds.some((id) => strings.includes(id))) { return true; } return sampleCategoryProductIds.some((id) => strings.includes(id)); }, hasDuplicateSample: (items, root) => { const productId = TapProductSample.normalizeId(root.dataset.productId); const sampleProductId = TapProductSample.normalizeId(root.dataset.sampleProductId); const sampleVariantId = TapProductSample.normalizeId(root.dataset.sampleVariantId); const sampleProductIds = TapProductSample.splitIds(root.dataset.allSampleProductIds); const sampleCategoryProductIds = TapProductSample.getSampleCategoryProductIds(root); return items.some((item) => { if ( !TapProductSample.isSampleItem( item, sampleProductIds, sampleCategoryProductIds, sampleProductId, sampleVariantId ) ) { return false; } const strings = TapProductSample.collectStrings(item); return productId && strings.includes(productId); }); }, countSamples: (items, root) => { const sampleProductId = TapProductSample.normalizeId(root.dataset.sampleProductId); const sampleVariantId = TapProductSample.normalizeId(root.dataset.sampleVariantId); const sampleProductIds = TapProductSample.splitIds(root.dataset.allSampleProductIds); const sampleCategoryProductIds = TapProductSample.getSampleCategoryProductIds(root); return items.reduce((sum, item) => { if ( TapProductSample.isSampleItem( item, sampleProductIds, sampleCategoryProductIds, sampleProductId, sampleVariantId ) ) { return sum + TapProductSample.getQuantity(item); } return sum; }, 0); }, hasNativeSampleProductDuplicate: (items, root) => { const productId = TapProductSample.normalizeId(root?.dataset?.productId); if (!productId) { return false; } return items.some((item) => { if (TapProductSample.getItemProductId(item) === productId) { return true; } return TapProductSample.collectStrings(item).includes(productId); }); }, bindNativeSampleProductGuard: (root) => { if (root.dataset.isSampleCategoryProduct !== '1') { return; } const currentProductId = TapProductSample.normalizeId(root.dataset.productId); document .querySelectorAll(TapProductSample.settings.selectors.nativeBuyButton) .forEach((buyButton) => { if ( TapProductSample.normalizeId(buyButton.getAttribute('product-id')) !== currentProductId || buyButton.dataset.tapProductSampleGuardReady === '1' ) { return; } buyButton.dataset.tapProductSampleGuardReady = '1'; buyButton.setAttribute('quantity', '1'); buyButton.addEventListener( 'click', async (event) => { if (buyButton.dataset.tapProductSampleGuardBypass === '1') { delete buyButton.dataset.tapProductSampleGuardBypass; return; } const nativeButton = event.target.closest('button'); if (!nativeButton || !buyButton.contains(nativeButton)) { return; } event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); try { const items = await TapProductSample.getBasketItems(); const sampleLimit = TapProductSample.getSampleLimit(root); const apiSamplesCount = TapProductSample.countSamples(items, root); const domSamplesCount = TapProductSample.countSamplesInDom(); const currentSamplesCount = Math.max( apiSamplesCount, domSamplesCount ); if ( TapProductSample.hasNativeSampleProductDuplicate(items, root) || TapProductSample.hasNativeSampleProductInDom(root) ) { TapProductSample.showModal( root.dataset.duplicateMessage || 'Już dodałeś próbkę tego produktu' ); return; } if (currentSamplesCount + 1 > sampleLimit) { TapProductSample.showModal( root.dataset.limitMessage || 'Możesz zamówić maksymalnie 5 próbek!' ); return; } TapProductSample.protectNativeSampleProductQuantity(root); buyButton.setAttribute('quantity', '1'); buyButton.dataset.tapProductSampleGuardBypass = '1'; nativeButton.click(); } catch (error) { console.error( '[TapProductSample] Native sample limit check failed.', error ); TapProductSample.showModal( root.dataset.errorMessage || 'Wystąpił błąd podczas dodawania próbki' ); } }, true ); }); }, createModal: () => { let modal = document.querySelector('.tap-product-sample-modal'); if (modal) { return modal; } modal = document.createElement('div'); modal.className = 'tap-product-sample-modal'; modal.innerHTML = ` `; document.body.appendChild(modal); modal.addEventListener('click', (event) => { if (event.target === modal) { TapProductSample.closeModal(); } }); modal.querySelector('.tap-product-sample-modal__close').addEventListener('click', () => { TapProductSample.closeModal(); }); document.addEventListener('keydown', (event) => { if (event.key === 'Escape') { TapProductSample.closeModal(); } }); return modal; }, showModal: (message, type = 'info') => { const modal = TapProductSample.createModal(); const messageNode = modal.querySelector('.tap-product-sample-modal__message'); const iconNode = modal.querySelector('.tap-product-sample-modal__icon'); if (messageNode) { messageNode.textContent = message; } if (iconNode) { if (type === 'success') { iconNode.innerHTML = ` `; } else { iconNode.textContent = 'i'; } } modal.classList.add(TapProductSample.settings.classes.modalActive); }, closeModal: () => { const modal = document.querySelector('.tap-product-sample-modal'); if (modal) { modal.classList.remove(TapProductSample.settings.classes.modalActive); } }, addSampleToBasket: async (root) => { await TapProductSample.waitForBasket(); const basketUpdaterApi = TapProductSample.settings.basketUpdaterApi; if (!basketUpdaterApi || typeof basketUpdaterApi.addItem !== 'function') { throw new Error('Basket updater API is not available.'); } const variantOptions = {}; variantOptions[root.dataset.productNameOptionId] = root.dataset.productName || ''; variantOptions[root.dataset.productIdOptionId] = root.dataset.productId || ''; const item = { variantId: Number(root.dataset.sampleVariantId), quantity: 1, variantOptions, showAddedModal: false, bundleItems: [] }; const basket = await basketUpdaterApi.addItem(item); const returnedItems = TapProductSample.getBasketItemsFromBasketObject(basket); if (returnedItems.length) { TapProductSample.settings.basketProducts = returnedItems; } if (basketUpdaterApi.refreshBasket && typeof basketUpdaterApi.refreshBasket === 'function') { try { await basketUpdaterApi.refreshBasket(); } catch (error) {} } return basket; }, handleClick: async (root, button) => { if (button.classList.contains(TapProductSample.settings.classes.loading)) { return; } button.classList.add(TapProductSample.settings.classes.loading); button.disabled = true; try { const items = await TapProductSample.getBasketItems(); const sampleLimit = TapProductSample.getSampleLimit(root); const apiSamplesCount = TapProductSample.countSamples(items, root); const domSamplesCount = TapProductSample.countSamplesInDom(); const currentSamplesCount = Math.max(apiSamplesCount, domSamplesCount); if ( TapProductSample.hasDuplicateSample(items, root) || TapProductSample.hasDuplicateSampleInDom(root) ) { TapProductSample.showModal( root.dataset.duplicateMessage || 'Już dodałeś próbkę tego produktu' ); return; } if (currentSamplesCount + 1 > sampleLimit) { TapProductSample.showModal(root.dataset.limitMessage || 'Możesz zamówić maksymalnie 5 próbek!'); return; } await TapProductSample.addSampleToBasket(root); TapProductSample.scheduleBasketDomProtection(); if (root.dataset.afterAddAction === 'cart') { window.location.href = root.dataset.basketUrl || '/basket'; return; } TapProductSample.showModal( root.dataset.addedMessage || 'Próbka została dodana do koszyka', 'success' ); } catch (error) { console.error('[TapProductSample] Sample add failed.', error); TapProductSample.showModal(root.dataset.errorMessage || 'Wystąpił błąd podczas dodawania próbki'); } finally { button.classList.remove(TapProductSample.settings.classes.loading); button.disabled = false; } } }; useStorefront(async (storefront) => { TapProductSample.init(storefront); storefront.eventBus.on('PageManager.loaded', () => { TapProductSample.load(); if (TapProductSample.shouldPrepareBasketApis()) { TapProductSample.ensureBasketApis().catch((error) => { console.warn( '[TapProductSample] Basket resolve after PageManager.loaded failed.', error ); }); } }); });

Oblicz zapotrzebowanie

? Aby obliczyć zapotrzebowanie, należy podać wymiary ściany

Podaj wymiary ściany:

Potrzebujesz porady? Odpowiemy na Twoje pytania!

Gwarancja bezpiecznego zakupu

Kolor

  • Czarny tak
  • Srebrny nie
  • Niebieski tak
  • Złoty nie
  • Żółty tak
Zapytaj o produkt
Udostępnij

Opis produktu

Mural Rebel Walls Botanical Gardens Meadow Bouquet R21682 Black

Mural Rebel Walls Botanical Gardens Meadow Bouquet R21682 Black bazuje na regularnym układzie bukietów, które można odczytać jako ornamentalną interpretację łąkowych roślin. Rytmiczne powtórzenia nadają ścianie uporządkowaną strukturę i pozwalają zachować równowagę między detalem a tłem. Kompozycja czerpie z estetyki klasycznych tkanin dekoracyjnych, dlatego każdy motyw funkcjonuje jako zamknięta forma, a całość pozostaje czytelna i spójna. Delikatnie nieregularne krawędzie liści i płatków przywołują skojarzenia z tradycyjnymi technikami druku, co wprowadza subtelny charakter vintage i wizualną miękkość. Mural w bukiety dobrze współgra z aranżacjami angielskimi oraz modern classic, szczególnie w zestawieniu z wyrazistymi meblami i dopracowaną sztukaterią. Mural Rebel Walls R21682 Botanical Gardens Meadow Bouquet Black organizuje powierzchnię i nadaje jej dekoracyjny, uporządkowany rytm.

O kolekcji Rebel Walls Botanical Gardens

Kolekcja Rebel Walls Botanical Gardens to stylowy ukłon w stronę klasycznej botaniki i rzemieślniczej precyzji. Przenosi ona piękno dzikiej natury oraz strukturę uporządkowanych ogrodów na ściany. Te dopracowane motywy roślinne tworzą we wnętrzu atmosferę spokoju, elegancji oraz ponadczasowego szyku.


Zobacz wszystkie murale Rebel Walls

Producent: Rebel Walls
7dfe44ae4b079cd81b5cc7c4732b7947
Rebel Walls R21682 Botanical Gardens Meadow Bouquet Black - mural w bukiety

Inne produkty z tej kategorii