@story-map/react-story-map 0.1.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/LICENSE +21 -0
- package/dist/StoryMap.d.ts +9 -0
- package/dist/StoryMap.d.ts.map +1 -0
- package/dist/StoryMap.js +118 -0
- package/dist/StoryMap.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
- package/src/styles.css +136 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yu-Zhi Chen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { StoryMapConfig, StorySlide } from '@story-map/story-map-core';
|
|
2
|
+
export interface StoryMapProps {
|
|
3
|
+
story: StoryMapConfig;
|
|
4
|
+
initialSlide?: number;
|
|
5
|
+
className?: string;
|
|
6
|
+
onSlideChange?: (index: number, slide: StorySlide) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare function StoryMap({ story, initialSlide, className, onSlideChange }: StoryMapProps): import("react").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=StoryMap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StoryMap.d.ts","sourceRoot":"","sources":["../src/StoryMap.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5E,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,cAAc,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;CAC5D;AAED,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,YAAgB,EAAE,SAAS,EAAE,aAAa,EAAE,EAAE,aAAa,+BAyI5F"}
|
package/dist/StoryMap.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import ReactMarkdown from 'react-markdown';
|
|
4
|
+
import remarkGfm from 'remark-gfm';
|
|
5
|
+
export function StoryMap({ story, initialSlide = 0, className, onSlideChange }) {
|
|
6
|
+
const mapElementRef = useRef(null);
|
|
7
|
+
const mapRef = useRef(null);
|
|
8
|
+
const markerLayerRef = useRef(null);
|
|
9
|
+
const markersRef = useRef([]);
|
|
10
|
+
const pathRef = useRef(null);
|
|
11
|
+
const activeIndexRef = useRef(0);
|
|
12
|
+
const [rawActiveIndex, setActiveIndex] = useState(() => clamp(initialSlide, 0, story.slides.length - 1));
|
|
13
|
+
const activeIndex = clamp(rawActiveIndex, 0, story.slides.length - 1);
|
|
14
|
+
const activeSlide = story.slides[activeIndex];
|
|
15
|
+
activeIndexRef.current = activeIndex;
|
|
16
|
+
const locatedSlides = useMemo(() => story.slides.filter((slide) => slide.location), [story]);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
let cancelled = false;
|
|
19
|
+
async function mountMap() {
|
|
20
|
+
if (!mapElementRef.current)
|
|
21
|
+
return;
|
|
22
|
+
const L = await import('leaflet');
|
|
23
|
+
if (cancelled || !mapElementRef.current)
|
|
24
|
+
return;
|
|
25
|
+
const initial = story.slides[activeIndexRef.current]?.location;
|
|
26
|
+
const center = initial
|
|
27
|
+
? [initial.lat, initial.lng]
|
|
28
|
+
: story.map.center ?? [0, 0];
|
|
29
|
+
const zoom = initial?.zoom ?? story.map.zoom;
|
|
30
|
+
const zoomOptions = {
|
|
31
|
+
...(story.map.minZoom === undefined ? {} : { minZoom: story.map.minZoom }),
|
|
32
|
+
...(story.map.maxZoom === undefined ? {} : { maxZoom: story.map.maxZoom }),
|
|
33
|
+
};
|
|
34
|
+
const map = L.map(mapElementRef.current, zoomOptions).setView(center, zoom);
|
|
35
|
+
L.tileLayer(story.map.tileUrl, {
|
|
36
|
+
attribution: story.map.attribution,
|
|
37
|
+
...zoomOptions,
|
|
38
|
+
}).addTo(map);
|
|
39
|
+
const markerLayer = L.layerGroup().addTo(map);
|
|
40
|
+
const markers = locatedSlides.map((slide) => {
|
|
41
|
+
const location = slide.location;
|
|
42
|
+
return L.circleMarker([location.lat, location.lng], {
|
|
43
|
+
radius: 6,
|
|
44
|
+
weight: 2,
|
|
45
|
+
fillOpacity: 0.85,
|
|
46
|
+
}).addTo(markerLayer);
|
|
47
|
+
});
|
|
48
|
+
const path = story.map.showPath && locatedSlides.length >= 2
|
|
49
|
+
? L.polyline(locatedSlides.map((slide) => [slide.location.lat, slide.location.lng]), { weight: 3, opacity: 0.65 }).addTo(map)
|
|
50
|
+
: null;
|
|
51
|
+
mapRef.current = map;
|
|
52
|
+
markerLayerRef.current = markerLayer;
|
|
53
|
+
markersRef.current = markers;
|
|
54
|
+
pathRef.current = path;
|
|
55
|
+
updateMarkerStyles(story, activeIndexRef.current, markers);
|
|
56
|
+
}
|
|
57
|
+
void mountMap();
|
|
58
|
+
return () => {
|
|
59
|
+
cancelled = true;
|
|
60
|
+
pathRef.current = null;
|
|
61
|
+
markersRef.current = [];
|
|
62
|
+
markerLayerRef.current = null;
|
|
63
|
+
mapRef.current?.remove();
|
|
64
|
+
mapRef.current = null;
|
|
65
|
+
};
|
|
66
|
+
}, [story, locatedSlides]);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
const location = activeSlide.location;
|
|
69
|
+
if (location && mapRef.current) {
|
|
70
|
+
mapRef.current.flyTo([location.lat, location.lng], location.zoom ?? story.map.zoom, { duration: 1.1 });
|
|
71
|
+
}
|
|
72
|
+
updateMarkerStyles(story, activeIndex, markersRef.current);
|
|
73
|
+
onSlideChange?.(activeIndex, activeSlide);
|
|
74
|
+
}, [activeIndex, activeSlide, onSlideChange, story]);
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
setActiveIndex((current) => clamp(current, 0, story.slides.length - 1));
|
|
77
|
+
}, [story.slides.length]);
|
|
78
|
+
function goTo(next) {
|
|
79
|
+
setActiveIndex(clamp(next, 0, story.slides.length - 1));
|
|
80
|
+
}
|
|
81
|
+
return (_jsxs("section", { className: ['story-map', className].filter(Boolean).join(' '), style: { height: story.height }, tabIndex: 0, "aria-label": story.title ?? 'Story map', onKeyDown: (event) => {
|
|
82
|
+
if (event.key === 'ArrowLeft')
|
|
83
|
+
goTo(activeIndex - 1);
|
|
84
|
+
if (event.key === 'ArrowRight')
|
|
85
|
+
goTo(activeIndex + 1);
|
|
86
|
+
}, children: [_jsx("div", { className: "story-map__map", ref: mapElementRef }), _jsxs("article", { className: "story-map__panel", children: [story.title && _jsx("div", { className: "story-map__story-title", children: story.title }), activeSlide.title && _jsx("h2", { children: activeSlide.title }), _jsx(StoryMediaView, { slide: activeSlide }), activeSlide.text && (_jsx("div", { className: "story-map__text", children: _jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], children: activeSlide.text }) })), _jsxs("nav", { className: "story-map__nav", "aria-label": "Story navigation", children: [_jsx("button", { type: "button", disabled: activeIndex === 0, onClick: () => goTo(activeIndex - 1), children: "Previous" }), _jsxs("span", { children: [activeIndex + 1, " / ", story.slides.length] }), _jsx("button", { type: "button", disabled: activeIndex === story.slides.length - 1, onClick: () => goTo(activeIndex + 1), children: "Next" })] })] })] }));
|
|
87
|
+
}
|
|
88
|
+
function StoryMediaView({ slide }) {
|
|
89
|
+
const media = slide.media;
|
|
90
|
+
if (!media)
|
|
91
|
+
return null;
|
|
92
|
+
if (media.type === 'video') {
|
|
93
|
+
return _jsx("video", { className: "story-map__media", src: media.src, controls: true });
|
|
94
|
+
}
|
|
95
|
+
if (media.type === 'iframe') {
|
|
96
|
+
return (_jsx("iframe", { className: "story-map__media story-map__iframe", src: media.src, title: media.alt ?? slide.title ?? 'Story media', loading: "lazy" }));
|
|
97
|
+
}
|
|
98
|
+
return (_jsxs("figure", { className: "story-map__figure", children: [_jsx("img", { className: "story-map__media", src: media.src, alt: media.alt ?? '', loading: "lazy" }), media.caption && _jsx("figcaption", { children: media.caption })] }));
|
|
99
|
+
}
|
|
100
|
+
function updateMarkerStyles(story, activeIndex, markers) {
|
|
101
|
+
let markerIndex = 0;
|
|
102
|
+
story.slides.forEach((slide, slideIndex) => {
|
|
103
|
+
if (!slide.location)
|
|
104
|
+
return;
|
|
105
|
+
const marker = markers[markerIndex++];
|
|
106
|
+
if (!marker)
|
|
107
|
+
return;
|
|
108
|
+
marker.setRadius(slideIndex === activeIndex ? 8 : 5);
|
|
109
|
+
marker.setStyle({
|
|
110
|
+
weight: slideIndex === activeIndex ? 3 : 2,
|
|
111
|
+
fillOpacity: slideIndex === activeIndex ? 1 : 0.7,
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function clamp(value, min, max) {
|
|
116
|
+
return Math.max(min, Math.min(max, value));
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=StoryMap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StoryMap.js","sourceRoot":"","sources":["../src/StoryMap.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EACV,cAAc,EACd,eAAe,EACf,UAAU,EACV,aAAa,EACb,UAAU,GACX,MAAM,2BAA2B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@story-map/react-story-map",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "React and Leaflet renderer for the StoryMap v1 config format.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/kywk/story-map-monorepo.git",
|
|
9
|
+
"directory": "packages/react-story-map"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.js",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./styles.css": "./src/styles.css"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"src/styles.css"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@story-map/story-map-core": "0.1.0",
|
|
31
|
+
"leaflet": "^1.9.4",
|
|
32
|
+
"react-markdown": "^10.1.0",
|
|
33
|
+
"remark-gfm": "^4.0.1"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"react": "^19.0.0",
|
|
37
|
+
"react-dom": "^19.0.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/leaflet": "1.9.22",
|
|
41
|
+
"react": "19.3.0",
|
|
42
|
+
"react-dom": "19.3.0"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsc -b",
|
|
46
|
+
"typecheck": "tsc -b",
|
|
47
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo"
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/styles.css
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
.story-map {
|
|
2
|
+
--story-map-bg: var(--background-primary, #ffffff);
|
|
3
|
+
--story-map-fg: var(--text-normal, #1f2937);
|
|
4
|
+
--story-map-muted: var(--text-muted, #6b7280);
|
|
5
|
+
--story-map-border: var(--background-modifier-border, rgba(0, 0, 0, 0.14));
|
|
6
|
+
--story-map-accent: var(--interactive-accent, #2563eb);
|
|
7
|
+
|
|
8
|
+
position: relative;
|
|
9
|
+
width: 100%;
|
|
10
|
+
min-height: 360px;
|
|
11
|
+
overflow: hidden;
|
|
12
|
+
border: 1px solid var(--story-map-border);
|
|
13
|
+
border-radius: 12px;
|
|
14
|
+
background: var(--story-map-bg);
|
|
15
|
+
color: var(--story-map-fg);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.story-map:focus-visible {
|
|
19
|
+
outline: 2px solid var(--story-map-accent);
|
|
20
|
+
outline-offset: 2px;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.story-map__map {
|
|
24
|
+
position: absolute;
|
|
25
|
+
inset: 0;
|
|
26
|
+
z-index: 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.story-map__panel {
|
|
30
|
+
position: absolute;
|
|
31
|
+
z-index: 500;
|
|
32
|
+
left: 16px;
|
|
33
|
+
bottom: 16px;
|
|
34
|
+
width: min(380px, calc(100% - 32px));
|
|
35
|
+
max-height: calc(100% - 32px);
|
|
36
|
+
overflow: auto;
|
|
37
|
+
box-sizing: border-box;
|
|
38
|
+
padding: 16px;
|
|
39
|
+
border: 1px solid var(--story-map-border);
|
|
40
|
+
border-radius: 10px;
|
|
41
|
+
background: color-mix(in srgb, var(--story-map-bg) 94%, transparent);
|
|
42
|
+
backdrop-filter: blur(6px);
|
|
43
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.story-map__panel h2 {
|
|
47
|
+
margin: 0 0 10px;
|
|
48
|
+
font-size: 1.2rem;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.story-map__story-title {
|
|
52
|
+
margin-bottom: 6px;
|
|
53
|
+
color: var(--story-map-muted);
|
|
54
|
+
font-size: 0.82rem;
|
|
55
|
+
font-weight: 600;
|
|
56
|
+
text-transform: uppercase;
|
|
57
|
+
letter-spacing: 0.04em;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.story-map__text > :first-child {
|
|
61
|
+
margin-top: 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.story-map__text > :last-child {
|
|
65
|
+
margin-bottom: 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.story-map__figure {
|
|
69
|
+
margin: 0 0 12px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.story-map__media {
|
|
73
|
+
display: block;
|
|
74
|
+
width: 100%;
|
|
75
|
+
max-height: 210px;
|
|
76
|
+
object-fit: cover;
|
|
77
|
+
border-radius: 7px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.story-map__iframe {
|
|
81
|
+
min-height: 200px;
|
|
82
|
+
border: 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.story-map__figure figcaption {
|
|
86
|
+
margin-top: 4px;
|
|
87
|
+
color: var(--story-map-muted);
|
|
88
|
+
font-size: 0.8rem;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.story-map__nav {
|
|
92
|
+
display: grid;
|
|
93
|
+
grid-template-columns: 1fr auto 1fr;
|
|
94
|
+
gap: 8px;
|
|
95
|
+
align-items: center;
|
|
96
|
+
margin-top: 14px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.story-map__nav button {
|
|
100
|
+
border: 1px solid var(--story-map-border);
|
|
101
|
+
border-radius: 6px;
|
|
102
|
+
padding: 7px 10px;
|
|
103
|
+
background: var(--story-map-bg);
|
|
104
|
+
color: var(--story-map-fg);
|
|
105
|
+
cursor: pointer;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.story-map__nav button:last-child {
|
|
109
|
+
justify-self: end;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.story-map__nav button:disabled {
|
|
113
|
+
opacity: 0.45;
|
|
114
|
+
cursor: default;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.story-map__nav span {
|
|
118
|
+
color: var(--story-map-muted);
|
|
119
|
+
font-size: 0.85rem;
|
|
120
|
+
white-space: nowrap;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@media (max-width: 640px) {
|
|
124
|
+
.story-map__panel {
|
|
125
|
+
left: 8px;
|
|
126
|
+
right: 8px;
|
|
127
|
+
bottom: 8px;
|
|
128
|
+
width: auto;
|
|
129
|
+
max-height: 48%;
|
|
130
|
+
padding: 12px;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.story-map__media {
|
|
134
|
+
max-height: 140px;
|
|
135
|
+
}
|
|
136
|
+
}
|