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}