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:
33
src/components/molecules/ShopLocationSwitcher.svelte
Normal file
33
src/components/molecules/ShopLocationSwitcher.svelte
Normal 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>
|
||||||
@@ -6,8 +6,7 @@
|
|||||||
// Components
|
// Components
|
||||||
import Button from '$components/atoms/Button.svelte'
|
import Button from '$components/atoms/Button.svelte'
|
||||||
import CartItem from '$components/molecules/CartItem.svelte'
|
import CartItem from '$components/molecules/CartItem.svelte'
|
||||||
|
import ShopLocationSwitcher from '$components/molecules/ShopLocationSwitcher.svelte'
|
||||||
let open = false
|
|
||||||
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
@@ -99,6 +98,13 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $cartOpen}
|
{#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"
|
<aside class="cart shadow-box-dark"
|
||||||
class:is-updating={$cartIsUpdating}
|
class:is-updating={$cartIsUpdating}
|
||||||
transition:fly={{ x: 48, duration: 600, easing: quartOut }}
|
transition:fly={{ x: 48, duration: 600, easing: quartOut }}
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
import { shopLocations, cartOpen, cartNotifications } from '$utils/stores/shop'
|
import { shopLocations, cartOpen, cartNotifications } from '$utils/stores/shop'
|
||||||
// Components
|
// Components
|
||||||
import Metas from '$components/Metas.svelte'
|
import Metas from '$components/Metas.svelte'
|
||||||
|
import PosterLayout from '$components/layouts/PosterLayout.svelte'
|
||||||
import SiteTitle from '$components/atoms/SiteTitle.svelte'
|
import SiteTitle from '$components/atoms/SiteTitle.svelte'
|
||||||
import Image from '$components/atoms/Image.svelte'
|
import Image from '$components/atoms/Image.svelte'
|
||||||
import ButtonCart from '$components/atoms/ButtonCart.svelte'
|
import ButtonCart from '$components/atoms/ButtonCart.svelte'
|
||||||
import PosterLayout from '$components/layouts/PosterLayout.svelte'
|
|
||||||
import Poster from '$components/molecules/Poster.svelte'
|
import Poster from '$components/molecules/Poster.svelte'
|
||||||
import NotificationCart from '$components/molecules/NotificationCart.svelte'
|
import NotificationCart from '$components/molecules/NotificationCart.svelte'
|
||||||
import EmailForm from '$components/molecules/EmailForm.svelte'
|
import EmailForm from '$components/molecules/EmailForm.svelte'
|
||||||
|
import ShopLocationSwitcher from '$components/molecules/ShopLocationSwitcher.svelte';
|
||||||
import Cart from '$components/organisms/Cart.svelte'
|
import Cart from '$components/organisms/Cart.svelte'
|
||||||
|
|
||||||
export let shop: any
|
export let shop: any
|
||||||
@@ -18,15 +19,23 @@
|
|||||||
export let product: any
|
export let product: any
|
||||||
export let productShop: any
|
export let productShop: any
|
||||||
|
|
||||||
let navEl: HTMLElement, introEl: HTMLElement
|
let introEl: HTMLElement
|
||||||
let navObserver: IntersectionObserver
|
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 () => {
|
onMount(async () => {
|
||||||
// Reveal the nav past the Intro
|
// Reveal the nav past the Intro
|
||||||
navObserver = new IntersectionObserver(entries => {
|
navObserver = new IntersectionObserver(entries => {
|
||||||
entries.forEach(entry => {
|
entries.forEach(entry => {
|
||||||
navEl.classList.toggle('is-visible', !entry.isIntersecting)
|
scrolledPastIntro = !entry.isIntersecting
|
||||||
})
|
})
|
||||||
}, {
|
}, {
|
||||||
threshold: 0,
|
threshold: 0,
|
||||||
@@ -48,6 +57,7 @@
|
|||||||
image=""
|
image=""
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{#key product}
|
||||||
<main class="shop-page">
|
<main class="shop-page">
|
||||||
<Cart />
|
<Cart />
|
||||||
|
|
||||||
@@ -66,12 +76,10 @@
|
|||||||
<p class="text-label">Shop your city</p>
|
<p class="text-label">Shop your city</p>
|
||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
{#each locations as { name, slug }}
|
{#each $shopLocations as { name, slug }}
|
||||||
{#if posters.find(poster => poster.location.slug === slug )}
|
<li class:is-active={slug === product.location.slug}>
|
||||||
<li class:is-active={slug === product.location.slug}>
|
<a href="/shop/poster-{slug}">{name}</a>
|
||||||
<a href="/shop/poster-{slug}">{name}</a>
|
</li>
|
||||||
</li>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -92,17 +100,11 @@
|
|||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<nav class="shop-location" bind:this={navEl}>
|
<nav class="shop-location"
|
||||||
<dl class="shop-location__left">
|
class:is-visible={scrolledPastIntro}
|
||||||
<dt class="text-label">shop your city</dt>
|
class:is-overlaid={$cartOpen}
|
||||||
<dd>
|
>
|
||||||
<img src="/images/icons/pin.svg" alt="">
|
<ShopLocationSwitcher />
|
||||||
<select name="" id="">
|
|
||||||
<option value="melbourne">Melbourne</option>
|
|
||||||
</select>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
<ButtonCart />
|
<ButtonCart />
|
||||||
|
|
||||||
<div class="shop-location__notifications">
|
<div class="shop-location__notifications">
|
||||||
@@ -141,6 +143,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
{/key}
|
||||||
|
|
||||||
<slot />
|
<slot />
|
||||||
|
|
||||||
|
|||||||
43
src/style/molecules/_shop-locationswitcher.scss
Normal file
43
src/style/molecules/_shop-locationswitcher.scss
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding-bottom: 8px;
|
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
padding-bottom: 8px;
|
||||||
border-bottom: 1px solid #E1D0C0;
|
border-bottom: 1px solid #E1D0C0;
|
||||||
|
|
||||||
@include bp (sm) {
|
@include bp (sm) {
|
||||||
@@ -59,7 +59,6 @@
|
|||||||
// Total
|
// Total
|
||||||
&__total {
|
&__total {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
margin: 24px 0;
|
|
||||||
color: $color-gray;
|
color: $color-gray;
|
||||||
|
|
||||||
@include bp (md) {
|
@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
|
||||||
&-overlay {
|
&-overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|||||||
@@ -21,73 +21,20 @@
|
|||||||
|
|
||||||
// Nav
|
// Nav
|
||||||
.shop-location {
|
.shop-location {
|
||||||
|
--inset: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 100;
|
z-index: 20;
|
||||||
top: 18px;
|
top: var(--inset);
|
||||||
left: 20px;
|
left: var(--inset);
|
||||||
right: 20px;
|
right: var(--inset);
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
transform: translate3d(0, -96px, 0);
|
transform: translate3d(0, -88px, 0);
|
||||||
transition: transform 1s var(--ease-quart);
|
transition: transform 1s var(--ease-quart);
|
||||||
transition-delay: 100ms;
|
transition-delay: 100ms;
|
||||||
|
|
||||||
@include bp (sm) {
|
@include bp (sm) {
|
||||||
top: 32px;
|
--inset: 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notifications
|
// Notifications
|
||||||
@@ -106,6 +53,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Visible state
|
||||||
|
&.is-visible {
|
||||||
|
transform: translate3d(0,0,0);
|
||||||
|
.shop-location__notifications {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intro
|
// Intro
|
||||||
|
|||||||
@@ -55,6 +55,7 @@
|
|||||||
@import "molecules/poster";
|
@import "molecules/poster";
|
||||||
@import "molecules/notification-cart";
|
@import "molecules/notification-cart";
|
||||||
@import "molecules/cart-item";
|
@import "molecules/cart-item";
|
||||||
|
@import "molecules/shop-locationswitcher";
|
||||||
|
|
||||||
// Organisms
|
// Organisms
|
||||||
@import "organisms/locations";
|
@import "organisms/locations";
|
||||||
|
|||||||
Reference in New Issue
Block a user