mirror of
https://github.com/LukeHagar/website.git
synced 2025-12-10 04:22:18 +00:00
update
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { classNames } from '$lib/utils/classnames';
|
import { classNames } from '$lib/utils/classnames';
|
||||||
import { slugify } from '$lib/utils/slugify';
|
import { slugify } from '$lib/utils/slugify';
|
||||||
import { Tooltip } from 'bits-ui';
|
|
||||||
import { latLongToSvgPosition } from './utils/projections';
|
import { latLongToSvgPosition } from './utils/projections';
|
||||||
|
import { tooltipData } from './map-tooltip.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
city: string;
|
city: string;
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
west: number;
|
west: number;
|
||||||
east: number;
|
east: number;
|
||||||
};
|
};
|
||||||
available?: boolean;
|
available: boolean;
|
||||||
class?: string;
|
class?: string;
|
||||||
animate?: boolean;
|
animate?: boolean;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -28,22 +28,32 @@
|
|||||||
index = 0,
|
index = 0,
|
||||||
lat,
|
lat,
|
||||||
lng,
|
lng,
|
||||||
available = false,
|
available,
|
||||||
class: className = '',
|
|
||||||
animate = false,
|
animate = false,
|
||||||
isOpen = false
|
isOpen = false
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
const position = $derived(latLongToSvgPosition({ latitude: lat, longitude: lng }));
|
const position = $derived(latLongToSvgPosition({ latitude: lat, longitude: lng }));
|
||||||
|
|
||||||
let open = $state(isOpen);
|
const handleSetActiveMarker = () => {
|
||||||
|
tooltipData.set({
|
||||||
$effect(() => {
|
city,
|
||||||
open = isOpen;
|
code,
|
||||||
|
available
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResetActiveMarker = () => {
|
||||||
|
console.log('reset');
|
||||||
|
// tooltipData.set({
|
||||||
|
// city: null,
|
||||||
|
// code: null,
|
||||||
|
// available: null
|
||||||
|
// });
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<button
|
||||||
class={classNames(
|
class={classNames(
|
||||||
'group absolute z-10 flex size-2 cursor-pointer items-center justify-center opacity-0 [animation-delay:var(--delay)]',
|
'group absolute z-10 flex size-2 cursor-pointer items-center justify-center opacity-0 [animation-delay:var(--delay)]',
|
||||||
{ 'animate-fade-in': animate }
|
{ 'animate-fade-in': animate }
|
||||||
@@ -51,6 +61,11 @@
|
|||||||
style="left: {position.x}%; top: {position.y}%;--delay: {index * 10}ms;"
|
style="left: {position.x}%; top: {position.y}%;--delay: {index * 10}ms;"
|
||||||
data-region={slugify(city)}
|
data-region={slugify(city)}
|
||||||
data-active={isOpen}
|
data-active={isOpen}
|
||||||
|
onmouseenter={handleSetActiveMarker}
|
||||||
|
onfocus={handleSetActiveMarker}
|
||||||
|
onmouseout={handleResetActiveMarker}
|
||||||
|
onblur={handleResetActiveMarker}
|
||||||
|
aria-label={city}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="from-accent/20 to-accent/10 border-gradient ease-spring absolute inline-flex h-5 w-5 rounded-full bg-gradient-to-b opacity-0 transition-opacity group-hover:animate-ping group-hover:opacity-75 before:rounded-full"
|
class="from-accent/20 to-accent/10 border-gradient ease-spring absolute inline-flex h-5 w-5 rounded-full bg-gradient-to-b opacity-0 transition-opacity group-hover:animate-ping group-hover:opacity-75 before:rounded-full"
|
||||||
@@ -58,30 +73,4 @@
|
|||||||
></span>
|
></span>
|
||||||
<span class="bg-accent absolute inline-flex h-full w-full rounded-full"></span>
|
<span class="bg-accent absolute inline-flex h-full w-full rounded-full"></span>
|
||||||
<span class="absolute size-1/2 rounded-full bg-white/80 transition-all"></span>
|
<span class="absolute size-1/2 rounded-full bg-white/80 transition-all"></span>
|
||||||
</div>
|
</button>
|
||||||
|
|
||||||
<!-- <div
|
|
||||||
class={classNames(
|
|
||||||
'bg-card/90 border-gradient relative z-100 flex w-[190px] flex-col gap-2 rounded-[10px] p-2 backdrop-blur-lg before:rounded-[10px] after:rounded-[10px]',
|
|
||||||
'data-[state="closed"]:animate-menu-out data-[state="instant-open"]:animate-menu-in data-[state="delayed-open"]:animate-menu-in',
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<span class="text-primary text-caption w-fit">
|
|
||||||
{city}
|
|
||||||
({code})
|
|
||||||
</span>
|
|
||||||
{#if available}
|
|
||||||
<div
|
|
||||||
class="text-caption flex h-5 items-center justify-center place-self-start rounded-md bg-[#10B981]/24 p-1 text-center text-[#B4F8E2]"
|
|
||||||
>
|
|
||||||
<span class="text-micro -tracking-tight">Available now</span>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div
|
|
||||||
class="text-caption flex h-5 items-center justify-center place-self-start rounded-md bg-white/6 p-1 text-center text-white/60"
|
|
||||||
>
|
|
||||||
<span class="text-micro -tracking-tight">Planned</span>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div> -->
|
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
<script lang="ts" module>
|
||||||
|
import { classNames } from '$lib/utils/classnames';
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
|
export const tooltipData = writable<{
|
||||||
|
city: string | null;
|
||||||
|
code: string | null;
|
||||||
|
available: boolean | null;
|
||||||
|
}>({
|
||||||
|
city: null,
|
||||||
|
code: null,
|
||||||
|
available: null
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
type Props = {
|
type Props = {
|
||||||
coords: {
|
coords: {
|
||||||
@@ -6,7 +21,38 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const { coords } = $props();
|
const { coords }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div style:left="{coords.x}px" style:top="{coords.y}px">Tooltip</div>
|
{#if $tooltipData.city}
|
||||||
|
<div
|
||||||
|
class="pointer-events-none absolute"
|
||||||
|
style:left="{coords.x + 10}px"
|
||||||
|
style:top="{coords.y - 25}px"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class={classNames(
|
||||||
|
'bg-card/90 border-gradient relative z-100 flex w-[190px] flex-col gap-2 rounded-[10px] p-2 backdrop-blur-lg before:rounded-[10px] after:rounded-[10px]',
|
||||||
|
'data-[state="closed"]:animate-menu-out data-[state="instant-open"]:animate-menu-in data-[state="delayed-open"]:animate-menu-in'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span class="text-primary text-caption w-fit">
|
||||||
|
{$tooltipData.city}
|
||||||
|
({$tooltipData.code})
|
||||||
|
</span>
|
||||||
|
{#if $tooltipData.available}
|
||||||
|
<div
|
||||||
|
class="text-caption flex h-5 items-center justify-center place-self-start rounded-md bg-[#10B981]/24 p-1 text-center text-[#B4F8E2]"
|
||||||
|
>
|
||||||
|
<span class="text-micro -tracking-tight">Available now</span>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div
|
||||||
|
class="text-caption flex h-5 items-center justify-center place-self-start rounded-md bg-white/6 p-1 text-center text-white/60"
|
||||||
|
>
|
||||||
|
<span class="text-micro -tracking-tight">Planned</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|||||||
@@ -11,12 +11,10 @@
|
|||||||
import MapMarker from './map-marker.svelte';
|
import MapMarker from './map-marker.svelte';
|
||||||
import { slugify } from '$lib/utils/slugify';
|
import { slugify } from '$lib/utils/slugify';
|
||||||
import { classNames } from '$lib/utils/classnames';
|
import { classNames } from '$lib/utils/classnames';
|
||||||
import { Tooltip } from 'bits-ui';
|
|
||||||
import MapNav from './map-nav.svelte';
|
import MapNav from './map-nav.svelte';
|
||||||
import { useMousePosition } from '$lib/actions/mouse-position';
|
import { useMousePosition } from '$lib/actions/mouse-position';
|
||||||
import { useAnimateInView } from '$lib/actions/animate-in-view';
|
import { useAnimateInView } from '$lib/actions/animate-in-view';
|
||||||
import { pins, type PinSegment } from './data/pins';
|
import { pins, type PinSegment } from './data/pins';
|
||||||
import { latLongToSvgPosition } from './utils/projections';
|
|
||||||
import MapTooltip from './map-tooltip.svelte';
|
import MapTooltip from './map-tooltip.svelte';
|
||||||
|
|
||||||
let dimensions = $state({
|
let dimensions = $state({
|
||||||
@@ -123,15 +121,13 @@
|
|||||||
alt="Map of the world"
|
alt="Map of the world"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{#each pins[activeSegment as PinSegment].map( (pin) => ({ ...pin, isOpen: activeRegion === slugify(pin.city), position: latLongToSvgPosition( { latitude: pin.lat, longitude: pin.lng, ...dimensions } ) }) ) as pin, index}
|
{#each pins[activeSegment as PinSegment].map( (pin) => ({ ...pin, isOpen: activeRegion === slugify(pin.city) }) ) as pin, index}
|
||||||
<MapMarker {...pin} animate={$animate} {index} bounds={MAP_BOUNDS} />
|
<MapMarker {...pin} animate={$animate} {index} bounds={MAP_BOUNDS} />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MapTooltip coords={$position} />
|
<MapTooltip coords={$position} />
|
||||||
|
|
||||||
<MapNav onValueChange={(value) => (activeSegment = value)} />
|
<MapNav onValueChange={(value) => (activeSegment = value)} />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
Reference in New Issue
Block a user