Album location hovers (#345)

This commit is contained in:
Sam Becker 2025-10-21 22:46:28 -05:00 committed by GitHub
parent a03b758e3b
commit c2a0e1585f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 256 additions and 20 deletions

View File

@ -48,6 +48,7 @@
"nowrap",
"Oklab",
"oklch",
"openlayers",
"parameterizes",
"piexif",
"piexifjs",

View File

@ -40,11 +40,13 @@
"next": "15.5.4",
"next-auth": "5.0.0-beta.29",
"next-themes": "^0.4.6",
"ol": "^10.6.1",
"pg": "^8.16.3",
"piexifjs": "^1.0.6",
"react": "19.1.1",
"react-dom": "19.1.1",
"react-icons": "^5.5.0",
"react-openlayers": "^10.5.1",
"sanitize-html": "^2.17.0",
"sharp": "^0.34.4",
"sonner": "^2.0.7",
@ -57,7 +59,7 @@
"devDependencies": {
"@eslint/eslintrc": "^3.3.1",
"@next/bundle-analyzer": "15.5.4",
"@next/eslint-plugin-next": "^15.5.4",
"@next/eslint-plugin-next": "15.5.4",
"@stylistic/eslint-plugin": "^5.4.0",
"@tailwindcss/postcss": "^4.1.13",
"@testing-library/dom": "^10.4.1",

130
pnpm-lock.yaml generated
View File

@ -98,6 +98,9 @@ importers:
next-themes:
specifier: ^0.4.6
version: 0.4.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
ol:
specifier: ^10.6.1
version: 10.6.1
pg:
specifier: ^8.16.3
version: 8.16.3
@ -113,6 +116,9 @@ importers:
react-icons:
specifier: ^5.5.0
version: 5.5.0(react@19.1.1)
react-openlayers:
specifier: ^10.5.1
version: 10.5.1(ol@10.6.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
sanitize-html:
specifier: ^2.17.0
version: 2.17.0
@ -145,7 +151,7 @@ importers:
specifier: 15.5.4
version: 15.5.4
'@next/eslint-plugin-next':
specifier: ^15.5.4
specifier: 15.5.4
version: 15.5.4
'@stylistic/eslint-plugin':
specifier: ^5.4.0
@ -1066,6 +1072,9 @@ packages:
'@panva/hkdf@1.2.1':
resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==}
'@petamoriken/float16@3.9.3':
resolution: {integrity: sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g==}
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
@ -1805,6 +1814,9 @@ packages:
'@types/piexifjs@1.0.0':
resolution: {integrity: sha512-PPiGeCkmkZQgYjvqtjD3kp4OkbCox2vEFVuK4DaLVOIazJLAXk+/ujbizkIPH5CN4AnN9Clo5ckzUlaj3+SzCA==}
'@types/rbush@4.0.0':
resolution: {integrity: sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ==}
'@types/react-dom@19.1.9':
resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==}
peerDependencies:
@ -2491,6 +2503,9 @@ packages:
duplexer@0.1.2:
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
earcut@3.0.2:
resolution: {integrity: sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==}
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
@ -2820,6 +2835,10 @@ packages:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
geotiff@2.1.3:
resolution: {integrity: sha512-PT6uoF5a1+kbC3tHmZSUsLHBp2QJlHasxxxxPW47QIY1VBKpFB+FcDvX+MxER6UzgLQZ0xDzJ9s48B9JbOCTqA==}
engines: {node: '>=10.19'}
get-caller-file@2.0.5:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
@ -3375,6 +3394,9 @@ packages:
resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==}
engines: {node: '>=0.10'}
lerc@3.0.0:
resolution: {integrity: sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==}
leven@3.1.0:
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
engines: {node: '>=6'}
@ -3673,6 +3695,9 @@ packages:
resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
engines: {node: '>= 0.4'}
ol@10.6.1:
resolution: {integrity: sha512-xp174YOwPeLj7c7/8TCIEHQ4d41tgTDDhdv6SqNdySsql5/MaFJEJkjlsYcvOPt7xA6vrum/QG4UdJ0iCGT1cg==}
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
@ -3715,10 +3740,16 @@ packages:
package-json-from-dist@1.0.1:
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
pako@2.1.0:
resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==}
parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
parse-headers@2.0.6:
resolution: {integrity: sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==}
parse-json@5.2.0:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
@ -3748,6 +3779,10 @@ packages:
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
engines: {node: '>=16 || 14 >=14.18'}
pbf@4.0.1:
resolution: {integrity: sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==}
hasBin: true
pg-cloudflare@1.2.7:
resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==}
@ -3859,6 +3894,9 @@ packages:
prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
protocol-buffers-schema@3.6.0:
resolution: {integrity: sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==}
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@ -3869,10 +3907,20 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
quick-lru@6.1.2:
resolution: {integrity: sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==}
engines: {node: '>=12'}
quick-lru@7.1.0:
resolution: {integrity: sha512-Pzd/4IFnTb8E+I1P5rbLQoqpUHcXKg48qTYKi4EANg+sTPwGFEMOcYGiiZz6xuQcOMZP7MPsrdAPx+16Q8qahg==}
engines: {node: '>=18'}
quickselect@3.0.0:
resolution: {integrity: sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==}
rbush@4.0.1:
resolution: {integrity: sha512-IP0UpfeWQujYC8Jg162rMNc01Rf0gWMMAb2Uxus/Q0qOFw4lCcq6ZnQEZwUoJqWyUGJ9th7JjwI4yIWo+uvoAQ==}
react-dom@19.1.1:
resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==}
peerDependencies:
@ -3892,6 +3940,13 @@ packages:
react-is@18.3.1:
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
react-openlayers@10.5.1:
resolution: {integrity: sha512-IWEXerUjn/N6sbUP/FURKvYDtJj/FZVxmK0WBw4kbAVVyQvs/5JTlD/x9+QEPqNzbAITX6lwasdrQ+WW51y/6g==}
peerDependencies:
ol: ^10.5.0
react: ^18 || ^19
react-dom: ^18 || ^19
react-remove-scroll-bar@2.3.8:
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
engines: {node: '>=10'}
@ -3957,6 +4012,9 @@ packages:
resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
resolve-protobuf-schema@2.1.0:
resolution: {integrity: sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==}
resolve@1.22.10:
resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
engines: {node: '>= 0.4'}
@ -4402,6 +4460,9 @@ packages:
walker@1.0.8:
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
web-worker@1.5.0:
resolution: {integrity: sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==}
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
@ -4497,6 +4558,9 @@ packages:
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
engines: {node: '>=18'}
xml-utils@1.10.2:
resolution: {integrity: sha512-RqM+2o1RYs6T8+3DzDSoTRAUfrvaejbVHcp3+thnAtDKo8LskR+HomLajEy5UjTz24rpka7AxVBRR3g2wTUkJA==}
xmlchars@2.2.0:
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
@ -4534,6 +4598,9 @@ packages:
zod@4.1.11:
resolution: {integrity: sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==}
zstddec@0.1.0:
resolution: {integrity: sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg==}
snapshots:
'@adobe/css-tools@4.4.4': {}
@ -5783,6 +5850,8 @@ snapshots:
'@panva/hkdf@1.2.1': {}
'@petamoriken/float16@3.9.3': {}
'@pkgjs/parseargs@0.11.0':
optional: true
@ -6630,6 +6699,8 @@ snapshots:
'@types/piexifjs@1.0.0': {}
'@types/rbush@4.0.0': {}
'@types/react-dom@19.1.9(@types/react@19.1.15)':
dependencies:
'@types/react': 19.1.15
@ -7294,6 +7365,8 @@ snapshots:
duplexer@0.1.2: {}
earcut@3.0.2: {}
eastasianwidth@0.2.0: {}
electron-to-chromium@1.5.218: {}
@ -7764,6 +7837,17 @@ snapshots:
gensync@1.0.0-beta.2: {}
geotiff@2.1.3:
dependencies:
'@petamoriken/float16': 3.9.3
lerc: 3.0.0
pako: 2.1.0
parse-headers: 2.0.6
quick-lru: 6.1.2
web-worker: 1.5.0
xml-utils: 1.10.2
zstddec: 0.1.0
get-caller-file@2.0.5: {}
get-intrinsic@1.3.0:
@ -8554,6 +8638,8 @@ snapshots:
dependencies:
language-subtag-registry: 0.3.23
lerc@3.0.0: {}
leven@3.1.0: {}
levn@0.4.1:
@ -8792,6 +8878,14 @@ snapshots:
define-properties: 1.2.1
es-object-atoms: 1.1.1
ol@10.6.1:
dependencies:
'@types/rbush': 4.0.0
earcut: 3.0.2
geotiff: 2.1.3
pbf: 4.0.1
rbush: 4.0.1
once@1.4.0:
dependencies:
wrappy: 1.0.2
@ -8837,10 +8931,14 @@ snapshots:
package-json-from-dist@1.0.1: {}
pako@2.1.0: {}
parent-module@1.0.1:
dependencies:
callsites: 3.1.0
parse-headers@2.0.6: {}
parse-json@5.2.0:
dependencies:
'@babel/code-frame': 7.27.1
@ -8867,6 +8965,10 @@ snapshots:
lru-cache: 10.4.3
minipass: 7.1.2
pbf@4.0.1:
dependencies:
resolve-protobuf-schema: 2.1.0
pg-cloudflare@1.2.7:
optional: true
@ -8972,14 +9074,24 @@ snapshots:
object-assign: 4.1.1
react-is: 16.13.1
protocol-buffers-schema@3.6.0: {}
punycode@2.3.1: {}
pure-rand@7.0.1: {}
queue-microtask@1.2.3: {}
quick-lru@6.1.2: {}
quick-lru@7.1.0: {}
quickselect@3.0.0: {}
rbush@4.0.1:
dependencies:
quickselect: 3.0.0
react-dom@19.1.1(react@19.1.1):
dependencies:
react: 19.1.1
@ -8995,6 +9107,12 @@ snapshots:
react-is@18.3.1: {}
react-openlayers@10.5.1(ol@10.6.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1):
dependencies:
ol: 10.6.1
react: 19.1.1
react-dom: 19.1.1(react@19.1.1)
react-remove-scroll-bar@2.3.8(@types/react@19.1.15)(react@19.1.1):
dependencies:
react: 19.1.1
@ -9061,6 +9179,10 @@ snapshots:
resolve-pkg-maps@1.0.0: {}
resolve-protobuf-schema@2.1.0:
dependencies:
protocol-buffers-schema: 3.6.0
resolve@1.22.10:
dependencies:
is-core-module: 2.16.1
@ -9589,6 +9711,8 @@ snapshots:
dependencies:
makeerror: 1.0.12
web-worker@1.5.0: {}
webidl-conversions@3.0.1: {}
webidl-conversions@7.0.0: {}
@ -9700,6 +9824,8 @@ snapshots:
xml-name-validator@5.0.0: {}
xml-utils@1.10.2: {}
xmlchars@2.2.0: {}
xtend@4.0.2: {}
@ -9727,3 +9853,5 @@ snapshots:
yocto-queue@0.1.0: {}
zod@4.1.11: {}
zstddec@0.1.0: {}

View File

@ -66,8 +66,8 @@ export default async function AlbumHeader({
>
{album.location &&
<PlaceEntity
location={album.location}
className="translate-x-[-2px] mr-3!"
place={album.location}
className="translate-x-[-2px] mr-1.5!"
/>}
{tags.length > 0 && <>
<IconTag

View File

@ -152,7 +152,7 @@ export default function EntityHover({
width,
height,
color,
}} >
}}>
{children}
</SharedHover>;
}

View File

@ -1,11 +1,15 @@
'use client';
import { ReactNode, useRef, useEffect } from 'react';
import { SharedHoverProps, useSharedHoverState } from '../shared-hover/state';
import useSupportsHover from '@/utility/useSupportsHover';
import clsx from 'clsx/lite';
export default function SharedHover({
hoverKey: key,
children,
content,
className,
width,
height,
offsetAbove = -1,
@ -15,6 +19,7 @@ export default function SharedHover({
hoverKey: string
children :ReactNode
content: ReactNode
className?: string
width: number
height: number
offsetAbove?: number
@ -47,7 +52,7 @@ export default function SharedHover({
return (
<div
className="max-w-full"
className={clsx('max-w-full', className)}
ref={ref}
onMouseEnter={() => supportsHover &&
showHover?.(ref.current, {

View File

@ -4,7 +4,7 @@ import MaskedScroll from '@/components/MaskedScroll';
import PhotoAlbum from '@/album/PhotoAlbum';
import PhotoTag from '@/tag/PhotoTag';
import PhotoFavs from '@/tag/PhotoFavs';
import clsx from 'clsx';
import clsx from 'clsx/lite';
import { CATEGORY_VISIBILITY } from '@/app/config';
import PhotoRecents from '@/recents/PhotoRecents';
import PhotoFilm from '@/film/PhotoFilm';

View File

@ -3,23 +3,50 @@ import { Place } from '.';
import EntityLink, {
EntityLinkExternalProps,
} from '@/components/entity/EntityLink';
import { getDimensionsFromSize } from '@/utility/size';
import PlaceMap from './PlaceMap';
import SharedHover from '@/components/shared-hover/SharedHover';
import clsx from 'clsx/lite';
const { width, height } = getDimensionsFromSize(300, 16 / 9);
export default function PlaceEntity({
location,
place,
...props
}: {
location: Place
place: Place
} & EntityLinkExternalProps) {
return (
<SharedHover {...{
hoverKey: place.id,
content: <div className="relative">
<PlaceMap {...{ place, width, height }} />
<span
className={clsx(
'absolute bottom-2.5 left-2.5',
'px-1.5 py-0.5 rounded-sm',
'text-white/90 bg-black/40 backdrop-blur-lg',
'outline-medium shadow-sm',
'uppercase text-[0.7rem]',
)}
>
{place.nameFormatted || place.name}
</span>
</div>,
className: 'inline-flex',
width,
height,
}}>
<EntityLink
{...props}
icon={<IconPlace
className="text-[13px] translate-x-[2px]"
/>}
label={location.nameFormatted || location.name}
path={location.link}
label={place.nameFormatted || place.name}
path={place.link}
pathTarget="_blank"
badged
/>
</SharedHover>
);
}

73
src/place/PlaceMap.tsx Normal file
View File

@ -0,0 +1,73 @@
'use client';
import { OSM } from 'ol/source';
import { fromLonLat } from 'ol/proj';
import { useMemo, useRef } from 'react';
import { Map, View, TileLayer } from 'react-openlayers';
import { Place } from '.';
import { boundingExtent } from 'ol/extent';
import 'react-openlayers/dist/index.css'; // for css
const MULTIPLIER = 2;
export default function PlaceMap({
place,
width,
height,
monochrome,
}: {
place: Place
width: number
height: number
monochrome?: boolean
}) {
const osm = useRef(new OSM());
const centerLon = place.location?.longitude ?? 0;
const centerLat = place.location?.latitude ?? 0;
const highLon = place.viewport?.high?.longitude ?? 0;
const highLat = place.viewport?.high?.latitude ?? 0;
const lowLon = place.viewport?.low?.longitude ?? 0;
const lowLat = place.viewport?.low?.latitude ?? 0;
const center = useMemo(() =>
fromLonLat([centerLon, centerLat])
, [centerLon, centerLat]);
const extent = useMemo(() =>
boundingExtent([
fromLonLat([highLon, highLat]),
fromLonLat([lowLon, lowLat]),
])
, [highLon, highLat, lowLon, lowLat]);
return (
<div style={{ width, height }}>
<div style={{
transform: `scale(${1 / MULTIPLIER})`,
transformOrigin: 'top left',
}}>
<Map
controls={[]}
interactions={[]}
style={{
width: width * MULTIPLIER,
height: height * MULTIPLIER,
...monochrome && {
filter: 'saturate(0) contrast(1.05) brightness(1.05)',
},
}}
>
<TileLayer source={osm.current} />
<View {...{
center,
extent,
// Zoom must be defined despite setting extent
zoom: 1,
}} />
</Map>
</div>
</div>
);
}