Files
tabler/shared/components/demo/Map.astro
T

92 lines
3.8 KiB
Plaintext

---
// A map drawn by the Tabler `MapView` component (core/js/src/map.ts) on top of
// MapLibre GL JS: the markup below is all a page needs, there is no script.
// Markers go in the default slot, as elements with `data-bs-lng` and
// `data-bs-lat`; content laid over the map goes in the `overlay` slot.
// Renders nothing unless maps[mapId] exists or `center` is set.
import maps from '@data/maps.json';
interface Props {
/** Key into the maps data; also the suffix of the container id */
mapId: string;
ratio?: string;
/** for a map with no entry in the maps data: [latitude, longitude] */
center?: [number, number];
zoom?: number;
title?: string;
style?: string;
/** buttons over the map: true for zoom, or a list such as "zoom,north,fullscreen" */
controls?: boolean | string;
/** draws the map as a 3D globe instead of a flat map */
globe?: boolean;
/** rounded corners; turn off when the map fills a box that has its own corners */
rounded?: boolean;
/** fixed height (any CSS length) instead of an aspect ratio */
height?: string;
/** a path drawn as a line: [longitude, latitude] points */
route?: number[][];
/** index of the route point reached so far: the rest of the path is dashed */
routeProgress?: number;
/** color of the route and the GeoJSON shapes */
layerColor?: string;
/** accessible names of the control buttons, by action */
labels?: Record<string, string>;
/** GeoJSON drawn over the map: an object, or the URL of a file */
geojson?: object | string;
}
const { mapId, ratio, center: centerProp, zoom: zoomProp, title, style: styleProp, controls: controlsProp, globe = false, rounded = true, height, route, routeProgress, layerColor, labels, geojson } = Astro.props;
type MapData = {
title?: string | undefined;
ratio?: string;
card?: boolean;
/** OpenFreeMap style name: positron (default), liberty, bright, dark or fiord */
style?: string | undefined;
/** style used in dark mode, `dark` by default */
darkStyle?: string;
/** buttons over the map: true for zoom, or a list such as "zoom,north,fullscreen" */
controls?: boolean | string | undefined;
zoom?: number | undefined;
center?: [number, number];
markers?: { name?: string; center: [number, number] }[];
};
const data: MapData | undefined = (maps as unknown as Record<string, MapData>)[mapId] ?? (centerProp ? { title, center: centerProp, zoom: zoomProp, style: styleProp, controls: controlsProp } : undefined);
// The data keeps [latitude, longitude]; the component takes longitude first
const center = data?.center ? `${data.center[1]},${data.center[0]}` : '13.4049,52.518827';
const ratioClass = data?.ratio ?? ratio ?? '16x9';
---
{
data && (
<div class:list={height ? 'position-relative' : `ratio ratio-${ratioClass}`} style={height ? `height: ${height}` : undefined}>
<div class:list={[height && 'h-100']}>
<div
id={`map-${mapId}`}
class:list={['w-100 h-100', rounded && 'rounded']}
role="region"
aria-label={data.title ?? 'Map'}
data-bs-toggle="map"
data-bs-center={center}
data-bs-zoom={data.zoom ?? 13}
data-bs-map-style={data.style}
data-bs-map-style-dark={data.darkStyle}
data-bs-controls={data.controls ? String(data.controls) : undefined}
data-bs-globe={globe ? 'true' : undefined}
data-bs-route={route ? JSON.stringify(route) : undefined}
data-bs-route-progress={routeProgress}
data-bs-layer-color={layerColor}
data-bs-labels={labels ? JSON.stringify(labels) : undefined}
data-bs-geojson={typeof geojson === 'object' ? JSON.stringify(geojson) : geojson}
>
{(data.markers ?? []).map((marker) => <span class="map-marker map-marker-dot" data-bs-lng={marker.center[1]} data-bs-lat={marker.center[0]} data-bs-popup={marker.name} />)}
<slot />
</div>
<slot name="overlay" />
</div>
</div>
)
}