@gramps-connect/story 0.1.2 → 0.2.0
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/README.md +49 -2
- package/dist/editor.js +3539 -0
- package/dist/editor.js.map +7 -0
- package/dist/embed.js +43 -43
- package/dist/player.js +74 -13
- package/dist/player.js.map +2 -2
- package/dist/types/editor/DateField.d.ts +12 -0
- package/dist/types/editor/SlideList.d.ts +12 -0
- package/dist/types/editor/StoryEditor.d.ts +28 -0
- package/dist/types/editor/StoryTextField.d.ts +9 -0
- package/dist/types/editor/dates.d.ts +6 -0
- package/dist/types/editor/index.d.ts +7 -0
- package/dist/types/editor/model.d.ts +55 -0
- package/dist/types/editor/strings.d.ts +40 -0
- package/dist/types/player/MapBackground.d.ts +4 -0
- package/dist/types/player/StoryPlayer.d.ts +4 -1
- package/dist/types/player/geo.d.ts +8 -0
- package/package.json +21 -5
- package/src/editor/DateField.tsx +95 -0
- package/src/editor/SlideList.tsx +54 -0
- package/src/editor/StoryEditor.tsx +333 -0
- package/src/editor/StoryTextField.tsx +61 -0
- package/src/editor/dates.ts +21 -0
- package/src/editor/index.ts +10 -0
- package/src/editor/model.ts +167 -0
- package/src/editor/strings.ts +82 -0
- package/src/player/MapBackground.tsx +49 -18
- package/src/player/StoryPlayer.tsx +22 -3
- package/src/player/geo.ts +22 -0
package/dist/player.js
CHANGED
|
@@ -83,6 +83,19 @@ function placeCenter(place) {
|
|
|
83
83
|
const bounds = placeBounds(place);
|
|
84
84
|
return bounds ? [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2] : null;
|
|
85
85
|
}
|
|
86
|
+
function storyPlaces(places) {
|
|
87
|
+
const bounds = [Infinity, Infinity, -Infinity, -Infinity];
|
|
88
|
+
const points = /* @__PURE__ */ new Map();
|
|
89
|
+
for (const place of places) {
|
|
90
|
+
const center = placeCenter(place);
|
|
91
|
+
if (!center) continue;
|
|
92
|
+
points.set(`${center[0].toFixed(5)},${center[1].toFixed(5)}`, center);
|
|
93
|
+
visit(center, bounds);
|
|
94
|
+
const box = placeBounds(place);
|
|
95
|
+
if (box) visit([[box[0], box[1]], [box[2], box[3]]], bounds);
|
|
96
|
+
}
|
|
97
|
+
return points.size ? { points: [...points.values()], bounds } : null;
|
|
98
|
+
}
|
|
86
99
|
|
|
87
100
|
// src/player/MapBackground.tsx
|
|
88
101
|
import { useEffect, useRef, useState } from "react";
|
|
@@ -149,8 +162,17 @@ var LINE_LAYER = "story-shape-line";
|
|
|
149
162
|
var POINT_LAYER = "story-shape-points";
|
|
150
163
|
var LABEL_LAYER = "story-shape-labels";
|
|
151
164
|
var OVERLAY_PREFIX = "story-overlay-";
|
|
165
|
+
var PLACES_SOURCE = "story-places";
|
|
166
|
+
var PLACES_LAYER = "story-places-dots";
|
|
167
|
+
var OVERVIEW_PADDING = 60;
|
|
152
168
|
var EMPTY = { type: "FeatureCollection", features: [] };
|
|
153
|
-
function moveCamera(map,
|
|
169
|
+
function moveCamera(map, slide, duration) {
|
|
170
|
+
if (slide.overview) {
|
|
171
|
+
const [west, south, east, north] = slide.overview.bounds;
|
|
172
|
+
map.fitBounds([[west, south], [east, north]], { maxZoom: 9, padding: OVERVIEW_PADDING, duration });
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const place = slide.place;
|
|
154
176
|
const bounds = placeBounds(place);
|
|
155
177
|
if (bounds) {
|
|
156
178
|
map.fitBounds([[bounds[0], bounds[1]], [bounds[2], bounds[3]]], { maxZoom: 17, duration });
|
|
@@ -159,11 +181,20 @@ function moveCamera(map, place, duration) {
|
|
|
159
181
|
const center = placeCenter(place);
|
|
160
182
|
if (center) map.flyTo({ center, zoom: 9, essential: true, duration });
|
|
161
183
|
}
|
|
162
|
-
function placeMarker(marker,
|
|
184
|
+
function placeMarker(marker, slide) {
|
|
163
185
|
if (!marker) return;
|
|
186
|
+
const center = placeCenter(slide.place);
|
|
164
187
|
if (center) marker.setLngLat(center);
|
|
165
|
-
marker.
|
|
188
|
+
marker.setOpacity(slide.overview ? 0 : center ? 1 : 0.4);
|
|
166
189
|
}
|
|
190
|
+
var placeDots = (overview) => ({
|
|
191
|
+
type: "FeatureCollection",
|
|
192
|
+
features: (overview?.points ?? []).map((coordinates) => ({
|
|
193
|
+
type: "Feature",
|
|
194
|
+
properties: {},
|
|
195
|
+
geometry: { type: "Point", coordinates }
|
|
196
|
+
}))
|
|
197
|
+
});
|
|
167
198
|
function MapBackground({ initialCenter, current, dark, colors, panelFraction, ohmYear, resolveMedia }) {
|
|
168
199
|
const [containerEl, setContainerEl] = useState(null);
|
|
169
200
|
const mapRef = useRef(null);
|
|
@@ -194,10 +225,9 @@ function MapBackground({ initialCenter, current, dark, colors, panelFraction, oh
|
|
|
194
225
|
mapRef.current = map;
|
|
195
226
|
map.on("style.load", () => setReady(true));
|
|
196
227
|
map.setPadding({ top: 0, bottom: 0, left: 0, right: containerEl.clientWidth * panelFraction });
|
|
197
|
-
const start = currentRef.current
|
|
198
|
-
|
|
199
|
-
map
|
|
200
|
-
if (startCenter) moveCamera(map, start, 0);
|
|
228
|
+
const start = currentRef.current;
|
|
229
|
+
map.jumpTo({ center: placeCenter(start.place) ?? initialCenter });
|
|
230
|
+
moveCamera(map, start, 0);
|
|
201
231
|
map.on("load", () => {
|
|
202
232
|
const c = colorsRef.current;
|
|
203
233
|
const el = document.createElement("div");
|
|
@@ -206,7 +236,7 @@ function MapBackground({ initialCenter, current, dark, colors, panelFraction, oh
|
|
|
206
236
|
el.style.transition = "opacity 300ms";
|
|
207
237
|
el.innerHTML = `<svg viewBox="0 0 24 24" width="26" height="26"><path d="${PIN_PATH_D}" fill="${c.mark}" stroke="${c.surface}" stroke-width="1.5" /></svg>`;
|
|
208
238
|
markerRef.current = new maplibregl.Marker({ element: el, anchor: "bottom" }).setLngLat(initialCenter).addTo(map);
|
|
209
|
-
placeMarker(markerRef.current,
|
|
239
|
+
placeMarker(markerRef.current, currentRef.current);
|
|
210
240
|
});
|
|
211
241
|
return () => {
|
|
212
242
|
markerRef.current?.remove();
|
|
@@ -273,6 +303,13 @@ function MapBackground({ initialCenter, current, dark, colors, panelFraction, oh
|
|
|
273
303
|
"text-halo-width": 1.5
|
|
274
304
|
}
|
|
275
305
|
});
|
|
306
|
+
map.addSource(PLACES_SOURCE, { type: "geojson", data: EMPTY });
|
|
307
|
+
map.addLayer({
|
|
308
|
+
id: PLACES_LAYER,
|
|
309
|
+
type: "circle",
|
|
310
|
+
source: PLACES_SOURCE,
|
|
311
|
+
paint: { "circle-radius": 6, "circle-color": c.mark, "circle-stroke-width": 2, "circle-stroke-color": c.surface }
|
|
312
|
+
});
|
|
276
313
|
}, [ready]);
|
|
277
314
|
const place = current.place;
|
|
278
315
|
useEffect(() => {
|
|
@@ -280,13 +317,14 @@ function MapBackground({ initialCenter, current, dark, colors, panelFraction, oh
|
|
|
280
317
|
if (!map || !ready) return;
|
|
281
318
|
const source = map.getSource(SHAPE_SOURCE);
|
|
282
319
|
source?.setData(place?.geometry ?? EMPTY);
|
|
283
|
-
|
|
320
|
+
const dots = map.getSource(PLACES_SOURCE);
|
|
321
|
+
dots?.setData(placeDots(current.overview));
|
|
322
|
+
}, [ready, current.key, place, current.overview]);
|
|
284
323
|
useEffect(() => {
|
|
285
324
|
const map = mapRef.current;
|
|
286
325
|
if (!map) return;
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
if (center) moveCamera(map, place, 900);
|
|
326
|
+
placeMarker(markerRef.current, current);
|
|
327
|
+
moveCamera(map, current, 900);
|
|
290
328
|
}, [current.key]);
|
|
291
329
|
const overlayKey = JSON.stringify(place?.overlays ?? []);
|
|
292
330
|
useEffect(() => {
|
|
@@ -719,6 +757,7 @@ function StoryPlayer({
|
|
|
719
757
|
onSlideChange,
|
|
720
758
|
renderMapModeControl,
|
|
721
759
|
brand,
|
|
760
|
+
actions,
|
|
722
761
|
initialSlide = 0
|
|
723
762
|
}) {
|
|
724
763
|
const colors = useMemo(() => ({ ...defaultColors(dark), ...colorOverrides }), [dark, colorOverrides]);
|
|
@@ -802,7 +841,13 @@ function StoryPlayer({
|
|
|
802
841
|
else goPrev();
|
|
803
842
|
}
|
|
804
843
|
};
|
|
805
|
-
const
|
|
844
|
+
const places = useMemo(() => storyPlaces(slides.map((s) => s.place)), [slides]);
|
|
845
|
+
const mapSlide = useMemo(() => ({
|
|
846
|
+
key: slide?.id ?? String(index),
|
|
847
|
+
place: slide?.place,
|
|
848
|
+
// The opening card, with no place of its own, shows where the story goes.
|
|
849
|
+
overview: index === 0 && !placeCenter(slide?.place) ? places : null
|
|
850
|
+
}), [slide, index, places]);
|
|
806
851
|
const firstCenter = useMemo(() => {
|
|
807
852
|
if (story.presentation?.basemap === "none") return null;
|
|
808
853
|
for (const s of slides) {
|
|
@@ -894,6 +939,22 @@ function StoryPlayer({
|
|
|
894
939
|
children: /* @__PURE__ */ jsx4("div", { style: { pointerEvents: "auto" }, children: renderMapModeControl ? renderMapModeControl(modeControlProps) : /* @__PURE__ */ jsx4(MapModeControl, { ...modeControlProps, strings, colors }) })
|
|
895
940
|
}
|
|
896
941
|
),
|
|
942
|
+
actions && /* @__PURE__ */ jsx4(
|
|
943
|
+
"div",
|
|
944
|
+
{
|
|
945
|
+
style: {
|
|
946
|
+
position: "absolute",
|
|
947
|
+
top: 0,
|
|
948
|
+
right: onClose ? 60 : 12,
|
|
949
|
+
height: BAR_HEIGHT,
|
|
950
|
+
zIndex: 3,
|
|
951
|
+
display: "flex",
|
|
952
|
+
alignItems: "center",
|
|
953
|
+
gap: 8
|
|
954
|
+
},
|
|
955
|
+
children: actions
|
|
956
|
+
}
|
|
957
|
+
),
|
|
897
958
|
onClose && /* @__PURE__ */ jsx4(
|
|
898
959
|
RoundButton,
|
|
899
960
|
{
|