✨ Finish Photo viewer styling with responsive
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
export let type: string = undefined
|
||||
export let form: string = undefined
|
||||
export let clone: boolean = false
|
||||
export let disabled: boolean = undefined
|
||||
|
||||
const className = 'button-circle'
|
||||
const classes = [
|
||||
@@ -13,7 +14,7 @@
|
||||
].join(' ').trim()
|
||||
</script>
|
||||
|
||||
<button {type} {form} class={classes} on:click>
|
||||
<button {type} {form} class={classes} on:click disabled={disabled}>
|
||||
{#if clone}
|
||||
{#each Array(2) as _}
|
||||
<span class="clone">
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
export let flip: boolean = false
|
||||
</script>
|
||||
|
||||
<svg class="arrow arrow--{color}" class:arrow--flip={flip} width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.67961 11.8224C4.34487 12.1571 4.34487 12.6999 4.67961 13.0346C5.01434 13.3693 5.55705 13.3693 5.89179 13.0346L11.3204 7.60602C11.6551 7.27129 11.6551 6.72857 11.3204 6.39384L5.89179 0.965267C5.55705 0.630532 5.01434 0.630532 4.67961 0.965267C4.34487 1.3 4.34487 1.84271 4.67961 2.17745L8.64494 6.14279L1.2857 6.14279C0.812311 6.14279 0.428555 6.52654 0.428555 6.99993C0.428555 7.47332 0.812311 7.85707 1.2857 7.85707L8.64494 7.85707L4.67961 11.8224Z" fill="#000"/>
|
||||
<svg class="arrow arrow--{color}" class:arrow--flip={flip} width="12" height="14" viewBox="0 0 12 14" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.67961 11.8224C4.34487 12.1571 4.34487 12.6999 4.67961 13.0346C5.01434 13.3693 5.55705 13.3693 5.89179 13.0346L11.3204 7.60602C11.6551 7.27129 11.6551 6.72857 11.3204 6.39384L5.89179 0.965267C5.55705 0.630532 5.01434 0.630532 4.67961 0.965267C4.34487 1.3 4.34487 1.84271 4.67961 2.17745L8.64494 6.14279L1.2857 6.14279C0.812311 6.14279 0.428555 6.52654 0.428555 6.99993C0.428555 7.47332 0.812311 7.85707 1.2857 7.85707L8.64494 7.85707L4.67961 11.8224Z"/>
|
||||
</svg>
|
||||
@@ -2,53 +2,96 @@
|
||||
import { page } from '$app/stores'
|
||||
import dayjs from 'dayjs'
|
||||
import advancedFormat from 'dayjs/plugin/advancedFormat.js'
|
||||
import { getAssetUrlKey } from '$utils/helpers'
|
||||
// Components
|
||||
import Metas from '$components/Metas.svelte'
|
||||
import Image from '$components/atoms/Image.svelte'
|
||||
import IconArrow from '$components/atoms/IconArrow.svelte'
|
||||
import ButtonCircle from '$components/atoms/ButtonCircle.svelte'
|
||||
import IconArrow from '$components/atoms/IconArrow.svelte'
|
||||
import ButtonCircle from '$components/atoms/ButtonCircle.svelte'
|
||||
|
||||
export let photos: any[]
|
||||
export let location: any
|
||||
export let currentIndex: number
|
||||
export let totalPhotos: number
|
||||
|
||||
dayjs.extend(advancedFormat)
|
||||
|
||||
// let currentIndex: number = 0
|
||||
|
||||
// Find current photo from the slug
|
||||
$: currentPhoto = photos.find((photo: any) => photo.slug === $page.params.photo)
|
||||
$: currentPhotoIndex = photos.findIndex((photo: any) => photo.slug === $page.params.photo)
|
||||
// Define reactive last and first photos
|
||||
$: isLast = currentIndex === totalPhotos - 1
|
||||
$: isFirst = currentIndex === 0
|
||||
// Define current photo
|
||||
// $: currentPhoto = photos.find((photo: any) => photo.slug === $page.params.photo)
|
||||
$: currentPhoto = photos[photos.findIndex((photo: any) => photo.slug === $page.params.photo)]
|
||||
// Reactive photos showns
|
||||
$: shownPhotos = [
|
||||
...photos.filter((photo: any, index: number) => {
|
||||
// Grab the 4 prev and next photos depending the index
|
||||
console.log(index)
|
||||
// console.log(index >= currentIndex - 4 && index < currentIndex + 4)
|
||||
})
|
||||
]
|
||||
|
||||
|
||||
/**
|
||||
* Go to next photo
|
||||
* Photo navigation
|
||||
*/
|
||||
// Go to next photo
|
||||
const goToNext = () => {
|
||||
if (!isLast) {
|
||||
currentIndex++
|
||||
}
|
||||
|
||||
// TODO: Fetch new photos
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to previous photo
|
||||
*/
|
||||
// Fo to previous photo
|
||||
const goToPrevious = () => {
|
||||
if (!isFirst) {
|
||||
currentIndex--
|
||||
}
|
||||
|
||||
// TODO: Fetch new photos
|
||||
}
|
||||
|
||||
// Manage navigation with keyboard
|
||||
const handleKeydown = ({ key, defaultPrevented }: KeyboardEvent) => {
|
||||
if (defaultPrevented) return
|
||||
switch (key) {
|
||||
case 'ArrowLeft': goToNext(); break;
|
||||
case 'ArrowRight': goToPrevious(); break;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load photos
|
||||
*/
|
||||
const loadPhotos = (index: number) => {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={handleKeydown} />
|
||||
|
||||
<main class="viewer-photo grid">
|
||||
{#if currentPhoto}
|
||||
<Metas
|
||||
title="{currentPhoto.title} - Houses Of {location.name}"
|
||||
description=""
|
||||
image={getAssetUrlKey(currentPhoto.image.id, 'share')}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
|
||||
<main class="viewer-photo">
|
||||
<div class="container grid">
|
||||
<div class="viewer-photo__carousel">
|
||||
<div class="viewer-photo__images">
|
||||
{#each photos as photo}
|
||||
<Image
|
||||
class="photo"
|
||||
id={currentPhoto.image.id}
|
||||
alt={currentPhoto.title}
|
||||
class="photo {photo.id === currentPhoto.id ? 'is-active' : ''}"
|
||||
id={photo.image.id}
|
||||
alt={photo.title}
|
||||
sizeKey="photo-list"
|
||||
sizes={{
|
||||
small: { width: 500 },
|
||||
@@ -57,25 +100,28 @@ import ButtonCircle from '$components/atoms/ButtonCircle.svelte'
|
||||
}}
|
||||
ratio={1.5}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<div class="viewer-photo__controls">
|
||||
<ButtonCircle on:click={goToPrevious}>
|
||||
<ButtonCircle on:click={goToNext} disabled={isLast}>
|
||||
<IconArrow color="pink" flip={true} />
|
||||
</ButtonCircle>
|
||||
<ButtonCircle on:click={goToPrevious}>
|
||||
<ButtonCircle on:click={goToPrevious} disabled={isFirst}>
|
||||
<IconArrow color="pink" />
|
||||
</ButtonCircle>
|
||||
</div>
|
||||
|
||||
<p class="viewer-photo__index title-index">{currentIndex + 1}</p>
|
||||
<span class="viewer-photo__index title-index">
|
||||
{currentIndex + 1}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="viewer-photo__info">
|
||||
<h1 class="title-medium">{currentPhoto.title}</h1>
|
||||
|
||||
<div class="detail text-date">
|
||||
<img src="/images/icons/map-pin.svg" alt=""><span>{location.name}, {location.country.name}</span> <span class="sep">·</span> <time datetime={dayjs(currentPhoto.date_taken).format('YYYY-MM-DD')}>{dayjs(currentPhoto.date_taken).format('MMMM, Do YYYY')}</time>
|
||||
<img src="/images/icons/map-pin.svg" width={16} height={18} alt="Map icon"><span>{location.name}, {location.country.name}</span> <span class="sep">·</span> <time datetime={dayjs(currentPhoto.date_taken).format('YYYY-MM-DD')}>{dayjs(currentPhoto.date_taken).format('MMMM, Do YYYY')}</time>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,8 +137,9 @@ import ButtonCircle from '$components/atoms/ButtonCircle.svelte'
|
||||
photos: photo (
|
||||
filter: { location: { slug: { _eq: "${page.params.location}" }}},
|
||||
sort: "-date_created",
|
||||
limit: 9,
|
||||
limit: 5,
|
||||
) {
|
||||
id
|
||||
title
|
||||
slug
|
||||
date_taken
|
||||
@@ -111,15 +158,19 @@ import ButtonCircle from '$components/atoms/ButtonCircle.svelte'
|
||||
`)
|
||||
|
||||
const { data } = res
|
||||
const location = stuff.locations.find((location: any) => location.slug === page.params.location)
|
||||
const totalPhotos = stuff.countTotalPhotosByLocation.find((total: any) => total.group.location === Number(location.id)).count.id
|
||||
|
||||
// Find photo's index
|
||||
const currentIndex = data.photos.findIndex((photo: any) => photo.slug === page.params.photo)
|
||||
const currentPhotoIndex = data.photos.findIndex((photo: any) => photo.slug === page.params.photo)
|
||||
const currentIndex = (totalPhotos - 1) - currentPhotoIndex
|
||||
|
||||
return {
|
||||
props: {
|
||||
photos: data.photos,
|
||||
location: data.location[0],
|
||||
currentIndex,
|
||||
totalPhotos,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
.arrow {
|
||||
display: block;
|
||||
|
||||
// Colors
|
||||
&--white {
|
||||
path {
|
||||
fill: #fff;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&--pink {
|
||||
path {
|
||||
fill: $color-secondary;
|
||||
}
|
||||
color: $color-secondary;
|
||||
}
|
||||
|
||||
// Variants
|
||||
&--flip {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
object-fit: contain;
|
||||
transition: opacity 0.4s var(--ease-quart), filter 0.4s var(--ease-quart);
|
||||
}
|
||||
|
||||
// Clones
|
||||
@@ -35,6 +36,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** States
|
||||
*/
|
||||
// Hover
|
||||
&:hover {
|
||||
.clone {
|
||||
@@ -49,6 +54,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Disabled
|
||||
&[disabled] {
|
||||
background: $color-primary;
|
||||
border: 3px solid #ffffff20;
|
||||
|
||||
svg {
|
||||
fill: $color-primary-tertiary20;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Variants
|
||||
|
||||
@@ -1,39 +1,142 @@
|
||||
.viewer-photo {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.container {
|
||||
height: 100%;
|
||||
|
||||
@include bp (md, max) {
|
||||
display: block;
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
// Carousel
|
||||
&__carousel {
|
||||
display: grid;
|
||||
grid-column: span var(--columns);
|
||||
grid-row-gap: 20px;
|
||||
margin: auto 8px;
|
||||
max-width: 720px;
|
||||
margin: auto 0;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
@include bp (md) {
|
||||
max-width: none;
|
||||
margin: auto 0;
|
||||
grid-column: 3 / span 16;
|
||||
grid-row-gap: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
// Images
|
||||
&__images {
|
||||
position: relative;
|
||||
margin: auto 0 0;
|
||||
width: 100%;
|
||||
margin: auto auto 0;
|
||||
padding-top: 66.66%;
|
||||
|
||||
.photo {
|
||||
position: relative;
|
||||
--opacity: 1;
|
||||
--scale: 1.0;
|
||||
--rotate: 0deg;
|
||||
--offset-x: -50%;
|
||||
--offset-y: -50%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 3;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
transform: translate3d(var(--offset-x), var(--offset-y), 0) scale(var(--scale)) rotate(var(--rotate));
|
||||
transform-origin: top center;
|
||||
cursor: default;
|
||||
will-change: transform;
|
||||
transition: opacity 1s var(--ease-quart), transform 1s var(--ease-quart);
|
||||
|
||||
@include bp (md) {
|
||||
--offset-x: 0%;
|
||||
--offset-y: -50%;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
transform-origin: bottom right;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
opacity: var(--opacity);
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
--scale: 0.9;
|
||||
--opacity: 0.75;
|
||||
--offset-y: -34%;
|
||||
z-index: 7;
|
||||
|
||||
@include bp (md) {
|
||||
--scale: 0.9;
|
||||
--rotate: 1deg;
|
||||
--offset-x: 4.5%;
|
||||
--offset-y: -54%;
|
||||
}
|
||||
}
|
||||
&:nth-child(3) {
|
||||
--scale: 0.83;
|
||||
--opacity: 0.55;
|
||||
--offset-y: -22.5%;
|
||||
z-index: 6;
|
||||
|
||||
@include bp (md) {
|
||||
--scale: 0.83;
|
||||
--offset-x: 7%;
|
||||
--offset-y: -56.5%;
|
||||
}
|
||||
}
|
||||
&:nth-child(4) {
|
||||
--scale: 0.75;
|
||||
--opacity: 0.45;
|
||||
--offset-y: -11%;
|
||||
z-index: 5;
|
||||
|
||||
@include bp (md) {
|
||||
--scale: 0.75;
|
||||
--rotate: 3deg;
|
||||
--offset-x: 9%;
|
||||
--offset-y: -59%;
|
||||
}
|
||||
}
|
||||
&:nth-child(5) {
|
||||
--scale: 0.68;
|
||||
--opacity: 0.3;
|
||||
--offset-y: -1.5%;
|
||||
z-index: 4;
|
||||
|
||||
@include bp (md) {
|
||||
--scale: 0.68;
|
||||
--rotate: 4deg;
|
||||
--offset-x: 10.5%;
|
||||
--offset-y: -61.5%;
|
||||
}
|
||||
}
|
||||
|
||||
// Active state
|
||||
&.is-active {
|
||||
z-index: 8;
|
||||
box-shadow:
|
||||
0 12px 12px rgba(#000, 0.15),
|
||||
0 20px 20px rgba(#000, 0.15),
|
||||
0 48px 48px rgba(#000, 0.15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Infos
|
||||
&__info {
|
||||
@@ -43,7 +146,7 @@
|
||||
text-align: center;
|
||||
|
||||
@include bp (md) {
|
||||
margin-top: 26px;
|
||||
margin-top: 0;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
}
|
||||
@@ -55,8 +158,12 @@
|
||||
|
||||
h1 {
|
||||
color: $color-secondary;
|
||||
font-size: rem(32px);
|
||||
font-size: clamp(#{rem(20px)}, 6.5vw, #{rem(28px)});
|
||||
line-height: 1.1;
|
||||
|
||||
@include bp (md) {
|
||||
font-size: rem(32px);
|
||||
}
|
||||
}
|
||||
|
||||
// Details
|
||||
@@ -67,6 +174,9 @@
|
||||
color: $color-tertiary;
|
||||
line-height: 1.5;
|
||||
|
||||
@include bp (md) {
|
||||
margin-top: 16px;
|
||||
}
|
||||
@include bp (lg) {
|
||||
margin-top: 0;
|
||||
margin-left: auto;
|
||||
@@ -95,16 +205,17 @@
|
||||
z-index: 1;
|
||||
left: 50%;
|
||||
bottom: calc(92% + 1vw);
|
||||
display: block;
|
||||
line-height: 1;
|
||||
transform: translate3d(-50%, 0, 0);
|
||||
color: rgba($color-tertiary, 0.4);
|
||||
transform: translate3d(-50%, 0, 0);
|
||||
|
||||
@include bp (md, max) {
|
||||
font-size: clamp(#{rem(80px)}, 24vw, #{rem(120px)});
|
||||
}
|
||||
@include bp (md) {
|
||||
top: 50%;
|
||||
left: 95%;
|
||||
left: min(68vw, 1296px);
|
||||
width: 350px;
|
||||
text-align: center;
|
||||
bottom: auto;
|
||||
@@ -125,17 +236,11 @@
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
justify-content: space-between;
|
||||
}
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// Image
|
||||
.photo {
|
||||
grid-column: 3 / span 16;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
button {
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user