flip.js
4.6 kB · javascript · 125 lines
1const node = document.getElementById('siblings');2const siblings = node ? JSON.parse(node.textContent) : {};3const keys = Object.keys(siblings);45const style = (list, want) => list.find((image) => image.style === want) ?? list[0];67const size = (url, width) => `${url}${url.includes('?') ? '&' : '?'}width=${width}&format=auto`;89const money = (amount) => `$${Number(amount).toFixed(2)}`;1011function cdn(key, name) {12 return `/cdn/printful/${key}/${name}.png`;13}1415function swap(key) {16 const one = siblings[key];17 if (!one) return;18 const was = document.querySelector('[data-add]');19 const old = was ? was.dataset.key : '';20 for (const image of document.querySelectorAll('[data-mockups] img[data-style]')) {21 const found = style(one.images, image.dataset.style);22 if (!found) continue;23 image.src = size(found.url, 1000);24 image.alt = found.alt;25 const link = image.closest('a');26 if (link) link.href = size(found.url, 2000);27 }28 for (const image of document.querySelectorAll('[data-strip] img[data-tile]')) {29 image.src = cdn(key, `tile-${image.dataset.tile}`);30 image.alt = `${key} ${image.dataset.tile}x${image.dataset.tile}`;31 const link = image.closest('a');32 if (link) link.href = image.src;33 }34 const downloads = document.querySelector('[data-downloads]');35 if (downloads) {36 const list = [...one.files.map((name) => [name, cdn(key, name)])];37 for (const tile of document.querySelectorAll('[data-strip] a')) list.push([`${tile.dataset.tile}x${tile.dataset.tile}`, tile.href]);38 list.push(['og', cdn(key, 'og')]);39 downloads.replaceChildren(Object.assign(document.createElement('span'), { textContent: 'Download ' }));40 for (const [name, href] of list) {41 const link = document.createElement('a');42 link.href = href;43 link.download = '';44 link.textContent = name;45 downloads.append(link);46 }47 }48 const buttons = [...document.querySelectorAll('.sizes button')];49 buttons.forEach((button, i) => {50 const variant = one.variants[i];51 if (!variant) return;52 button.dataset.variant = variant.id;53 button.dataset.price = variant.price;54 button.dataset.size = variant.size;55 });56 const add = document.querySelector('[data-add]');57 if (add) {58 add.dataset.key = key;59 add.dataset.variant = one.variants[0] ? one.variants[0].id : '';60 add.dataset.price = one.price;61 add.dataset.size = one.variants[0] ? one.variants[0].size : '';62 add.disabled = !one.available;63 }64 const price = document.querySelector('[data-price]');65 if (price) price.textContent = money(one.price);66 const count = document.querySelector('[data-expiry]');67 if (count) count.dataset.expiry = String(new Date(one.created).getTime() + Number(count.dataset.live || 0));68 const at = keys.indexOf(key);69 const index = document.querySelector('[data-index]');70 if (index) index.textContent = `${at + 1} / ${keys.length}`;71 for (const tile of document.querySelectorAll('[data-siblings] a[data-key]')) {72 if (tile.dataset.key === key) tile.setAttribute('aria-current', 'page');73 else tile.removeAttribute('aria-current');74 }75 const fine = document.querySelector('.lede .fine');76 if (fine) fine.textContent = `Design ${key}`;77 if (old !== key) history.pushState({ key }, '', `/products/${key}/`);78 window.dispatchEvent(new CustomEvent('flip', { detail: key }));79}8081function step(by) {82 const add = document.querySelector('[data-add]');83 const at = keys.indexOf(add ? add.dataset.key : '');84 if (at < 0 || keys.length < 2) return;85 swap(keys[(at + by + keys.length) % keys.length]);86}8788async function share() {89 const url = location.href;90 const title = document.title;91 if (navigator.share) {92 try {93 await navigator.share({ title, url });94 return;95 } catch {}96 }97 try {98 await navigator.clipboard.writeText(url);99 } catch {}100 const button = document.querySelector('[data-share]');101 if (!button) return;102 button.textContent = 'Copied!';103 setTimeout(() => {104 button.textContent = 'Share';105 }, 1500);106}107108if (keys.length) {109 document.addEventListener('click', (event) => {110 const target = event.target instanceof Element ? event.target : null;111 if (!target) return;112 if (target.closest('[data-share]')) return void share();113 if (target.closest('[data-prev]')) return step(-1);114 if (target.closest('[data-next]')) return step(1);115 const tile = target.closest('[data-siblings] a[data-key]');116 if (tile) {117 event.preventDefault();118 swap(tile.dataset.key);119 }120 });121 window.addEventListener('popstate', () => {122 const key = location.pathname.split('/').filter(Boolean).pop();123 if (siblings[key]) swap(key);124 });125}