Tonight
This commit is contained in:
parent
28323f145e
commit
94f9c0d594
76 changed files with 3238 additions and 1230 deletions
|
|
@ -12,6 +12,7 @@ import type {
|
|||
POI,
|
||||
FeatureMeta,
|
||||
Bounds,
|
||||
MapFlyToOptions,
|
||||
} from '../../types';
|
||||
|
||||
import {
|
||||
|
|
@ -19,6 +20,7 @@ import {
|
|||
getBoundsFromViewState,
|
||||
getMapStyle,
|
||||
getPoiIconUrl,
|
||||
getMapCenterForTargetScreenPoint,
|
||||
} from '../../lib/map-utils';
|
||||
import {
|
||||
INITIAL_VIEW_STATE,
|
||||
|
|
@ -56,7 +58,9 @@ interface MapProps {
|
|||
onHexagonClick: (id: string, isPostcode?: boolean, geometry?: PostcodeGeometry) => void;
|
||||
onHexagonHover: (h3: string | null, x?: number, y?: number) => void;
|
||||
initialViewState?: ViewState;
|
||||
flyToRef?: React.MutableRefObject<((lat: number, lng: number, zoom: number) => void) | null>;
|
||||
flyToRef?: React.MutableRefObject<
|
||||
((lat: number, lng: number, zoom: number, options?: MapFlyToOptions) => void) | null
|
||||
>;
|
||||
theme?: 'light' | 'dark';
|
||||
screenshotMode?: boolean;
|
||||
ogMode?: boolean;
|
||||
|
|
@ -80,6 +84,61 @@ interface Dimensions {
|
|||
height: number;
|
||||
}
|
||||
|
||||
function resolveInset(pixelValue: number | undefined, ratioValue: number | undefined, size: number) {
|
||||
return Math.max(0, (pixelValue ?? 0) + (ratioValue ?? 0) * size);
|
||||
}
|
||||
|
||||
function clamp(value: number, min: number, max: number) {
|
||||
return Math.min(max, Math.max(min, value));
|
||||
}
|
||||
|
||||
function getMapRelativeVisibleAreaCenter(dimensions: Dimensions, options?: MapFlyToOptions) {
|
||||
const area = options?.visibleArea;
|
||||
const leftInset = resolveInset(area?.left, area?.leftRatio, dimensions.width);
|
||||
const rightInset = resolveInset(area?.right, area?.rightRatio, dimensions.width);
|
||||
const topInset = resolveInset(area?.top, area?.topRatio, dimensions.height);
|
||||
const bottomInset = resolveInset(area?.bottom, area?.bottomRatio, dimensions.height);
|
||||
|
||||
const left = Math.min(dimensions.width, leftInset);
|
||||
const right = Math.max(left, dimensions.width - Math.min(dimensions.width, rightInset));
|
||||
const top = Math.min(dimensions.height, topInset);
|
||||
const bottom = Math.max(top, dimensions.height - Math.min(dimensions.height, bottomInset));
|
||||
|
||||
return {
|
||||
x: (left + right) / 2,
|
||||
y: (top + bottom) / 2,
|
||||
};
|
||||
}
|
||||
|
||||
function getViewportRelativeVisibleAreaCenter(
|
||||
dimensions: Dimensions,
|
||||
container: HTMLDivElement | null,
|
||||
options?: MapFlyToOptions
|
||||
) {
|
||||
const area = options?.visibleViewportArea;
|
||||
if (!area || !container) return null;
|
||||
|
||||
const rect = container.getBoundingClientRect();
|
||||
const viewportWidth = window.innerWidth;
|
||||
const viewportHeight = window.innerHeight;
|
||||
const viewportLeft = resolveInset(area.left, area.leftRatio, viewportWidth);
|
||||
const viewportRight =
|
||||
viewportWidth - resolveInset(area.right, area.rightRatio, viewportWidth);
|
||||
const viewportTop = resolveInset(area.top, area.topRatio, viewportHeight);
|
||||
const viewportBottom =
|
||||
viewportHeight - resolveInset(area.bottom, area.bottomRatio, viewportHeight);
|
||||
|
||||
const left = clamp(viewportLeft - rect.left, 0, dimensions.width);
|
||||
const right = clamp(viewportRight - rect.left, left, dimensions.width);
|
||||
const top = clamp(viewportTop - rect.top, 0, dimensions.height);
|
||||
const bottom = clamp(viewportBottom - rect.top, top, dimensions.height);
|
||||
|
||||
return {
|
||||
x: (left + right) / 2,
|
||||
y: (top + bottom) / 2,
|
||||
};
|
||||
}
|
||||
|
||||
interface DeckWithPrivateDraw {
|
||||
_drawLayers?: (
|
||||
redrawReason: string,
|
||||
|
|
@ -255,9 +314,27 @@ export default memo(function Map({
|
|||
if (screenshotMode) window.__map_idle = true;
|
||||
}, [screenshotMode]);
|
||||
|
||||
const handleFlyTo = useCallback((lat: number, lng: number, zoom: number) => {
|
||||
setInternalViewState((prev) => ({ ...prev, latitude: lat, longitude: lng, zoom }));
|
||||
}, []);
|
||||
const handleFlyTo = useCallback(
|
||||
(lat: number, lng: number, zoom: number, options?: MapFlyToOptions) => {
|
||||
setInternalViewState((prev) => {
|
||||
const targetPoint =
|
||||
getViewportRelativeVisibleAreaCenter(dimensions, containerRef.current, options) ??
|
||||
getMapRelativeVisibleAreaCenter(dimensions, options);
|
||||
const center = getMapCenterForTargetScreenPoint(
|
||||
lat,
|
||||
lng,
|
||||
zoom,
|
||||
dimensions.width,
|
||||
dimensions.height,
|
||||
targetPoint.x,
|
||||
targetPoint.y
|
||||
);
|
||||
|
||||
return { ...prev, ...center, zoom };
|
||||
});
|
||||
},
|
||||
[dimensions]
|
||||
);
|
||||
|
||||
if (flyToRef) flyToRef.current = handleFlyTo;
|
||||
|
||||
|
|
@ -361,7 +438,7 @@ export default memo(function Map({
|
|||
) : null
|
||||
) : (
|
||||
<>
|
||||
<div className="absolute top-3 left-3 right-3 z-[60] flex flex-wrap items-start justify-between gap-2 pointer-events-none">
|
||||
<div className="absolute top-3 left-3 right-3 z-20 flex flex-wrap items-start justify-between gap-2 pointer-events-none">
|
||||
{!hideLocationSearch && (
|
||||
<LocationSearch
|
||||
onFlyTo={handleFlyTo}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue