@protobase/ui 0.19.3 → 0.19.4
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/dist/app/displays/map-display.js +3 -1
- package/dist/app/map/map-layer-choice.js +11 -0
- package/dist/app/map/map-layer-switch.js +55 -0
- package/dist/app/map/map-section.js +3 -1
- package/dist/app/map/position-areas.js +4 -2
- package/dist/app/map/record-map.js +16 -11
- package/dist/app/map/records-map.js +7 -2
- package/dist/data/meta-data.d.ts +3 -2
- package/dist/package.js +1 -1
- package/dist/shell/assets/{app-ChEqQ9ca.js → app-BYFiTXK6.js} +34 -34
- package/dist/shell/assets/{index-5frCs7fQ.js → index-CEvfMgFJ.js} +1 -1
- package/dist/shell/assets/index-CiGP6kle.css +2 -0
- package/dist/shell/assets/maplibre-D6zfLfTV.js +814 -0
- package/dist/shell/assets/record-map-B6_rbCcG.js +1 -0
- package/dist/shell/assets/records-map-bQbuIc13.js +1 -0
- package/dist/shell/assets/{shared-protobase-ui-Ceamz_7l.js → shared-protobase-ui-8pJgwjfc.js} +1 -1
- package/dist/shell/index.html +4 -4
- package/dist/shell/protobase.shell.json +1 -1
- package/package.json +4 -4
- package/dist/shell/assets/index-D-4IHhqX.css +0 -2
- package/dist/shell/assets/maplibre-D8plsjP2.js +0 -814
- package/dist/shell/assets/record-map-BDJTOwZo.js +0 -1
- package/dist/shell/assets/records-map-C-XKw6m1.js +0 -1
|
@@ -25,7 +25,8 @@ const arrow = "flex size-8 items-center justify-center rounded-full border borde
|
|
|
25
25
|
* elsewhere clears it. A marker and a card whose record's position is approximate say so.
|
|
26
26
|
*/
|
|
27
27
|
const MapDisplay = ({ model, view, display, filter, orderBy, fields, at, onMove, footer = true, flush = false }) => {
|
|
28
|
-
const
|
|
28
|
+
const maps = useAdminMeta().maps;
|
|
29
|
+
const style = maps?.style;
|
|
29
30
|
const [chosen, setChosen] = useState();
|
|
30
31
|
const [area, setArea] = useState();
|
|
31
32
|
const settled = useDebounced(area, settleMs);
|
|
@@ -70,6 +71,7 @@ const MapDisplay = ({ model, view, display, filter, orderBy, fields, at, onMove,
|
|
|
70
71
|
fallback: null,
|
|
71
72
|
children: /* @__PURE__ */ jsx(RecordsMap, {
|
|
72
73
|
style,
|
|
74
|
+
layers: maps?.layers,
|
|
73
75
|
initial: at,
|
|
74
76
|
points,
|
|
75
77
|
buckets,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { guarded } from "../../lib/guarded.js";
|
|
2
|
+
//#region src/app/map/map-layer-choice.ts
|
|
3
|
+
const mapLayerStorageKey = "protobase:map-layer";
|
|
4
|
+
/** The layer the browser last chose, if the app still offers it: else the style's own map, which is the default. */
|
|
5
|
+
const chosenMapLayer = (layers, id) => layers.find((layer) => layer.id === id);
|
|
6
|
+
/** The id of the layer chosen in this browser, if any; unavailable storage reads as the default map. */
|
|
7
|
+
const readMapLayer = (storage) => guarded(() => storage.getItem(mapLayerStorageKey), null);
|
|
8
|
+
/** Remembers a layer by its id, and the default map by forgetting the choice, so the default costs no storage. */
|
|
9
|
+
const writeMapLayer = (storage, id) => guarded(() => id === void 0 ? storage.removeItem(mapLayerStorageKey) : storage.setItem(mapLayerStorageKey, id), void 0);
|
|
10
|
+
//#endregion
|
|
11
|
+
export { chosenMapLayer, mapLayerStorageKey, readMapLayer, writeMapLayer };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Segmented } from "../../primitives/segmented.js";
|
|
2
|
+
import { chosenMapLayer, readMapLayer, writeMapLayer } from "./map-layer-choice.js";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { Layer, Source, useMap } from "react-map-gl/maplibre";
|
|
6
|
+
//#region src/app/map/map-layer-switch.tsx
|
|
7
|
+
const styleValue = "";
|
|
8
|
+
/**
|
|
9
|
+
* Inside a map, a switch in its top left corner between the style's map and each of `layers`, and the chosen layer's
|
|
10
|
+
* tiles drawn over the style's, under `below` when the map has that layer, such as the areas around records. The
|
|
11
|
+
* layer's credit joins the map's attribution while it shows. Outside its bounds the style's map shows through. The
|
|
12
|
+
* choice is remembered in this browser, for every map. Nothing without layers.
|
|
13
|
+
*/
|
|
14
|
+
const MapLayerSwitch = ({ layers, below }) => {
|
|
15
|
+
const [chosenId, setChosenId] = useState(() => readMapLayer(localStorage));
|
|
16
|
+
const { current: map } = useMap();
|
|
17
|
+
if (!layers || layers.length === 0) return null;
|
|
18
|
+
const chosen = chosenMapLayer(layers, chosenId);
|
|
19
|
+
const choose = (value) => {
|
|
20
|
+
const id = value === styleValue ? void 0 : value;
|
|
21
|
+
writeMapLayer(localStorage, id);
|
|
22
|
+
setChosenId(id ?? null);
|
|
23
|
+
};
|
|
24
|
+
const beforeId = below && map?.getLayer(below) ? below : void 0;
|
|
25
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [chosen && /* @__PURE__ */ jsx(Source, {
|
|
26
|
+
id: `map-layer-${chosen.id}`,
|
|
27
|
+
type: "raster",
|
|
28
|
+
tiles: [chosen.tiles],
|
|
29
|
+
tileSize: 256,
|
|
30
|
+
attribution: chosen.attribution,
|
|
31
|
+
maxzoom: chosen.maxzoom ?? 22,
|
|
32
|
+
...chosen.bounds && { bounds: chosen.bounds },
|
|
33
|
+
children: /* @__PURE__ */ jsx(Layer, {
|
|
34
|
+
id: `map-layer-${chosen.id}`,
|
|
35
|
+
type: "raster",
|
|
36
|
+
...beforeId && { beforeId }
|
|
37
|
+
})
|
|
38
|
+
}, chosen.id), /* @__PURE__ */ jsx("div", {
|
|
39
|
+
className: "absolute left-2.5 top-2.5 z-10 rounded-md shadow",
|
|
40
|
+
children: /* @__PURE__ */ jsx(Segmented, {
|
|
41
|
+
label: "Map layer",
|
|
42
|
+
value: chosen?.id ?? styleValue,
|
|
43
|
+
onChange: choose,
|
|
44
|
+
options: [{
|
|
45
|
+
value: styleValue,
|
|
46
|
+
label: "Map"
|
|
47
|
+
}, ...layers.map((layer) => ({
|
|
48
|
+
value: layer.id,
|
|
49
|
+
label: layer.label
|
|
50
|
+
}))]
|
|
51
|
+
})
|
|
52
|
+
})] });
|
|
53
|
+
};
|
|
54
|
+
//#endregion
|
|
55
|
+
export { MapLayerSwitch };
|
|
@@ -12,7 +12,8 @@ const defaultZoom = 15;
|
|
|
12
12
|
* `/meta` names. Without a style, or for a record without a position, there is nothing to draw and no section.
|
|
13
13
|
*/
|
|
14
14
|
const MapSection = ({ item, record }) => {
|
|
15
|
-
const
|
|
15
|
+
const maps = useAdminMeta().maps;
|
|
16
|
+
const style = maps?.style;
|
|
16
17
|
const position = recordPosition(record, item);
|
|
17
18
|
if (!style || !position) return null;
|
|
18
19
|
const map = /* @__PURE__ */ jsx("div", {
|
|
@@ -21,6 +22,7 @@ const MapSection = ({ item, record }) => {
|
|
|
21
22
|
fallback: null,
|
|
22
23
|
children: /* @__PURE__ */ jsx(RecordMap, {
|
|
23
24
|
style,
|
|
25
|
+
layers: maps?.layers,
|
|
24
26
|
position,
|
|
25
27
|
zoom: item.zoom ?? defaultZoom
|
|
26
28
|
}, `${position.latitude},${position.longitude}`)
|
|
@@ -2,6 +2,8 @@ import { areaFeatures, fadeIn } from "./area-features.js";
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Layer, Source } from "react-map-gl/maplibre";
|
|
4
4
|
//#region src/app/map/position-areas.tsx
|
|
5
|
+
/** The areas' lowest layer, which a layer of tiles goes under. */
|
|
6
|
+
const areaLayerId = "position-area-fills";
|
|
5
7
|
const polygons = [
|
|
6
8
|
"==",
|
|
7
9
|
["get", "kind"],
|
|
@@ -38,7 +40,7 @@ const PositionAreas = ({ points, selected, colors }) => {
|
|
|
38
40
|
data,
|
|
39
41
|
children: [
|
|
40
42
|
/* @__PURE__ */ jsx(Layer, {
|
|
41
|
-
id:
|
|
43
|
+
id: areaLayerId,
|
|
42
44
|
type: "fill",
|
|
43
45
|
filter: polygons,
|
|
44
46
|
paint: {
|
|
@@ -75,4 +77,4 @@ const PositionAreas = ({ points, selected, colors }) => {
|
|
|
75
77
|
});
|
|
76
78
|
};
|
|
77
79
|
//#endregion
|
|
78
|
-
export { PositionAreas };
|
|
80
|
+
export { PositionAreas, areaLayerId };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MapLayerSwitch } from "./map-layer-switch.js";
|
|
1
2
|
import { mapLib } from "./maplibre.js";
|
|
2
3
|
import { MapPin } from "lucide-react";
|
|
3
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -5,9 +6,9 @@ import { Map as Map$1, Marker, NavigationControl } from "react-map-gl/maplibre";
|
|
|
5
6
|
//#region src/app/map/record-map.tsx
|
|
6
7
|
/**
|
|
7
8
|
* A map of `style` centred on `position` at `zoom`, with a pin on it. It pans and zooms with two fingers on a phone and
|
|
8
|
-
* with Ctrl or Cmd and the wheel, so scrolling the page past it does not move it.
|
|
9
|
+
* with Ctrl or Cmd and the wheel, so scrolling the page past it does not move it. With `layers`, it switches to them.
|
|
9
10
|
*/
|
|
10
|
-
const RecordMap = ({ style, position, zoom }) => /* @__PURE__ */ jsxs(Map$1, {
|
|
11
|
+
const RecordMap = ({ style, layers, position, zoom }) => /* @__PURE__ */ jsxs(Map$1, {
|
|
11
12
|
mapLib,
|
|
12
13
|
mapStyle: style,
|
|
13
14
|
initialViewState: {
|
|
@@ -19,16 +20,20 @@ const RecordMap = ({ style, position, zoom }) => /* @__PURE__ */ jsxs(Map$1, {
|
|
|
19
20
|
width: "100%",
|
|
20
21
|
height: "100%"
|
|
21
22
|
},
|
|
22
|
-
children: [
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
children: [
|
|
24
|
+
/* @__PURE__ */ jsx(NavigationControl, { showCompass: false }),
|
|
25
|
+
/* @__PURE__ */ jsx(MapLayerSwitch, { layers }),
|
|
26
|
+
/* @__PURE__ */ jsx(Marker, {
|
|
27
|
+
latitude: position.latitude,
|
|
28
|
+
longitude: position.longitude,
|
|
29
|
+
anchor: "bottom",
|
|
30
|
+
children: /* @__PURE__ */ jsx(MapPin, {
|
|
31
|
+
"aria-hidden": true,
|
|
32
|
+
className: "size-8 fill-primary text-primary-foreground drop-shadow",
|
|
33
|
+
strokeWidth: 1.5
|
|
34
|
+
})
|
|
30
35
|
})
|
|
31
|
-
|
|
36
|
+
]
|
|
32
37
|
});
|
|
33
38
|
//#endregion
|
|
34
39
|
export { RecordMap };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { cn } from "../../lib/cn.js";
|
|
2
2
|
import { groupPoints } from "../displays/map-groups.js";
|
|
3
|
+
import { MapLayerSwitch } from "./map-layer-switch.js";
|
|
3
4
|
import { mapLib } from "./maplibre.js";
|
|
4
|
-
import { PositionAreas } from "./position-areas.js";
|
|
5
|
+
import { PositionAreas, areaLayerId } from "./position-areas.js";
|
|
5
6
|
import { themeColor } from "./theme-color.js";
|
|
6
7
|
import { useEffect, useRef, useState } from "react";
|
|
7
8
|
import { MapPin } from "lucide-react";
|
|
@@ -32,7 +33,7 @@ const corners = (bounds) => [[bounds.west, bounds.south], [bounds.east, bounds.n
|
|
|
32
33
|
* marker selects its records; choosing the map elsewhere clears the selection. A chosen marker the covered bottom would
|
|
33
34
|
* hide moves up into the open part of the map.
|
|
34
35
|
*/
|
|
35
|
-
const RecordsMap = ({ style, initial, points, buckets, fit, onFitted, onMove, selected, covered, onSelect, onClear, markerLabel, bucketLabel }) => {
|
|
36
|
+
const RecordsMap = ({ style, layers, initial, points, buckets, fit, onFitted, onMove, selected, covered, onSelect, onClear, markerLabel, bucketLabel }) => {
|
|
36
37
|
const ref = useRef(null);
|
|
37
38
|
const [groups, setGroups] = useState([]);
|
|
38
39
|
const [colors, setColors] = useState();
|
|
@@ -117,6 +118,10 @@ const RecordsMap = ({ style, initial, points, buckets, fit, onFitted, onMove, se
|
|
|
117
118
|
onClick: onClear,
|
|
118
119
|
children: [
|
|
119
120
|
/* @__PURE__ */ jsx(NavigationControl, { showCompass: false }),
|
|
121
|
+
/* @__PURE__ */ jsx(MapLayerSwitch, {
|
|
122
|
+
layers,
|
|
123
|
+
below: areaLayerId
|
|
124
|
+
}),
|
|
120
125
|
colors && /* @__PURE__ */ jsx(PositionAreas, {
|
|
121
126
|
points,
|
|
122
127
|
selected,
|
package/dist/data/meta-data.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PluginRef, ResourcePermissions, SourceRef } from "@protobase/client";
|
|
1
|
+
import { MapLayerModel, PluginRef, ResourcePermissions, SourceRef } from "@protobase/client";
|
|
2
2
|
import { BottomBarModel, ResourceModel, SignInViewModel, UserMenuModel, ViewModel } from "@protobase/schema";
|
|
3
3
|
import { PageModel } from "@protobase/layout";
|
|
4
4
|
//#region src/data/meta-data.d.ts
|
|
@@ -27,9 +27,10 @@ export type MetaData = {
|
|
|
27
27
|
dsn: string;
|
|
28
28
|
environment?: string;
|
|
29
29
|
};
|
|
30
|
-
/** The map style the
|
|
30
|
+
/** The map style the app's maps are drawn with, and the layers they may switch to, when the app has one (see `MapSection`). */
|
|
31
31
|
maps?: {
|
|
32
32
|
style: string;
|
|
33
|
+
layers?: MapLayerModel[];
|
|
33
34
|
};
|
|
34
35
|
/** The hosts besides the app's own whose images are drawn with the page's referrer (see `FileImage`). */
|
|
35
36
|
files?: {
|
package/dist/package.js
CHANGED