Make Shop location switcher a component using a global store

Using two switchers (one in the shop nav and the other in the cart) makes possible to have the switcher over the Cart overlay (over intro)
This commit is contained in:
2021-11-07 22:40:52 +01:00
parent 3198fd8545
commit 5923afed3b
7 changed files with 140 additions and 84 deletions

View File

@@ -0,0 +1,33 @@
<script lang="ts">
import { page } from '$app/stores'
import { shopLocations } from '$utils/stores/shop'
export let isOver: boolean = false
const classes = [
'shop-locationswitcher',
isOver && 'is-over',
$$props.class
].join(' ').trim()
// Quick location change
const quickLocationChange = ({ target: { value }}: any) => {
const newPath = $page.path.split('-')[0] + `-${value}`
// goto(newPath, { replaceState: true, noscroll: true, keepfocus: true })
}
</script>
<dl class={classes}>
<dt class="text-label">Shop your city</dt>
<dd>
<svg width="14" height="17" viewBox="0 0 14 17" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.1 11.18a.52.52 0 0 1-.92 0L3.55 6.05a3.46 3.46 0 1 1 6.19 0L7.1 11.18Zm1.2-6.62c0-.91-.73-1.63-1.65-1.63a1.63 1.63 0 1 0 0 3.26c.92 0 1.65-.72 1.65-1.63Zm-2.88 8.18.03.05-.08 1.1 2.98.95v-3.48l.96-1.8v5.28l2.98-.95-.41-5.65-2.31.84.62-1.14 1.91-.7c.3-.09.52.16.55.46l.61 6c.04.42-.21.8-.61.93l-3.82 1.21-3.96-1.21-4.25 1.21a.54.54 0 0 1-.45-.08.4.4 0 0 1-.17-.38l.95-6.53c.04-.16.15-.3.32-.35l1.78-.57.4.77-1.59.51-.82 5.63 3.4-.97.2-2.76c.47.95.78 1.63.78 1.63Z"/>
</svg>
<select on:change={quickLocationChange}>
{#each $shopLocations as { name, slug }}
<option value={slug} selected={slug === $page.params.name}>{name}</option>
{/each}
</select>
</dd>
</dl>

View File

@@ -6,8 +6,7 @@
// Components
import Button from '$components/atoms/Button.svelte'
import CartItem from '$components/molecules/CartItem.svelte'
let open = false
import ShopLocationSwitcher from '$components/molecules/ShopLocationSwitcher.svelte'
onMount(async () => {
@@ -99,6 +98,13 @@
</script>
{#if $cartOpen}
<div class="cart-switcher"
in:fly={{ y: -24, duration: 1000, easing: quartOut }}
out:fly={{ y: -24, duration: 1000, easing: quartOut }}
>
<ShopLocationSwitcher isOver={true} />
</div>
<aside class="cart shadow-box-dark"
class:is-updating={$cartIsUpdating}
transition:fly={{ x: 48, duration: 600, easing: quartOut }}

View File

@@ -3,13 +3,14 @@
import { shopLocations, cartOpen, cartNotifications } from '$utils/stores/shop'
// Components
import Metas from '$components/Metas.svelte'
import PosterLayout from '$components/layouts/PosterLayout.svelte'
import SiteTitle from '$components/atoms/SiteTitle.svelte'
import Image from '$components/atoms/Image.svelte'
import ButtonCart from '$components/atoms/ButtonCart.svelte'
import PosterLayout from '$components/layouts/PosterLayout.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'
export let shop: any
@@ -18,15 +19,23 @@
export let product: any
export let productShop: any
let navEl: HTMLElement, introEl: HTMLElement
let introEl: HTMLElement
let navObserver: IntersectionObserver
let scrolledPastIntro = false
// Locations with an existing poster product
$shopLocations = locations.filter(({ slug }: any) => {
if (posters.find((poster: any) => poster.location.slug === slug)) {
return true
}
})
onMount(async () => {
// Reveal the nav past the Intro
navObserver = new IntersectionObserver(entries => {
entries.forEach(entry => {
navEl.classList.toggle('is-visible', !entry.isIntersecting)
scrolledPastIntro = !entry.isIntersecting
})
}, {
threshold: 0,
@@ -48,6 +57,7 @@
image=""
/>
{#key product}
<main class="shop-page">
<Cart />
@@ -66,12 +76,10 @@
<p class="text-label">Shop your city</p>
<nav>
<ul>
{#each locations as { name, slug }}
{#if posters.find(poster => poster.location.slug === slug )}
<li class:is-active={slug === product.location.slug}>
<a href="/shop/poster-{slug}">{name}</a>
</li>
{/if}
{#each $shopLocations as { name, slug }}
<li class:is-active={slug === product.location.slug}>
<a href="/shop/poster-{slug}">{name}</a>
</li>
{/each}
</ul>
</nav>
@@ -92,17 +100,11 @@
/>
</section>
<nav class="shop-location" bind:this={navEl}>
<dl class="shop-location__left">
<dt class="text-label">shop your city</dt>
<dd>
<img src="/images/icons/pin.svg" alt="">
<select name="" id="">
<option value="melbourne">Melbourne</option>
</select>
</dd>
</dl>
<nav class="shop-location"
class:is-visible={scrolledPastIntro}
class:is-overlaid={$cartOpen}
>
<ShopLocationSwitcher />
<ButtonCart />
<div class="shop-location__notifications">
@@ -141,6 +143,7 @@
</div>
</section>
</main>
{/key}
<slot />

View File

@@ -0,0 +1,43 @@
.shop-locationswitcher {
dt {
color: $color-primary;
font-weight: 400;
line-height: 1;
font-size: rem(12px);
}
dd {
color: $color-secondary;
svg {
display: inline-block;
width: 14px;
height: 14px;
margin-right: 4px;
@include bp (sm) {
width: 18px;
height: 18px;
}
}
select {
background: none;
border: none;
font-size: rem(18px);
color: $color-secondary;
font-family: $font-serif;
cursor: pointer;
appearance: none;
@include bp (sm) {
font-size: rem(24px);
}
}
}
// Overlaid
&.is-over {
dt {
color: #fff;
}
}
}

View File

@@ -15,8 +15,8 @@
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 8px;
margin-bottom: 24px;
padding-bottom: 8px;
border-bottom: 1px solid #E1D0C0;
@include bp (sm) {
@@ -59,7 +59,6 @@
// Total
&__total {
margin-top: auto;
margin: 24px 0;
color: $color-gray;
@include bp (md) {
@@ -189,6 +188,20 @@
}
}
// Location switcher
&-switcher {
position: fixed;
z-index: 100;
top: 20px;
left: 20px;
will-change: transform, opacity;
@include bp (sm) {
top: 32px;
left: 32px;
}
}
// Overlay
&-overlay {
position: fixed;

View File

@@ -21,73 +21,20 @@
// Nav
.shop-location {
--inset: 20px;
display: flex;
position: fixed;
z-index: 100;
top: 18px;
left: 20px;
right: 20px;
z-index: 20;
top: var(--inset);
left: var(--inset);
right: var(--inset);
justify-content: space-between;
transform: translate3d(0, -96px, 0);
transform: translate3d(0, -88px, 0);
transition: transform 1s var(--ease-quart);
transition-delay: 100ms;
@include bp (sm) {
top: 32px;
left: 32px;
right: 32px;
}
// Visible state
&.is-visible {
transform: translate3d(0,0,0);
.shop-location__notifications {
opacity: 1;
pointer-events: auto;
}
}
// Left
&__left {
dt {
color: $color-primary;
font-weight: 400;
line-height: 1;
font-size: rem(12px);
color: #fff;
}
dd {
img {
display: none;
@include bp (sm) {
display: inline-block;
width: 14px;
height: 14px;
margin-right: 8px;
}
}
select {
background: none;
border: none;
font-size: rem(18px);
color: $color-secondary;
font-family: $font-serif;
appearance: none;
line-height: 1;
@include bp (sm) {
font-size: rem(24px);
}
option {
font-size: rem(16px);
}
}
}
--inset: 32px;
}
// Notifications
@@ -106,6 +53,16 @@
}
}
}
// Visible state
&.is-visible {
transform: translate3d(0,0,0);
.shop-location__notifications {
opacity: 1;
pointer-events: auto;
}
}
}
// Intro

View File

@@ -55,6 +55,7 @@
@import "molecules/poster";
@import "molecules/notification-cart";
@import "molecules/cart-item";
@import "molecules/shop-locationswitcher";
// Organisms
@import "organisms/locations";