🚧 Switch to monorepo with Turbo
This commit is contained in:
125
apps/website/src/components/molecules/Location.svelte
Normal file
125
apps/website/src/components/molecules/Location.svelte
Normal file
@@ -0,0 +1,125 @@
|
||||
<style lang="scss">
|
||||
@import "../../style/molecules/location";
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import { spring } from 'svelte/motion'
|
||||
import dayjs from 'dayjs'
|
||||
import { lerp } from '$utils/functions'
|
||||
import { PUBLIC_PREVIEW_COUNT } from '$env/static/public'
|
||||
import { seenLocations } from '$utils/stores'
|
||||
// Components
|
||||
import Image from '$components/atoms/Image.svelte'
|
||||
import Badge from '$components/atoms/Badge.svelte'
|
||||
|
||||
export let location: any
|
||||
export let latestPhoto: any
|
||||
|
||||
const { settings }: any = getContext('global')
|
||||
|
||||
let locationEl: HTMLElement
|
||||
let photoIndex = 0
|
||||
|
||||
// Location date limit
|
||||
let isNew = false
|
||||
const dateNowOffset = dayjs().subtract(settings.limit_new, 'day')
|
||||
const parsedSeenLocations = JSON.parse($seenLocations)
|
||||
|
||||
$: if (latestPhoto) {
|
||||
const dateUpdated = dayjs(latestPhoto.date_created)
|
||||
|
||||
// Detect if location has new content
|
||||
const seenLocationDate = dayjs(parsedSeenLocations[location.id])
|
||||
const isLocationSeen = parsedSeenLocations?.hasOwnProperty(location.id)
|
||||
|
||||
// Define if location is has new photos
|
||||
if (seenLocationDate && isLocationSeen) {
|
||||
// A more recent photo has been added (if has been seen and has a seen date)
|
||||
isNew = dateUpdated.isAfter(dateNowOffset) && dateUpdated.isAfter(seenLocationDate)
|
||||
} else {
|
||||
// The photo is after the offset
|
||||
isNew = dateUpdated.isAfter(dateNowOffset)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Moving cursor over
|
||||
*/
|
||||
const offset = spring({ x: 0, y: 0 }, {
|
||||
stiffness: 0.075,
|
||||
damping: 0.9
|
||||
})
|
||||
const handleMouseMove = ({ clientX }: MouseEvent) => {
|
||||
const { width, left } = locationEl.getBoundingClientRect()
|
||||
const moveProgress = (clientX - left) / width // 0 to 1
|
||||
|
||||
// Move horizontally
|
||||
offset.update(_ => ({
|
||||
x: lerp(-56, 56, moveProgress),
|
||||
y: 0
|
||||
}))
|
||||
|
||||
// Change photo index from mouse position percentage
|
||||
photoIndex = Math.round(lerp(0, Number(PUBLIC_PREVIEW_COUNT) - 1, moveProgress))
|
||||
}
|
||||
|
||||
// Leaving mouseover
|
||||
const handleMouseLeave = () => {
|
||||
offset.update($c => ({
|
||||
x: $c.x,
|
||||
y: 40
|
||||
}))
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="location" bind:this={locationEl}
|
||||
style:--offset-x="{$offset.x}px"
|
||||
style:--offset-y="{$offset.y}px"
|
||||
style:--rotate="{$offset.x * 0.125}deg"
|
||||
>
|
||||
<a href="/{location.country.slug}/{location.slug}"
|
||||
on:mousemove={handleMouseMove}
|
||||
on:mouseleave={handleMouseLeave}
|
||||
tabindex="0"
|
||||
>
|
||||
<Image
|
||||
class="flag"
|
||||
id={location.country.flag.id}
|
||||
sizeKey="square-small"
|
||||
width={32} height={32}
|
||||
alt="Flag of {location.country.name}"
|
||||
/>
|
||||
<div class="text">
|
||||
<dl>
|
||||
<dt class="location__name">
|
||||
{location.name}
|
||||
</dt>
|
||||
<dd class="location__country text-label">
|
||||
{location.country.name}
|
||||
</dd>
|
||||
</dl>
|
||||
{#if isNew}
|
||||
<Badge text="New" />
|
||||
{/if}
|
||||
</div>
|
||||
</a>
|
||||
|
||||
{#if location.photos.length}
|
||||
<div class="location__photos">
|
||||
{#each location.photos as { image }, index}
|
||||
{#if image}
|
||||
{@const classes = ['location__photo', index === photoIndex ? 'is-visible' : null].join(' ').trim()}
|
||||
<Image
|
||||
class={classes}
|
||||
id={image.id}
|
||||
sizeKey="photo-thumbnail"
|
||||
width={340} height={226}
|
||||
alt={image.title}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
Reference in New Issue
Block a user