Use a component for the pagination
Make the component and the page communicate with events to add more photos
This commit is contained in:
67
src/organisms/Pagination.svelte
Normal file
67
src/organisms/Pagination.svelte
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<script>
|
||||||
|
import { onMount, createEventDispatcher } from 'svelte'
|
||||||
|
import { currentLocation } from '../store'
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
// Props
|
||||||
|
export let photos
|
||||||
|
export let paginatedPhotos
|
||||||
|
export let photosPerPage
|
||||||
|
|
||||||
|
// Variables
|
||||||
|
let currentIndex = photosPerPage
|
||||||
|
const pagesTotal = Math.ceil(photos.length / photosPerPage)
|
||||||
|
const pagesArray = Array.from({ length: pagesTotal }, (v, k) => k + 1).reverse()
|
||||||
|
let pageTranslate = 100 - (100 / pagesTotal)
|
||||||
|
|
||||||
|
// Add more photos to the loop
|
||||||
|
const displayMorePhotos = () => {
|
||||||
|
if (currentIndex < photos.length) {
|
||||||
|
// Display the X next hidden photos
|
||||||
|
const photosToAppend = photos.filter(photo => photo.hidden === true).splice(0, photosPerPage)
|
||||||
|
photosToAppend.forEach(photo => photo.hidden = false)
|
||||||
|
|
||||||
|
// Merge new photos to show to the existing ones
|
||||||
|
dispatch('updatePagination', {
|
||||||
|
paginatedPhotos: [...paginatedPhotos, ...photosToAppend]
|
||||||
|
})
|
||||||
|
|
||||||
|
// Increment current index
|
||||||
|
currentIndex = currentIndex + photosPerPage
|
||||||
|
|
||||||
|
// Animate the pagination
|
||||||
|
pageTranslate = pageTranslate - (100 / pagesTotal)
|
||||||
|
} else {
|
||||||
|
currentIndex = photos.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section class="pagination">
|
||||||
|
{#if photos.length && currentIndex <= photos.length}
|
||||||
|
<div class="pagination__page" class:disabled={currentIndex === photos.length}
|
||||||
|
on:click={displayMorePhotos}
|
||||||
|
on:mouseenter={() => pageTranslate = pageTranslate - (100 / pagesTotal) * 0.666}
|
||||||
|
on:mouseleave={() => pageTranslate = pageTranslate + (100 / pagesTotal) * 0.666}
|
||||||
|
>
|
||||||
|
<div class="pagination__info">page</div>
|
||||||
|
<div class="pagination__numbers">
|
||||||
|
<div class="scroll" style="transform: translateY(-{pageTranslate}%);">
|
||||||
|
{#each pagesArray as page}
|
||||||
|
<span>{page}</span>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
/{pagesTotal}
|
||||||
|
</div>
|
||||||
|
<p class="pagination__caption style-caps">See more photos</p>
|
||||||
|
|
||||||
|
{:else}
|
||||||
|
{#if $currentLocation}
|
||||||
|
<div class="pagination__message">
|
||||||
|
<h3>That's all folks!</h3>
|
||||||
|
<p class="pagination__caption style-caps">Come back later to check out <br>new photos of {$currentLocation.name}</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</section>
|
||||||
@@ -1,212 +1,194 @@
|
|||||||
<script context="module">
|
<script context="module">
|
||||||
import {
|
import {
|
||||||
apiEndpoints,
|
apiEndpoints,
|
||||||
site,
|
site,
|
||||||
locations,
|
locations,
|
||||||
currentLocation,
|
currentLocation,
|
||||||
currentPhotos
|
currentPhotos
|
||||||
} from '../../../store'
|
} from '../../../store'
|
||||||
import { stores } from '@sapper/app'
|
import { stores } from '@sapper/app'
|
||||||
|
|
||||||
// Preload data
|
// Preload data
|
||||||
export async function preload (page, session) {
|
export async function preload (page, session) {
|
||||||
// Load photos
|
// Load photos
|
||||||
const req = await this.fetch(`${apiEndpoints.rest}/items/photos?fields=id,name,date,slug,image.*,location.*,location.country.*,created_on,modified_on&filter[location.slug][rlike]=%${page.params.location}%&limit=-1&sort=-created_on,name`)
|
const req = await this.fetch(`${apiEndpoints.rest}/items/photos?fields=id,name,slug,image.*,location.*,location.country.*,created_on,modified_on&filter[location.slug][rlike]=%${page.params.location}%&limit=-1&sort=-created_on,name`)
|
||||||
const photos = await req.json()
|
console.log(req.headers.host)
|
||||||
if (req.ok) {
|
const photos = await req.json()
|
||||||
return { photos: photos.data }
|
if (req.ok) {
|
||||||
}
|
return { photos: photos.data }
|
||||||
this.error(404, 'Not found')
|
}
|
||||||
}
|
this.error(404, 'Not found')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
const { page } = stores()
|
import * as fn from '../../../functions'
|
||||||
|
const { page } = stores()
|
||||||
|
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import AOS from 'aos'
|
import AOS from 'aos'
|
||||||
import 'lazysizes'
|
import 'lazysizes'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import advancedFormat from 'dayjs/plugin/advancedFormat'
|
import advancedFormat from 'dayjs/plugin/advancedFormat'
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
dayjs.extend(advancedFormat)
|
dayjs.extend(advancedFormat)
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import IconGlobe from '../../../atoms/IconGlobe'
|
import IconGlobe from '../../../atoms/IconGlobe'
|
||||||
import IconGlobeSmall from '../../../atoms/IconGlobeSmall'
|
import IconGlobeSmall from '../../../atoms/IconGlobeSmall'
|
||||||
import LinkChange from '../../../atoms/LinkChange'
|
import LinkChange from '../../../atoms/LinkChange'
|
||||||
import ToggleLayout from '../../../atoms/ToggleLayout'
|
import ToggleLayout from '../../../atoms/ToggleLayout'
|
||||||
import Photo from '../../../molecules/Photo'
|
import Photo from '../../../molecules/Photo'
|
||||||
import Switcher from '../../../molecules/Switcher'
|
import Switcher from '../../../molecules/Switcher'
|
||||||
import Footer from '../../../organisms/Footer'
|
import Pagination from '../../../organisms/Pagination'
|
||||||
|
import Footer from '../../../organisms/Footer'
|
||||||
|
import SocialMetas from '../../../utils/SocialMetas'
|
||||||
|
|
||||||
// Props and variables
|
// Props and variables
|
||||||
export let photos
|
export let photos
|
||||||
let layoutSetting
|
let layoutSetting
|
||||||
|
|
||||||
// Update current location
|
// Update current location
|
||||||
const location = $locations.find(loc => loc.slug === $page.params.location)
|
const location = $locations.find(loc => loc.slug === $page.params.location)
|
||||||
currentLocation.set(location)
|
currentLocation.set(location)
|
||||||
currentPhotos.set(photos)
|
currentPhotos.set(photos)
|
||||||
|
|
||||||
// Define dates
|
// Define dates
|
||||||
$: latestPhoto = photos[0]
|
$: latestPhoto = photos[0]
|
||||||
$: dateUpdatedFull = latestPhoto ? dayjs(latestPhoto.modified_on).format('MMM Do, YYYY') : ''
|
$: dateUpdatedFull = latestPhoto ? dayjs(latestPhoto.modified_on).format('MMM Do, YYYY') : ''
|
||||||
$: dateUpdatedDatetime = latestPhoto ?dayjs(latestPhoto.modified_on).format('YYYY-MM-DDThh:mm:ss') : ''
|
$: dateUpdatedDatetime = latestPhoto ?dayjs(latestPhoto.modified_on).format('YYYY-MM-DDThh:mm:ss') : ''
|
||||||
$: dateUpdatedRelative = latestPhoto ?dayjs().to(dayjs(latestPhoto.modified_on)) : ''
|
$: dateUpdatedRelative = latestPhoto ?dayjs().to(dayjs(latestPhoto.modified_on)) : ''
|
||||||
$: lastUpdated = latestPhoto ? (dayjs(latestPhoto.modified_on).isBefore(dayjs().subtract(1, 'M'))) ? dateUpdatedFull : dateUpdatedRelatives : ''
|
$: lastUpdated = latestPhoto ? (dayjs(latestPhoto.modified_on).isBefore(dayjs().subtract(1, 'M'))) ? dateUpdatedFull : dateUpdatedRelatives : ''
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Pagination
|
** Pagination
|
||||||
*/
|
*/
|
||||||
let photosPerPage = 2 // 12
|
let photosPerPage = 2 // 12
|
||||||
let currentIndex = photosPerPage
|
let currentIndex = photosPerPage
|
||||||
const pagesTotal = Math.ceil(photos.length / photosPerPage)
|
|
||||||
const pages = Array.from({ length: pagesTotal }, (v, k) => k + 1).reverse()
|
|
||||||
let pageTranslate = 100 - (100 / pagesTotal)
|
|
||||||
|
|
||||||
// Hide photos by default
|
// Hide photos by default
|
||||||
photos.forEach((photo, index) => photo.hidden = (index + 1 > photosPerPage) ? true : false)
|
photos.forEach((photo, index) => photo.hidden = (index + 1 > photosPerPage) ? true : false)
|
||||||
let paginatedPhotos = photos.filter(photo => photo.hidden === false)
|
let paginatedPhotos = photos.filter(photo => photo.hidden === false)
|
||||||
|
|
||||||
// Add more photos to the loop
|
// Update pagination event from Pagination component
|
||||||
const displayMorePhotos = () => {
|
const updatePagination = event => paginatedPhotos = event.detail.paginatedPhotos
|
||||||
if (currentIndex < photos.length) {
|
|
||||||
// Display the X next hidden photos
|
|
||||||
const photosToAppend = photos.filter(photo => photo.hidden === true).splice(0, photosPerPage)
|
|
||||||
photosToAppend.forEach(photo => photo.hidden = false)
|
|
||||||
|
|
||||||
// Merge new photos to show to the existing ones
|
|
||||||
paginatedPhotos = [...paginatedPhotos, ...photosToAppend]
|
|
||||||
|
|
||||||
// Increment current index
|
|
||||||
currentIndex = currentIndex + photosPerPage
|
|
||||||
|
|
||||||
// Animate the pagination
|
|
||||||
pageTranslate = pageTranslate - (100 / pagesTotal)
|
|
||||||
} else {
|
|
||||||
currentIndex = photos.length
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Run code on browser only
|
** Run code on browser only
|
||||||
*/
|
*/
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
// Get layout setting from storage
|
// Get layout setting from storage
|
||||||
layoutSetting = localStorage.getItem('photosLayout')
|
layoutSetting = localStorage.getItem('photosLayout')
|
||||||
|
|
||||||
|
if (process.browser) {
|
||||||
|
// Scroll apparitions
|
||||||
|
AOS.init()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
if (process.browser) {
|
|
||||||
// Scroll apparitions
|
|
||||||
AOS.init()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Houses Of - Beautiful houses of {location.name}, {location.country.name}</title>
|
<title>Houses Of – Beautiful houses of {location.name}, {location.country.name}</title>
|
||||||
|
<meta name="description" content="Houses Of {location.name} {location.description}">
|
||||||
|
<SocialMetas
|
||||||
|
url="https://housesof.world/location/{location.country.slug}/{location.slug}"
|
||||||
|
title="Houses Of – Beautiful houses of {location.name}, {location.country.name}"
|
||||||
|
description="Houses Of {location.name} {location.description}"
|
||||||
|
image={latestPhoto ? fn.getThumbnail(latestPhoto.image.private_hash, 1200, 630) : null}
|
||||||
|
/>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<section class="place">
|
<section class="place">
|
||||||
<div class="place__title">
|
<div class="place__title">
|
||||||
<h1 class="title-location title-location--big">
|
<h1 class="title-location title-location--big">
|
||||||
<span class="top">Houses <em>of</em></span>
|
<span class="top">Houses <em>of</em></span>
|
||||||
<span class="bottom">{location.name}</span>
|
<span class="bottom">{location.name}</span>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<a href="/choose" class="button-control button-control--big button-control--dashed">
|
<a href="/choose" class="button-control button-control--big button-control--dashed">
|
||||||
<span class="center">
|
<span class="center">
|
||||||
<IconGlobe width="44" color="#fff" />
|
<IconGlobe width="44" color="#fff" />
|
||||||
<span>Change</span>
|
<span>Change</span>
|
||||||
</span>
|
</span>
|
||||||
<svg>
|
<svg>
|
||||||
<circle cx="50%" cy="50%" r="43%" />
|
<circle cx="50%" cy="50%" r="43%" />
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="place__wrap wrap">
|
<div class="place__wrap wrap">
|
||||||
<div class="place__description">
|
<div class="place__description">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<p>{$site.description}</p>
|
<p>{$site.description}</p>
|
||||||
<p>
|
{#if location.description}
|
||||||
Houses Of
|
<p>
|
||||||
<LinkChange href="/choose" text={location.name}>
|
Houses Of
|
||||||
<IconGlobeSmall width="14" color="#999" />
|
<LinkChange href="/choose" text={location.name}>
|
||||||
</LinkChange>
|
<IconGlobeSmall width="14" color="#999" />
|
||||||
{location.description}
|
</LinkChange>
|
||||||
</p>
|
{location.description}
|
||||||
<p class="updated style-caps">
|
</p>
|
||||||
<strong>Updated</strong>
|
{/if}
|
||||||
<time datetime={dateUpdatedDatetime} title={dateUpdatedFull}>{lastUpdated}</time>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<ToggleLayout />
|
{#if photos.length}
|
||||||
</div>
|
<p class="updated style-caps">
|
||||||
</div>
|
<strong>Updated</strong>
|
||||||
</div>
|
<time datetime={dateUpdatedDatetime} title={dateUpdatedFull}>{lastUpdated}</time>
|
||||||
|
</p>
|
||||||
|
|
||||||
<div class="place__illustration">
|
<ToggleLayout />
|
||||||
<div class="place__illustration--left side"></div>
|
{/if}
|
||||||
<div class="place__illustration--center"></div>
|
</div>
|
||||||
<div class="place__illustration--right side"></div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="place__illustration">
|
||||||
|
<div class="place__illustration--left side"></div>
|
||||||
|
<div class="place__illustration--center"></div>
|
||||||
|
<div class="place__illustration--right side"></div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="photos photos--{layoutSetting || 'list'}">
|
<section class="photos photos--{layoutSetting || 'list'}">
|
||||||
<div class="photos__sidewrap wrap">
|
<div class="photos__sidewrap wrap">
|
||||||
<aside class="photos__side">
|
<aside class="photos__side">
|
||||||
<Switcher type="switcher--side" />
|
<Switcher type="switcher--side" />
|
||||||
|
|
||||||
<p class="updated style-caps">
|
{#if photos.length}
|
||||||
<strong>Updated</strong>
|
<p class="updated style-caps">
|
||||||
<time datetime={dateUpdatedDatetime} title={dateUpdatedFull}>{lastUpdated}</time>
|
<strong>Updated</strong>
|
||||||
</p>
|
<time datetime={dateUpdatedDatetime} title={dateUpdatedFull}>{lastUpdated}</time>
|
||||||
</aside>
|
</p>
|
||||||
</div>
|
{/if}
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
|
||||||
{#if photos.length}
|
{#if photos.length}
|
||||||
<div class="photos__view wrap">
|
<div class="photos__view wrap">
|
||||||
{#each paginatedPhotos as photo, index}
|
{#each paginatedPhotos as photo, index}
|
||||||
<Photo
|
<Photo
|
||||||
photo={photo}
|
photo={photo}
|
||||||
layout={layoutSetting}
|
index={photos.length - photos.indexOf(photo)}
|
||||||
index={photos.length - (photos.indexOf(photo))}
|
layout={layoutSetting}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="pagination">
|
<Pagination
|
||||||
{#if photos.length && currentIndex <= photos.length}
|
{photos} {paginatedPhotos} {photosPerPage}
|
||||||
<div class="pagination__page" class:disabled={currentIndex === photos.length}
|
on:updatePagination={updatePagination}
|
||||||
on:click={displayMorePhotos}
|
/>
|
||||||
on:mouseenter={() => pageTranslate = pageTranslate - ((100 / pagesTotal) * 0.666)}
|
|
||||||
on:mouseleave={() => pageTranslate = pageTranslate + ((100 / pagesTotal) * 0.666)}
|
|
||||||
>
|
|
||||||
<div class="pagination__info">page</div>
|
|
||||||
<div class="pagination__numbers">
|
|
||||||
<div class="scroll" style="transform: translateY(-{pageTranslate}%);">
|
|
||||||
{#each pages as page}
|
|
||||||
<span>{page}</span>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
/{pagesTotal}
|
|
||||||
</div>
|
|
||||||
<p class="pagination__caption style-caps">See more photos</p>
|
|
||||||
|
|
||||||
{:else}
|
{:else}
|
||||||
<div class="pagination__message">
|
<div class="wrap" style="padding-top: 20vw; padding-bottom: 20vw;">
|
||||||
<h3>That's all folks!</h3>
|
<p style="text-align: center; color: #333;">No photo for {location.name}</p>
|
||||||
<p class="pagination__caption style-caps">Come back later to check out <br>new photos of {location.name}</p>
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
{/if}
|
|
||||||
</section>
|
|
||||||
{/if}
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
Reference in New Issue
Block a user