@projektemacher/storymapjs 0.10.8 → 0.11.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/CHANGELOG +81 -0
- package/README.md +129 -12
- package/dist/assets/{demo-DBpeF31K.js → demo-bWnOyfUw.js} +1 -1
- package/dist/assets/harness-C3JUv5Ii.js +1 -0
- package/dist/assets/{harness-multi-CYHgI0a9.js → harness-multi-XZNex3tb.js} +1 -1
- package/dist/assets/{harness-support-Do7sIp2j.js → harness-support-CjfZIVeb.js} +1 -1
- package/dist/assets/main-DQ32hWBM.js +17 -0
- package/dist/context.json +13 -2
- package/dist/css/storymap.css +1 -1
- package/dist/demo.html +3 -2
- package/dist/docs/api/assets/navigation.js +1 -1
- package/dist/docs/api/assets/search.js +1 -1
- package/dist/docs/api/classes/StoryMap.html +777 -269
- package/dist/docs/api/functions/MediaType.html +1 -1
- package/dist/docs/api/functions/isPresentation3Collection.html +1 -1
- package/dist/docs/api/functions/isPresentation3Manifest.html +1 -1
- package/dist/docs/api/functions/isSlideshowCollection.html +51 -0
- package/dist/docs/api/functions/loadCSS.html +1 -1
- package/dist/docs/api/functions/manifestToStorymapData.html +1 -1
- package/dist/docs/api/functions/setLanguage.html +1 -1
- package/dist/docs/api/functions/slideshowToStorymapData.html +51 -0
- package/dist/docs/api/functions/slideshowVersion.html +48 -0
- package/dist/docs/api/functions/storymapToManifest.html +1 -1
- package/dist/docs/api/functions/validateStorymap.html +1 -1
- package/dist/docs/api/functions/validateStorymapAndReport.html +1 -1
- package/dist/docs/api/index.html +115 -12
- package/dist/docs/api/interfaces/AnimationHandle.html +2 -2
- package/dist/docs/api/interfaces/ImagereadyPayload.html +4 -4
- package/dist/docs/api/interfaces/LanguageEntry.html +6 -6
- package/dist/docs/api/interfaces/LatLngLiteral.html +4 -4
- package/dist/docs/api/interfaces/LoadOptions.html +2 -2
- package/dist/docs/api/interfaces/MapEvents.html +116 -0
- package/dist/docs/api/interfaces/MapMarkerEvents.html +66 -0
- package/dist/docs/api/interfaces/MarkerEventPayload.html +57 -0
- package/dist/docs/api/interfaces/MediaEvents.html +78 -0
- package/dist/docs/api/interfaces/MenuBarEvents.html +114 -0
- package/dist/docs/api/interfaces/MessageEvents.html +72 -0
- package/dist/docs/api/interfaces/OlLayer.html +1 -1
- package/dist/docs/api/interfaces/OlMap.html +1 -1
- package/dist/docs/api/interfaces/OlSource.html +1 -1
- package/dist/docs/api/interfaces/OlTileLayer.html +1 -1
- package/dist/docs/api/interfaces/OlVectorLayer.html +1 -1
- package/dist/docs/api/interfaces/OlView.html +1 -1
- package/dist/docs/api/interfaces/SlideBackgroundState.html +66 -0
- package/dist/docs/api/interfaces/SlideEvents.html +78 -0
- package/dist/docs/api/interfaces/SlideNavEvents.html +72 -0
- package/dist/docs/api/interfaces/SlideshowPlayerSettings.html +182 -0
- package/dist/docs/api/interfaces/SlideshowToStorymapResult.html +60 -0
- package/dist/docs/api/interfaces/SlideshowTranslatorOptions.html +65 -0
- package/dist/docs/api/interfaces/SlideshowWarning.html +72 -0
- package/dist/docs/api/interfaces/StoryMapEvents.html +146 -0
- package/dist/docs/api/interfaces/StoryMapMarker.html +763 -0
- package/dist/docs/api/interfaces/StorySliderEvents.html +102 -0
- package/dist/docs/api/interfaces/StorymapBasemap.html +69 -0
- package/dist/docs/api/interfaces/StorymapData.html +12 -6
- package/dist/docs/api/interfaces/StorymapDataWrapper.html +2 -2
- package/dist/docs/api/interfaces/StorymapDocument.html +2 -2
- package/dist/docs/api/interfaces/StorymapError.html +3 -3
- package/dist/docs/api/interfaces/StorymapGeoreference.html +5 -5
- package/dist/docs/api/interfaces/StorymapGeoreferenceBody.html +4 -4
- package/dist/docs/api/interfaces/StorymapGroundControlPoint.html +4 -4
- package/dist/docs/api/interfaces/StorymapLanguageMap.html +2 -2
- package/dist/docs/api/interfaces/StorymapManifest.html +12 -12
- package/dist/docs/api/interfaces/StorymapManifestAnnotation.html +43 -11
- package/dist/docs/api/interfaces/StorymapManifestBackground.html +6 -6
- package/dist/docs/api/interfaces/StorymapManifestCanvas.html +65 -17
- package/dist/docs/api/interfaces/StorymapManifestContentResource.html +8 -8
- package/dist/docs/api/interfaces/StorymapManifestFeatureCollection.html +4 -4
- package/dist/docs/api/interfaces/StorymapManifestGeoreferencing.html +7 -7
- package/dist/docs/api/interfaces/StorymapManifestImageService.html +4 -4
- package/dist/docs/api/interfaces/StorymapManifestRange.html +5 -5
- package/dist/docs/api/interfaces/StorymapManifestService.html +4 -4
- package/dist/docs/api/interfaces/StorymapManifestSpecificResource.html +4 -4
- package/dist/docs/api/interfaces/StorymapManifestStatement.html +3 -3
- package/dist/docs/api/interfaces/StorymapMapOptions.html +5 -5
- package/dist/docs/api/interfaces/StorymapOptions.html +277 -75
- package/dist/docs/api/interfaces/StorymapOverlayLayer.html +42 -11
- package/dist/docs/api/interfaces/StorymapSlide.html +70 -16
- package/dist/docs/api/interfaces/StorymapSlideBackground.html +4 -4
- package/dist/docs/api/interfaces/StorymapSlideLocation.html +58 -14
- package/dist/docs/api/interfaces/StorymapSlideMarker.html +8 -9
- package/dist/docs/api/interfaces/StorymapSlideMedia.html +49 -10
- package/dist/docs/api/interfaces/StorymapSlideNarration.html +61 -9
- package/dist/docs/api/interfaces/StorymapSlideText.html +4 -4
- package/dist/docs/api/interfaces/StorymapTilejson.html +8 -8
- package/dist/docs/api/interfaces/SwipableEvents.html +102 -0
- package/dist/docs/api/interfaces/TextEvents.html +66 -0
- package/dist/docs/api/media/events.md +66 -0
- package/dist/docs/api/media/migration-from-knightlab.md +89 -49
- package/dist/docs/api/media/slideshow.md +109 -0
- package/dist/docs/api/media/storymap-as-iiif-manifest.md +1007 -0
- package/dist/docs/api/modules.html +6 -6
- package/dist/docs/api/types/ContentState.html +3 -3
- package/dist/docs/api/types/FiredEvent.html +43 -0
- package/dist/docs/api/types/ImagereadyKind.html +1 -1
- package/dist/docs/api/types/MediaTypeMatch.html +5 -5
- package/dist/docs/api/types/SwipeDirection.html +36 -0
- package/dist/docs/api/types/TileSourceFactory.html +1 -1
- package/dist/docs/authoring.html +20 -7
- package/dist/docs/events.html +263 -0
- package/dist/docs/iiif.html +136 -22
- package/dist/docs/migration.html +66 -19
- package/dist/docs/readme.html +113 -12
- package/dist/embed/index.html +7 -0
- package/dist/examples/font-css-false.json +33 -0
- package/dist/examples/issue-image-region.json +1 -1
- package/dist/examples/layers-control.json +60 -0
- package/dist/examples/map-template-provider.json +36 -0
- package/dist/examples/multi-media.json +71 -0
- package/dist/examples-iiif/annotated-image.json +16 -0
- package/dist/examples-iiif/font-css-false.json +114 -0
- package/dist/examples-iiif/issue-image-region.json +1 -1
- package/dist/examples-iiif/layers-control.json +226 -0
- package/dist/examples-iiif/map-template-provider.json +133 -0
- package/dist/examples-iiif/multi-media.json +237 -0
- package/dist/examples-slideshow/single.json +79 -0
- package/dist/examples-slideshow/static/pixel.png +0 -0
- package/dist/examples-slideshow/static.json +44 -0
- package/dist/harness-multi.html +3 -3
- package/dist/harness.html +4 -3
- package/dist/index.html +3 -2
- package/dist/js/storymap.d.ts +1326 -289
- package/dist/js/storymap.js +6690 -4784
- package/dist/js/storymap.js.map +1 -1
- package/dist/navplace-properties.json +14 -0
- package/dist/site.css +54 -36
- package/docs/migration-from-knightlab.md +89 -49
- package/docs/storymap-as-iiif-manifest.md +199 -126
- package/package.json +4 -2
- package/schema/storymap.schema.json +334 -34
- package/src/core/Util.ts +3 -1
- package/src/core/mixins.ts +42 -28
- package/src/language/Language.ts +7 -0
- package/src/language/locale/.expected-gaps.json +287 -27
- package/src/language/locale/de.json +11 -1
- package/src/language/locale/en.json +11 -1
- package/src/main.ts +51 -0
- package/src/map/Map.ts +44 -6
- package/src/map/MapMarker.ts +16 -3
- package/src/map/openlayers/Map.OpenLayers.ts +787 -62
- package/src/map/openlayers/MapMarker.OpenLayers.ts +113 -13
- package/src/map/slideView.ts +149 -0
- package/src/map/types.ts +2 -0
- package/src/media/EmbedUtil.ts +27 -2
- package/src/media/Media.ts +27 -4
- package/src/media/embedId.ts +6 -0
- package/src/media/types/Flickr.ts +14 -3
- package/src/media/types/HtmlMedia.ts +66 -0
- package/src/media/types/Image.ts +15 -3
- package/src/media/types/SoundCloud.ts +1 -1
- package/src/media/types/Text.ts +27 -8
- package/src/media/types/Twitter.ts +9 -7
- package/src/media/types/Vimeo.ts +1 -1
- package/src/media/types/YouTube.ts +2 -2
- package/src/scss/Typography.scss +15 -15
- package/src/scss/VCO.StoryMap.scss +173 -8
- package/src/scss/core/Reset.scss +5 -18
- package/src/scss/map/VCO.Map.scss +1 -1
- package/src/scss/map/VCO.MapMarker.scss +20 -9
- package/src/scss/map/openlayers/VCO.Map.OpenLayers.scss +7 -4
- package/src/scss/media/VCO.Media.scss +7 -7
- package/src/scss/media/types/VCO.Media.Blockquote.scss +1 -1
- package/src/scss/media/types/VCO.Media.Text.scss +5 -5
- package/src/scss/media/types/VCO.Media.Twitter.scss +4 -4
- package/src/scss/media/types/VCO.Media.Wikipedia.scss +2 -19
- package/src/scss/site/site.scss +55 -36
- package/src/scss/slider/VCO.Slide.scss +49 -11
- package/src/scss/slider/VCO.SlideNav.scss +23 -7
- package/src/scss/slider/VCO.StorySlider.scss +50 -0
- package/src/scss/snapmap/VCO.SnapMap.scss +3 -3
- package/src/scss/ui/VCO.Consent.scss +20 -14
- package/src/scss/ui/VCO.Layers.scss +119 -0
- package/src/scss/ui/VCO.MenuBar.Button.scss +140 -19
- package/src/scss/ui/VCO.MenuBar.scss +9 -2
- package/src/scss/ui/VCO.Message.scss +14 -0
- package/src/slider/Slide.ts +232 -74
- package/src/slider/SlideNav.ts +15 -4
- package/src/slider/StorySlider.ts +203 -22
- package/src/storymap/Consent.ts +22 -0
- package/src/storymap/StoryMap.ts +944 -131
- package/src/storymap/content-state.ts +3 -8
- package/src/storymap/font-css.ts +72 -0
- package/src/storymap/from-slideshow.ts +785 -0
- package/src/storymap/iiif-shared.ts +228 -0
- package/src/storymap/iiif.ts +289 -100
- package/src/storymap/to-iiif.ts +131 -44
- package/src/types.ts +382 -17
- package/src/ui/LayersControl.ts +302 -0
- package/src/ui/MenuBar.ts +232 -21
- package/src/ui/Message.ts +36 -3
- package/src/ui/Swipable.ts +25 -7
- package/dist/assets/harness-Cr0rYpvt.js +0 -1
- package/dist/assets/main-DXalctuR.js +0 -17
- package/src/scss/VCO.StoryMap.Dark.scss +0 -150
- package/src/scss/Variables.Dark.scss +0 -57
package/src/media/types/Image.ts
CHANGED
|
@@ -33,6 +33,9 @@ export function iiifSizedUrl(url: string | null, width: number): string | null {
|
|
|
33
33
|
================================================== */
|
|
34
34
|
|
|
35
35
|
export default class Image extends Media {
|
|
36
|
+
/** The `load` handler on the image, stored so dispose() can detach it. */
|
|
37
|
+
declare "_onLoad": (() => void) | null;
|
|
38
|
+
|
|
36
39
|
/* Load the media
|
|
37
40
|
================================================== */
|
|
38
41
|
_loadMedia() {
|
|
@@ -61,10 +64,12 @@ export default class Image extends Media {
|
|
|
61
64
|
);
|
|
62
65
|
}
|
|
63
66
|
|
|
64
|
-
// Media Loaded Event
|
|
65
|
-
|
|
67
|
+
// Media Loaded Event. The reference is kept so dispose() can detach
|
|
68
|
+
// it: an inline arrow function has no other handle.
|
|
69
|
+
this._onLoad = () => {
|
|
66
70
|
this.onMediaLoaded();
|
|
67
|
-
}
|
|
71
|
+
};
|
|
72
|
+
this._el.content_item.addEventListener("load", this._onLoad);
|
|
68
73
|
|
|
69
74
|
const img = this._el.content_item as HTMLImageElement;
|
|
70
75
|
// offscreen/preloaded slides stay lazy (the active slide is
|
|
@@ -112,4 +117,11 @@ export default class Image extends Media {
|
|
|
112
117
|
_updateMediaDisplay(layout?: string) {
|
|
113
118
|
// modern browsers size media correctly without engine-specific fixes
|
|
114
119
|
}
|
|
120
|
+
|
|
121
|
+
_disposeMedia(): void {
|
|
122
|
+
if (this._onLoad) {
|
|
123
|
+
this._el.content_item?.removeEventListener("load", this._onLoad);
|
|
124
|
+
this._onLoad = null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
115
127
|
}
|
package/src/media/types/Text.ts
CHANGED
|
@@ -14,13 +14,21 @@ interface TextData {
|
|
|
14
14
|
interface TextOptions {
|
|
15
15
|
title?: boolean;
|
|
16
16
|
text_align?: string;
|
|
17
|
+
/** BCP 47 tag for the slide text (from `StorymapSlide.language`). */
|
|
18
|
+
language?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface TextEvents {
|
|
22
|
+
loaded: TextData;
|
|
23
|
+
added: TextData;
|
|
24
|
+
removed: TextData;
|
|
17
25
|
}
|
|
18
26
|
|
|
19
27
|
class TextBase {
|
|
20
28
|
declare "_el": Record<string, HTMLElement>;
|
|
21
29
|
declare "data": TextData;
|
|
22
30
|
declare "options": TextOptions;
|
|
23
|
-
declare "fire": EventedInstance["fire"];
|
|
31
|
+
declare "fire": EventedInstance<TextEvents>["fire"];
|
|
24
32
|
|
|
25
33
|
/* Constructor
|
|
26
34
|
================================================== */
|
|
@@ -57,6 +65,15 @@ class TextBase {
|
|
|
57
65
|
|
|
58
66
|
this._el.container = Dom.create("div", "vco-text");
|
|
59
67
|
this._el.container.id = this.data.uniqueid ?? "";
|
|
68
|
+
// Pronounce mixed-language slides correctly: the reader reports
|
|
69
|
+
// which language map it picked, and the DOM should say so too.
|
|
70
|
+
if (
|
|
71
|
+
typeof this.options.language === "string" &&
|
|
72
|
+
this.options.language !== "" &&
|
|
73
|
+
this.options.language !== "none"
|
|
74
|
+
) {
|
|
75
|
+
this._el.container.setAttribute("lang", this.options.language);
|
|
76
|
+
}
|
|
60
77
|
|
|
61
78
|
this._initLayout();
|
|
62
79
|
|
|
@@ -124,12 +141,6 @@ class TextBase {
|
|
|
124
141
|
this._el.content_container.classList.add("vco-text-align-" + align);
|
|
125
142
|
}
|
|
126
143
|
|
|
127
|
-
// Date (only rendered when the slide has one; issue #286)
|
|
128
|
-
if (this.data.date && this.data.date.created_time && this.data.date.created_time !== "") {
|
|
129
|
-
this._el.date = Dom.create("h3", "vco-headline-date", this._el.content_container);
|
|
130
|
-
this.addDateText(convertUnixTime(this.data.date.created_time));
|
|
131
|
-
}
|
|
132
|
-
|
|
133
144
|
// Headline (sanitized; issue #358 keeps formatting but drops scripts)
|
|
134
145
|
const headline = this.data.headline ?? "";
|
|
135
146
|
if (headline !== "") {
|
|
@@ -141,6 +152,14 @@ class TextBase {
|
|
|
141
152
|
this._el.headline.appendChild(sanitizeSlideText(headline));
|
|
142
153
|
}
|
|
143
154
|
|
|
155
|
+
// Date (only rendered when the slide has one; issue #286). Emitted
|
|
156
|
+
// after the headline and as a <p> (issue #385 wave): an <h3> before
|
|
157
|
+
// the <h2> was a heading-order skip
|
|
158
|
+
if (this.data.date && this.data.date.created_time && this.data.date.created_time !== "") {
|
|
159
|
+
this._el.date = Dom.create("p", "vco-headline-date", this._el.content_container);
|
|
160
|
+
this.addDateText(convertUnixTime(this.data.date.created_time));
|
|
161
|
+
}
|
|
162
|
+
|
|
144
163
|
// Text (sanitized; issue #358 allows extra iframe media in the text field)
|
|
145
164
|
const text = this.data.text ?? "";
|
|
146
165
|
if (text !== "") {
|
|
@@ -171,7 +190,7 @@ class TextBase {
|
|
|
171
190
|
}
|
|
172
191
|
}
|
|
173
192
|
|
|
174
|
-
export default class Text extends Evented(TextBase) {
|
|
193
|
+
export default class Text extends Evented<TextEvents, typeof TextBase>(TextBase) {
|
|
175
194
|
constructor(...args: ConstructorParameters<typeof TextBase>) {
|
|
176
195
|
super(...args);
|
|
177
196
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Media } from "../Media";
|
|
2
2
|
import Dom from "../../dom/Dom";
|
|
3
3
|
import { sanitizeSlideText } from "../EmbedUtil";
|
|
4
|
+
import { tweetAuthor, tweetId } from "../embedId";
|
|
4
5
|
import { loadJSONP, uniqueGlobalName } from "../../core/Load";
|
|
5
6
|
|
|
6
7
|
/* Media.Twitter
|
|
@@ -20,13 +21,14 @@ export default class Twitter extends Media {
|
|
|
20
21
|
// Create Dom element
|
|
21
22
|
this._el.content_item = Dom.create("div", "vco-media-twitter", this._el.content);
|
|
22
23
|
|
|
23
|
-
// Get Media ID
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.
|
|
24
|
+
// Get Media ID — routed here by MediaType, extracted by the shared
|
|
25
|
+
// helpers so the accepted shapes cannot diverge again. The id stays
|
|
26
|
+
// numeric-only, as before: anything else is not a status URL.
|
|
27
|
+
const media_id = tweetId(this._url());
|
|
28
|
+
const user_id = tweetAuthor(this._url());
|
|
29
|
+
if (media_id && user_id && /^\d+$/.test(media_id)) {
|
|
30
|
+
this.user_id = user_id;
|
|
31
|
+
this.media_id = media_id;
|
|
30
32
|
} else {
|
|
31
33
|
throw new Error("Invalid Twitter URL");
|
|
32
34
|
}
|
package/src/media/types/Vimeo.ts
CHANGED
|
@@ -94,7 +94,7 @@ export default class YouTube extends Media {
|
|
|
94
94
|
this.player.pauseVideo();
|
|
95
95
|
}
|
|
96
96
|
} catch (err) {
|
|
97
|
-
console.
|
|
97
|
+
console.warn("StoryMapJS: error pausing YouTube player.", err);
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
}
|
|
@@ -186,7 +186,7 @@ export default class YouTube extends Media {
|
|
|
186
186
|
try {
|
|
187
187
|
this.player?.destroy?.();
|
|
188
188
|
} catch (err) {
|
|
189
|
-
console.
|
|
189
|
+
console.warn("StoryMapJS: error destroying YouTube player.", err);
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
this.player = null;
|
package/src/scss/Typography.scss
CHANGED
|
@@ -5,11 +5,6 @@
|
|
|
5
5
|
.vco-storymap {
|
|
6
6
|
font-family: $font-main;
|
|
7
7
|
|
|
8
|
-
// runtime-themable text colors (issue #177); set inline via the
|
|
9
|
-
// text_color/text_background_color options
|
|
10
|
-
--vco-color-text: #{$color-text};
|
|
11
|
-
--vco-color-text-muted: #{color.adjust($color-dark, $lightness: 40%)};
|
|
12
|
-
|
|
13
8
|
h1,
|
|
14
9
|
h2,
|
|
15
10
|
h3 {
|
|
@@ -56,15 +51,20 @@
|
|
|
56
51
|
font-size: $base-font-size;
|
|
57
52
|
|
|
58
53
|
// font-family: $font-secondary;
|
|
59
|
-
color: var(--vco-color-text-muted);
|
|
54
|
+
color: var(--vco-color-text-muted, #{color.adjust($color-dark, $lightness: 40%)});
|
|
60
55
|
|
|
61
56
|
a {
|
|
62
|
-
color: var(--vco-color-text-muted);
|
|
57
|
+
color: var(--vco-color-text-muted, #{color.adjust($color-dark, $lightness: 40%)});
|
|
63
58
|
text-decoration: none;
|
|
64
59
|
background-image: linear-gradient(
|
|
65
60
|
to bottom,
|
|
66
|
-
rgba(color.adjust($color-dark, $lightness: 40%), 0%)
|
|
67
|
-
|
|
61
|
+
rgba(var(--vco-color-text-muted, #{color.adjust($color-dark, $lightness: 40%)}), 0%)
|
|
62
|
+
50%,
|
|
63
|
+
rgba(
|
|
64
|
+
var(--vco-color-text-muted, #{color.adjust($color-dark, $lightness: 40%)}),
|
|
65
|
+
60%
|
|
66
|
+
)
|
|
67
|
+
50%
|
|
68
68
|
);
|
|
69
69
|
background-repeat: repeat-x;
|
|
70
70
|
background-size: 2px 2px;
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
|
|
78
78
|
&:hover,
|
|
79
79
|
&:focus {
|
|
80
|
-
color: $color-theme;
|
|
80
|
+
color: var(--vco-color-theme, #{$color-theme});
|
|
81
81
|
text-decoration: none;
|
|
82
82
|
}
|
|
83
83
|
}
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
|
|
91
91
|
&:hover,
|
|
92
92
|
&:focus {
|
|
93
|
-
color: $color-theme;
|
|
93
|
+
color: var(--vco-color-theme, #{$color-theme});
|
|
94
94
|
text-decoration: underline;
|
|
95
95
|
}
|
|
96
96
|
}
|
|
@@ -111,12 +111,12 @@
|
|
|
111
111
|
|
|
112
112
|
a {
|
|
113
113
|
text-decoration: none;
|
|
114
|
-
color: $color-theme;
|
|
114
|
+
color: var(--vco-color-theme, #{$color-theme});
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
a:hover {
|
|
118
118
|
text-decoration: underline;
|
|
119
|
-
color: color.adjust($color-theme, $lightness: -20%);
|
|
119
|
+
color: var(--vco-color-theme-deep, #{color.adjust($color-theme, $lightness: -20%)});
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
.vco-caption,
|
|
@@ -128,14 +128,14 @@
|
|
|
128
128
|
line-height: $base-font-size-small;
|
|
129
129
|
|
|
130
130
|
a {
|
|
131
|
-
color: $color-dark;
|
|
131
|
+
color: var(--vco-color-dark, #{$color-dark});
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
blockquote,
|
|
136
136
|
blockquote p {
|
|
137
137
|
font-family: $font-serif;
|
|
138
|
-
color:
|
|
138
|
+
color: var(--vco-color-text, #{$color-text});
|
|
139
139
|
text-align: left;
|
|
140
140
|
background: transparent;
|
|
141
141
|
border: 0;
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
/* Includes
|
|
13
13
|
================================================== */
|
|
14
|
+
@use "sass:color";
|
|
14
15
|
@use "Variables" as *; // Variables (easy way to make style changes)
|
|
15
16
|
@use "core/Mixins" as *; // Less shortcuts and functions
|
|
16
17
|
@use "core/Reset" as *; // CSS Reset
|
|
@@ -22,6 +23,7 @@
|
|
|
22
23
|
// Components
|
|
23
24
|
@use "ui/VCO.MenuBar" as *;
|
|
24
25
|
@use "ui/VCO.MenuBar.Button" as *;
|
|
26
|
+
@use "ui/VCO.Layers" as *;
|
|
25
27
|
@use "ui/VCO.Message" as *;
|
|
26
28
|
@use "ui/VCO.Consent" as *;
|
|
27
29
|
@use "slider/VCO.Slide" as *;
|
|
@@ -51,11 +53,60 @@
|
|
|
51
53
|
position: relative;
|
|
52
54
|
user-select: none;
|
|
53
55
|
|
|
56
|
+
// Theme palette (dark theme plan): every Variables.scss colour as a
|
|
57
|
+
// custom property, so a dark theme redeclares values instead of
|
|
58
|
+
// shipping a second stylesheet. Unused declarations cost nothing, and
|
|
59
|
+
// use sites keep their Sass value as the fallback, so each partial
|
|
60
|
+
// migrates independently. Includes the two runtime-themable text
|
|
61
|
+
// colors (issue #177), set inline via text_color/text_background_color.
|
|
62
|
+
--vco-color-background: #{$color-background};
|
|
63
|
+
--vco-color-foreground: #{$color-foreground};
|
|
64
|
+
--vco-color-dark: #{$color-dark};
|
|
65
|
+
--vco-color-theme: #{$color-theme};
|
|
66
|
+
--vco-color-theme-dark: #{$color-theme-dark};
|
|
67
|
+
--vco-color-text: #{$color-text};
|
|
68
|
+
--vco-color-text-muted: #{color.adjust($color-dark, $lightness: 40%)};
|
|
69
|
+
--vco-color-text-background: #{$color-background};
|
|
70
|
+
--vco-color-header-text: #{$color-header-text};
|
|
71
|
+
--vco-color-text-inverted: #{$color-text-inverted};
|
|
72
|
+
--vco-color-header-text-inverted: #{$color-header-text-inverted};
|
|
73
|
+
--vco-color-ui-background: #{$ui-background-color};
|
|
74
|
+
|
|
75
|
+
// Derived tones. Each is computed from the light palette here so the
|
|
76
|
+
// default render is unchanged; the dark block redefines them from its
|
|
77
|
+
// own palette. One property per distinct derivation, shared by every
|
|
78
|
+
// use site with the same arguments.
|
|
79
|
+
--vco-color-border: #{color.adjust($color-background, $lightness: -10%)};
|
|
80
|
+
--vco-color-hover: #{color.adjust($color-background, $lightness: -5%)};
|
|
81
|
+
--vco-color-nav-border: #{color.adjust($color-background, $lightness: -30%)};
|
|
82
|
+
--vco-color-theme-faint: #{color.adjust($color-theme, $lightness: 90%)};
|
|
83
|
+
--vco-color-theme-deep: #{color.adjust($color-theme, $lightness: -20%)};
|
|
84
|
+
--vco-color-theme-bright: #{color.adjust($color-theme, $lightness: 15%)};
|
|
85
|
+
--vco-color-link: #{color.adjust($color-dark, $lightness: 20%)};
|
|
86
|
+
--vco-color-ui-fade: #{fadeout($ui-background-color, 10%)};
|
|
87
|
+
--vco-color-ui-faint: #{fadeout($ui-background-color, 33%)};
|
|
88
|
+
|
|
89
|
+
/* Skip link: first tab stop inside the widget, off-screen until
|
|
90
|
+
focused, so keyboard visitors can jump past the chrome. */
|
|
91
|
+
.vco-skip-link {
|
|
92
|
+
position: absolute;
|
|
93
|
+
top: -999px;
|
|
94
|
+
left: 8px;
|
|
95
|
+
z-index: 100;
|
|
96
|
+
padding: 8px 12px;
|
|
97
|
+
background-color: var(--vco-color-background, #{$color-background});
|
|
98
|
+
color: var(--vco-color-text, #{$color-text});
|
|
99
|
+
|
|
100
|
+
&:focus-visible {
|
|
101
|
+
top: 8px;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
54
105
|
/* fullscreen: fill the screen and sit above everything else */
|
|
55
106
|
&:fullscreen {
|
|
56
107
|
width: 100%;
|
|
57
108
|
height: 100%;
|
|
58
|
-
background-color: $color-background;
|
|
109
|
+
background-color: var(--vco-color-background, #{$color-background});
|
|
59
110
|
}
|
|
60
111
|
|
|
61
112
|
*,
|
|
@@ -65,6 +116,52 @@
|
|
|
65
116
|
}
|
|
66
117
|
}
|
|
67
118
|
|
|
119
|
+
/* Dark theme (dark theme plan): the same property names redeclared from
|
|
120
|
+
the dark palette. Values below were carried over from
|
|
121
|
+
`src/scss/Variables.Dark.scss` (now deleted), except `$color-theme`
|
|
122
|
+
(`#c34528` is 3.18:1 on `#222`) and `$color-text` (`#808080` is 4.03:1),
|
|
123
|
+
which are lightened to `#dd735a` (5.06:1) and `#949494` (5.24:1) so
|
|
124
|
+
every text pair clears AA. Hover tints go brighter in dark mode rather
|
|
125
|
+
than darker, so they stay visible on dark surfaces.
|
|
126
|
+
Selected with `data-vco-theme="dark"`, or (when unset) the OS
|
|
127
|
+
`prefers-color-scheme`; explicit `"light"` pins the light palette. */
|
|
128
|
+
@mixin vco-dark-palette {
|
|
129
|
+
--vco-color-background: #222;
|
|
130
|
+
--vco-color-foreground: #ccc;
|
|
131
|
+
--vco-color-dark: #fff;
|
|
132
|
+
--vco-color-theme: #dd735a;
|
|
133
|
+
--vco-color-theme-dark: #c24628;
|
|
134
|
+
--vco-color-text: #949494;
|
|
135
|
+
--vco-color-text-muted: #999;
|
|
136
|
+
--vco-color-text-background: #222;
|
|
137
|
+
--vco-color-header-text: #fff;
|
|
138
|
+
--vco-color-text-inverted: #bfbfbf;
|
|
139
|
+
--vco-color-header-text-inverted: #fff;
|
|
140
|
+
--vco-color-ui-background: #4d4d4d;
|
|
141
|
+
--vco-color-border: #080808;
|
|
142
|
+
--vco-color-hover: #151515;
|
|
143
|
+
--vco-color-nav-border: #000;
|
|
144
|
+
--vco-color-theme-faint: #fff;
|
|
145
|
+
|
|
146
|
+
// the three link colours share #eaa999 on purpose: in dark mode links,
|
|
147
|
+
// hovers and deep accents are one tone (5.06:1 on #222)
|
|
148
|
+
--vco-color-theme-deep: #eaa999;
|
|
149
|
+
--vco-color-theme-bright: #eaa999;
|
|
150
|
+
--vco-color-link: #eaa999;
|
|
151
|
+
--vco-color-ui-fade: #{fadeout(#4d4d4d, 10%)};
|
|
152
|
+
--vco-color-ui-faint: #{fadeout(#4d4d4d, 33%)};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.vco-storymap[data-vco-theme="dark"] {
|
|
156
|
+
@include vco-dark-palette;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@media (prefers-color-scheme: dark) {
|
|
160
|
+
.vco-storymap:not([data-vco-theme="light"]) {
|
|
161
|
+
@include vco-dark-palette;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
68
165
|
/* Portrait
|
|
69
166
|
================================================== */
|
|
70
167
|
.vco-layout-portrait {
|
|
@@ -89,12 +186,14 @@
|
|
|
89
186
|
width: 120%;
|
|
90
187
|
overflow: hidden;
|
|
91
188
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
189
|
+
// map-to-panel fade in the panel color so it stays seamless in
|
|
190
|
+
// both themes (the old white gradient washed dark slides)
|
|
191
|
+
background-image: linear-gradient(
|
|
192
|
+
to right,
|
|
193
|
+
transparent 0%,
|
|
194
|
+
var(--vco-color-background, #fff) 15%
|
|
97
195
|
);
|
|
196
|
+
background-repeat: repeat-x;
|
|
98
197
|
}
|
|
99
198
|
|
|
100
199
|
.vco-slider-container-mask {
|
|
@@ -116,7 +215,7 @@
|
|
|
116
215
|
top: 0;
|
|
117
216
|
left: 0;
|
|
118
217
|
width: 100%;
|
|
119
|
-
background-color: $color-background;
|
|
218
|
+
background-color: var(--vco-color-background, #{$color-background});
|
|
120
219
|
|
|
121
220
|
.vco-slider-background {
|
|
122
221
|
display: none;
|
|
@@ -130,7 +229,7 @@
|
|
|
130
229
|
================================================== */
|
|
131
230
|
.vco-map-area-left {
|
|
132
231
|
.vco-storyslider {
|
|
133
|
-
background-color: $color-background;
|
|
232
|
+
background-color: var(--vco-color-background, #{$color-background});
|
|
134
233
|
}
|
|
135
234
|
|
|
136
235
|
.vco-slider-background {
|
|
@@ -193,3 +292,69 @@
|
|
|
193
292
|
white-space: nowrap;
|
|
194
293
|
border: 0;
|
|
195
294
|
}
|
|
295
|
+
|
|
296
|
+
/* Slide panel dock + size (textmode/textsize): right is the long-standing
|
|
297
|
+
landscape layout; left mirrors it; bottom docks the panel below the map
|
|
298
|
+
(the component split is computed in StoryMap._updateDisplay). Unset means
|
|
299
|
+
right at 50/50, so existing stories render unchanged.
|
|
300
|
+
================================================== */
|
|
301
|
+
.vco-layout-landscape {
|
|
302
|
+
.vco-storyslider {
|
|
303
|
+
width: var(--vco-panel-size, 50%);
|
|
304
|
+
left: calc(100% - var(--vco-panel-size, 50%));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
&.vco-textmode-left {
|
|
308
|
+
.vco-storyslider {
|
|
309
|
+
left: 0;
|
|
310
|
+
|
|
311
|
+
.vco-slider-background {
|
|
312
|
+
margin-left: 0;
|
|
313
|
+
width: 120%;
|
|
314
|
+
background-image: linear-gradient(
|
|
315
|
+
to left,
|
|
316
|
+
transparent 0%,
|
|
317
|
+
var(--vco-color-background, #fff) 15%
|
|
318
|
+
);
|
|
319
|
+
background-repeat: repeat-x;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
&.vco-textmode-bottom {
|
|
325
|
+
.vco-storyslider {
|
|
326
|
+
left: 0;
|
|
327
|
+
width: 100%;
|
|
328
|
+
|
|
329
|
+
.vco-slider-background {
|
|
330
|
+
margin-left: 0;
|
|
331
|
+
width: 100%;
|
|
332
|
+
background-image: linear-gradient(
|
|
333
|
+
to top,
|
|
334
|
+
transparent 0%,
|
|
335
|
+
var(--vco-color-background, #fff) 15%
|
|
336
|
+
);
|
|
337
|
+
background-repeat: repeat-x;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/* Headline visibility (show_headings: false hides slide headlines) */
|
|
344
|
+
.vco-no-headings {
|
|
345
|
+
.vco-headline {
|
|
346
|
+
display: none;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/* Scrollbar visibility (show_scrollbars: false hides the panel scrollbars;
|
|
351
|
+
content still scrolls) */
|
|
352
|
+
.vco-no-scrollbars {
|
|
353
|
+
.vco-slide {
|
|
354
|
+
scrollbar-width: none;
|
|
355
|
+
|
|
356
|
+
&::-webkit-scrollbar {
|
|
357
|
+
display: none;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
package/src/scss/core/Reset.scss
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
/* Reset
|
|
2
2
|
------------------------------------------------------------------------------------------- */
|
|
3
|
+
@use "../Variables" as *;
|
|
3
4
|
|
|
4
|
-
.vco-
|
|
5
|
+
.vco-storymap {
|
|
5
6
|
/* Reset tags and common classes
|
|
6
7
|
Display in IE6-9 and FF3
|
|
7
8
|
Based on: http://github.com/necolas/normalize.css
|
|
8
9
|
------------------------------------------------------------------------------------------- */
|
|
9
10
|
|
|
10
|
-
div * {
|
|
11
|
-
box-sizing: content-box;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
11
|
h1,
|
|
15
12
|
h2,
|
|
16
13
|
h3,
|
|
@@ -91,7 +88,6 @@
|
|
|
91
88
|
font-weight: normal;
|
|
92
89
|
font-style: normal;
|
|
93
90
|
font-size: 100%;
|
|
94
|
-
line-height: 1;
|
|
95
91
|
font-family: inherit;
|
|
96
92
|
width: auto;
|
|
97
93
|
float: none;
|
|
@@ -111,11 +107,6 @@
|
|
|
111
107
|
border-spacing: 0;
|
|
112
108
|
}
|
|
113
109
|
|
|
114
|
-
ol,
|
|
115
|
-
ul {
|
|
116
|
-
list-style: none;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
110
|
q::before,
|
|
120
111
|
q::after,
|
|
121
112
|
blockquote::before,
|
|
@@ -123,13 +114,9 @@
|
|
|
123
114
|
content: "";
|
|
124
115
|
}
|
|
125
116
|
|
|
126
|
-
a:focus {
|
|
127
|
-
outline:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
a:hover,
|
|
131
|
-
a:active {
|
|
132
|
-
outline: 0;
|
|
117
|
+
a:focus-visible {
|
|
118
|
+
outline: 2px solid var(--vco-color-dark, #{$color-dark});
|
|
119
|
+
outline-offset: 2px;
|
|
133
120
|
}
|
|
134
121
|
|
|
135
122
|
article,
|
|
@@ -46,7 +46,18 @@ $map-icon-width: 38px;
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
.vco-mapmarker-active {
|
|
49
|
-
color: $color-theme;
|
|
49
|
+
color: var(--vco-color-theme, #{$color-theme});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// pins are keyboard-focusable (tabindex + role=button): every marker
|
|
53
|
+
// shape gets the same visible ring
|
|
54
|
+
.vco-mapmarker:focus-visible,
|
|
55
|
+
.vco-mapmarker-active:focus-visible,
|
|
56
|
+
.vco-mapmarker-custom:focus-visible,
|
|
57
|
+
.vco-mapmarker-image-icon:focus-visible,
|
|
58
|
+
.vco-mapmarker-image-icon-active:focus-visible {
|
|
59
|
+
outline: 2px solid var(--vco-color-dark, #{$color-dark});
|
|
60
|
+
outline-offset: 2px;
|
|
50
61
|
}
|
|
51
62
|
|
|
52
63
|
// Custom icon markers render only their image (no vco-mapmarker class,
|
|
@@ -72,7 +83,7 @@ $map-icon-width: 38px;
|
|
|
72
83
|
}
|
|
73
84
|
|
|
74
85
|
.vco-mapmarker-image-icon-active {
|
|
75
|
-
border: 4px solid $color-theme;
|
|
86
|
+
border: 4px solid var(--vco-color-theme, #{$color-theme});
|
|
76
87
|
filter: grayscale(0);
|
|
77
88
|
}
|
|
78
89
|
|
|
@@ -89,9 +100,9 @@ $map-icon-width: 38px;
|
|
|
89
100
|
font-family: $font-sanserif;
|
|
90
101
|
font-size: 12px;
|
|
91
102
|
font-weight: bold;
|
|
92
|
-
color: $color-text;
|
|
93
|
-
background-color: $color-background;
|
|
94
|
-
border: 1px solid $color-theme;
|
|
103
|
+
color: var(--vco-color-text, #{$color-text});
|
|
104
|
+
background-color: var(--vco-color-background, #{$color-background});
|
|
105
|
+
border: 1px solid var(--vco-color-theme, #{$color-theme});
|
|
95
106
|
border-radius: 4px;
|
|
96
107
|
box-shadow: 2px 2px 2px rgb(0 0 0 / 30%);
|
|
97
108
|
pointer-events: none;
|
|
@@ -145,7 +156,7 @@ $map-icon-width: 38px;
|
|
|
145
156
|
}
|
|
146
157
|
}
|
|
147
158
|
|
|
148
|
-
/* Marker popup and audio badge
|
|
159
|
+
/* Marker popup and audio badge
|
|
149
160
|
================================================== */
|
|
150
161
|
// A card anchored to the marker element: it is a DOM child, so it travels
|
|
151
162
|
// with the pin and needs no positioning maths. 44px tap targets and a
|
|
@@ -163,7 +174,7 @@ $map-icon-width: 38px;
|
|
|
163
174
|
gap: 8px;
|
|
164
175
|
align-items: flex-start;
|
|
165
176
|
width: max-content;
|
|
166
|
-
max-width: 260px;
|
|
177
|
+
max-width: min(260px, calc(100vw - 32px));
|
|
167
178
|
padding: 10px 12px;
|
|
168
179
|
border: 1px solid rgb(0 0 0 / 20%);
|
|
169
180
|
border-radius: 4px;
|
|
@@ -178,8 +189,8 @@ $map-icon-width: 38px;
|
|
|
178
189
|
|
|
179
190
|
.vco-marker-popup-close {
|
|
180
191
|
flex: 0 0 auto;
|
|
181
|
-
min-width:
|
|
182
|
-
min-height:
|
|
192
|
+
min-width: 44px;
|
|
193
|
+
min-height: 44px;
|
|
183
194
|
margin: -4px -4px 0 0;
|
|
184
195
|
padding: 0;
|
|
185
196
|
border: 0;
|
|
@@ -33,13 +33,13 @@
|
|
|
33
33
|
position: absolute;
|
|
34
34
|
bottom: 0;
|
|
35
35
|
right: 0;
|
|
36
|
-
background-color: rgb(255 255 255 /
|
|
36
|
+
background-color: rgb(255 255 255 / 90%);
|
|
37
37
|
font-size: 10px;
|
|
38
38
|
padding: 1px 4px;
|
|
39
39
|
z-index: 1000;
|
|
40
40
|
|
|
41
41
|
a {
|
|
42
|
-
color: #
|
|
42
|
+
color: #333;
|
|
43
43
|
text-decoration: none;
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -64,14 +64,17 @@
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
span {
|
|
67
|
-
color: $color-theme;
|
|
67
|
+
color: var(--vco-color-theme, #{$color-theme});
|
|
68
68
|
display: inline-block;
|
|
69
69
|
transform: rotate(45deg);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
&:hover {
|
|
73
73
|
text-decoration: none;
|
|
74
|
-
color:
|
|
74
|
+
color: var(
|
|
75
|
+
--vco-color-theme-bright,
|
|
76
|
+
#{color.adjust($color-theme, $lightness: 15%)}
|
|
77
|
+
);
|
|
75
78
|
}
|
|
76
79
|
}
|
|
77
80
|
}
|