[wip] Rework Shop
- Change strategy for content fetching - Add a route per page instead of using __layout for all - Change the behaviors of the posters section (add a carousel on small screens) - Change Poster buttons styling and make interactions only for desktop
This commit is contained in:
@@ -1,215 +1,47 @@
|
||||
<script lang="ts">
|
||||
import { browser } from '$app/env'
|
||||
import { onMount } from 'svelte'
|
||||
import { shopLocations, cartOpen, cartNotifications } from '$utils/stores/shop'
|
||||
import anime from 'animejs'
|
||||
import type { AnimeTimelineInstance } from 'animejs'
|
||||
import { setContext } from 'svelte'
|
||||
import { cartNotifications } from '$utils/stores/shop'
|
||||
// Components
|
||||
import Metas from '$components/Metas.svelte'
|
||||
import PageTransition from '$components/PageTransition.svelte'
|
||||
import SiteTitle from '$components/atoms/SiteTitle.svelte'
|
||||
import Image from '$components/atoms/Image.svelte'
|
||||
import ButtonCart from '$components/atoms/ButtonCart.svelte'
|
||||
import Poster from '$components/molecules/Poster.svelte'
|
||||
import NotificationCart from '$components/molecules/NotificationCart.svelte'
|
||||
import EmailForm from '$components/molecules/EmailForm.svelte'
|
||||
import ShopLocationSwitcher from '$components/molecules/ShopLocationSwitcher.svelte'
|
||||
import Cart from '$components/organisms/Cart.svelte'
|
||||
import NotificationCart from '$components/molecules/NotificationCart.svelte'
|
||||
|
||||
export let shop: any
|
||||
export let locations: any
|
||||
export let posters: any
|
||||
export let product: any
|
||||
export let shopProducts: any
|
||||
|
||||
let introEl: HTMLElement
|
||||
let navObserver: IntersectionObserver
|
||||
let scrolledPastIntro = false
|
||||
|
||||
|
||||
// Locations with an existing poster product
|
||||
$shopLocations = locations.filter(({ slug }: any) => {
|
||||
const shopLocations = locations.filter(({ slug }: any) => {
|
||||
if (posters.find((poster: any) => poster.location.slug === slug)) {
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* Transition: Anime timeline
|
||||
*/
|
||||
let timeline: AnimeTimelineInstance
|
||||
|
||||
if (browser) {
|
||||
requestAnimationFrame(() => {
|
||||
timeline = anime.timeline({
|
||||
duration: 1600,
|
||||
easing: 'easeOutQuart',
|
||||
autoplay: false,
|
||||
})
|
||||
|
||||
// Hero image
|
||||
timeline.add({
|
||||
targets: '.shop-page__background',
|
||||
scale: [1.06, 1],
|
||||
opacity: [0, 1],
|
||||
duration: 2400,
|
||||
}, 400)
|
||||
|
||||
// Intro top elements
|
||||
timeline.add({
|
||||
targets: '.shop-page__intro .top > *',
|
||||
translateY: [-100, 0],
|
||||
delay: anime.stagger(250),
|
||||
}, 400)
|
||||
|
||||
// Intro navbar
|
||||
timeline.add({
|
||||
targets: '.shop-page__nav .container > *',
|
||||
translateY: [100, 0],
|
||||
delay: anime.stagger(250),
|
||||
}, 700)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
onMount(() => {
|
||||
// Reveal the nav past the Intro
|
||||
navObserver = new IntersectionObserver(entries => {
|
||||
entries.forEach(entry => {
|
||||
scrolledPastIntro = !entry.isIntersecting
|
||||
})
|
||||
}, {
|
||||
threshold: 0,
|
||||
rootMargin: '0px 0px 0px'
|
||||
})
|
||||
navObserver.observe(introEl)
|
||||
|
||||
// Transition in
|
||||
requestAnimationFrame(() => {
|
||||
timeline.play()
|
||||
})
|
||||
|
||||
|
||||
// Destroy
|
||||
return () => {
|
||||
navObserver && navObserver.unobserve(introEl)
|
||||
}
|
||||
setContext('shop', {
|
||||
shop,
|
||||
posters,
|
||||
shopLocations,
|
||||
shopProducts,
|
||||
})
|
||||
</script>
|
||||
|
||||
<Metas
|
||||
title="Shop – Houses Of"
|
||||
description=""
|
||||
image=""
|
||||
/>
|
||||
|
||||
|
||||
<PageTransition name="shop-page">
|
||||
<Cart />
|
||||
|
||||
<section class="shop-page__intro" bind:this={introEl}>
|
||||
<div class="top container">
|
||||
<a href="/" class="back" sveltekit:noscroll>
|
||||
<svg width="5" height="8" viewBox="0 0 5 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 1 1 4l3 3" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<span>Back to site</span>
|
||||
</a>
|
||||
|
||||
<span class="shop-title">Shop</span>
|
||||
</div>
|
||||
|
||||
<SiteTitle
|
||||
variant="inline"
|
||||
<Cart />
|
||||
<div class="cart-notifications">
|
||||
{#each $cartNotifications as { id, title, name, image } (id)}
|
||||
<NotificationCart
|
||||
title={title}
|
||||
name={name}
|
||||
image={image}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<nav class="shop-page__nav">
|
||||
<div class="container">
|
||||
<p class="text-label">Shop your city</p>
|
||||
<nav>
|
||||
<ul>
|
||||
{#each $shopLocations as { name, slug }}
|
||||
<li class:is-active={slug === product.location.slug}>
|
||||
<a href="/shop/poster-{slug}" sveltekit:prefetch sveltekit:noscroll>
|
||||
{name}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<ButtonCart />
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<Image
|
||||
class="shop-page__background"
|
||||
id={shop.page_heroimage.id}
|
||||
alt="photo"
|
||||
sizeKey="hero"
|
||||
sizes={{
|
||||
large: { width: 1800, height: 1200 },
|
||||
medium: { width: 1200, height: 800 },
|
||||
small: { width: 700, height: 700 },
|
||||
}}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<nav class="shop-location"
|
||||
class:is-visible={scrolledPastIntro}
|
||||
class:is-overlaid={$cartOpen}
|
||||
>
|
||||
<ShopLocationSwitcher />
|
||||
<ButtonCart />
|
||||
|
||||
<div class="shop-location__notifications">
|
||||
{#each $cartNotifications as { id, title, name, image } (id)}
|
||||
<NotificationCart
|
||||
title={title}
|
||||
name={name}
|
||||
image={image}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section class="shop-page__about grid">
|
||||
<div class="description grid text-normal">
|
||||
<div class="description__inner">
|
||||
{@html shop.about}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{#key product}
|
||||
<slot />
|
||||
{/key}
|
||||
|
||||
{#if posters}
|
||||
<section class="shop-page__posters grid">
|
||||
<h3>View all of our available posters</h3>
|
||||
<div class="set">
|
||||
{#each posters as { location, photos_product }}
|
||||
<Poster
|
||||
location={location}
|
||||
image={photos_product.length && photos_product[1].directus_files_id}
|
||||
product={shopProducts.find(item => item.slug.includes(location.slug))}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="subscribe">
|
||||
<label class="subscribe__text" for="SUB_EMAIL">Subscribe to be notified when new posters become available</label>
|
||||
<EmailForm />
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
</PageTransition>
|
||||
<slot />
|
||||
|
||||
|
||||
<script context="module" lang="ts">
|
||||
import { fetchAPI } from '$utils/api'
|
||||
import { getRandomElement } from '$utils/functions'
|
||||
|
||||
export async function load ({ page, fetch, session, stuff }) {
|
||||
// Get content from API
|
||||
@@ -259,24 +91,13 @@
|
||||
}
|
||||
`)
|
||||
|
||||
const { data } = res
|
||||
|
||||
|
||||
/**
|
||||
* Define product
|
||||
*/
|
||||
const productAPI = (!page.params.type && !page.params.name)
|
||||
// Get a random product
|
||||
? getRandomElement(data.posters)
|
||||
// Get the current product from slug
|
||||
: data.posters.find(({ location }: any) => location.slug === page.params.name)
|
||||
const { data: { shop, location, posters } } = res
|
||||
|
||||
|
||||
/**
|
||||
* Get products data from Swell
|
||||
*/
|
||||
let shopProducts: any
|
||||
let shopProduct: any
|
||||
|
||||
const shopProductRes = await fetch('/api/swell', {
|
||||
method: 'POST',
|
||||
@@ -291,27 +112,20 @@
|
||||
if (results) {
|
||||
shopProducts = results
|
||||
}
|
||||
|
||||
// Define current product
|
||||
const product = results.find((result: any) => result.slug.includes(productAPI.location.slug))
|
||||
if (product) {
|
||||
shopProduct = product
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
props: {
|
||||
shop: data.shop,
|
||||
locations: data.location,
|
||||
posters: data.posters,
|
||||
product: productAPI,
|
||||
shop,
|
||||
locations: location,
|
||||
posters,
|
||||
shopProducts,
|
||||
shopProduct,
|
||||
},
|
||||
stuff: {
|
||||
product: productAPI,
|
||||
shopProduct,
|
||||
shop,
|
||||
posters,
|
||||
shopProducts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user