@lad-sapienza/scms-core 0.1.0-alpha.12 → 0.1.0-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/components/Map/Map.tsx
CHANGED
|
@@ -78,6 +78,8 @@ export function Map({
|
|
|
78
78
|
csv,
|
|
79
79
|
json,
|
|
80
80
|
directus,
|
|
81
|
+
markers,
|
|
82
|
+
markersZoom,
|
|
81
83
|
}: MapProps) {
|
|
82
84
|
const t = useTranslation();
|
|
83
85
|
const [lng, lat, zoom] = center.split(',').map(Number);
|
|
@@ -323,8 +325,29 @@ export function Map({
|
|
|
323
325
|
|
|
324
326
|
layers.push(layerConfig);
|
|
325
327
|
}
|
|
328
|
+
|
|
329
|
+
if (markers) {
|
|
330
|
+
const list = Array.isArray(markers) ? markers : [markers];
|
|
331
|
+
const features = list
|
|
332
|
+
.filter(m => Number.isFinite(Number(m.lng)) && Number.isFinite(Number(m.lat)))
|
|
333
|
+
.map(m => ({
|
|
334
|
+
type: 'Feature' as const,
|
|
335
|
+
geometry: { type: 'Point' as const, coordinates: [Number(m.lng), Number(m.lat)] },
|
|
336
|
+
properties: { label: m.label ?? '' }
|
|
337
|
+
}));
|
|
338
|
+
if (features.length > 0) {
|
|
339
|
+
layers.push({
|
|
340
|
+
name: t('map.markers'),
|
|
341
|
+
source: { type: 'geojson', data: { type: 'FeatureCollection', features } },
|
|
342
|
+
popupTemplate: list.some(m => m.label) ? '<b>${label}</b>' : undefined,
|
|
343
|
+
fitToContent: true,
|
|
344
|
+
fitZoom: markersZoom,
|
|
345
|
+
visible: true
|
|
346
|
+
} as OverlayLayerConfig);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
326
349
|
return layers;
|
|
327
|
-
}, [overlayLayers, implicitSource, geojson, csv, mapStyle, parsedStyleLayers.overlayLayers]);
|
|
350
|
+
}, [overlayLayers, implicitSource, geojson, csv, markers, markersZoom, mapStyle, parsedStyleLayers.overlayLayers]);
|
|
328
351
|
|
|
329
352
|
// Initialize and update visibility when layers change
|
|
330
353
|
useEffect(() => {
|
|
@@ -479,7 +502,15 @@ export function Map({
|
|
|
479
502
|
[Math.min(...lons), Math.min(...lats)],
|
|
480
503
|
[Math.max(...lons), Math.max(...lats)]
|
|
481
504
|
];
|
|
482
|
-
|
|
505
|
+
const [[w, s], [e, n]] = bounds;
|
|
506
|
+
if (layerToFit.fitZoom !== undefined) {
|
|
507
|
+
map.easeTo({ center: [(w + e) / 2, (s + n) / 2], zoom: layerToFit.fitZoom, duration: 1000 });
|
|
508
|
+
} else if (w === e && s === n) {
|
|
509
|
+
// A lone point has no extent: fitBounds would zoom to maxZoom.
|
|
510
|
+
map.easeTo({ center: [w, s], zoom: Math.max(map.getZoom(), 12), duration: 1000 });
|
|
511
|
+
} else {
|
|
512
|
+
map.fitBounds(bounds, { padding: 50, duration: 1000 });
|
|
513
|
+
}
|
|
483
514
|
}
|
|
484
515
|
}, 100);
|
|
485
516
|
}, [mapLoaded, layersData, allOverlayLayers]);
|
|
@@ -22,6 +22,7 @@ export function Map(props: MapProps) {
|
|
|
22
22
|
const stableGeojson = useMemo(() => props?.geojson, [JSON.stringify(props?.geojson || null)]);
|
|
23
23
|
const stableCsv = useMemo(() => props?.csv, [JSON.stringify(props?.csv || null)]);
|
|
24
24
|
const stableJson = useMemo(() => props?.json, [JSON.stringify(props?.json || null)]);
|
|
25
|
+
const stableMarkers = useMemo(() => props?.markers, [JSON.stringify(props?.markers || null)]);
|
|
25
26
|
// `directus` feeds the same `implicitSource` → `allOverlayLayers` memo chain
|
|
26
27
|
// as `geojson`/`csv`/`json`; without stabilising it here an inline
|
|
27
28
|
// `directus={{ table: '…' }}` object from MDX gets a new identity on every
|
|
@@ -38,6 +39,7 @@ export function Map(props: MapProps) {
|
|
|
38
39
|
csv={stableCsv}
|
|
39
40
|
json={stableJson}
|
|
40
41
|
directus={stableDirectus}
|
|
42
|
+
markers={stableMarkers}
|
|
41
43
|
/>
|
|
42
44
|
);
|
|
43
45
|
}
|
package/components/Map/README.md
CHANGED
|
@@ -31,6 +31,30 @@ touching `overlayLayers` directly:
|
|
|
31
31
|
<Map directus={{ table: 'sites', geoField: 'location' }} />
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
For one or a few hand-placed points (e.g. the location of a single site) use
|
|
35
|
+
`markers` — no data file needed. The map fits to them automatically; a lone
|
|
36
|
+
marker is centred at a regional zoom (≥ 12) rather than zoomed to the max:
|
|
37
|
+
|
|
38
|
+
```mdx
|
|
39
|
+
<Map markers={{ lng: 12.4964, lat: 41.9028, label: 'Roma' }} />
|
|
40
|
+
|
|
41
|
+
<Map markers={[
|
|
42
|
+
{ lng: 12.49, lat: 41.90, label: 'Site A' },
|
|
43
|
+
{ lng: 12.51, lat: 41.88, label: 'Site B' },
|
|
44
|
+
]} />
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`label` is optional (shown in a popup as plain text). Markers are an extra
|
|
48
|
+
overlay layer and combine with any of the shorthands below.
|
|
49
|
+
|
|
50
|
+
`markersZoom` fixes the zoom: the map is centred on the markers at that level
|
|
51
|
+
instead of being fitted to their extent — useful to give a single site the
|
|
52
|
+
same geographic context on every page:
|
|
53
|
+
|
|
54
|
+
```mdx
|
|
55
|
+
<Map markers={{ lng: 12.4964, lat: 41.9028, label: 'Roma' }} markersZoom={9} />
|
|
56
|
+
```
|
|
57
|
+
|
|
34
58
|
`geojson`/`csv`/`json`/`directus` each accept either a bare string (path/URL)
|
|
35
59
|
or an object with `name`, `style`, `popup`, `fitToContent`, `filter` — see
|
|
36
60
|
`MapProps` in [`types.ts`](./types.ts) for the exact shape of each.
|
package/components/Map/types.ts
CHANGED
|
@@ -124,6 +124,12 @@ export interface OverlayLayerConfig {
|
|
|
124
124
|
/** Fit map bounds to layer content */
|
|
125
125
|
fitToContent?: boolean;
|
|
126
126
|
|
|
127
|
+
/**
|
|
128
|
+
* With `fitToContent`: centre on the content's bounds at this fixed zoom
|
|
129
|
+
* instead of fitting them (used by the `markers` shorthand).
|
|
130
|
+
*/
|
|
131
|
+
fitZoom?: number;
|
|
132
|
+
|
|
127
133
|
/** Optional client-side filter for features */
|
|
128
134
|
filter?: FilterObject | ((feature: any) => boolean);
|
|
129
135
|
|
|
@@ -131,6 +137,17 @@ export interface OverlayLayerConfig {
|
|
|
131
137
|
searchInFields?: SearchInFields;
|
|
132
138
|
}
|
|
133
139
|
|
|
140
|
+
/**
|
|
141
|
+
* A single point marker for the `markers` shorthand — for maps with one or a
|
|
142
|
+
* few hand-placed points, no data file needed.
|
|
143
|
+
*/
|
|
144
|
+
export interface MarkerConfig {
|
|
145
|
+
lng: number;
|
|
146
|
+
lat: number;
|
|
147
|
+
/** Text shown in the popup on hover/click (plain text, HTML-escaped) */
|
|
148
|
+
label?: string;
|
|
149
|
+
}
|
|
150
|
+
|
|
134
151
|
export interface MapProps {
|
|
135
152
|
/** Map height (CSS string) */
|
|
136
153
|
height?: string;
|
|
@@ -197,6 +214,19 @@ export interface MapProps {
|
|
|
197
214
|
filter?: FilterObject | ((row: any) => boolean);
|
|
198
215
|
};
|
|
199
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Inline markers - one or a few hand-placed points, e.g. to give the
|
|
219
|
+
* geographic context of a single site. Rendered as an extra overlay layer
|
|
220
|
+
* and, by default, the map is fitted to them.
|
|
221
|
+
*/
|
|
222
|
+
markers?: MarkerConfig | MarkerConfig[];
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Fixed zoom for the `markers`: the map is centred on them at this zoom
|
|
226
|
+
* instead of being fitted to their extent. Handy for a single site.
|
|
227
|
+
*/
|
|
228
|
+
markersZoom?: number;
|
|
229
|
+
|
|
200
230
|
/** JSON shorthand - for simple single-layer maps */
|
|
201
231
|
json?: any[] | string;
|
|
202
232
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lad-sapienza/scms-core",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.13",
|
|
4
4
|
"description": "Framework components and Astro integrations for s:CMS — DataTb, Gallery, Map, ZoteroGeoViewer, Record.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BSD-0-Clause",
|